diff --git a/.vscode/settings.json b/.vscode/settings.json index 8fd8dc7b7c9d..46c3a654d23b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,7 @@ "**/.DS_Store": true, "**/*.bk": true, "/target": true, - "**/.yarn": true, + "**/.yarn": true }, "[typescript]": { "editor.formatOnSave": true @@ -19,9 +19,7 @@ "cSpell.caseSensitive": true, "json.schemas": [ { - "fileMatch": [ - "test.config.json" - ], + "fileMatch": ["test.config.json"], // Point to the options schema of rspack. "url": "./crates/rspack_testing/test.config.scheme.json" } diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 417c482868a4..b62b631c99c9 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -192,6 +192,7 @@ export interface JsHooks { beforeCompile: (...args: any[]) => any afterCompile: (...args: any[]) => any finishModules: (...args: any[]) => any + finishMake: (...args: any[]) => any beforeResolve: (...args: any[]) => any afterResolve: (...args: any[]) => any contextModuleBeforeResolve: (...args: any[]) => any diff --git a/crates/node_binding/src/hook.rs b/crates/node_binding/src/hook.rs index 230973353052..64865c45e686 100644 --- a/crates/node_binding/src/hook.rs +++ b/crates/node_binding/src/hook.rs @@ -4,6 +4,7 @@ use std::sync::{Arc, RwLock}; #[derive(PartialEq)] pub enum Hook { Make, + FinishMake, Compilation, ThisCompilation, ProcessAssetsStageAdditional, @@ -33,6 +34,7 @@ impl From for Hook { fn from(s: String) -> Self { match s.as_str() { "make" => Hook::Make, + "finishMake" => Hook::FinishMake, "compilation" => Hook::Compilation, "thisCompilation" => Hook::ThisCompilation, "processAssetsStageAdditional" => Hook::ProcessAssetsStageAdditional, diff --git a/crates/node_binding/src/js_values/hooks.rs b/crates/node_binding/src/js_values/hooks.rs index a8e7a5253e46..2fa3fe4c62fc 100644 --- a/crates/node_binding/src/js_values/hooks.rs +++ b/crates/node_binding/src/js_values/hooks.rs @@ -21,6 +21,7 @@ pub struct JsHooks { pub before_compile: JsFunction, pub after_compile: JsFunction, pub finish_modules: JsFunction, + pub finish_make: JsFunction, pub before_resolve: JsFunction, pub after_resolve: JsFunction, pub context_module_before_resolve: JsFunction, diff --git a/crates/node_binding/src/plugins/mod.rs b/crates/node_binding/src/plugins/mod.rs index 28899380024d..b7d7cb1ff559 100644 --- a/crates/node_binding/src/plugins/mod.rs +++ b/crates/node_binding/src/plugins/mod.rs @@ -42,6 +42,7 @@ pub struct JsHooksAdapter { pub before_compile_tsfn: ThreadsafeFunction<(), ()>, pub after_compile_tsfn: ThreadsafeFunction, pub finish_modules_tsfn: ThreadsafeFunction, + pub finish_make_tsfn: ThreadsafeFunction, pub chunk_asset_tsfn: ThreadsafeFunction, pub before_resolve: ThreadsafeFunction, BeforeResolveData)>, pub after_resolve: ThreadsafeFunction>, @@ -439,6 +440,28 @@ impl rspack_core::Plugin for JsHooksAdapter { .map_err(|err| internal_error!("Failed to call after compile: {err}"))? } + async fn finish_make( + &mut self, + compilation: &mut rspack_core::Compilation, + ) -> rspack_error::Result<()> { + if self.is_hook_disabled(&Hook::FinishMake) { + return Ok(()); + } + + let compilation = JsCompilation::from_compilation(unsafe { + std::mem::transmute::<&'_ mut rspack_core::Compilation, &'static mut rspack_core::Compilation>( + compilation, + ) + }); + + self + .finish_make_tsfn + .call(compilation, ThreadsafeFunctionCallMode::NonBlocking) + .into_rspack_result()? + .await + .map_err(|err| internal_error!("Failed to call finish make: {err}"))? + } + async fn finish_modules( &mut self, compilation: &mut rspack_core::Compilation, @@ -528,6 +551,7 @@ impl JsHooksAdapter { before_compile, after_compile, finish_modules, + finish_make, chunk_asset, } = js_hooks; @@ -564,6 +588,8 @@ impl JsHooksAdapter { js_fn_into_theadsafe_fn!(before_compile, env); let after_compile_tsfn: ThreadsafeFunction = js_fn_into_theadsafe_fn!(after_compile, env); + let finish_make_tsfn: ThreadsafeFunction = + js_fn_into_theadsafe_fn!(finish_make, env); let finish_modules_tsfn: ThreadsafeFunction = js_fn_into_theadsafe_fn!(finish_modules, env); let context_module_before_resolve: ThreadsafeFunction> = @@ -603,6 +629,7 @@ impl JsHooksAdapter { context_module_before_resolve, normal_module_factory_resolve_for_scheme, finish_modules_tsfn, + finish_make_tsfn, chunk_asset_tsfn, after_resolve, }) diff --git a/crates/rspack_core/src/compiler/mod.rs b/crates/rspack_core/src/compiler/mod.rs index f17945dd9f26..97d00f19bac1 100644 --- a/crates/rspack_core/src/compiler/mod.rs +++ b/crates/rspack_core/src/compiler/mod.rs @@ -147,6 +147,14 @@ where async fn compile(&mut self, params: SetupMakeParam) -> Result<()> { let option = self.options.clone(); self.compilation.make(params).await?; + + self + .plugin_driver + .write() + .await + .finish_make(&mut self.compilation) + .await?; + self.compilation.finish(self.plugin_driver.clone()).await?; // by default include all module in final chunk self.compilation.include_module_ids = self diff --git a/crates/rspack_core/src/plugin/api.rs b/crates/rspack_core/src/plugin/api.rs index b0dda8d8d420..a45ce815c0b1 100644 --- a/crates/rspack_core/src/plugin/api.rs +++ b/crates/rspack_core/src/plugin/api.rs @@ -325,6 +325,10 @@ pub trait Plugin: Debug + Send + Sync { Ok(()) } + async fn finish_make(&mut self, _compilation: &mut Compilation) -> Result<()> { + Ok(()) + } + async fn finish_modules(&mut self, _modules: &mut Compilation) -> Result<()> { Ok(()) } diff --git a/crates/rspack_core/src/plugin/plugin_driver.rs b/crates/rspack_core/src/plugin/plugin_driver.rs index f76030b4eccd..e58958bb529e 100644 --- a/crates/rspack_core/src/plugin/plugin_driver.rs +++ b/crates/rspack_core/src/plugin/plugin_driver.rs @@ -188,6 +188,17 @@ impl PluginDriver { Ok(()) } + + pub async fn finish_make( + &mut self, + compilation: &mut Compilation, + ) -> PluginCompilationHookOutput { + for plugin in &mut self.plugins { + plugin.finish_make(compilation).await?; + } + + Ok(()) + } /// Executed while initializing the compilation, right before emitting the compilation event. This hook is not copied to child compilers. /// /// See: https://webpack.js.org/api/compiler-hooks/#thiscompilation diff --git a/packages/rspack/src/compiler.ts b/packages/rspack/src/compiler.ts index c8449cdef673..e7b6b71c8f46 100644 --- a/packages/rspack/src/compiler.ts +++ b/packages/rspack/src/compiler.ts @@ -136,6 +136,7 @@ class Compiler { beforeCompile: tapable.AsyncSeriesHook; afterCompile: tapable.AsyncSeriesHook<[Compilation]>; finishModules: tapable.AsyncSeriesHook<[any]>; + finishMake: tapable.AsyncSeriesHook<[Compilation]>; }; options: RspackOptionsNormalized; #disabledHooks: string[]; @@ -234,6 +235,7 @@ class Compiler { make: new tapable.AsyncParallelHook(["compilation"]), beforeCompile: new tapable.AsyncSeriesHook(["params"]), afterCompile: new tapable.AsyncSeriesHook(["compilation"]), + finishMake: new tapable.AsyncSeriesHook(["compilation"]), finishModules: new tapable.AsyncSeriesHook(["modules"]) }; this.modifiedFiles = undefined; @@ -284,6 +286,7 @@ class Compiler { { beforeCompile: this.#beforeCompile.bind(this), afterCompile: this.#afterCompile.bind(this), + finishMake: this.#finishMake.bind(this), make: this.#make.bind(this), emit: this.#emit.bind(this), assetEmitted: this.#assetEmitted.bind(this), @@ -565,6 +568,7 @@ class Compiler { make: this.hooks.make, beforeCompile: this.hooks.beforeCompile, afterCompile: this.hooks.afterCompile, + finishMake: this.hooks.finishMake, emit: this.hooks.emit, assetEmitted: this.hooks.assetEmitted, afterEmit: this.hooks.afterEmit, @@ -624,6 +628,11 @@ class Compiler { this.#updateDisabledHooks(); } + async #finishMake() { + await this.hooks.finishMake.promise(this.compilation); + this.#updateDisabledHooks(); + } + async #processAssets(stage: number) { await this.compilation .__internal_getProcessAssetsHookByStage(stage) diff --git a/packages/rspack/tests/configCases/hooks/compilation-hooks/a.js b/packages/rspack/tests/configCases/hooks/compilation-hooks/a.js new file mode 100644 index 000000000000..84c67edefbd4 --- /dev/null +++ b/packages/rspack/tests/configCases/hooks/compilation-hooks/a.js @@ -0,0 +1 @@ +export const a = 3; diff --git a/packages/rspack/tests/configCases/hooks/compilation-hooks/index.js b/packages/rspack/tests/configCases/hooks/compilation-hooks/index.js new file mode 100644 index 000000000000..443ec81977b6 --- /dev/null +++ b/packages/rspack/tests/configCases/hooks/compilation-hooks/index.js @@ -0,0 +1,5 @@ +import { a } from "./a.js"; + +it("should compile successfully", () => { + expect(a).toBe(3); +}); diff --git a/packages/rspack/tests/configCases/hooks/compilation-hooks/webpack.config.js b/packages/rspack/tests/configCases/hooks/compilation-hooks/webpack.config.js new file mode 100644 index 000000000000..3097f1fdd7cc --- /dev/null +++ b/packages/rspack/tests/configCases/hooks/compilation-hooks/webpack.config.js @@ -0,0 +1,41 @@ +const { deepEqual, strict } = require("assert"); +const pluginName = "plugin"; + +class Plugin { + apply(compiler) { + let order = []; + + compiler.hooks.beforeCompile.tap(pluginName, () => { + order.push("hooks.beforeCompile"); + }); + compiler.hooks.make.tap(pluginName, () => { + order.push("hooks.make"); + }); + compiler.hooks.finishMake.tap(pluginName, () => { + order.push("hooks.finishMake"); + }); + compiler.hooks.afterCompile.tap(pluginName, () => { + order.push("hooks.afterCompile"); + }); + + compiler.hooks.done.tap(pluginName, stats => { + let json = stats.toJson(); + strict(json.errors.length === 0, `${json.errors}`); + deepEqual(order, [ + "hooks.beforeCompile", + "hooks.make", + "hooks.finishMake", + "hooks.afterCompile" + ]); + }); + } +} + +/**@type {import('@rspack/cli').Configuration}*/ +module.exports = { + context: __dirname, + module: { + rules: [] + }, + plugins: [new Plugin()] +};