diff --git a/.changeset/silver-phones-learn.md b/.changeset/silver-phones-learn.md new file mode 100644 index 000000000000..44fb149ee5fb --- /dev/null +++ b/.changeset/silver-phones-learn.md @@ -0,0 +1,6 @@ +--- +"@rspack/binding": patch +"@rspack/core": patch +--- + +add finishModules hook diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 9f6dafcc8102..6c91cd5d0467 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -433,6 +433,7 @@ export interface JsHooks { afterEmit: (...args: any[]) => any make: (...args: any[]) => any optimizeChunkModule: (...args: any[]) => any + finishModules: (...args: any[]) => any normalModuleFactoryResolveForScheme: (...args: any[]) => any } export interface JsModule { diff --git a/crates/node_binding/src/hook.rs b/crates/node_binding/src/hook.rs index 1a696a9b2cd7..4c3a69a262db 100644 --- a/crates/node_binding/src/hook.rs +++ b/crates/node_binding/src/hook.rs @@ -15,6 +15,7 @@ pub enum Hook { Emit, AfterEmit, OptimizeChunkModules, + FinishModules, } impl From for Hook { @@ -32,6 +33,7 @@ impl From for Hook { "emit" => Hook::Emit, "afterEmit" => Hook::AfterEmit, "optimizeChunkModules" => Hook::OptimizeChunkModules, + "finishModules" => Hook::FinishModules, hook_name => panic!("{hook_name} is an invalid hook name"), } } diff --git a/crates/node_binding/src/js_values/hooks.rs b/crates/node_binding/src/js_values/hooks.rs index 76ca8260d14e..4a35b27f6a5c 100644 --- a/crates/node_binding/src/js_values/hooks.rs +++ b/crates/node_binding/src/js_values/hooks.rs @@ -14,5 +14,6 @@ pub struct JsHooks { pub after_emit: JsFunction, pub make: JsFunction, pub optimize_chunk_module: JsFunction, + pub finish_modules: JsFunction, pub normal_module_factory_resolve_for_scheme: JsFunction, } diff --git a/crates/node_binding/src/plugins/mod.rs b/crates/node_binding/src/plugins/mod.rs index d0157379b7ba..5dc566af69a5 100644 --- a/crates/node_binding/src/plugins/mod.rs +++ b/crates/node_binding/src/plugins/mod.rs @@ -29,6 +29,7 @@ pub struct JsHooksAdapter { pub emit_tsfn: ThreadsafeFunction<(), ()>, pub after_emit_tsfn: ThreadsafeFunction<(), ()>, pub optimize_chunk_modules_tsfn: ThreadsafeFunction, + pub finish_modules_tsfn: ThreadsafeFunction, pub normal_module_factory_resolve_for_scheme: ThreadsafeFunction, } @@ -257,6 +258,28 @@ impl rspack_core::Plugin for JsHooksAdapter { .map_err(|err| internal_error!("Failed to compilation: {err}"))? } + async fn finish_modules( + &mut self, + compilation: &mut rspack_core::Compilation, + ) -> rspack_error::Result<()> { + if self.is_hook_disabled(&Hook::FinishModules) { + return Ok(()); + } + + let compilation = JsCompilation::from_compilation(unsafe { + std::mem::transmute::<&'_ mut rspack_core::Compilation, &'static mut rspack_core::Compilation>( + compilation, + ) + }); + + self + .finish_modules_tsfn + .call(compilation, ThreadsafeFunctionCallMode::NonBlocking) + .into_rspack_result()? + .await + .map_err(|err| internal_error!("Failed to finish modules: {err}"))? + } + async fn emit(&mut self, _: &mut rspack_core::Compilation) -> rspack_error::Result<()> { if self.is_hook_disabled(&Hook::Emit) { return Ok(()); @@ -300,6 +323,7 @@ impl JsHooksAdapter { after_emit, optimize_chunk_module, normal_module_factory_resolve_for_scheme, + finish_modules, } = js_hooks; let process_assets_stage_additional_tsfn: ThreadsafeFunction<(), ()> = @@ -323,7 +347,8 @@ impl JsHooksAdapter { let make_tsfn: ThreadsafeFunction<(), ()> = js_fn_into_theadsafe_fn!(make, env); let optimize_chunk_modules_tsfn: ThreadsafeFunction = js_fn_into_theadsafe_fn!(optimize_chunk_module, env); - + let finish_modules_tsfn: ThreadsafeFunction = + js_fn_into_theadsafe_fn!(finish_modules, env); let normal_module_factory_resolve_for_scheme: ThreadsafeFunction< SchemeAndJsResourceData, JsResourceData, @@ -344,6 +369,7 @@ impl JsHooksAdapter { after_emit_tsfn, optimize_chunk_modules_tsfn, normal_module_factory_resolve_for_scheme, + finish_modules_tsfn, }) } diff --git a/crates/rspack_core/src/compiler/compilation.rs b/crates/rspack_core/src/compiler/compilation.rs index 7ab7b8422eb3..a5565894e030 100644 --- a/crates/rspack_core/src/compiler/compilation.rs +++ b/crates/rspack_core/src/compiler/compilation.rs @@ -979,6 +979,13 @@ impl Compilation { .get(ukey) .expect("entrypoint not found by ukey") } + + #[instrument(name = "compilation:finish", skip_all)] + pub async fn finish(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> { + plugin_driver.write().await.finish_modules(self).await?; + Ok(()) + } + #[instrument(name = "compilation:seal", skip_all)] pub async fn seal(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> { use_code_splitting_cache(self, |compilation| async { diff --git a/crates/rspack_core/src/compiler/mod.rs b/crates/rspack_core/src/compiler/mod.rs index be52d75e904e..7bc2f900d2ec 100644 --- a/crates/rspack_core/src/compiler/mod.rs +++ b/crates/rspack_core/src/compiler/mod.rs @@ -138,6 +138,7 @@ where async fn compile(&mut self, params: SetupMakeParam) -> Result<()> { let option = self.options.clone(); self.compilation.make(params).await?; + self.compilation.finish(self.plugin_driver.clone()).await?; if option.builtins.tree_shaking { let (analyze_result, diagnostics) = self .compilation diff --git a/crates/rspack_core/src/plugin/api.rs b/crates/rspack_core/src/plugin/api.rs index 9564bcbb3430..7c8571149f97 100644 --- a/crates/rspack_core/src/plugin/api.rs +++ b/crates/rspack_core/src/plugin/api.rs @@ -244,6 +244,10 @@ pub trait Plugin: Debug + Send + Sync { Ok(()) } + async fn finish_modules(&mut self, _modules: &mut Compilation) -> Result<()> { + Ok(()) + } + async fn build_module(&self, _module: &mut dyn Module) -> Result<()> { Ok(()) } diff --git a/crates/rspack_core/src/plugin/plugin_driver.rs b/crates/rspack_core/src/plugin/plugin_driver.rs index d5602ce33a9b..60f98b38d069 100644 --- a/crates/rspack_core/src/plugin/plugin_driver.rs +++ b/crates/rspack_core/src/plugin/plugin_driver.rs @@ -375,6 +375,14 @@ impl PluginDriver { Ok(()) } + #[instrument(name = "plugin:finish_modules", skip_all)] + pub async fn finish_modules(&mut self, modules: &mut Compilation) -> Result<()> { + for plugin in &mut self.plugins { + plugin.finish_modules(modules).await?; + } + Ok(()) + } + pub async fn build_module(&self, module: &mut dyn Module) -> Result<()> { for plugin in &self.plugins { plugin.build_module(module).await?; diff --git a/packages/rspack/src/compilation.ts b/packages/rspack/src/compilation.ts index 1574bc944c96..a473f2a55588 100644 --- a/packages/rspack/src/compilation.ts +++ b/packages/rspack/src/compilation.ts @@ -77,6 +77,7 @@ export class Compilation { [Iterable, Iterable], undefined >; + finishModules: tapable.AsyncSeriesHook<[Iterable], undefined>; }; options: RspackOptionsNormalized; outputOptions: OutputNormalized; @@ -103,7 +104,8 @@ export class Compilation { optimizeChunkModules: new tapable.AsyncSeriesBailHook([ "chunks", "modules" - ]) + ]), + finishModules: new tapable.AsyncSeriesHook(["modules"]) }; this.compiler = compiler; this.resolverFactory = compiler.resolverFactory; diff --git a/packages/rspack/src/compiler.ts b/packages/rspack/src/compiler.ts index a41d3a3b5654..1bc34b1ee8ff 100644 --- a/packages/rspack/src/compiler.ts +++ b/packages/rspack/src/compiler.ts @@ -181,6 +181,7 @@ class Compiler { // No matter how it will be implemented, it will be copied to the child compiler. compilation: this.#compilation.bind(this), optimizeChunkModule: this.#optimize_chunk_modules.bind(this), + finishModules: this.#finish_modules.bind(this), normalModuleFactoryResolveForScheme: this.#normalModuleFactoryResolveForScheme.bind(this) }, @@ -303,7 +304,8 @@ class Compiler { Compilation.PROCESS_ASSETS_STAGE_REPORT ), compilation: this.hooks.compilation, - optimizeChunkModules: this.compilation.hooks.optimizeChunkModules + optimizeChunkModules: this.compilation.hooks.optimizeChunkModules, + finishModules: this.compilation.hooks.finishModules // normalModuleFactoryResolveForScheme: this.# }; for (const [name, hook] of Object.entries(hookMap)) { @@ -342,6 +344,14 @@ class Compiler { ); this.#updateDisabledHooks(); } + + async #finish_modules() { + await this.compilation.hooks.finishModules.promise( + this.compilation.getModules() + ); + this.#updateDisabledHooks(); + } + async #make() { await this.hooks.make.promise(this.compilation); this.#updateDisabledHooks();