diff --git a/crates/rspack_core/src/context_module.rs b/crates/rspack_core/src/context_module.rs index b52f8c761e4..d8d0f56f633 100644 --- a/crates/rspack_core/src/context_module.rs +++ b/crates/rspack_core/src/context_module.rs @@ -22,14 +22,15 @@ use rustc_hash::FxHashMap as HashMap; use rustc_hash::FxHashSet as HashSet; use crate::{ - contextify, get_exports_type_with_strict, impl_module_meta_info, returning_function, - stringify_map, to_path, AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, BoxDependency, - BuildContext, BuildInfo, BuildMeta, BuildMetaDefaultObject, BuildMetaExportsType, BuildResult, - ChunkGraph, ChunkGroupOptions, CodeGenerationResult, Compilation, ConcatenationScope, - ContextElementDependency, DependenciesBlock, Dependency, DependencyCategory, DependencyId, - DynamicImportMode, ExportsType, FactoryMeta, FakeNamespaceObjectMode, GroupOptions, - LibIdentOptions, Module, ModuleType, Resolve, ResolveInnerOptions, - ResolveOptionsWithDependencyType, ResolverFactory, RuntimeGlobals, RuntimeSpec, SourceType, + block_promise, contextify, get_exports_type_with_strict, impl_module_meta_info, + returning_function, stringify_map, to_path, AsyncDependenciesBlock, + AsyncDependenciesBlockIdentifier, BoxDependency, BuildContext, BuildInfo, BuildMeta, + BuildMetaDefaultObject, BuildMetaExportsType, BuildResult, ChunkGraph, ChunkGroupOptions, + CodeGenerationResult, Compilation, ConcatenationScope, ContextElementDependency, + DependenciesBlock, Dependency, DependencyCategory, DependencyId, DynamicImportMode, ExportsType, + FactoryMeta, FakeNamespaceObjectMode, GroupOptions, LibIdentOptions, Module, ModuleType, Resolve, + ResolveInnerOptions, ResolveOptionsWithDependencyType, ResolverFactory, RuntimeGlobals, + RuntimeSpec, SourceType, }; #[derive(Debug, Clone)] @@ -277,7 +278,7 @@ impl ContextModule { fn get_fake_map_init_statement(&self, fake_map: &FakeMapValue) -> String { match fake_map { FakeMapValue::Bit(_) => "".to_string(), - FakeMapValue::Map(map) => json_stringify(map), + FakeMapValue::Map(map) => format!("var fakeMap = {}", json_stringify(map)), } } @@ -377,7 +378,11 @@ impl ContextModule { } #[inline] - fn get_source_string(&self, compilation: &Compilation) -> BoxSource { + fn get_source_string( + &self, + compilation: &Compilation, + code_gen_result: &mut CodeGenerationResult, + ) -> BoxSource { match self.options.context_options.mode { ContextMode::Lazy => { if !self.get_blocks().is_empty() { @@ -387,13 +392,11 @@ impl ContextModule { } } ContextMode::LazyOnce => { - let module_graph = compilation.get_module_graph(); - let block = self - .get_blocks() - .first() - .expect("LazyOnce ContextModule should have first block"); - let block = module_graph.block_by_id(block).expect("should have block"); - self.generate_source(block.get_dependencies(), compilation) + if let Some(block) = self.get_blocks().first() { + self.get_lazy_once_source(compilation, block, code_gen_result) + } else { + self.get_source_for_empty_async_context(compilation) + } } ContextMode::Sync => { if !self.get_dependencies().is_empty() { @@ -560,6 +563,67 @@ impl ContextModule { source.boxed() } + fn get_lazy_once_source( + &self, + compilation: &Compilation, + block_id: &AsyncDependenciesBlockIdentifier, + code_gen_result: &mut CodeGenerationResult, + ) -> BoxSource { + let mg = compilation.get_module_graph(); + let block = mg.block_by_id_expect(block_id); + let dependencies = block.get_dependencies(); + let promise = block_promise( + Some(block_id), + &mut code_gen_result.runtime_requirements, + compilation, + ); + let map = self.get_user_request_map(dependencies, compilation); + let fake_map = self.get_fake_map(dependencies, compilation); + let then_function = if !matches!( + fake_map, + FakeMapValue::Bit(FakeNamespaceObjectMode::NAMESPACE) + ) { + formatdoc! {r#" + function(id) {{ + {} + }} + "#, + self.get_return_module_object_source(&fake_map, true, "fakeMap[id]"), + } + } else { + RuntimeGlobals::REQUIRE.name().to_string() + }; + let source = formatdoc! {r#" + var map = {map}; + {fake_map_init_statement} + + function webpackAsyncContext(req) {{ + return webpackAsyncContextResolve(req).then({then_function}); + }} + function webpackAsyncContextResolve(req) {{ + return {promise}.then(function() {{ + if(!{has_own_property}(map, req)) {{ + var e = new Error("Cannot find module '" + req + "'"); + e.code = 'MODULE_NOT_FOUND'; + throw e; + }} + return map[req]; + }}) + }} + webpackAsyncContext.keys = {keys}; + webpackAsyncContext.resolve = webpackAsyncContextResolve; + webpackAsyncContext.id = {id}; + module.exports = webpackAsyncContext; + "#, + map = json_stringify(&map), + fake_map_init_statement = self.get_fake_map_init_statement(&fake_map), + has_own_property = RuntimeGlobals::HAS_OWN_PROPERTY, + keys = returning_function("Object.keys(map)", ""), + id = json_stringify(self.id(&compilation.chunk_graph)) + }; + RawSource::from(source).boxed() + } + fn get_sync_source(&self, compilation: &Compilation) -> BoxSource { let dependencies = self.get_dependencies(); let map = self.get_user_request_map(dependencies, compilation); @@ -799,7 +863,7 @@ impl Module for ContextModule { _: Option, ) -> Result { let mut code_generation_result = CodeGenerationResult::default(); - let source = self.get_source_string(compilation); + let source = self.get_source_string(compilation, &mut code_generation_result); code_generation_result.add(SourceType::JavaScript, source); let mut all_deps = self.get_dependencies().to_vec(); let module_graph = compilation.get_module_graph(); diff --git a/crates/rspack_core/src/module_graph/mod.rs b/crates/rspack_core/src/module_graph/mod.rs index 1c7d1b952bf..8588d0276d3 100644 --- a/crates/rspack_core/src/module_graph/mod.rs +++ b/crates/rspack_core/src/module_graph/mod.rs @@ -646,6 +646,17 @@ impl<'a> ModuleGraph<'a> { self.loop_partials(|p| p.blocks.get(block_id))?.as_ref() } + pub fn block_by_id_expect( + &self, + block_id: &AsyncDependenciesBlockIdentifier, + ) -> &AsyncDependenciesBlock { + self + .loop_partials(|p| p.blocks.get(block_id)) + .expect("should insert block before get it") + .as_ref() + .expect("block has been removed to None") + } + pub fn dependencies(&self) -> HashMap { let mut res = HashMap::default(); for item in self.partials.iter() { diff --git a/crates/rspack_core/src/options/module.rs b/crates/rspack_core/src/options/module.rs index 505af7424f8..e4a811e4f2c 100644 --- a/crates/rspack_core/src/options/module.rs +++ b/crates/rspack_core/src/options/module.rs @@ -74,7 +74,7 @@ impl From<&str> for DynamicImportMode { "weak" => DynamicImportMode::Weak, "eager" => DynamicImportMode::Eager, "lazy" => DynamicImportMode::Lazy, - "lazyOnce" => DynamicImportMode::LazyOnce, + "lazy-once" => DynamicImportMode::LazyOnce, _ => { // TODO: warning DynamicImportMode::default() diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/index.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/index.js index 4c3ef9ae4b9..5d431fce6da 100644 --- a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/index.js +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/index.js @@ -1,2 +1,3 @@ import "./dynamic-import"; import "./cjs-require"; +import "./namespace-object-lazy" diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/cjs-esmodule.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/cjs-esmodule.js new file mode 100644 index 00000000000..fc200c1c13a --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/cjs-esmodule.js @@ -0,0 +1,3 @@ +exports.__esModule = true; +exports.default = "default"; +exports.named = "named"; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/cjs.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/cjs.js new file mode 100644 index 00000000000..c338450af88 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/cjs.js @@ -0,0 +1,2 @@ +exports.default = "default"; +exports.named = "named"; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-cjs/null.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-cjs/null.js new file mode 100644 index 00000000000..b894a23a24d --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-cjs/null.js @@ -0,0 +1 @@ +module.exports = null; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-cjs/one.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-cjs/one.js new file mode 100644 index 00000000000..46685d2544b --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-cjs/one.js @@ -0,0 +1,2 @@ +exports.named = "named"; +exports.default = "default"; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-cjs/three.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-cjs/three.js new file mode 100644 index 00000000000..46685d2544b --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-cjs/three.js @@ -0,0 +1,2 @@ +exports.named = "named"; +exports.default = "default"; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-cjs/two.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-cjs/two.js new file mode 100644 index 00000000000..50613550899 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-cjs/two.js @@ -0,0 +1,4 @@ +exports.__esModule = true; +exports.named = "named"; +exports.default = "default"; + diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-harmony/one.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-harmony/one.js new file mode 100644 index 00000000000..95dac8cca28 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-harmony/one.js @@ -0,0 +1,2 @@ +export default "default"; +export var named = "named"; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-harmony/three.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-harmony/three.js new file mode 100644 index 00000000000..95dac8cca28 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-harmony/three.js @@ -0,0 +1,2 @@ +export default "default"; +export var named = "named"; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-harmony/two.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-harmony/two.js new file mode 100644 index 00000000000..95dac8cca28 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-harmony/two.js @@ -0,0 +1,2 @@ +export default "default"; +export var named = "named"; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-mixed/json.json b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-mixed/json.json new file mode 100644 index 00000000000..9c8ca3f82ff --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-mixed/json.json @@ -0,0 +1,4 @@ +{ + "default": "default", + "named": "named" +} \ No newline at end of file diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-mixed/null.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-mixed/null.js new file mode 100644 index 00000000000..b894a23a24d --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-mixed/null.js @@ -0,0 +1 @@ +module.exports = null; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-mixed/one.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-mixed/one.js new file mode 100644 index 00000000000..46685d2544b --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-mixed/one.js @@ -0,0 +1,2 @@ +exports.named = "named"; +exports.default = "default"; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-mixed/three.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-mixed/three.js new file mode 100644 index 00000000000..95dac8cca28 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-mixed/three.js @@ -0,0 +1,2 @@ +export default "default"; +export var named = "named"; diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-mixed/two.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-mixed/two.js new file mode 100644 index 00000000000..50613550899 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/dir-mixed/two.js @@ -0,0 +1,4 @@ +exports.__esModule = true; +exports.named = "named"; +exports.default = "default"; + diff --git a/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/index.js b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/index.js new file mode 100644 index 00000000000..82f82f54368 --- /dev/null +++ b/packages/rspack-test-tools/tests/runtimeDiffCases/module-context-module/src/namespace-object-lazy/index.js @@ -0,0 +1,88 @@ +// non-mjs-namespace-object-lazy + +it("should receive a namespace object when importing commonjs", function(done) { + import("./cjs").then(function(result) { + expect(result).toEqual(nsObj({ named: "named", default: { named: "named", default: "default" } })); + done(); + }).catch(done); +}); + +it("should receive a namespace object when importing commonjs with __esModule", function(done) { + import("./cjs-esmodule").then(function(result) { + expect(result).toEqual({ __esModule: true, named: "named", default: "default" }); + done(); + }).catch(done); +}); + +function contextCJS(name) { + return Promise.all([ + import(`./dir-cjs/${name}`), + import(/* webpackMode: "lazy-once", webpackChunkName: "dir-cjs-1" */`./dir-cjs?1/${name}`), + // import(/* webpackMode: "eager" */`./dir-cjs?2/${name}`) + ]).then(function(results) { + // return import(/* webpackMode: "weak" */`./dir-cjs/${name}`).then(function(r) { + // results.push(r); + // return results; + // }); + }); +} + +function contextHarmony(name) { + return Promise.all([ + import(`./dir-harmony/${name}`), + import(/* webpackMode: "lazy-once", webpackChunkName: "dir-harmony-1" */`./dir-harmony?1/${name}`), + // import(/* webpackMode: "eager" */`./dir-harmony?2/${name}`) + ]).then(function(results) { + // return import(/* webpackMode: "weak" */`./dir-harmony/${name}`).then(function(r) { + // results.push(r); + // return results; + // }); + }); +} + +function contextMixed(name) { + return Promise.all([ + import(`./dir-mixed/${name}`), + import(/* webpackMode: "lazy-once", webpackChunkName: "dir-mixed-1" */`./dir-mixed?1/${name}`), + // import(/* webpackMode: "eager" */`./dir-mixed?2/${name}`) + ]).then(function(results) { + // return import(/* webpackMode: "weak" */`./dir-mixed/${name}`).then(function(r) { + // results.push(r); + // return results; + // }); + }); +} + +function promiseTest(promise, equalsTo) { + return promise.then(function(results) { + for(const result of results) + expect(result).toEqual(equalsTo); + }); +} + +it("should receive a namespace object when importing commonjs via context", function() { + return Promise.all([ + promiseTest(contextCJS("one"), nsObj({ named: "named", default: { named: "named", default: "default" } })), + promiseTest(contextCJS("two"), { __esModule: true, named: "named", default: "default" }), + promiseTest(contextCJS("three"), nsObj({ named: "named", default: { named: "named", default: "default" } })), + promiseTest(contextCJS("null"), nsObj({ default: null })) + ]); +}); + +it("should receive a namespace object when importing harmony via context", function() { + return Promise.all([ + promiseTest(contextHarmony("one"), nsObj({ named: "named", default: "default" })), + promiseTest(contextHarmony("two"), nsObj({ named: "named", default: "default" })), + promiseTest(contextHarmony("three"), nsObj({ named: "named", default: "default" })) + ]); +}); + +it("should receive a namespace object when importing mixed content via context", function() { + return Promise.all([ + promiseTest(contextMixed("one"), nsObj({ named: "named", default: { named: "named", default: "default" } })), + promiseTest(contextMixed("two"), { __esModule: true, named: "named", default: "default" }), + promiseTest(contextMixed("three"), nsObj({ named: "named", default: "default" })), + promiseTest(contextMixed("null"), nsObj({ default: null })), + promiseTest(contextMixed("json.json"), nsObj({ named: "named", default: { named: "named", default: "default" } })) + ]); +}); diff --git a/webpack-test/cases/runtime/issue-15518/test.filter.js b/webpack-test/cases/runtime/issue-15518/test.filter.js deleted file mode 100644 index 48ce6deba3d..00000000000 --- a/webpack-test/cases/runtime/issue-15518/test.filter.js +++ /dev/null @@ -1,4 +0,0 @@ - -module.exports = () => {return "https://github.com/web-infra-dev/rspack/issues/4306"} - - \ No newline at end of file