diff --git a/core/modules/map.rs b/core/modules/map.rs index 3c58d579b..5b81b092b 100644 --- a/core/modules/map.rs +++ b/core/modules/map.rs @@ -1971,7 +1971,7 @@ impl ModuleMap { .unwrap() .clone(); match state.phase { - ModuleImportPhase::Defer | ModuleImportPhase::Evaluation => { + ModuleImportPhase::Evaluation => { // The top-level module from a dynamic import has been instantiated. // Load is done. let module_id = @@ -1987,6 +1987,34 @@ impl ModuleMap { state, )?; } + ModuleImportPhase::Defer => { + // For defer phase imports, the module is instantiated but NOT evaluated. + // V8 will handle creating the deferred namespace object that triggers + // evaluation on first property access. + // Requires V8 flag --js-defer-import-eval (enabled in runtime/setup.rs). + let module_id = + load.root_module_id.expect("Root module should be loaded"); + let result = self.instantiate_module(scope, module_id); + if let Err(exception) = result { + self.dynamic_import_reject(scope, dyn_import_id, exception); + continue; + } + // Resolve with the module namespace without evaluating. + // V8's deferred namespace will trigger evaluation on property access. + let module_handle = + self.get_handle(module_id).expect("ModuleInfo not found"); + let module = module_handle.open(scope); + let module_namespace = module.get_module_namespace(); + let resolver_handle = self + .dynamic_import_map + .borrow_mut() + .remove(&dyn_import_id) + .expect("Invalid dynamic import id") + .resolver; + let resolver = resolver_handle.open(scope); + resolver.resolve(scope, module_namespace).unwrap(); + scope.perform_microtask_checkpoint(); + } ModuleImportPhase::Source => { let module_reference = load.root_module_reference.as_ref().expect("Root module reference had to have been resolved to get here."); let key = ModuleSourceKey::from_reference(module_reference); diff --git a/core/runtime/setup.rs b/core/runtime/setup.rs index 6aba446ff..7ebd465ec 100644 --- a/core/runtime/setup.rs +++ b/core/runtime/setup.rs @@ -26,7 +26,8 @@ fn v8_init( " --harmony-temporal", " --js-float16array", " --js-explicit-resource-management", - " --js-source-phase-imports" + " --js-source-phase-imports", + " --js-defer-import-eval" ); let snapshot_flags = "--predictable --random-seed=42"; let expose_natives_flags = "--expose_gc --allow_natives_syntax"; diff --git a/testing/integration/import_defer/deferred.js b/testing/integration/import_defer/deferred.js new file mode 100644 index 000000000..969e057bc --- /dev/null +++ b/testing/integration/import_defer/deferred.js @@ -0,0 +1,6 @@ +// Copyright 2018-2025 the Deno authors. MIT license. +console.log("deferred module evaluated"); +export const value = 42; +export function add(a, b) { + return a + b; +} diff --git a/testing/integration/import_defer/import_defer.js b/testing/integration/import_defer/import_defer.js new file mode 100644 index 000000000..70b979a32 --- /dev/null +++ b/testing/integration/import_defer/import_defer.js @@ -0,0 +1,25 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +// Test for TC39 proposal: Deferred Module Evaluation +// https://github.com/tc39/proposal-defer-import-eval +// +// The `import defer` syntax allows loading a module without immediately +// executing it. The module is executed synchronously when any property +// on the namespace is first accessed. +// +console.log("before import defer"); + +// Static import defer syntax - module is loaded but not executed +import defer * as deferred from "./deferred.js"; + +console.log("after import defer, before access"); + +// First property access triggers module evaluation +console.log(`value: ${deferred.value}`); + +console.log("after first access"); + +// Subsequent accesses use the already-evaluated module +console.log(`add: ${deferred.add(1, 2)}`); + +console.log("done"); diff --git a/testing/integration/import_defer/import_defer.out b/testing/integration/import_defer/import_defer.out new file mode 100644 index 000000000..a422e0c8d --- /dev/null +++ b/testing/integration/import_defer/import_defer.out @@ -0,0 +1,7 @@ +before import defer +after import defer, before access +deferred module evaluated +value: 42 +after first access +add: 3 +done diff --git a/testing/lib.rs b/testing/lib.rs index 6e783dd38..ac7ed8235 100644 --- a/testing/lib.rs +++ b/testing/lib.rs @@ -60,6 +60,7 @@ integration_test!( dyn_import_op, dyn_import_no_hang, dyn_import_pending_tla, + import_defer, error_async_stack, error_callsite, error_non_existent_eval_source,