diff --git a/crates/rspack_core/src/compilation/build_module_graph/module_executor/mod.rs b/crates/rspack_core/src/compilation/build_module_graph/module_executor/mod.rs index 3295e29ab7d4..b089def62ae6 100644 --- a/crates/rspack_core/src/compilation/build_module_graph/module_executor/mod.rs +++ b/crates/rspack_core/src/compilation/build_module_graph/module_executor/mod.rs @@ -63,6 +63,10 @@ impl Default for ModuleExecutor { } impl ModuleExecutor { + pub fn is_running(&self) -> bool { + self.event_sender.is_some() + } + pub async fn before_build_module_graph(&mut self, compilation: &Compilation) -> Result<()> { let mut make_artifact = self.make_artifact.steal(); let mut exports_info_artifact = self.exports_info_artifact.steal(); diff --git a/crates/rspack_core/src/compilation/mod.rs b/crates/rspack_core/src/compilation/mod.rs index 930f321ebaf2..15264b4ea7f2 100644 --- a/crates/rspack_core/src/compilation/mod.rs +++ b/crates/rspack_core/src/compilation/mod.rs @@ -652,9 +652,20 @@ impl Compilation { self.add_entry(entry, options).await?; } + // ensure module executor is running so loaders can call `importModule` + let started_executor = match &self.module_executor { + Some(me) if !me.is_running() => { + let mut module_executor = self.module_executor.take().expect("checked above"); + module_executor.before_build_module_graph(self).await?; + self.module_executor = Some(module_executor); + true + } + _ => false, + }; + let make_artifact = self.build_module_graph_artifact.steal(); let exports_info_artifact = self.exports_info_artifact.steal(); - let (make_artifact, exports_info_artifact) = update_module_graph( + let build_result = update_module_graph( self, make_artifact, exports_info_artifact, @@ -668,11 +679,29 @@ impl Compilation { .collect(), )], ) - .await?; - self.build_module_graph_artifact = make_artifact.into(); - self.exports_info_artifact = exports_info_artifact.into(); + .await; + + // restore artifacts before teardown — after_build_module_graph reads them + let build_result = match build_result { + Ok((make_artifact, exports_info_artifact)) => { + self.build_module_graph_artifact = make_artifact.into(); + self.exports_info_artifact = exports_info_artifact.into(); + Ok(()) + } + Err(e) => { + self.build_module_graph_artifact = StealCell::new(BuildModuleGraphArtifact::new()); + self.exports_info_artifact = StealCell::new(ExportsInfoArtifact::default()); + Err(e) + } + }; - Ok(()) + // tear down the executor so build_module_graph_pass can set it up fresh + if started_executor && let Some(mut module_executor) = self.module_executor.take() { + module_executor.after_build_module_graph(self).await?; + self.module_executor = Some(module_executor); + } + + build_result } pub async fn add_include(&mut self, args: Vec<(BoxDependency, EntryOptions)>) -> Result<()> { diff --git a/tests/rspack-test/configCases/compilation/add-entry-import-module/data.js b/tests/rspack-test/configCases/compilation/add-entry-import-module/data.js new file mode 100644 index 000000000000..ee83bb053e7c --- /dev/null +++ b/tests/rspack-test/configCases/compilation/add-entry-import-module/data.js @@ -0,0 +1 @@ +export default "hello from imported module"; diff --git a/tests/rspack-test/configCases/compilation/add-entry-import-module/entry.js b/tests/rspack-test/configCases/compilation/add-entry-import-module/entry.js new file mode 100644 index 000000000000..7a917cddbbf3 --- /dev/null +++ b/tests/rspack-test/configCases/compilation/add-entry-import-module/entry.js @@ -0,0 +1,2 @@ +import data from "./data.js"; +export default data; diff --git a/tests/rspack-test/configCases/compilation/add-entry-import-module/index.js b/tests/rspack-test/configCases/compilation/add-entry-import-module/index.js new file mode 100644 index 000000000000..491cdce92f64 --- /dev/null +++ b/tests/rspack-test/configCases/compilation/add-entry-import-module/index.js @@ -0,0 +1,3 @@ +it("should have built the dynamically added entry", () => { + expect(__STATS__.compilation.namedChunks).toHaveProperty("dynamic"); +}); diff --git a/tests/rspack-test/configCases/compilation/add-entry-import-module/loader.js b/tests/rspack-test/configCases/compilation/add-entry-import-module/loader.js new file mode 100644 index 000000000000..4465ab561fc2 --- /dev/null +++ b/tests/rspack-test/configCases/compilation/add-entry-import-module/loader.js @@ -0,0 +1,7 @@ +/** @type {import("@rspack/core").PitchLoaderDefinitionFunction} */ +exports.pitch = async function (remaining) { + const result = await this.importModule( + this.resourcePath + ".webpack[javascript/auto]" + "!=!" + remaining + ); + return result.default || result; +}; diff --git a/tests/rspack-test/configCases/compilation/add-entry-import-module/rspack.config.js b/tests/rspack-test/configCases/compilation/add-entry-import-module/rspack.config.js new file mode 100644 index 000000000000..7a395668c044 --- /dev/null +++ b/tests/rspack-test/configCases/compilation/add-entry-import-module/rspack.config.js @@ -0,0 +1,45 @@ +const path = require("path"); + +const PLUGIN_NAME = "AddEntryWithImportModulePlugin"; + +class AddEntryWithImportModulePlugin { + /** + * @param {import("@rspack/core").Compiler} compiler + */ + apply(compiler) { + const { EntryPlugin } = compiler.rspack; + + compiler.hooks.make.tapPromise(PLUGIN_NAME, compilation => { + return new Promise((resolve, reject) => { + compilation.addEntry( + compiler.context, + EntryPlugin.createDependency( + path.resolve(__dirname, "entry.js") + ), + { name: "dynamic" }, + (err) => { + if (err) reject(err); + else resolve(); + } + ); + }); + }); + } +} + +/**@type {import("@rspack/core").Configuration}*/ +module.exports = { + output: { + filename: "[name].js" + }, + module: { + rules: [ + { + test: /entry\.js$/, + use: [{ loader: "./loader", options: {} }], + type: "asset/source" + } + ] + }, + plugins: [new AddEntryWithImportModulePlugin()] +};