From 7e15f6816c0ff5f440d2f7eb07235a03b51686e8 Mon Sep 17 00:00:00 2001 From: Cong-Cong Pan Date: Fri, 10 May 2024 15:59:18 +0800 Subject: [PATCH] feat: support contextModuleFactory beforeResolve hook (#6420) * fix: contextModuleFactory afterResolve hook * fix: js side logic * fix: afterResolve in ContextModuleFactory should be AsyncSeriesWaterfallHook * fix: refactor context module factory hooks * fix: js side logic * chore: update api.md * fix: use AsyncSeriesWaterfallHook in js side * fix: format js * fix: cargo lint * fix: context module factory afterResolve hook * chore: update snapshot * feat: new test case for context module factory before resolve hook --- Cargo.lock | 1 + crates/node_binding/Cargo.toml | 1 + crates/node_binding/binding.d.ts | 16 +- .../node_binding/src/plugins/interceptor.rs | 123 +++++++------- .../src/context_module_factory.rs | 22 +++ crates/rspack_binding_values/src/lib.rs | 2 + crates/rspack_core/src/context_module.rs | 2 +- .../rspack_core/src/context_module_factory.rs | 153 ++++++++++++++---- crates/rspack_macros/src/hook.rs | 31 +++- crates/rspack_plugin_ignore/src/lib.rs | 29 ++-- .../context-module-before-resolve/index.js | 15 ++ .../locale/en.js | 1 + .../locale/fr.js | 1 + .../locale/zh.js | 1 + .../webpack.config.js | 26 +++ .../tests/errorCases/warning-test-push.js | 4 +- .../tests/errorCases/warning-test-shift.js | 4 +- .../tests/errorCases/warning-test-splice-1.js | 4 +- .../tests/errorCases/warning-test-splice-2.js | 4 +- packages/rspack/etc/api.md | 35 +++- packages/rspack/src/Compiler.ts | 66 +++++--- packages/rspack/src/ContextModuleFactory.ts | 19 ++- packages/rspack/src/Module.ts | 17 ++ packages/rspack/src/lite-tapable/index.ts | 118 ++++++++++++++ 24 files changed, 555 insertions(+), 140 deletions(-) create mode 100644 crates/rspack_binding_values/src/context_module_factory.rs create mode 100644 packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/index.js create mode 100644 packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/locale/en.js create mode 100644 packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/locale/fr.js create mode 100644 packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/locale/zh.js create mode 100644 packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/webpack.config.js diff --git a/Cargo.lock b/Cargo.lock index 9461e230003..f06d15f62aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3108,6 +3108,7 @@ dependencies = [ "rspack_hook", "rspack_identifier", "rspack_napi", + "rspack_regex", "rspack_tracing", "tokio", "tracing", diff --git a/crates/node_binding/Cargo.toml b/crates/node_binding/Cargo.toml index f3c6877b463..cd82e107569 100644 --- a/crates/node_binding/Cargo.toml +++ b/crates/node_binding/Cargo.toml @@ -24,6 +24,7 @@ rspack_hook = { path = "../rspack_hook" } rspack_identifier = { path = "../rspack_identifier" } rspack_napi = { path = "../rspack_napi" } rspack_tracing = { path = "../rspack_tracing" } +rspack_regex = { path = "../rspack_regex" } tokio = { workspace = true, features = ["rt", "rt-multi-thread"] } async-trait = { workspace = true } diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 4103d44b4d3..a109fc6fa17 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -294,6 +294,18 @@ export interface JsCompatSource { map?: Buffer } +export interface JsContextModuleFactoryAfterResolveData { + resource: string + context: string + request: string + regExp?: string +} + +export interface JsContextModuleFactoryBeforeResolveData { + context: string + request?: string +} + export interface JsCreateData { request: string userRequest: string @@ -1415,8 +1427,8 @@ export interface RegisterJsTaps { registerNormalModuleFactoryResolveForSchemeTaps: (stages: Array) => Array<{ function: ((arg: JsResolveForSchemeArgs) => Promise<[boolean | undefined, JsResolveForSchemeArgs]>); stage: number; }> registerNormalModuleFactoryAfterResolveTaps: (stages: Array) => Array<{ function: ((arg: JsAfterResolveData) => Promise<[boolean | undefined, JsCreateData | undefined]>); stage: number; }> registerNormalModuleFactoryCreateModuleTaps: (stages: Array) => Array<{ function: ((arg: JsNormalModuleFactoryCreateModuleArgs) => Promise); stage: number; }> - registerContextModuleFactoryBeforeResolveTaps: (stages: Array) => Array<{ function: ((arg: JsBeforeResolveArgs) => Promise<[boolean | undefined, JsBeforeResolveArgs]>); stage: number; }> - registerContextModuleFactoryAfterResolveTaps: (stages: Array) => Array<{ function: ((arg: JsAfterResolveData) => Promise); stage: number; }> + registerContextModuleFactoryBeforeResolveTaps: (stages: Array) => Array<{ function: ((arg: false | JsContextModuleFactoryBeforeResolveData) => Promise); stage: number; }> + registerContextModuleFactoryAfterResolveTaps: (stages: Array) => Array<{ function: ((arg: false | JsContextModuleFactoryAfterResolveData) => Promise); stage: number; }> } /** Builtin loader runner */ diff --git a/crates/node_binding/src/plugins/interceptor.rs b/crates/node_binding/src/plugins/interceptor.rs index a2acaefea8e..0937ea7df60 100644 --- a/crates/node_binding/src/plugins/interceptor.rs +++ b/crates/node_binding/src/plugins/interceptor.rs @@ -11,12 +11,15 @@ use napi::{ }; use rspack_binding_values::{ CompatSource, JsAfterResolveData, JsAfterResolveOutput, JsAssetEmittedArgs, JsBeforeResolveArgs, - JsBeforeResolveOutput, JsChunk, JsChunkAssetArgs, JsCompilation, JsCreateData, + JsBeforeResolveOutput, JsChunk, JsChunkAssetArgs, JsCompilation, + JsContextModuleFactoryAfterResolveData, JsContextModuleFactoryAfterResolveResult, + JsContextModuleFactoryBeforeResolveData, JsContextModuleFactoryBeforeResolveResult, JsCreateData, JsExecuteModuleArg, JsModule, JsNormalModuleFactoryCreateModuleArgs, JsResolveForSchemeArgs, JsResolveForSchemeOutput, JsRuntimeModule, JsRuntimeModuleArg, ToJsCompatSource, ToJsModule, }; use rspack_core::{ - rspack_sources::SourceExt, AssetEmittedInfo, BoxModule, Chunk, ChunkUkey, CodeGenerationResults, + rspack_sources::SourceExt, AfterResolveData, AfterResolveResult, AssetEmittedInfo, + BeforeResolveData, BeforeResolveResult, BoxModule, Chunk, ChunkUkey, CodeGenerationResults, Compilation, CompilationAfterOptimizeModules, CompilationAfterOptimizeModulesHook, CompilationAfterProcessAssets, CompilationAfterProcessAssetsHook, CompilationAfterSeal, CompilationAfterSealHook, CompilationBuildModule, CompilationBuildModuleHook, @@ -42,6 +45,7 @@ use rspack_core::{ use rspack_hook::{Hook, Interceptor}; use rspack_identifier::IdentifierSet; use rspack_napi::threadsafe_function::ThreadsafeFunction; +use rspack_regex::RspackRegex; #[napi(object)] pub struct JsTap { @@ -434,15 +438,19 @@ pub struct RegisterJsTaps { pub register_normal_module_factory_create_module_taps: RegisterFunction>, #[napi( - ts_type = "(stages: Array) => Array<{ function: ((arg: JsBeforeResolveArgs) => Promise<[boolean | undefined, JsBeforeResolveArgs]>); stage: number; }>" + ts_type = "(stages: Array) => Array<{ function: ((arg: false | JsContextModuleFactoryBeforeResolveData) => Promise); stage: number; }>" )] - pub register_context_module_factory_before_resolve_taps: - RegisterFunction>, + pub register_context_module_factory_before_resolve_taps: RegisterFunction< + JsContextModuleFactoryBeforeResolveResult, + Promise, + >, #[napi( - ts_type = "(stages: Array) => Array<{ function: ((arg: JsAfterResolveData) => Promise); stage: number; }>" + ts_type = "(stages: Array) => Array<{ function: ((arg: false | JsContextModuleFactoryAfterResolveData) => Promise); stage: number; }>" )] - pub register_context_module_factory_after_resolve_taps: - RegisterFunction>>, + pub register_context_module_factory_after_resolve_taps: RegisterFunction< + JsContextModuleFactoryAfterResolveResult, + Promise, + >, } /* Compiler Hooks */ @@ -662,7 +670,7 @@ define_register!( /* ContextModuleFactory Hooks */ define_register!( RegisterContextModuleFactoryBeforeResolveTaps, - tap = ContextModuleFactoryBeforeResolveTap> @ ContextModuleFactoryBeforeResolveHook, + tap = ContextModuleFactoryBeforeResolveTap> @ ContextModuleFactoryBeforeResolveHook, cache = true, sync = false, kind = RegisterJsTapKind::ContextModuleFactoryBeforeResolve, @@ -670,7 +678,7 @@ define_register!( ); define_register!( RegisterContextModuleFactoryAfterResolveTaps, - tap = ContextModuleFactoryAfterResolveTap>> @ ContextModuleFactoryAfterResolveHook, + tap = ContextModuleFactoryAfterResolveTap> @ ContextModuleFactoryAfterResolveHook, cache = true, sync = false, kind = RegisterJsTapKind::ContextModuleFactoryAfterResolve, @@ -1198,24 +1206,27 @@ impl NormalModuleFactoryCreateModule for NormalModuleFactoryCreateModuleTap { #[async_trait] impl ContextModuleFactoryBeforeResolve for ContextModuleFactoryBeforeResolveTap { - async fn run(&self, data: &mut ModuleFactoryCreateData) -> rspack_error::Result> { - let dependency = data - .dependency - .as_context_dependency_mut() - .expect("should be context dependency"); - match self - .function - .call_with_promise(JsBeforeResolveArgs { - request: dependency.request().to_string(), - context: data.context.to_string(), - }) - .await - { - Ok((ret, resolve_data)) => { - dependency.set_request(resolve_data.request); - data.context = resolve_data.context.into(); - Ok(ret) + async fn run(&self, result: BeforeResolveResult) -> rspack_error::Result { + let js_result = match result { + BeforeResolveResult::Ignored => JsContextModuleFactoryBeforeResolveResult::A(false), + BeforeResolveResult::Data(d) => { + JsContextModuleFactoryBeforeResolveResult::B(JsContextModuleFactoryBeforeResolveData { + context: d.context, + request: d.request, + }) } + }; + match self.function.call_with_promise(js_result).await { + Ok(js_result) => match js_result { + napi::bindgen_prelude::Either::A(_) => Ok(BeforeResolveResult::Ignored), + napi::bindgen_prelude::Either::B(d) => { + let data = BeforeResolveData { + context: d.context, + request: d.request, + }; + Ok(BeforeResolveResult::Data(Box::new(data))) + } + }, Err(err) => Err(err), } } @@ -1227,37 +1238,33 @@ impl ContextModuleFactoryBeforeResolve for ContextModuleFactoryBeforeResolveTap #[async_trait] impl ContextModuleFactoryAfterResolve for ContextModuleFactoryAfterResolveTap { - async fn run(&self, data: &mut ModuleFactoryCreateData) -> rspack_error::Result> { - let dependency = data - .dependency - .as_context_dependency_mut() - .expect("should be context dependency"); - self - .function - .call_with_promise(JsAfterResolveData { - request: dependency.request().to_string(), - context: data.context.to_string(), - file_dependencies: data - .file_dependencies - .clone() - .into_iter() - .map(|item| item.to_string_lossy().to_string()) - .collect::>(), - context_dependencies: data - .context_dependencies - .clone() - .into_iter() - .map(|item| item.to_string_lossy().to_string()) - .collect::>(), - missing_dependencies: data - .missing_dependencies - .clone() - .into_iter() - .map(|item| item.to_string_lossy().to_string()) - .collect::>(), - create_data: None, - }) - .await + async fn run(&self, result: AfterResolveResult) -> rspack_error::Result { + let js_result = match result { + AfterResolveResult::Ignored => JsContextModuleFactoryAfterResolveResult::A(false), + AfterResolveResult::Data(d) => { + JsContextModuleFactoryAfterResolveResult::B(JsContextModuleFactoryAfterResolveData { + resource: d.resource.to_owned(), + context: d.context.to_owned(), + request: d.request.to_owned(), + reg_exp: d.reg_exp.clone().map(|r| r.to_string()), + }) + } + }; + match self.function.call_with_promise(js_result).await? { + napi::Either::A(_) => Ok(AfterResolveResult::Ignored), + napi::Either::B(d) => { + let data = AfterResolveData { + resource: d.resource, + context: d.context, + request: d.request, + reg_exp: match d.reg_exp { + Some(r) => Some(RspackRegex::new(&r)?), + None => None, + }, + }; + Ok(AfterResolveResult::Data(Box::new(data))) + } + } } fn stage(&self) -> i32 { diff --git a/crates/rspack_binding_values/src/context_module_factory.rs b/crates/rspack_binding_values/src/context_module_factory.rs new file mode 100644 index 00000000000..d061d21046f --- /dev/null +++ b/crates/rspack_binding_values/src/context_module_factory.rs @@ -0,0 +1,22 @@ +use napi::bindgen_prelude::Either; +use napi_derive::napi; + +#[napi(object)] +pub struct JsContextModuleFactoryBeforeResolveData { + pub context: String, + pub request: Option, +} + +pub type JsContextModuleFactoryBeforeResolveResult = + Either; + +#[napi(object)] +pub struct JsContextModuleFactoryAfterResolveData { + pub resource: String, + pub context: String, + pub request: String, + pub reg_exp: Option, +} + +pub type JsContextModuleFactoryAfterResolveResult = + Either; diff --git a/crates/rspack_binding_values/src/lib.rs b/crates/rspack_binding_values/src/lib.rs index 576f8ab07b7..dd35fc923b4 100644 --- a/crates/rspack_binding_values/src/lib.rs +++ b/crates/rspack_binding_values/src/lib.rs @@ -5,6 +5,7 @@ mod chunk_graph; mod chunk_group; mod codegen_result; mod compilation; +mod context_module_factory; mod filename; mod module; mod normal_module_factory; @@ -19,6 +20,7 @@ pub use chunk_graph::*; pub use chunk_group::*; pub use codegen_result::*; pub use compilation::*; +pub use context_module_factory::*; pub use filename::*; pub use module::*; pub use normal_module_factory::*; diff --git a/crates/rspack_core/src/context_module.rs b/crates/rspack_core/src/context_module.rs index 661fba4e13c..37560ff757d 100644 --- a/crates/rspack_core/src/context_module.rs +++ b/crates/rspack_core/src/context_module.rs @@ -151,7 +151,7 @@ pub struct ContextOptions { pub end: u32, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct ContextModuleOptions { pub addon: String, pub resource: String, diff --git a/crates/rspack_core/src/context_module_factory.rs b/crates/rspack_core/src/context_module_factory.rs index 5db1fd09a1b..6a41cd8bd26 100644 --- a/crates/rspack_core/src/context_module_factory.rs +++ b/crates/rspack_core/src/context_module_factory.rs @@ -2,17 +2,71 @@ use std::sync::Arc; use rspack_error::{error, Result}; use rspack_hook::define_hook; +use rspack_regex::RspackRegex; use tracing::instrument; use crate::{ - cache::Cache, resolve, BoxModule, ContextModule, ContextModuleOptions, DependencyCategory, - ModuleExt, ModuleFactory, ModuleFactoryCreateData, ModuleFactoryResult, ModuleIdentifier, - RawModule, ResolveArgs, ResolveOptionsWithDependencyType, ResolveResult, Resolver, - ResolverFactory, SharedPluginDriver, + cache::Cache, resolve, ContextModule, ContextModuleOptions, DependencyCategory, ModuleExt, + ModuleFactory, ModuleFactoryCreateData, ModuleFactoryResult, ModuleIdentifier, RawModule, + ResolveArgs, ResolveOptionsWithDependencyType, ResolveResult, Resolver, ResolverFactory, + SharedPluginDriver, }; -define_hook!(ContextModuleFactoryBeforeResolve: AsyncSeriesBail(data: &mut ModuleFactoryCreateData) -> bool); -define_hook!(ContextModuleFactoryAfterResolve: AsyncSeriesBail(data: &mut ModuleFactoryCreateData) -> bool); +#[derive(Clone)] +pub enum BeforeResolveResult { + Ignored, + Data(Box), +} + +#[derive(Clone)] +pub struct BeforeResolveData { + // context_info + // resolve_options + pub context: String, + pub request: Option, + // assertions + // dependencies + // dependency_type + // file_dependencies + // missing_dependencies + // context_dependencies + // create_data + // cacheable +} + +#[derive(Clone)] +pub enum AfterResolveResult { + Ignored, + Data(Box), +} + +#[derive(Clone)] +pub struct AfterResolveData { + pub resource: String, + pub context: String, + // dependencies + // layer + // resolve_options + // file_dependencies: HashSet, + // missing_dependencies: HashSet, + // context_dependencies: HashSet, + pub request: String, + // mode + // recursive: bool, + pub reg_exp: Option, + // namespace_object + // addon: String, + // chunk_name: String, + // include + // exclude + // group_options + // type_prefix: String, + // category: String, + // referenced_exports +} + +define_hook!(ContextModuleFactoryBeforeResolve: AsyncSeriesWaterfall(data: BeforeResolveResult) -> BeforeResolveResult); +define_hook!(ContextModuleFactoryAfterResolve: AsyncSeriesWaterfall(data: AfterResolveResult) -> AfterResolveResult); #[derive(Debug, Default)] pub struct ContextModuleFactoryHooks { @@ -35,10 +89,12 @@ impl ModuleFactory for ContextModuleFactory { return Ok(before_resolve_result); } - let factorize_result = self.resolve(data).await?; + let (factorize_result, mut context_module_options) = self.resolve(data).await?; - if let Some(false) = self.after_resolve(data).await? { - return Ok(ModuleFactoryResult::default()); + if let Some(context_module_options) = context_module_options.as_mut() { + if let Some(factorize_result) = self.after_resolve(context_module_options).await? { + return Ok(factorize_result); + } } Ok(factorize_result) @@ -62,19 +118,24 @@ impl ContextModuleFactory { &self, data: &mut ModuleFactoryCreateData, ) -> Result> { - if let Some(false) = self + let before_resolve_data = BeforeResolveData { + context: data.context.to_string(), + request: data.request().map(|r| r.to_string()), + }; + + match self .plugin_driver .context_module_factory_hooks .before_resolve - .call(data) + .call(BeforeResolveResult::Data(Box::new(before_resolve_data))) .await? { - // ignored - // See https://github.com/webpack/webpack/blob/6be4065ade1e252c1d8dcba4af0f43e32af1bdc1/lib/ContextModuleFactory.js#L115 - return Ok(Some(ModuleFactoryResult::default())); + BeforeResolveResult::Ignored => Ok(Some(ModuleFactoryResult::default())), + BeforeResolveResult::Data(d) => { + data.context = d.context.into(); + Ok(None) + } } - - Ok(None) } fn get_loader_resolver(&self) -> Arc { @@ -87,7 +148,10 @@ impl ContextModuleFactory { }) } - async fn resolve(&self, data: &mut ModuleFactoryCreateData) -> Result { + async fn resolve( + &self, + data: &mut ModuleFactoryCreateData, + ) -> Result<(ModuleFactoryResult, Option)> { let plugin_driver = &self.plugin_driver; let dependency = data .dependency @@ -175,18 +239,22 @@ impl ContextModuleFactory { Err(err) => (Err(err), false), }; - let module = match resource_data { - Ok(ResolveResult::Resource(resource)) => Box::new(ContextModule::new( - ContextModuleOptions { + let (module, context_module_options) = match resource_data { + Ok(ResolveResult::Resource(resource)) => { + let options = ContextModuleOptions { addon: loader_request.to_string(), resource: resource.path.to_string_lossy().to_string(), resource_query: resource.query, resource_fragment: resource.fragment, resolve_options: data.resolve_options.clone(), context_options: dependency.options().clone(), - }, - plugin_driver.resolver_factory.clone(), - )) as BoxModule, + }; + let module = Box::new(ContextModule::new( + options.clone(), + plugin_driver.resolver_factory.clone(), + )); + (module, Some(options)) + } Ok(ResolveResult::Ignored) => { let ident = format!("{}/{}", data.context, specifier); let module_identifier = ModuleIdentifier::from(format!("ignored|{ident}")); @@ -197,7 +265,7 @@ impl ContextModuleFactory { Default::default(), ) .boxed(); - return Ok(ModuleFactoryResult::new_with_module(raw_module)); + return Ok((ModuleFactoryResult::new_with_module(raw_module), None)); } Err(err) => { return Err(err); @@ -208,18 +276,43 @@ impl ContextModuleFactory { data.add_missing_dependencies(missing_dependencies); // data.add_context_dependencies(context_dependencies); - Ok(ModuleFactoryResult { + let module_factory_result = ModuleFactoryResult { module: Some(module), from_cache, - }) + }; + Ok((module_factory_result, context_module_options)) } - async fn after_resolve(&self, data: &mut ModuleFactoryCreateData) -> Result> { - self + async fn after_resolve( + &self, + context_module_options: &mut ContextModuleOptions, + ) -> Result> { + let context_options = &context_module_options.context_options; + let after_resolve_data = AfterResolveData { + resource: context_module_options.resource.to_owned(), + context: context_options.context.to_owned(), + request: context_options.request.to_owned(), + reg_exp: context_options.reg_exp.clone(), + }; + + match self .plugin_driver .context_module_factory_hooks .after_resolve - .call(data) - .await + .call(AfterResolveResult::Data(Box::new(after_resolve_data))) + .await? + { + AfterResolveResult::Ignored => Ok(Some(ModuleFactoryResult::default())), + AfterResolveResult::Data(d) => { + context_module_options.resource = d.resource; + context_module_options.context_options.reg_exp = d.reg_exp; + + let module = ContextModule::new( + context_module_options.clone(), + self.loader_resolver_factory.clone(), + ); + Ok(Some(ModuleFactoryResult::new_with_module(Box::new(module)))) + } + } } } diff --git a/crates/rspack_macros/src/hook.rs b/crates/rspack_macros/src/hook.rs index ce6e97ad03a..60ad7831fc2 100644 --- a/crates/rspack_macros/src/hook.rs +++ b/crates/rspack_macros/src/hook.rs @@ -29,6 +29,18 @@ impl Parse for DefineHookInput { "SyncSeriesBail" => ExecKind::SyncSeriesBail { ret: ExecKind::parse_ret(input)?, }, + "AsyncSeriesWaterfall" => { + let ret = match ExecKind::parse_ret(input)? { + Some(t) => t, + None => { + return Err(Error::new( + input.span(), + "Waterfall hooks must explicitly define a return type", + )) + } + }; + ExecKind::AsyncSeriesWaterfall { ret } + } "AsyncSeries" => ExecKind::AsyncSeries, "AsyncParallel" => ExecKind::AsyncParallel, "SyncSeries" => ExecKind::SyncSeries, @@ -142,6 +154,7 @@ impl DefineHookInput { enum ExecKind { AsyncSeries, AsyncSeriesBail { ret: Option }, + AsyncSeriesWaterfall { ret: TypePath }, AsyncParallel, SyncSeries, SyncSeriesBail { ret: Option }, @@ -160,7 +173,10 @@ impl ExecKind { pub fn is_async(&self) -> bool { match self { - Self::AsyncSeries | Self::AsyncSeriesBail { .. } | Self::AsyncParallel => true, + Self::AsyncSeries + | Self::AsyncSeriesBail { .. } + | Self::AsyncSeriesWaterfall { .. } + | Self::AsyncParallel => true, Self::SyncSeries | Self::SyncSeriesBail { .. } => false, } } @@ -174,6 +190,9 @@ impl ExecKind { quote! { rspack_hook::__macro_helper::Result> } } } + Self::AsyncSeriesWaterfall { ret } => { + quote! { rspack_hook::__macro_helper::Result<#ret> } + } _ => quote! { rspack_hook::__macro_helper::Result<()> }, } } @@ -219,6 +238,16 @@ impl ExecKind { Ok(None) } } + Self::AsyncSeriesWaterfall { .. } => { + quote! { + #additional_taps + let mut data = #args; + for tap in all_taps { + data = tap.run(data).await? + } + Ok(data) + } + } Self::AsyncParallel => { quote! { #additional_taps diff --git a/crates/rspack_plugin_ignore/src/lib.rs b/crates/rspack_plugin_ignore/src/lib.rs index 32fea1f2209..4b2fe3461f4 100644 --- a/crates/rspack_plugin_ignore/src/lib.rs +++ b/crates/rspack_plugin_ignore/src/lib.rs @@ -3,8 +3,8 @@ use std::fmt::Debug; use derivative::Derivative; use futures::future::BoxFuture; use rspack_core::{ - ApplyContext, CompilerOptions, ContextModuleFactoryBeforeResolve, ModuleFactoryCreateData, - NormalModuleFactoryBeforeResolve, Plugin, PluginContext, + ApplyContext, BeforeResolveResult, CompilerOptions, ContextModuleFactoryBeforeResolve, + ModuleFactoryCreateData, NormalModuleFactoryBeforeResolve, Plugin, PluginContext, }; use rspack_error::Result; use rspack_hook::{plugin, plugin_hook}; @@ -37,12 +37,12 @@ impl IgnorePlugin { Self::new_inner(options) } - async fn check_ignore(&self, data: &mut ModuleFactoryCreateData) -> Option { + async fn check_ignore(&self, request: Option<&str>, context: &str) -> Option { if let Some(check_resource) = &self.options.check_resource { - if let Some(request) = data.request() { + if let Some(request) = request { match check_resource { CheckResourceContent::Fn(check) => { - if check(request, data.context.as_ref()) + if check(request, context) .await .expect("run IgnorePlugin check resource error") { @@ -54,9 +54,9 @@ impl IgnorePlugin { } if let Some(resource_reg_exp) = &self.options.resource_reg_exp { - if resource_reg_exp.test(data.request()?) { + if resource_reg_exp.test(request?) { if let Some(context_reg_exp) = &self.options.context_reg_exp { - if context_reg_exp.test(&data.context) { + if context_reg_exp.test(context) { return Some(false); } } else { @@ -71,12 +71,21 @@ impl IgnorePlugin { #[plugin_hook(NormalModuleFactoryBeforeResolve for IgnorePlugin)] async fn nmf_before_resolve(&self, data: &mut ModuleFactoryCreateData) -> Result> { - Ok(self.check_ignore(data).await) + Ok(self.check_ignore(data.request(), &data.context).await) } #[plugin_hook(ContextModuleFactoryBeforeResolve for IgnorePlugin)] -async fn cmf_before_resolve(&self, data: &mut ModuleFactoryCreateData) -> Result> { - Ok(self.check_ignore(data).await) +async fn cmf_before_resolve(&self, data: BeforeResolveResult) -> Result { + match data { + BeforeResolveResult::Ignored => Ok(BeforeResolveResult::Ignored), + BeforeResolveResult::Data(d) => { + if let Some(false) = self.check_ignore(d.request.as_deref(), &d.context).await { + Ok(BeforeResolveResult::Ignored) + } else { + Ok(BeforeResolveResult::Data(d)) + } + } + } } impl Plugin for IgnorePlugin { diff --git a/packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/index.js b/packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/index.js new file mode 100644 index 00000000000..e75f436b87e --- /dev/null +++ b/packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/index.js @@ -0,0 +1,15 @@ +it("should compile", async () => { + try { + ["fr.js"].map(file => { + require("./locale/" + file); + }); + } catch (e) { + expect(e.message).toContain("Cannot find module './fr.js'") + } + ["zh.js"].map(file => { + expect(require("./locale/" + file).default).toBe("你好"); + }); + ["en.js"].map(file => { + expect(require("./locale/" + file).default).toBe("hello"); + }); +}); diff --git a/packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/locale/en.js b/packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/locale/en.js new file mode 100644 index 00000000000..b514bd48f97 --- /dev/null +++ b/packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/locale/en.js @@ -0,0 +1 @@ +export default 'hello'; diff --git a/packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/locale/fr.js b/packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/locale/fr.js new file mode 100644 index 00000000000..a2bbb2f7390 --- /dev/null +++ b/packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/locale/fr.js @@ -0,0 +1 @@ +export default 'bonjour'; diff --git a/packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/locale/zh.js b/packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/locale/zh.js new file mode 100644 index 00000000000..9d379cf2367 --- /dev/null +++ b/packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/locale/zh.js @@ -0,0 +1 @@ +export default '你好'; \ No newline at end of file diff --git a/packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/webpack.config.js b/packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/webpack.config.js new file mode 100644 index 00000000000..fcc5a341e33 --- /dev/null +++ b/packages/rspack-test-tools/tests/configCases/hooks/context-module-before-resolve/webpack.config.js @@ -0,0 +1,26 @@ +const pluginName = "plugin"; + +class Plugin { + apply(compiler) { + compiler.hooks.contextModuleFactory.tap( + pluginName, + contextModuleFactory => { + contextModuleFactory.hooks.beforeResolve.tap(pluginName, resolveData => { + if (resolveData.request.includes("./locale")) { + resolveData.regExp = /[/\\](en(\.js)?|zh(\.js)?)$/; + return resolveData; + } + }); + } + ); + } +} +/**@type {import('@rspack/cli').Configuration}*/ +module.exports = { + context: __dirname, + entry: "./index.js", + module: { + rules: [] + }, + plugins: [new Plugin()] +}; diff --git a/packages/rspack-test-tools/tests/errorCases/warning-test-push.js b/packages/rspack-test-tools/tests/errorCases/warning-test-push.js index 40a8a9e17ae..a13ba23db6a 100644 --- a/packages/rspack-test-tools/tests/errorCases/warning-test-push.js +++ b/packages/rspack-test-tools/tests/errorCases/warning-test-push.js @@ -19,8 +19,8 @@ module.exports = { "errors": Array [], "warnings": Array [ Object { - "formatted": " ⚠ Error: test push\\n │ at packages/rspack-test-tools/tests/errorCases/warning-test-push.js:10:33\\n │ at Hook.eval [as callAsync] (eval at create (node_modules/tapable/lib/HookCodeFactory.js:33:10), :9:1)\\n │ at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (node_modules/tapable/lib/Hook.js:18:14)\\n │ at packages/rspack/dist/Compiler.js:419:41\\n │ at packages/rspack/dist/Compiler.js:745:65\\n", - "message": " ⚠ Error: test push\\n │ at packages/rspack-test-tools/tests/errorCases/warning-test-push.js:10:33\\n │ at Hook.eval [as callAsync] (eval at create (node_modules/tapable/lib/HookCodeFactory.js:33:10), :9:1)\\n │ at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (node_modules/tapable/lib/Hook.js:18:14)\\n │ at packages/rspack/dist/Compiler.js:419:41\\n │ at packages/rspack/dist/Compiler.js:745:65\\n", + "formatted": " ⚠ Error: test push\\n │ at packages/rspack-test-tools/tests/errorCases/warning-test-push.js:10:33\\n │ at Hook.eval [as callAsync] (eval at create (node_modules/tapable/lib/HookCodeFactory.js:33:10), :9:1)\\n │ at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (node_modules/tapable/lib/Hook.js:18:14)\\n │ at packages/rspack/dist/Compiler.js:419:41\\n │ at packages/rspack/dist/Compiler.js:751:65\\n", + "message": " ⚠ Error: test push\\n │ at packages/rspack-test-tools/tests/errorCases/warning-test-push.js:10:33\\n │ at Hook.eval [as callAsync] (eval at create (node_modules/tapable/lib/HookCodeFactory.js:33:10), :9:1)\\n │ at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (node_modules/tapable/lib/Hook.js:18:14)\\n │ at packages/rspack/dist/Compiler.js:419:41\\n │ at packages/rspack/dist/Compiler.js:751:65\\n", }, Object { "formatted": " ⚠ Module parse warning:\\n ╰─▶ ⚠ Module parse failed: require.main.require() is not supported by Rspack.\\n ╭────\\n 1 │ require.main.require('./file');\\n · ──────────────────────────────\\n ╰────\\n \\n", diff --git a/packages/rspack-test-tools/tests/errorCases/warning-test-shift.js b/packages/rspack-test-tools/tests/errorCases/warning-test-shift.js index d7f67da0e25..ed239f1456c 100644 --- a/packages/rspack-test-tools/tests/errorCases/warning-test-shift.js +++ b/packages/rspack-test-tools/tests/errorCases/warning-test-shift.js @@ -23,8 +23,8 @@ module.exports = { "errors": Array [], "warnings": Array [ Object { - "formatted": " ⚠ Error: test unshift\\n │ at packages/rspack-test-tools/tests/errorCases/warning-test-shift.js:13:37\\n │ at Hook.eval [as callAsync] (eval at create (node_modules/tapable/lib/HookCodeFactory.js:33:10), :9:1)\\n │ at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (node_modules/tapable/lib/Hook.js:18:14)\\n │ at packages/rspack/dist/Compiler.js:419:41\\n │ at packages/rspack/dist/Compiler.js:745:65\\n", - "message": " ⚠ Error: test unshift\\n │ at packages/rspack-test-tools/tests/errorCases/warning-test-shift.js:13:37\\n │ at Hook.eval [as callAsync] (eval at create (node_modules/tapable/lib/HookCodeFactory.js:33:10), :9:1)\\n │ at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (node_modules/tapable/lib/Hook.js:18:14)\\n │ at packages/rspack/dist/Compiler.js:419:41\\n │ at packages/rspack/dist/Compiler.js:745:65\\n", + "formatted": " ⚠ Error: test unshift\\n │ at packages/rspack-test-tools/tests/errorCases/warning-test-shift.js:13:37\\n │ at Hook.eval [as callAsync] (eval at create (node_modules/tapable/lib/HookCodeFactory.js:33:10), :9:1)\\n │ at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (node_modules/tapable/lib/Hook.js:18:14)\\n │ at packages/rspack/dist/Compiler.js:419:41\\n │ at packages/rspack/dist/Compiler.js:751:65\\n", + "message": " ⚠ Error: test unshift\\n │ at packages/rspack-test-tools/tests/errorCases/warning-test-shift.js:13:37\\n │ at Hook.eval [as callAsync] (eval at create (node_modules/tapable/lib/HookCodeFactory.js:33:10), :9:1)\\n │ at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (node_modules/tapable/lib/Hook.js:18:14)\\n │ at packages/rspack/dist/Compiler.js:419:41\\n │ at packages/rspack/dist/Compiler.js:751:65\\n", }, ], } diff --git a/packages/rspack-test-tools/tests/errorCases/warning-test-splice-1.js b/packages/rspack-test-tools/tests/errorCases/warning-test-splice-1.js index 0693d2f2dee..f1b86d30985 100644 --- a/packages/rspack-test-tools/tests/errorCases/warning-test-splice-1.js +++ b/packages/rspack-test-tools/tests/errorCases/warning-test-splice-1.js @@ -19,8 +19,8 @@ module.exports = { "errors": Array [], "warnings": Array [ Object { - "formatted": " ⚠ Error: test splice\\n │ at packages/rspack-test-tools/tests/errorCases/warning-test-splice-1.js:10:41\\n │ at Hook.eval [as callAsync] (eval at create (node_modules/tapable/lib/HookCodeFactory.js:33:10), :9:1)\\n │ at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (node_modules/tapable/lib/Hook.js:18:14)\\n │ at packages/rspack/dist/Compiler.js:419:41\\n │ at packages/rspack/dist/Compiler.js:745:65\\n", - "message": " ⚠ Error: test splice\\n │ at packages/rspack-test-tools/tests/errorCases/warning-test-splice-1.js:10:41\\n │ at Hook.eval [as callAsync] (eval at create (node_modules/tapable/lib/HookCodeFactory.js:33:10), :9:1)\\n │ at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (node_modules/tapable/lib/Hook.js:18:14)\\n │ at packages/rspack/dist/Compiler.js:419:41\\n │ at packages/rspack/dist/Compiler.js:745:65\\n", + "formatted": " ⚠ Error: test splice\\n │ at packages/rspack-test-tools/tests/errorCases/warning-test-splice-1.js:10:41\\n │ at Hook.eval [as callAsync] (eval at create (node_modules/tapable/lib/HookCodeFactory.js:33:10), :9:1)\\n │ at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (node_modules/tapable/lib/Hook.js:18:14)\\n │ at packages/rspack/dist/Compiler.js:419:41\\n │ at packages/rspack/dist/Compiler.js:751:65\\n", + "message": " ⚠ Error: test splice\\n │ at packages/rspack-test-tools/tests/errorCases/warning-test-splice-1.js:10:41\\n │ at Hook.eval [as callAsync] (eval at create (node_modules/tapable/lib/HookCodeFactory.js:33:10), :9:1)\\n │ at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (node_modules/tapable/lib/Hook.js:18:14)\\n │ at packages/rspack/dist/Compiler.js:419:41\\n │ at packages/rspack/dist/Compiler.js:751:65\\n", }, ], } diff --git a/packages/rspack-test-tools/tests/errorCases/warning-test-splice-2.js b/packages/rspack-test-tools/tests/errorCases/warning-test-splice-2.js index bd8b5f13986..016b2f215cd 100644 --- a/packages/rspack-test-tools/tests/errorCases/warning-test-splice-2.js +++ b/packages/rspack-test-tools/tests/errorCases/warning-test-splice-2.js @@ -19,8 +19,8 @@ module.exports = { "errors": Array [], "warnings": Array [ Object { - "formatted": " ⚠ Error: test splice\\n │ at packages/rspack-test-tools/tests/errorCases/warning-test-splice-2.js:10:41\\n │ at Hook.eval [as callAsync] (eval at create (node_modules/tapable/lib/HookCodeFactory.js:33:10), :9:1)\\n │ at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (node_modules/tapable/lib/Hook.js:18:14)\\n │ at packages/rspack/dist/Compiler.js:419:41\\n │ at packages/rspack/dist/Compiler.js:745:65\\n", - "message": " ⚠ Error: test splice\\n │ at packages/rspack-test-tools/tests/errorCases/warning-test-splice-2.js:10:41\\n │ at Hook.eval [as callAsync] (eval at create (node_modules/tapable/lib/HookCodeFactory.js:33:10), :9:1)\\n │ at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (node_modules/tapable/lib/Hook.js:18:14)\\n │ at packages/rspack/dist/Compiler.js:419:41\\n │ at packages/rspack/dist/Compiler.js:745:65\\n", + "formatted": " ⚠ Error: test splice\\n │ at packages/rspack-test-tools/tests/errorCases/warning-test-splice-2.js:10:41\\n │ at Hook.eval [as callAsync] (eval at create (node_modules/tapable/lib/HookCodeFactory.js:33:10), :9:1)\\n │ at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (node_modules/tapable/lib/Hook.js:18:14)\\n │ at packages/rspack/dist/Compiler.js:419:41\\n │ at packages/rspack/dist/Compiler.js:751:65\\n", + "message": " ⚠ Error: test splice\\n │ at packages/rspack-test-tools/tests/errorCases/warning-test-splice-2.js:10:41\\n │ at Hook.eval [as callAsync] (eval at create (node_modules/tapable/lib/HookCodeFactory.js:33:10), :9:1)\\n │ at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (node_modules/tapable/lib/Hook.js:18:14)\\n │ at packages/rspack/dist/Compiler.js:419:41\\n │ at packages/rspack/dist/Compiler.js:751:65\\n", }, Object { "formatted": " ⚠ Module parse warning:\\n ╰─▶ ⚠ Module parse failed: require.main.require() is not supported by Rspack.\\n ╭────\\n 1 │ require.main.require('./file');\\n · ──────────────────────────────\\n ╰────\\n \\n", diff --git a/packages/rspack/etc/api.md b/packages/rspack/etc/api.md index d418016175c..cecb6bc9864 100644 --- a/packages/rspack/etc/api.md +++ b/packages/rspack/etc/api.md @@ -413,6 +413,17 @@ class AsyncSeriesHook extends Hoo tapPromise(options: Options, fn: Fn): void; } +// @public (undocumented) +class AsyncSeriesWaterfallHook extends Hook { + constructor(args?: ArgumentNames>, name?: string); + // (undocumented) + callAsyncStageRange(queried: QueriedHook, ...args: Append, Callback>): void; + // (undocumented) + tapAsync(options: Options, fn: FnWithCallback): void; + // (undocumented) + tapPromise(options: Options, fn: Fn): void; +} + // @public (undocumented) export type AuxiliaryComment = z.infer; @@ -1463,11 +1474,30 @@ class ContextModuleFactory { constructor(); // (undocumented) hooks: { - beforeResolve: liteTapable.AsyncSeriesBailHook<[ResolveData], false | void>; - afterResolve: liteTapable.AsyncSeriesBailHook<[ResolveData], false | void>; + beforeResolve: liteTapable.AsyncSeriesWaterfallHook<[ + ContextModuleFactoryBeforeResolveResult + ], ContextModuleFactoryBeforeResolveResult | void>; + afterResolve: liteTapable.AsyncSeriesWaterfallHook<[ + ContextModuleFactoryAfterResolveResult + ], ContextModuleFactoryAfterResolveResult | void>; }; } +// @public (undocumented) +type ContextModuleFactoryAfterResolveResult = false | { + resource: string; + context: string; + request: string; + regExp?: RegExp; + dependencies: Array; +}; + +// @public (undocumented) +type ContextModuleFactoryBeforeResolveResult = false | { + context: string; + request?: string; +}; + // @public (undocumented) export const CopyRspackPlugin: { new (copy: CopyRspackPluginOptions): { @@ -4320,6 +4350,7 @@ declare namespace liteTapable { AsyncParallelHook, AsyncSeriesHook, AsyncSeriesBailHook, + AsyncSeriesWaterfallHook, HookMapKey, HookFactory, HookMapInterceptor, diff --git a/packages/rspack/src/Compiler.ts b/packages/rspack/src/Compiler.ts index 6db04a18adc..bf9084bb6d5 100644 --- a/packages/rspack/src/Compiler.ts +++ b/packages/rspack/src/Compiler.ts @@ -47,7 +47,12 @@ import { assertNotNill } from "./util/assertNotNil"; import { FileSystemInfoEntry } from "./FileSystemInfo"; import { RuntimeGlobals } from "./RuntimeGlobals"; import { tryRunOrWebpackError } from "./lib/HookWebpackError"; -import { CodeGenerationResult, Module, ResolveData } from "./Module"; +import { + CodeGenerationResult, + ContextModuleFactoryAfterResolveResult, + Module, + ResolveData +} from "./Module"; import { canInherentFromParent } from "./builtin-plugin/base"; import ExecuteModulePlugin from "./ExecuteModulePlugin"; import { Chunk } from "./Chunk"; @@ -530,33 +535,48 @@ class Compiler { binding.RegisterJsTapKind.ContextModuleFactoryBeforeResolve, () => this.compilationParams!.contextModuleFactory.hooks.beforeResolve, - queried => async (arg: binding.JsBeforeResolveArgs) => { - const data: ResolveData = { - request: arg.request, - context: arg.context, - fileDependencies: [], - missingDependencies: [], - contextDependencies: [] - }; - const ret = await queried.promise(data); - return [ret, data]; - } + queried => + async ( + bindingData: + | false + | binding.JsContextModuleFactoryBeforeResolveData + ) => { + return queried.promise(bindingData); + } ), registerContextModuleFactoryAfterResolveTaps: this.#createHookRegisterTaps( binding.RegisterJsTapKind.ContextModuleFactoryAfterResolve, () => this.compilationParams!.contextModuleFactory.hooks.afterResolve, - queried => async (arg: binding.JsAfterResolveData) => { - const data: ResolveData = { - request: arg.request, - context: arg.context, - fileDependencies: arg.fileDependencies, - missingDependencies: arg.missingDependencies, - contextDependencies: arg.contextDependencies, - createData: arg.createData - }; - return await queried.promise(data); - } + queried => + async ( + bindingData: + | false + | binding.JsContextModuleFactoryAfterResolveData + ) => { + const data = bindingData + ? ({ + resource: bindingData.resource, + regExp: bindingData.regExp + ? new RegExp(bindingData.regExp) + : undefined, + request: bindingData.request, + context: bindingData.context, + // TODO: Dependencies are not fully supported yet; this is a placeholder to prevent errors in moment-locales-webpack-plugin. + dependencies: [] + } satisfies ContextModuleFactoryAfterResolveResult) + : false; + const ret = await queried.promise(data); + const result = ret + ? ({ + resource: ret.resource, + context: ret.context, + request: ret.request, + regExp: ret.regExp?.toString() + } satisfies binding.JsContextModuleFactoryAfterResolveData) + : false; + return result; + } ) }; diff --git a/packages/rspack/src/ContextModuleFactory.ts b/packages/rspack/src/ContextModuleFactory.ts index 5c9bebdd9b9..fa56abea52a 100644 --- a/packages/rspack/src/ContextModuleFactory.ts +++ b/packages/rspack/src/ContextModuleFactory.ts @@ -1,5 +1,8 @@ import * as liteTapable from "./lite-tapable"; -import { ResolveData } from "./Module"; +import { + ContextModuleFactoryAfterResolveResult, + ContextModuleFactoryBeforeResolveResult +} from "./Module"; export class ContextModuleFactory { hooks: { @@ -7,8 +10,14 @@ export class ContextModuleFactory { // resolveForScheme: HookMap< // AsyncSeriesBailHook<[ResourceDataWithData], true | void> // >; - beforeResolve: liteTapable.AsyncSeriesBailHook<[ResolveData], false | void>; - afterResolve: liteTapable.AsyncSeriesBailHook<[ResolveData], false | void>; + beforeResolve: liteTapable.AsyncSeriesWaterfallHook< + [ContextModuleFactoryBeforeResolveResult], + ContextModuleFactoryBeforeResolveResult | void + >; + afterResolve: liteTapable.AsyncSeriesWaterfallHook< + [ContextModuleFactoryAfterResolveResult], + ContextModuleFactoryAfterResolveResult | void + >; }; constructor() { this.hooks = { @@ -25,8 +34,8 @@ export class ContextModuleFactory { // /** @type {AsyncSeriesBailHook<[ResolveData], Module>} */ // factorize: new AsyncSeriesBailHook(["resolveData"]), // /** @type {AsyncSeriesBailHook<[ResolveData], false | void>} */ - beforeResolve: new liteTapable.AsyncSeriesBailHook(["resolveData"]), - afterResolve: new liteTapable.AsyncSeriesBailHook(["resolveData"]) + beforeResolve: new liteTapable.AsyncSeriesWaterfallHook(["resolveData"]), + afterResolve: new liteTapable.AsyncSeriesWaterfallHook(["resolveData"]) // /** @type {AsyncSeriesBailHook<[ResolveData["createData"], ResolveData], Module | void>} */ // createModule: new AsyncSeriesBailHook(["createData", "resolveData"]), // /** @type {SyncWaterfallHook<[Module, ResolveData["createData"], ResolveData], Module>} */ diff --git a/packages/rspack/src/Module.ts b/packages/rspack/src/Module.ts index a17639df4eb..303eee73fc8 100644 --- a/packages/rspack/src/Module.ts +++ b/packages/rspack/src/Module.ts @@ -26,6 +26,23 @@ export type ResolveData = { createData?: CreateData; }; +export type ContextModuleFactoryBeforeResolveResult = + | false + | { + context: string; + request?: string; + }; + +export type ContextModuleFactoryAfterResolveResult = + | false + | { + resource: string; + context: string; + request: string; + regExp?: RegExp; + dependencies: Array; + }; + export class Module { #inner: JsModule; _originalSource?: Source; diff --git a/packages/rspack/src/lite-tapable/index.ts b/packages/rspack/src/lite-tapable/index.ts index 902f91e87e5..8e2905fa027 100644 --- a/packages/rspack/src/lite-tapable/index.ts +++ b/packages/rspack/src/lite-tapable/index.ts @@ -772,6 +772,124 @@ export class AsyncSeriesBailHook< } } +export class AsyncSeriesWaterfallHook< + T, + R, + AdditionalOptions = UnsetAdditionalOptions +> extends Hook { + constructor(args?: ArgumentNames>, name?: string) { + if (!args?.length) + throw new Error("Waterfall hooks must have at least one argument"); + super(args, name); + } + + callAsyncStageRange( + queried: QueriedHook, + ...args: Append, Callback> + ) { + const { + stageRange: [from, to], + tapsInRange + } = queried; + let data = args[0] as R; + const cb = args[1] as Callback; + if (from === minStage) { + this._runCallInterceptors(data); + } + const done = () => { + this._runDoneInterceptors(); + cb(null, data); + }; + const error = (e: Error) => { + this._runErrorInterceptors(e); + cb(e); + }; + if (tapsInRange.length === 0) return done(); + let index = 0; + const next = () => { + const tap = tapsInRange[index]; + this._runTapInterceptors(tap); + if (tap.type === "promise") { + const promise = tap.fn(data); + if (!promise || !promise.then) { + throw new Error( + "Tap function (tapPromise) did not return promise (returned " + + promise + + ")" + ); + } + promise.then( + (r: R) => { + index += 1; + if (r !== undefined) { + data = r; + } + if (index === tapsInRange.length) { + done(); + } else { + next(); + } + }, + (e: Error) => { + index = tapsInRange.length; + error(e); + } + ); + } else if (tap.type === "async") { + tap.fn(data, (e: Error, r: R) => { + if (e) { + index = tapsInRange.length; + error(e); + } else { + index += 1; + if (r !== undefined) { + data = r; + } + if (index === tapsInRange.length) { + done(); + } else { + next(); + } + } + }); + } else { + let hasError = false; + try { + const r = tap.fn(data); + if (r !== undefined) { + data = r; + } + } catch (e) { + hasError = true; + index = tapsInRange.length; + error(e as Error); + } + if (!hasError) { + index += 1; + if (index === tapsInRange.length) { + done(); + } else { + next(); + } + } + } + if (index === tapsInRange.length) return; + }; + next(); + } + + tapAsync( + options: Options, + fn: FnWithCallback + ): void { + this._tap("async", options, fn); + } + + tapPromise(options: Options, fn: Fn): void { + this._tap("promise", options, fn); + } +} + const defaultFactory = (key: HookMapKey, hook: unknown) => hook; export type HookMapKey = any;