From b940b48e8a6f0ccec0e43a7db41890c078a3995c Mon Sep 17 00:00:00 2001 From: Dmitriy Stepanenko <33101123+dmitry-stepanenko@users.noreply.github.com> Date: Tue, 13 Jun 2023 14:57:01 +0300 Subject: [PATCH] feat: add buildModule hook (#3466) * feat: add buildModule hook * feat: use object assign --------- Co-authored-by: Hana --- crates/node_binding/binding.d.ts | 1 + crates/node_binding/src/hook.rs | 2 ++ crates/node_binding/src/js_values/hooks.rs | 1 + crates/node_binding/src/plugins/mod.rs | 21 +++++++++++++++++ .../rspack_core/src/plugin/plugin_driver.rs | 1 + packages/rspack/src/compilation.ts | 12 ++++------ packages/rspack/src/compiler.ts | 13 +++++++++-- packages/rspack/src/util/normalization.ts | 11 +++++++++ .../tests/configCases/hooks/build-module/a.js | 1 + .../configCases/hooks/build-module/index.js | 5 ++++ .../hooks/build-module/webpack.config.js | 23 +++++++++++++++++++ 11 files changed, 82 insertions(+), 9 deletions(-) create mode 100644 packages/rspack/src/util/normalization.ts create mode 100644 packages/rspack/tests/configCases/hooks/build-module/a.js create mode 100644 packages/rspack/tests/configCases/hooks/build-module/index.js create mode 100644 packages/rspack/tests/configCases/hooks/build-module/webpack.config.js diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index be0473df627..515c2643813 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -199,6 +199,7 @@ export interface JsHooks { afterCompile: (...args: any[]) => any finishModules: (...args: any[]) => any finishMake: (...args: any[]) => any + buildModule: (...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 e224c5d8a43..9a4649118b9 100644 --- a/crates/node_binding/src/hook.rs +++ b/crates/node_binding/src/hook.rs @@ -5,6 +5,7 @@ use std::sync::{Arc, RwLock}; pub enum Hook { Make, FinishMake, + BuildModule, Compilation, ThisCompilation, ProcessAssetsStageAdditional, @@ -37,6 +38,7 @@ impl From for Hook { match s.as_str() { "make" => Hook::Make, "finishMake" => Hook::FinishMake, + "buildModule" => Hook::BuildModule, "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 a723eb84b07..a23f8720948 100644 --- a/crates/node_binding/src/js_values/hooks.rs +++ b/crates/node_binding/src/js_values/hooks.rs @@ -22,6 +22,7 @@ pub struct JsHooks { pub after_compile: JsFunction, pub finish_modules: JsFunction, pub finish_make: JsFunction, + pub build_module: 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 330e62529ea..09617fda4ee 100644 --- a/crates/node_binding/src/plugins/mod.rs +++ b/crates/node_binding/src/plugins/mod.rs @@ -43,6 +43,7 @@ pub struct JsHooksAdapter { pub after_compile_tsfn: ThreadsafeFunction, pub finish_modules_tsfn: ThreadsafeFunction, pub finish_make_tsfn: ThreadsafeFunction, + pub build_module_tsfn: ThreadsafeFunction, // TODO pub chunk_asset_tsfn: ThreadsafeFunction, pub before_resolve: ThreadsafeFunction, BeforeResolveData)>, pub after_resolve: ThreadsafeFunction>, @@ -464,6 +465,22 @@ impl rspack_core::Plugin for JsHooksAdapter { .map_err(|err| internal_error!("Failed to call finish make: {err}"))? } + async fn build_module(&self, module: &mut dyn rspack_core::Module) -> rspack_error::Result<()> { + if self.is_hook_disabled(&Hook::BuildModule) { + return Ok(()); + } + + self + .build_module_tsfn + .call( + module.to_js_module().expect("Convert to js_module failed."), + ThreadsafeFunctionCallMode::NonBlocking, + ) + .into_rspack_result()? + .await + .map_err(|err| internal_error!("Failed to call build module: {err}"))? + } + async fn finish_modules( &self, compilation: &mut rspack_core::Compilation, @@ -586,6 +603,7 @@ impl JsHooksAdapter { after_compile, finish_modules, finish_make, + build_module, chunk_asset, succeed_module, still_valid_module, @@ -626,6 +644,8 @@ impl JsHooksAdapter { js_fn_into_theadsafe_fn!(after_compile, env); let finish_make_tsfn: ThreadsafeFunction = js_fn_into_theadsafe_fn!(finish_make, env); + let build_module_tsfn: ThreadsafeFunction = + js_fn_into_theadsafe_fn!(build_module, env); let finish_modules_tsfn: ThreadsafeFunction = js_fn_into_theadsafe_fn!(finish_modules, env); let context_module_before_resolve: ThreadsafeFunction> = @@ -670,6 +690,7 @@ impl JsHooksAdapter { normal_module_factory_resolve_for_scheme, finish_modules_tsfn, finish_make_tsfn, + build_module_tsfn, chunk_asset_tsfn, after_resolve, succeed_module_tsfn, diff --git a/crates/rspack_core/src/plugin/plugin_driver.rs b/crates/rspack_core/src/plugin/plugin_driver.rs index 68b105a9f31..6d2e4e61aef 100644 --- a/crates/rspack_core/src/plugin/plugin_driver.rs +++ b/crates/rspack_core/src/plugin/plugin_driver.rs @@ -526,6 +526,7 @@ impl PluginDriver { Ok(()) } + #[instrument(name = "plugin:build_module", skip_all)] 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 8c75c995898..faedd758676 100644 --- a/packages/rspack/src/compilation.ts +++ b/packages/rspack/src/compilation.ts @@ -43,6 +43,7 @@ import { createFakeCompilationDependencies, createFakeProcessAssetsHook } from "./util/fake"; +import { NormalizedJsModule, normalizeJsModule } from "./util/normalization"; import MergeCaller from "./util/MergeCaller"; export type AssetInfo = Partial & Record; @@ -90,6 +91,7 @@ export class Compilation { processWarnings: tapable.SyncWaterfallHook<[Error[]]>; succeedModule: tapable.SyncHook<[JsModule], undefined>; stillValidModule: tapable.SyncHook<[JsModule], undefined>; + buildModule: tapable.SyncHook<[NormalizedJsModule]>; }; options: RspackOptionsNormalized; outputOptions: OutputNormalized; @@ -125,7 +127,8 @@ export class Compilation { chunkAsset: new tapable.SyncHook(["chunk", "filename"]), processWarnings: new tapable.SyncWaterfallHook(["warnings"]), succeedModule: new tapable.SyncHook(["module"]), - stillValidModule: new tapable.SyncHook(["module"]) + stillValidModule: new tapable.SyncHook(["module"]), + buildModule: new tapable.SyncHook(["module"]) }; this.compiler = compiler; this.resolverFactory = compiler.resolverFactory; @@ -608,12 +611,7 @@ export class Compilation { ); get modules() { - return this.getModules().map(item => { - return { - identifier: () => item.moduleIdentifier, - ...item - }; - }); + return this.getModules().map(item => normalizeJsModule(item)); } get chunks() { diff --git a/packages/rspack/src/compiler.ts b/packages/rspack/src/compiler.ts index b268a68806a..214520d1523 100644 --- a/packages/rspack/src/compiler.ts +++ b/packages/rspack/src/compiler.ts @@ -37,6 +37,7 @@ import { WatchFileSystem } from "./util/fs"; import { getScheme } from "./util/scheme"; import Watching from "./watching"; import { NormalModule } from "./normalModule"; +import { normalizeJsModule } from "./util/normalization"; class EntryPlugin { constructor( @@ -343,7 +344,8 @@ class Compiler { contextModuleBeforeResolve: this.#contextModuleBeforeResolve.bind(this), succeedModule: this.#succeedModule.bind(this), - stillValidModule: this.#stillValidModule.bind(this) + stillValidModule: this.#stillValidModule.bind(this), + buildModule: this.#buildModule.bind(this) }, createThreadsafeNodeFSFromRaw(this.outputFileSystem), loaderContext => runLoader(loaderContext, this) @@ -606,7 +608,8 @@ class Compiler { beforeResolve: this.compilation.normalModuleFactory?.hooks.beforeResolve, afterResolve: this.compilation.normalModuleFactory?.hooks.afterResolve, succeedModule: this.compilation.hooks.succeedModule, - stillValidModule: this.compilation.hooks.stillValidModule + stillValidModule: this.compilation.hooks.stillValidModule, + buildModule: this.compilation.hooks.buildModule }; for (const [name, hook] of Object.entries(hookMap)) { if (hook?.taps.length === 0) { @@ -637,6 +640,12 @@ class Compiler { this.#updateDisabledHooks(); } + async #buildModule(module: binding.JsModule) { + const normalized = normalizeJsModule(module); + this.compilation.hooks.buildModule.call(normalized); + this.#updateDisabledHooks(); + } + async #processAssets(stage: number) { await this.compilation .__internal_getProcessAssetsHookByStage(stage) diff --git a/packages/rspack/src/util/normalization.ts b/packages/rspack/src/util/normalization.ts new file mode 100644 index 00000000000..7666fa72725 --- /dev/null +++ b/packages/rspack/src/util/normalization.ts @@ -0,0 +1,11 @@ +import { JsModule } from "@rspack/binding"; + +export interface NormalizedJsModule extends JsModule { + identifier: () => string; +} + +export function normalizeJsModule(m: JsModule): NormalizedJsModule { + return Object.assign(m, { + identifier: () => m.moduleIdentifier + }); +} diff --git a/packages/rspack/tests/configCases/hooks/build-module/a.js b/packages/rspack/tests/configCases/hooks/build-module/a.js new file mode 100644 index 00000000000..84c67edefbd --- /dev/null +++ b/packages/rspack/tests/configCases/hooks/build-module/a.js @@ -0,0 +1 @@ +export const a = 3; diff --git a/packages/rspack/tests/configCases/hooks/build-module/index.js b/packages/rspack/tests/configCases/hooks/build-module/index.js new file mode 100644 index 00000000000..c888d204d59 --- /dev/null +++ b/packages/rspack/tests/configCases/hooks/build-module/index.js @@ -0,0 +1,5 @@ +import { a } from "./a.js"; + +it("should compile successfully with build-module", () => { + expect(a).toBe(3); +}); diff --git a/packages/rspack/tests/configCases/hooks/build-module/webpack.config.js b/packages/rspack/tests/configCases/hooks/build-module/webpack.config.js new file mode 100644 index 00000000000..5d06d626ccf --- /dev/null +++ b/packages/rspack/tests/configCases/hooks/build-module/webpack.config.js @@ -0,0 +1,23 @@ +const { strict } = require("assert"); +const pluginName = "plugin"; + +class Plugin { + apply(compiler) { + let identifiers = []; + compiler.hooks.compilation.tap(pluginName, compilation => { + compilation.hooks.buildModule.tap(pluginName, m => { + identifiers.push(m.identifier()); + }); + }); + compiler.hooks.done.tap(pluginName, () => { + strict(identifiers.some(i => i.endsWith("index.js"))); + strict(identifiers.some(i => i.endsWith("a.js"))); + }); + } +} + +/**@type {import('@rspack/cli').Configuration}*/ +module.exports = { + context: __dirname, + plugins: [new Plugin()] +};