From 7023168e287568d26c582ce87d2d2e8b26a17914 Mon Sep 17 00:00:00 2001 From: jserfeng <1114550440@qq.com> Date: Mon, 20 Nov 2023 19:59:46 +0800 Subject: [PATCH] feat(compilation): add pluginImport and executeModule --- crates/node_binding/binding.d.ts | 19 + crates/node_binding/src/plugins/mod.rs | 22 +- .../src/options/raw_module/js_loader.rs | 13 + .../src/codegen_result.rs | 40 +- .../rspack_binding_values/src/compilation.rs | 68 +++ crates/rspack_binding_values/src/module.rs | 5 +- .../rspack_core/src/compiler/compilation.rs | 238 ++++++---- .../src/compiler/execute_module.rs | 435 ++++++++++++++++++ crates/rspack_core/src/compiler/mod.rs | 1 + crates/rspack_core/src/compiler/queue.rs | 57 ++- .../src/dependency/dependency_category.rs | 2 + .../src/dependency/dependency_type.rs | 2 + .../dependency/loader_import_dependency.rs | 53 +++ crates/rspack_core/src/dependency/mod.rs | 2 + .../rspack_core/src/loader/loader_runner.rs | 49 +- crates/rspack_core/src/logger.rs | 1 + crates/rspack_core/src/plugin/api.rs | 33 +- .../rspack_core/src/plugin/plugin_driver.rs | 46 +- crates/rspack_core/src/tree_shaking/mod.rs | 1 + crates/rspack_database/src/database.rs | 4 +- crates/rspack_loader_swc/tests/fixtures.rs | 77 +++- .../harmony_import_specifier_dependency.rs | 6 +- .../src/plugin/module_concatenation_plugin.rs | 3 + packages/rspack/src/Compilation.ts | 2 +- packages/rspack/src/Compiler.ts | 29 +- packages/rspack/src/ExecuteModulePlugin.ts | 57 +++ packages/rspack/src/Module.ts | 5 + packages/rspack/src/config/adapterRuleUse.ts | 5 + packages/rspack/src/loader-runner/index.ts | 74 +++ .../loader-import-module/css/colors.js | 2 + .../loader-import-module/css/file.jpg | Bin 0 -> 6027 bytes .../loader-import-module/css/file.png | Bin 0 -> 14910 bytes .../loader-import-module/css/index.js | 11 + .../loader-import-module/css/loader.js | 13 + .../css/other-stylesheet.js | 3 + .../loader-import-module/css/stylesheet.js | 4 + .../css/webpack.config.js | 77 ++++ webpack-test/ConfigTestCases.template.js | 81 ++-- .../loader-import-module/css/test.filter.js | 9 +- 39 files changed, 1337 insertions(+), 212 deletions(-) create mode 100644 crates/rspack_core/src/compiler/execute_module.rs create mode 100644 crates/rspack_core/src/dependency/loader_import_dependency.rs create mode 100644 packages/rspack/src/ExecuteModulePlugin.ts create mode 100644 packages/rspack/tests/configCases/loader-import-module/css/colors.js create mode 100644 packages/rspack/tests/configCases/loader-import-module/css/file.jpg create mode 100644 packages/rspack/tests/configCases/loader-import-module/css/file.png create mode 100644 packages/rspack/tests/configCases/loader-import-module/css/index.js create mode 100644 packages/rspack/tests/configCases/loader-import-module/css/loader.js create mode 100644 packages/rspack/tests/configCases/loader-import-module/css/other-stylesheet.js create mode 100644 packages/rspack/tests/configCases/loader-import-module/css/stylesheet.js create mode 100644 packages/rspack/tests/configCases/loader-import-module/css/webpack.config.js diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 70db5dab060..006c3eaaf21 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -45,6 +45,7 @@ export class JsCompilation { addMissingDependencies(deps: Array): void addBuildDependencies(deps: Array): void rebuildModule(moduleIdentifiers: Array, f: (...args: any[]) => any): void + importModule(request: string, publicPath: string | undefined | null, baseUri: string | undefined | null, originalModule: string | undefined | null, originalModuleContext: string | undefined | null, callback: (...args: any[]) => any): void } export class JsStats { @@ -253,6 +254,11 @@ export interface JsAssetInfoRelated { sourceMap?: string } +export interface JsBuildTimeExecutionOption { + publicPath?: string + baseUri?: string +} + export interface JsChunk { __inner_ukey: number __inner_groups: Array @@ -302,8 +308,20 @@ export interface JsCompatSource { export interface JsExecuteModuleArg { entry: string + request: string + options: JsBuildTimeExecutionOption runtimeModules: Array codegenResults: JsCodegenerationResults + id: number +} + +export interface JsExecuteModuleResult { + fileDependencies: Array + contextDependencies: Array + buildDependencies: Array + missingDependencies: Array + assets: Array + id: number } export interface JsHooks { @@ -392,6 +410,7 @@ export interface JsLoaderContext { * @internal */ diagnosticsExternal: ExternalObject<'Diagnostic[]'> + _moduleIdentifier: string } export interface JsModule { diff --git a/crates/node_binding/src/plugins/mod.rs b/crates/node_binding/src/plugins/mod.rs index e1a872bd11c..3ddaa730ddc 100644 --- a/crates/node_binding/src/plugins/mod.rs +++ b/crates/node_binding/src/plugins/mod.rs @@ -10,10 +10,11 @@ use rspack_binding_values::{ ToJsCompatSource, }; use rspack_binding_values::{BeforeResolveData, JsAssetEmittedArgs, ToJsModule}; -use rspack_binding_values::{CreateModuleData, JsExecuteModuleArg}; +use rspack_binding_values::{CreateModuleData, JsBuildTimeExecutionOption, JsExecuteModuleArg}; use rspack_binding_values::{JsResolveForSchemeInput, JsResolveForSchemeResult}; use rspack_core::{ - Chunk, ChunkAssetArgs, Compilation, ModuleIdentifier, NormalModuleAfterResolveArgs, RuntimeModule, + BuildTimeExecutionOption, Chunk, ChunkAssetArgs, Compilation, ModuleIdentifier, + NormalModuleAfterResolveArgs, RuntimeModule, }; use rspack_core::{NormalModuleBeforeResolveArgs, PluginNormalModuleFactoryAfterResolveOutput}; use rspack_core::{ @@ -71,7 +72,7 @@ pub struct JsHooksAdapter { ThreadsafeFunction, pub succeed_module_tsfn: ThreadsafeFunction, pub still_valid_module_tsfn: ThreadsafeFunction, - pub execute_module_tsfn: ThreadsafeFunction>, + pub execute_module_tsfn: ThreadsafeFunction, pub runtime_module_tsfn: ThreadsafeFunction>, } @@ -832,11 +833,14 @@ impl rspack_core::Plugin for JsHooksAdapter { fn execute_module( &self, entry: ModuleIdentifier, + request: &str, + options: &BuildTimeExecutionOption, runtime_modules: Vec, codegen_results: &rspack_core::CodeGenerationResults, - ) -> rspack_error::Result> { + id: u32, + ) -> rspack_error::Result<()> { if self.is_hook_disabled(&Hook::ExecuteModule) { - return Ok(None); + return Ok(()); } self @@ -844,11 +848,17 @@ impl rspack_core::Plugin for JsHooksAdapter { .call( JsExecuteModuleArg { entry: entry.to_string(), + request: request.into(), + options: JsBuildTimeExecutionOption { + public_path: options.public_path.clone(), + base_uri: options.base_uri.clone(), + }, runtime_modules: runtime_modules .into_iter() .map(|id| id.to_string()) .collect(), codegen_results: codegen_results.clone().into(), + id, }, ThreadsafeFunctionCallMode::NonBlocking, ) @@ -1031,7 +1041,7 @@ impl JsHooksAdapter { js_fn_into_threadsafe_fn!(succeed_module, env); let still_valid_module_tsfn: ThreadsafeFunction = js_fn_into_threadsafe_fn!(still_valid_module, env); - let execute_module_tsfn: ThreadsafeFunction> = + let execute_module_tsfn: ThreadsafeFunction = js_fn_into_threadsafe_fn!(execute_module, env); let runtime_module_tsfn: ThreadsafeFunction> = js_fn_into_threadsafe_fn!(runtime_module, env); diff --git a/crates/rspack_binding_options/src/options/raw_module/js_loader.rs b/crates/rspack_binding_options/src/options/raw_module/js_loader.rs index 96a41c3e620..92854b169f4 100644 --- a/crates/rspack_binding_options/src/options/raw_module/js_loader.rs +++ b/crates/rspack_binding_options/src/options/raw_module/js_loader.rs @@ -215,6 +215,7 @@ fn sync_loader_context( } else { loader_context.additional_data.remove::(); } + loader_context.asset_filenames = loader_result.asset_filenames.into_iter().collect(); Ok(()) } @@ -256,6 +257,9 @@ pub struct JsLoaderContext { /// @internal #[napi(ts_type = "ExternalObject<'Diagnostic[]'>")] pub diagnostics_external: External>, + + #[napi(js_name = "_moduleIdentifier")] + pub module_identifier: String, } impl TryFrom<&mut rspack_core::LoaderContext<'_, rspack_core::LoaderRunnerContext>> @@ -316,6 +320,7 @@ impl TryFrom<&mut rspack_core::LoaderContext<'_, rspack_core::LoaderRunnerContex additional_data_external: External::new(cx.additional_data.clone()), context_external: External::new(cx.context.clone()), diagnostics_external: External::new(cx.__diagnostics.drain(..).collect()), + module_identifier: cx.context.module.to_string(), }) } } @@ -414,6 +419,7 @@ pub struct JsLoaderResult { pub context_dependencies: Vec, pub missing_dependencies: Vec, pub build_dependencies: Vec, + pub asset_filenames: Vec, pub source_map: Option, pub additional_data: Option, pub additional_data_external: External, @@ -439,6 +445,12 @@ impl napi::bindgen_prelude::FromNapiValue for JsLoaderResult { ) -> napi::bindgen_prelude::Result { let obj = napi::bindgen_prelude::Object::from_napi_value(env, napi_val)?; let content_: Option = obj.get("content")?; + let asset_filenames_: Vec = obj.get("assetFilenames")?.ok_or_else(|| { + napi::bindgen_prelude::Error::new( + napi::bindgen_prelude::Status::InvalidArg, + format!("Missing field `{}`", "assetFilenames"), + ) + })?; let file_dependencies_: Vec = obj.get("fileDependencies")?.ok_or_else(|| { napi::bindgen_prelude::Error::new( napi::bindgen_prelude::Status::InvalidArg, @@ -493,6 +505,7 @@ impl napi::bindgen_prelude::FromNapiValue for JsLoaderResult { context_dependencies: context_dependencies_, missing_dependencies: missing_dependencies_, build_dependencies: build_dependencies_, + asset_filenames: asset_filenames_, source_map: source_map_, additional_data: additional_data_, additional_data_external: additional_data_external_, diff --git a/crates/rspack_binding_values/src/codegen_result.rs b/crates/rspack_binding_values/src/codegen_result.rs index a430d8cd16a..b5618c69828 100644 --- a/crates/rspack_binding_values/src/codegen_result.rs +++ b/crates/rspack_binding_values/src/codegen_result.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use napi_derive::napi; -use rspack_core::{CodeGenerationResult, CodeGenerationResults}; +use rspack_core::{get_runtime_key, CodeGenerationResult, CodeGenerationResults}; #[napi(object)] #[derive(Debug)] @@ -36,23 +36,27 @@ impl From for JsCodegenerationResults { .map .into_iter() .map(|(module_id, runtime_result_map)| { - ( - module_id.to_string(), - runtime_result_map - .map - .into_iter() - .map(|(k, result_id)| { - ( - k, - id_result_map - .get(&result_id) - .expect("should exist codegenResult") - .clone() - .into(), - ) - }) - .collect(), - ) + let mut runtime_map: HashMap = Default::default(); + match &runtime_result_map.mode { + rspack_core::RuntimeMode::Empty => {} + rspack_core::RuntimeMode::SingleEntry => { + runtime_map.insert( + get_runtime_key(runtime_result_map.single_runtime.expect("exist")), + id_result_map + .get(&runtime_result_map.single_value.expect("TODO")) + .expect("TODO") + .clone() + .into(), + ); + } + rspack_core::RuntimeMode::Map => { + runtime_result_map.map.into_iter().for_each(|(k, v)| { + runtime_map.insert(k, id_result_map.get(&v).expect("TODO").clone().into()); + }); + } + }; + + (module_id.to_string(), runtime_map) }) .collect(), } diff --git a/crates/rspack_binding_values/src/compilation.rs b/crates/rspack_binding_values/src/compilation.rs index feb6a1d5d5f..ffb785af4d6 100644 --- a/crates/rspack_binding_values/src/compilation.rs +++ b/crates/rspack_binding_values/src/compilation.rs @@ -429,6 +429,67 @@ impl JsCompilation { ) }) } + + #[allow(clippy::too_many_arguments)] + #[napi] + pub fn import_module( + &'static self, + env: Env, + request: String, + public_path: Option, + base_uri: Option, + original_module: Option, + original_module_context: Option, + callback: JsFunction, + ) -> Result<()> { + callbackify(env, callback, async { + self + .inner + .import_module( + request, + public_path, + base_uri, + original_module.map(|s| s.into()), + original_module_context.map(|ctx| Box::new(rspack_core::Context::new(ctx))), + ) + .await + .map(|res| JsExecuteModuleResult { + file_dependencies: res + .file_dependencies + .into_iter() + .map(|d| d.to_string_lossy().to_string()) + .collect(), + context_dependencies: res + .context_dependencies + .into_iter() + .map(|d| d.to_string_lossy().to_string()) + .collect(), + build_dependencies: res + .build_dependencies + .into_iter() + .map(|d| d.to_string_lossy().to_string()) + .collect(), + missing_dependencies: res + .missing_dependencies + .into_iter() + .map(|d| d.to_string_lossy().to_string()) + .collect(), + assets: res.assets.into_iter().collect(), + id: res.id, + }) + .map_err(|e| Error::new(napi::Status::GenericFailure, format!("{e}"))) + }) + } +} + +#[napi(object)] +pub struct JsExecuteModuleResult { + pub file_dependencies: Vec, + pub context_dependencies: Vec, + pub build_dependencies: Vec, + pub missing_dependencies: Vec, + pub assets: Vec, + pub id: u32, } impl JsCompilation { @@ -436,3 +497,10 @@ impl JsCompilation { Self { inner } } } + +#[napi(object)] +#[derive(Clone, Debug)] +pub struct JsBuildTimeExecutionOption { + pub public_path: Option, + pub base_uri: Option, +} diff --git a/crates/rspack_binding_values/src/module.rs b/crates/rspack_binding_values/src/module.rs index 9a392868309..bb94e235458 100644 --- a/crates/rspack_binding_values/src/module.rs +++ b/crates/rspack_binding_values/src/module.rs @@ -3,7 +3,7 @@ use napi_derive::napi; use rspack_core::Module; use super::{JsCompatSource, ToJsCompatSource}; -use crate::{JsChunk, JsCodegenerationResults}; +use crate::{JsBuildTimeExecutionOption, JsChunk, JsCodegenerationResults}; #[derive(Default)] #[napi(object)] @@ -86,8 +86,11 @@ impl ToJsModule for dyn Module + '_ { #[napi(object)] pub struct JsExecuteModuleArg { pub entry: String, + pub request: String, + pub options: JsBuildTimeExecutionOption, pub runtime_modules: Vec, pub codegen_results: JsCodegenerationResults, + pub id: u32, } #[derive(Default)] diff --git a/crates/rspack_core/src/compiler/compilation.rs b/crates/rspack_core/src/compiler/compilation.rs index aeec0a41411..e635b62f2d7 100644 --- a/crates/rspack_core/src/compiler/compilation.rs +++ b/crates/rspack_core/src/compiler/compilation.rs @@ -34,16 +34,16 @@ use crate::{ tree_shaking::{optimizer, visitor::SymbolRef, BailoutFlag, OptimizeDependencyResult}, AddQueue, AddTask, AddTaskResult, AdditionalChunkRuntimeRequirementsArgs, AdditionalModuleRequirementsArgs, AsyncDependenciesBlock, BoxDependency, BoxModule, BuildQueue, - BuildTask, BuildTaskResult, CacheCount, CacheOptions, Chunk, ChunkByUkey, ChunkContentHash, - ChunkGraph, ChunkGroupByUkey, ChunkGroupUkey, ChunkHashArgs, ChunkKind, ChunkUkey, CleanQueue, - CleanTask, CleanTaskResult, CodeGenerationResult, CodeGenerationResults, CompilationLogger, - CompilationLogging, CompilerOptions, ContentHashArgs, ContextDependency, DependencyId, - DependencyParents, DependencyType, Entry, EntryData, EntryOptions, Entrypoint, ErrorSpan, - FactorizeQueue, FactorizeTask, FactorizeTaskResult, Filename, Logger, Module, - ModuleCreationCallback, ModuleFactory, ModuleFactoryResult, ModuleGraph, ModuleGraphModule, - ModuleIdentifier, ModuleProfile, NormalModuleSource, PathData, ProcessAssetsArgs, - ProcessDependenciesQueue, ProcessDependenciesResult, ProcessDependenciesTask, QueueHandler, - RenderManifestArgs, Resolve, ResolverFactory, RuntimeGlobals, RuntimeModule, + BuildTask, BuildTaskResult, BuildTimeExecutionQueue, BuildTimeExecutionTask, CacheCount, + CacheOptions, Chunk, ChunkByUkey, ChunkContentHash, ChunkGraph, ChunkGroupByUkey, ChunkGroupUkey, + ChunkHashArgs, ChunkKind, ChunkUkey, CleanQueue, CleanTask, CleanTaskResult, + CodeGenerationResults, CompilationLogger, CompilationLogging, CompilerOptions, ContentHashArgs, + ContextDependency, DependencyId, DependencyParents, DependencyType, Entry, EntryData, + EntryOptions, Entrypoint, ErrorSpan, FactorizeQueue, FactorizeTask, FactorizeTaskResult, + Filename, Logger, Module, ModuleCreationCallback, ModuleFactory, ModuleFactoryResult, + ModuleGraph, ModuleGraphModule, ModuleIdentifier, ModuleProfile, NormalModuleSource, PathData, + ProcessAssetsArgs, ProcessDependenciesQueue, ProcessDependenciesResult, ProcessDependenciesTask, + QueueHandler, RenderManifestArgs, Resolve, ResolverFactory, RuntimeGlobals, RuntimeModule, RuntimeRequirementsInTreeArgs, RuntimeSpec, SharedPluginDriver, SourceType, Stats, TaskResult, WorkerTask, }; @@ -538,6 +538,7 @@ impl Compilation { let mut add_queue = AddQueue::new(); let mut build_queue = BuildQueue::new(); let mut process_dependencies_queue = ProcessDependenciesQueue::new(); + let mut buildtime_execution_queue = BuildTimeExecutionQueue::new(); let mut make_failed_dependencies: HashSet = HashSet::default(); let mut make_failed_module: HashSet = HashSet::default(); @@ -577,6 +578,7 @@ impl Compilation { parent_module .and_then(|m| m.as_normal_module()) .and_then(|module| module.name_for_condition()), + true, None, ); }); @@ -585,6 +587,7 @@ impl Compilation { let mut process_deps_time = logger.time_aggregate("module process dependencies task"); let mut factorize_time = logger.time_aggregate("module factorize task"); let mut build_time = logger.time_aggregate("module build task"); + let mut buildtime_execution_time = logger.time_aggregate("buildtime execution task"); let mut build_cache_counter = None; let mut factorize_cache_counter = None; @@ -602,13 +605,18 @@ impl Compilation { &mut add_queue, &mut build_queue, &mut process_dependencies_queue, + &mut buildtime_execution_queue, ); while let Some(task) = factorize_queue.get_task() { + active_task_count += 1; + + // TODO: change when we insert dependency to module_graph + self.module_graph.add_dependency(task.dependency.clone()); + tokio::spawn({ let result_tx = result_tx.clone(); let is_expected_shutdown = is_expected_shutdown.clone(); - active_task_count += 1; async move { if is_expected_shutdown.load(Ordering::SeqCst) { @@ -628,10 +636,10 @@ impl Compilation { let start = build_time.start(); while let Some(task) = build_queue.get_task() { + active_task_count += 1; tokio::spawn({ let result_tx = result_tx.clone(); let is_expected_shutdown = is_expected_shutdown.clone(); - active_task_count += 1; async move { if is_expected_shutdown.load(Ordering::SeqCst) { @@ -714,6 +722,7 @@ impl Compilation { module .as_normal_module() .and_then(|module| module.name_for_condition()), + true, Some(Box::new(move |_| { tx.send(()) .expect("Failed to send callback to process_dependencies"); @@ -744,6 +753,23 @@ impl Compilation { } process_deps_time.end(start); + let start = buildtime_execution_time.start(); + while let Some(task) = buildtime_execution_queue.get_task() { + let BuildTimeExecutionTask { + module, + request, + options, + sender, + } = task; + + if let Err(e) = self.execute_module(module, &request, options, sender.clone()) { + result_tx + .send(Err(e)) + .expect("failed to send error message"); + }; + } + buildtime_execution_time.end(start); + match result_rx.try_recv() { Ok(item) => { if let Ok(item) = &item { @@ -797,6 +823,7 @@ impl Compilation { missing_dependencies, diagnostics, callback, + connect_origin, .. } = task_result; if !diagnostics.is_empty() { @@ -858,6 +885,7 @@ impl Compilation { is_entry, current_profile, callback, + connect_origin, }); tracing::trace!("Module created: {}", &module_identifier); } else { @@ -888,6 +916,7 @@ impl Compilation { plugin_driver: self.plugin_driver.clone(), cache: self.cache.clone(), current_profile, + queue_handler: self.queue_handle.clone(), }); } AddTaskResult::ModuleReused { module, .. } => { @@ -1197,6 +1226,7 @@ impl Compilation { resolve_options: Option>, lazy_visit_modules: std::collections::HashSet, issuer: Option>, + connect_origin: bool, callback: Option, ) { let current_profile = self.options.profile.then(Box::::default); @@ -1228,12 +1258,13 @@ impl Compilation { plugin_driver: self.plugin_driver.clone(), cache: self.cache.clone(), current_profile, + connect_origin, callback, }); } #[instrument(name = "compilation:code_generation", skip(self))] - async fn code_generation(&mut self) -> Result<()> { + fn code_generation(&mut self) -> Result<()> { let logger = self.get_logger("rspack.Compilation"); let mut codegen_cache_counter = match self.options.cache { CacheOptions::Disabled => None, @@ -1249,83 +1280,25 @@ impl Compilation { // Else, share same codegen result for all runtimes. let used_exports_optimization = compilation.options.is_new_tree_shaking() && compilation.options.optimization.used_exports.is_true(); - let results = compilation - .module_graph - .modules() - .par_iter() - .filter(filter_op) - .filter_map(|(module_identifier, module)| { - let runtimes = compilation - .chunk_graph - .get_module_runtimes(*module_identifier, &compilation.chunk_by_ukey); - if runtimes.is_empty() { - return None; - } + let results = compilation.code_generation_modules( + codegen_cache_counter, + used_exports_optimization, + compilation + .module_graph + .modules() + .iter() + .filter(filter_op) + .map(|(id, _)| *id) + .collect::>() + .into_par_iter(), + )?; - let res = compilation - .cache - .code_generate_occasion - .use_cache(module, runtimes, compilation, |module, runtimes| { - let take_length = if used_exports_optimization { - runtimes.len() - } else { - // Only codegen once - 1 - }; - let mut codegen_list = vec![]; - for runtime in runtimes.into_values().take(take_length) { - codegen_list.push(( - module.code_generation(compilation, Some(&runtime), None)?, - runtime, - )); - } - Ok(codegen_list) - }) - .map(|(result, from_cache)| (*module_identifier, result, from_cache)); - Some(res) - }) - .collect::, - bool, - )>, - >>()?; - results - .into_iter() - .for_each(|(module_identifier, item, from_cache)| { - item.into_iter().for_each(|(result, runtime)| { - if let Some(counter) = codegen_cache_counter { - if from_cache { - counter.hit(); - } else { - counter.miss(); - } - } - compilation.code_generated_modules.insert(module_identifier); + results.iter().for_each(|module_identifier| { + compilation + .code_generated_modules + .insert(*module_identifier); + }); - let runtimes = compilation - .chunk_graph - .get_module_runtimes(module_identifier, &compilation.chunk_by_ukey); - let result_id = result.id; - compilation - .code_generation_results - .module_generation_result_map - .insert(result.id, result); - if used_exports_optimization { - compilation - .code_generation_results - .add(module_identifier, runtime, result_id); - } else { - for runtime in runtimes.into_values() { - compilation - .code_generation_results - .add(module_identifier, runtime, result_id); - } - } - }) - }); - // dbg!(&compilation.code_generation_results.map); Ok(()) } @@ -1344,6 +1317,81 @@ impl Compilation { Ok(()) } + pub(crate) fn code_generation_modules( + &mut self, + codegen_cache_counter: &mut Option, + used_exports_optimization: bool, + modules: impl ParallelIterator, + ) -> Result> { + let chunk_graph = &self.chunk_graph; + #[allow(clippy::type_complexity)] + let results = modules + .filter_map(|module_identifier| { + let runtimes = chunk_graph.get_module_runtimes(module_identifier, &self.chunk_by_ukey); + if runtimes.is_empty() { + return None; + } + + let module = self + .module_graph + .module_by_identifier(&module_identifier) + .expect("module should exist"); + let res = self + .cache + .code_generate_occasion + .use_cache(module, runtimes, self, |module, runtimes| { + let take_length = if used_exports_optimization { + runtimes.len() + } else { + // Only codegen once + 1 + }; + let mut codegen_list = vec![]; + for runtime in runtimes.into_values().take(take_length) { + codegen_list.push((module.code_generation(self, Some(&runtime), None)?, runtime)); + } + Ok(codegen_list) + }) + .map(|(result, from_cache)| (module_identifier, result, from_cache)); + Some(res) + }) + .collect::>>()?; + let results = results + .into_iter() + .map(|(module_identifier, item, from_cache)| { + item.into_iter().for_each(|(result, runtime)| { + if let Some(counter) = codegen_cache_counter { + if from_cache { + counter.hit(); + } else { + counter.miss(); + } + } + + let runtimes = chunk_graph.get_module_runtimes(module_identifier, &self.chunk_by_ukey); + let result_id = result.id; + self + .code_generation_results + .module_generation_result_map + .insert(result.id, result); + if used_exports_optimization { + self + .code_generation_results + .add(module_identifier, runtime, result_id); + } else { + for runtime in runtimes.into_values() { + self + .code_generation_results + .add(module_identifier, runtime, result_id); + } + } + }); + module_identifier + }); + + Ok(results.collect()) + } + #[instrument(name = "compilation::create_module_assets", skip_all)] async fn create_module_assets(&mut self, _plugin_driver: SharedPluginDriver) { for (module_identifier, module) in self.module_graph.modules() { @@ -1542,7 +1590,7 @@ impl Compilation { logger.time_end(start); let start = logger.time("code generation"); - self.code_generation().await?; + self.code_generation()?; logger.time_end(start); // if self.options.is_new_tree_shaking() { // debug_all_exports_info!(&self.module_graph); @@ -1642,8 +1690,8 @@ impl Compilation { HashSet::from_iter(entries.chain(async_entries)) } - #[instrument(name = "compilation:process_runtime_requirements", skip_all)] - pub async fn process_runtime_requirements( + #[allow(clippy::unwrap_in_result)] + pub(crate) async fn process_runtime_requirements( &mut self, modules: impl IntoParallelIterator, chunks: impl Iterator, @@ -2029,14 +2077,6 @@ impl Compilation { CompilationLogger::new(name.into(), self.logging.clone()) } - pub fn execute_module(&self, entry: ModuleIdentifier) -> Result> { - let codegen_result = Default::default(); - // TODO - self - .plugin_driver - .execute_module(entry, vec![], &codegen_result) - } - pub fn set_dependency_factory( &mut self, dependency_type: DependencyType, diff --git a/crates/rspack_core/src/compiler/execute_module.rs b/crates/rspack_core/src/compiler/execute_module.rs new file mode 100644 index 00000000000..94fb77651ef --- /dev/null +++ b/crates/rspack_core/src/compiler/execute_module.rs @@ -0,0 +1,435 @@ +use std::sync::atomic::AtomicU32; +use std::{hash::BuildHasherDefault, iter::once, sync::Arc}; + +use rayon::prelude::*; +use rspack_error::Result; +use rspack_identifier::{Identifiable, IdentifierMap}; +use rustc_hash::{FxHashSet as HashSet, FxHasher}; +use tokio::runtime::Handle; +use tokio::sync::mpsc::{unbounded_channel, UnboundedSender}; +use tracing::instrument; + +use crate::{ + cache::Cache, BuildTimeExecutionOption, BuildTimeExecutionTask, Chunk, ChunkByUkey, ChunkGraph, + ChunkKind, CodeGenerationDataAssetInfo, CodeGenerationDataFilename, CodeGenerationResult, + CompilerOptions, Dependency, EntryOptions, Entrypoint, ExecuteModuleResult, FactorizeTask, + LoaderImportDependency, ModuleIdentifier, NormalModuleFactory, QueueHandler, QueueTask, + ResolverFactory, SharedPluginDriver, SourceType, +}; +use crate::{Compilation, CompilationAsset}; +use crate::{Context, RuntimeModule}; + +static EXECUTE_MODULE_ID: AtomicU32 = AtomicU32::new(0); + +impl Compilation { + #[allow(clippy::unwrap_in_result)] + #[instrument(name = "compilation:execute_module")] + pub fn execute_module( + &mut self, + module: ModuleIdentifier, + request: &str, + options: BuildTimeExecutionOption, + result_tx: UnboundedSender>, + ) -> Result<()> { + let id = EXECUTE_MODULE_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + + let mut modules: std::collections::HashSet< + rspack_identifier::Identifier, + BuildHasherDefault, + > = HashSet::default(); + let mut queue = vec![module]; + + while let Some(m) = queue.pop() { + modules.insert(m); + let m = self + .module_graph + .module_by_identifier(&m) + .expect("should have module"); + for m in self.module_graph.get_outgoing_connections(m) { + // TODO: handle circle + if !modules.contains(&m.module_identifier) { + queue.push(m.module_identifier); + } + } + } + + tracing::info!( + "modules: {:?}", + &modules.iter().map(|m| m.to_string()).collect::>() + ); + + let mut chunk_graph = ChunkGraph::default(); + + let mut chunk = Chunk::new(Some("build time chunk".into()), ChunkKind::Normal); + + chunk.id = chunk.name.clone(); + chunk.ids = vec![chunk.id.clone().expect("id is set")]; + let runtime = { + let mut runtime = HashSet::default(); + runtime.insert("build time".into()); + runtime + }; + + chunk.runtime = runtime.clone(); + + let mut entrypoint = Entrypoint::new( + crate::ChunkGroupKind::Entrypoint { + initial: true, + options: Box::new(EntryOptions { + name: Some("build time".into()), + runtime: Some("runtime".into()), + chunk_loading: Some(crate::ChunkLoading::Disable), + async_chunks: None, + public_path: options.public_path.clone().map(crate::PublicPath::String), + base_uri: options.base_uri.clone(), + filename: None, + library: None, + }), + }, + crate::ChunkGroupInfo { + chunk_loading: false, + async_chunks: false, + runtime: runtime.clone(), + }, + ); + + // add chunk to this compilation + let chunk_by_ukey = ChunkByUkey::default(); + let old_chunk_by_ukey = std::mem::replace(&mut self.chunk_by_ukey, chunk_by_ukey); + + let chunk = self.chunk_by_ukey.add(chunk); + let chunk_ukey = chunk.ukey; + + chunk_graph.connect_chunk_and_entry_module(chunk.ukey, module, entrypoint.ukey); + entrypoint.connect_chunk(chunk); + entrypoint.set_runtime_chunk(chunk.ukey); + entrypoint.set_entry_point_chunk(chunk.ukey); + + let entry_ukey = entrypoint.ukey; + self.chunk_group_by_ukey.add(entrypoint); + + // Assign ids to modules and modules to the chunk + for m in &modules { + let module = self + .module_graph + .module_by_identifier(m) + .expect("should have module"); + + let id = module.identifier(); + + chunk_graph.add_module(id); + chunk_graph.set_module_id(*m, id.to_string()); + chunk_graph.connect_chunk_and_module(chunk_ukey, *m); + } + + // Webpack uses this trick to make sure process_runtime_requirements access + // the new chunk_graph + // in rspack, if we decouple compilation and chunk_graph, we can't get exclusive ref + // to the chunk_graph in API that receives both compilation and chunk_graph + // + // replace code_generation_results is the same reason + let old_runtime_modules = std::mem::take(&mut self.runtime_modules); + let old_chunk_graph = std::mem::replace(&mut self.chunk_graph, chunk_graph); + + self.code_generation_modules(&mut None, false, modules.par_iter().copied())?; + + Handle::current().block_on(async { + self + .process_runtime_requirements( + modules.clone(), + once(chunk_ukey), + once(chunk_ukey), + self.plugin_driver.clone(), + ) + .await + })?; + + let runtime_modules = self + .chunk_graph + .get_chunk_runtime_modules_iterable(&chunk_ukey) + .copied() + .collect::>(); + + tracing::info!( + "runtime modules: {:?}", + &runtime_modules + .iter() + .map(|m| m.to_string()) + .collect::>() + ); + + for runtime_id in &runtime_modules { + let runtime_module = self + .runtime_modules + .get(runtime_id) + .expect("runtime module exist"); + + let result = CodeGenerationResult::default().with_javascript(runtime_module.generate(self)); + let result_id = result.id; + + self + .code_generation_results + .module_generation_result_map + .insert(result.id, result); + self + .code_generation_results + .add(*runtime_id, runtime.clone(), result_id); + } + + let codegen_results = self.code_generation_results.clone(); + let exports = self.plugin_driver.execute_module( + module, + request, + &options, + runtime_modules.iter().cloned().collect(), + &codegen_results, + id, + ); + + let execute_result = match exports { + Ok(_) => { + let mut result = modules + .iter() + .fold(ExecuteModuleResult::default(), |mut res, m| { + let module = self + .module_graph + .module_by_identifier(m) + .expect("unreachable"); + + let build_info = &module.build_info(); + if let Some(info) = build_info { + res + .file_dependencies + .extend(info.file_dependencies.iter().cloned()); + res + .context_dependencies + .extend(info.context_dependencies.iter().cloned()); + res + .missing_dependencies + .extend(info.missing_dependencies.iter().cloned()); + res + .build_dependencies + .extend(info.build_dependencies.iter().cloned()); + res.assets.extend(info.asset_filenames.iter().cloned()); + } + res + }); + + result.id = id; + + modules.iter().for_each(|m| { + let codegen_result = codegen_results.get(m, Some(&runtime)); + + if let Some(source) = codegen_result.get(&SourceType::Asset) + && let Some(filename) = codegen_result.data.get::() + && let Some(asset_info) = codegen_result.data.get::() + { + let filename = filename.filename(); + self.emit_asset( + filename.to_owned(), + CompilationAsset::new(Some(source.clone()), asset_info.inner().clone()), + ); + result.assets.insert(filename.to_owned()); + } + }); + + Ok(result) + } + Err(e) => Err(e), + }; + + // clear side effects stuff we caused + self.clear_execute_module_effects(old_chunk_graph, old_chunk_by_ukey, old_runtime_modules); + + self.chunk_group_by_ukey.remove(&entry_ukey); + + result_tx.send(execute_result).expect("todo"); + Ok(()) + } + + #[instrument(name = "compilation::import_module")] + pub async fn import_module( + &self, + request: String, + public_path: Option, + base_uri: Option, + original_module_identifier: Option, + original_module_context: Option>, + ) -> Result { + Self::import_module_impl( + self + .queue_handle + .clone() + .expect("call import module without queueHandler"), + self.resolver_factory.clone(), + self.options.clone(), + self.plugin_driver.clone(), + self.cache.clone(), + request, + public_path, + base_uri, + original_module_identifier, + original_module_context, + ) + .await + } + + #[allow(clippy::too_many_arguments)] + #[instrument(name = "compilation::import_module_impl")] + pub async fn import_module_impl( + queue_handler: QueueHandler, + resolver_factory: Arc, + options: Arc, + plugin_driver: SharedPluginDriver, + cache: Arc, + + request: String, + public_path: Option, + base_uri: Option, + original_module_identifier: Option, + original_module_context: Option>, + ) -> Result { + let (tx, mut rx) = unbounded_channel::<(ModuleIdentifier, Option>)>(); + + let dep = LoaderImportDependency::new(request.clone()); + let dep_id = *dep.id(); + + let tx_clone = tx.clone(); + + queue_handler.add_task(crate::QueueTask::Factorize(Box::new(FactorizeTask { + module_factory: Arc::new(NormalModuleFactory::new( + options.clone(), + resolver_factory.clone(), + plugin_driver.clone(), + cache.clone(), + )), + original_module_source: None, + original_module_identifier, + original_module_context, + issuer: None, + dependency: Box::new(dep), + dependencies: vec![dep_id], + is_entry: false, + resolve_options: Some(Box::new(options.resolve.clone())), + resolver_factory: resolver_factory.clone(), + loader_resolver_factory: resolver_factory.clone(), + options: options.clone(), + lazy_visit_modules: Default::default(), + plugin_driver: plugin_driver.clone(), + cache: cache.clone(), + current_profile: None, + connect_origin: false, + callback: Some(Box::new(move |m| { + tx_clone + .send((m.identifier(), None)) + .expect("failed to send entry module of buildtime execution modules") + })), + })))?; + + let mut to_be_completed = 0; + let mut completed = 0; + let mut waited = vec![]; + let mut modules = HashSet::default(); + + let mut entry = None; + + // wait for every child to be built + loop { + if let Some((module, deps)) = rx.recv().await { + if let Some(deps) = deps { + completed += 1; + modules.insert(module); + + for dep in deps { + if !modules.contains(&dep) { + waited.push(dep); + to_be_completed += 1; + } + } + } else { + entry = Some(module); + waited.push(module); + to_be_completed += 1; + } + + while let Some(module) = waited.pop() { + let tx_clone = tx.clone(); + + queue_handler.wait_for( + crate::WaitTask::ProcessDependencies(module), + Box::new(move |module, compilation| { + let m = compilation + .module_graph + .module_by_identifier(&module) + .expect("todo"); + + tx_clone + .send(( + module, + Some( + compilation + .module_graph + .get_outgoing_connections(m) + .into_iter() + .map(|conn| conn.module_identifier) + .collect(), + ), + )) + .expect("todo"); + }), + ); + } + } else { + unreachable!(); + } + + if to_be_completed == completed { + break; + } + } + + let (tx, mut rx) = unbounded_channel(); + + queue_handler.add_task(QueueTask::BuildTimeExecution(Box::new( + BuildTimeExecutionTask { + module: entry.expect("should has entry module"), + request, + options: BuildTimeExecutionOption { + public_path, + base_uri, + }, + sender: tx, + }, + )))?; + + rx.recv().await.unwrap_or_else(|| { + Err( + rspack_error::InternalError::new( + "failed on calling importModule at execution stage".into(), + rspack_error::RspackSeverity::Error, + ) + .into(), + ) + }) + } + + // modules that execute during build are called build-time modules. + // executeModule will invoke some modules' codegen and process their runtime, + // however during this process, some plugins might access compilation.chunk_graph + // or compilation.runtime_modules, and modify them, for example add runtimeModule + // to compilation.runtime_modules, but those runtime modules are only needed by + // build-time modules, and should not present in the output, thus we should + // clear them after we finish executing build-time modules + // compare to webpack, we have extra items to restore: runtime_modules, because webpack + // store runtimeModules in ChunkGraph, but we store them in compilation + fn clear_execute_module_effects( + &mut self, + chunk_graph: ChunkGraph, + chunk_by_ukey: ChunkByUkey, + runtime_modules: IdentifierMap>, + ) { + self.chunk_graph = chunk_graph; + self.chunk_by_ukey = chunk_by_ukey; + self.runtime_modules = runtime_modules; + } +} diff --git a/crates/rspack_core/src/compiler/mod.rs b/crates/rspack_core/src/compiler/mod.rs index 0362352270d..55b55e82f5c 100644 --- a/crates/rspack_core/src/compiler/mod.rs +++ b/crates/rspack_core/src/compiler/mod.rs @@ -1,4 +1,5 @@ mod compilation; +mod execute_module; mod hmr; mod make; mod queue; diff --git a/crates/rspack_core/src/compiler/queue.rs b/crates/rspack_core/src/compiler/queue.rs index 6f32b351b94..9e5155e27f5 100644 --- a/crates/rspack_core/src/compiler/queue.rs +++ b/crates/rspack_core/src/compiler/queue.rs @@ -13,7 +13,7 @@ use crate::{ ModuleGraph, ModuleGraphModule, ModuleIdentifier, ModuleProfile, Resolve, ResolverFactory, SharedPluginDriver, WorkerQueue, }; -use crate::{BoxModule, DependencyId, ExportInfo, ExportsInfo, UsageState}; +use crate::{BoxModule, DependencyId, ExecuteModuleResult, ExportInfo, ExportsInfo, UsageState}; #[derive(Debug)] pub enum TaskResult { @@ -47,6 +47,7 @@ pub struct FactorizeTask { pub plugin_driver: SharedPluginDriver, pub cache: Arc, pub current_profile: Option>, + pub connect_origin: bool, #[derivative(Debug = "ignore")] pub callback: Option, } @@ -77,6 +78,7 @@ pub struct FactorizeTaskResult { pub diagnostics: Vec, #[derivative(Debug = "ignore")] pub callback: Option, + pub connect_origin: bool, } impl FactorizeTaskResult { @@ -147,6 +149,7 @@ impl WorkerTask for FactorizeTask { context_dependencies: Default::default(), missing_dependencies: Default::default(), diagnostics: Default::default(), + connect_origin: self.connect_origin, callback: self.callback, }; @@ -220,6 +223,7 @@ pub struct AddTask { pub dependencies: Vec, pub is_entry: bool, pub current_profile: Option>, + pub connect_origin: bool, #[derivative(Debug = "ignore")] pub callback: Option, } @@ -241,7 +245,7 @@ impl AddTask { current_profile.mark_integration_start(); } - if self.module.as_self_module().is_some() { + if self.module.as_self_module().is_some() && self.connect_origin { let issuer = self .module_graph_module .get_issuer() @@ -262,10 +266,11 @@ impl AddTask { let module_identifier = self.module.identifier(); - if compilation - .module_graph - .module_graph_module_by_identifier(&module_identifier) - .is_some() + if self.connect_origin + && compilation + .module_graph + .module_graph_module_by_identifier(&module_identifier) + .is_some() { set_resolved_module( &mut compilation.module_graph, @@ -287,12 +292,14 @@ impl AddTask { .module_graph .add_module_graph_module(*self.module_graph_module); - set_resolved_module( - &mut compilation.module_graph, - self.original_module_identifier, - self.dependencies, - module_identifier, - )?; + if self.connect_origin { + set_resolved_module( + &mut compilation.module_graph, + self.original_module_identifier, + self.dependencies, + module_identifier, + )?; + } if self.is_entry { compilation @@ -337,6 +344,7 @@ pub struct BuildTask { pub plugin_driver: SharedPluginDriver, pub cache: Arc, pub current_profile: Option>, + pub queue_handler: Option, } #[derive(Debug)] @@ -378,6 +386,9 @@ impl WorkerTask for BuildTask { module: module.identifier(), module_context: module.as_normal_module().and_then(|m| m.get_context()), module_source_map_kind: module.get_source_map_kind().clone(), + queue_handler: self.queue_handler.clone(), + plugin_driver: plugin_driver.clone(), + cache: cache.clone(), }, plugin_driver: plugin_driver.clone(), compiler_options: &compiler_options, @@ -444,6 +455,22 @@ pub struct ProcessDependenciesResult { pub type ProcessDependenciesQueue = WorkerQueue; +#[derive(Clone, Debug)] +pub struct BuildTimeExecutionOption { + pub public_path: Option, + pub base_uri: Option, +} + +#[derive(Debug, Clone)] +pub struct BuildTimeExecutionTask { + pub module: ModuleIdentifier, + pub request: String, + pub options: BuildTimeExecutionOption, + pub sender: UnboundedSender>, +} + +pub type BuildTimeExecutionQueue = WorkerQueue; + pub struct CleanTask { pub module_identifier: ModuleIdentifier, } @@ -506,6 +533,8 @@ pub enum QueueTask { Add(Box), Build(Box), ProcessDependencies(Box), + BuildTimeExecution(Box), + Subscription(Box), } @@ -595,6 +624,7 @@ impl QueueHandlerProcessor { add_queue: &mut AddQueue, build_queue: &mut BuildQueue, process_dependencies_queue: &mut ProcessDependenciesQueue, + buildtime_execution_queue: &mut BuildTimeExecutionQueue, ) { while let Ok(task) = self.receiver.try_recv() { match task { @@ -610,6 +640,9 @@ impl QueueHandlerProcessor { QueueTask::ProcessDependencies(task) => { process_dependencies_queue.add_task(*task); } + QueueTask::BuildTimeExecution(task) => { + buildtime_execution_queue.add_task(*task); + } QueueTask::Subscription(subscription) => { let Subscription { task, callback } = *subscription; let (bucket, key) = Self::get_bucket_and_key(task); diff --git a/crates/rspack_core/src/dependency/dependency_category.rs b/crates/rspack_core/src/dependency/dependency_category.rs index fc095815753..56198b5fa1a 100644 --- a/crates/rspack_core/src/dependency/dependency_category.rs +++ b/crates/rspack_core/src/dependency/dependency_category.rs @@ -12,6 +12,7 @@ pub enum DependencyCategory { CssCompose, Wasm, Worker, + LoaderImport, } impl From<&str> for DependencyCategory { @@ -41,6 +42,7 @@ impl DependencyCategory { DependencyCategory::CssCompose => "css-compose", DependencyCategory::Wasm => "wasm", DependencyCategory::Worker => "worker", + DependencyCategory::LoaderImport => "loader import", } } } diff --git a/crates/rspack_core/src/dependency/dependency_type.rs b/crates/rspack_core/src/dependency/dependency_type.rs index dd6867ff951..1a00e1182d1 100644 --- a/crates/rspack_core/src/dependency/dependency_type.rs +++ b/crates/rspack_core/src/dependency/dependency_type.rs @@ -88,6 +88,7 @@ pub enum DependencyType { ConsumeSharedFallback, /// Webpack is included WebpackIsIncluded, + LoaderImport, Custom(Box), // TODO it will increase large layout size } @@ -128,6 +129,7 @@ impl DependencyType { DependencyType::WasmImport => Cow::Borrowed("wasm import"), DependencyType::WasmExportImported => Cow::Borrowed("wasm export imported"), DependencyType::StaticExports => Cow::Borrowed("static exports"), + DependencyType::LoaderImport => Cow::Borrowed("loader import"), DependencyType::Custom(ty) => Cow::Owned(format!("custom {ty}")), DependencyType::ExportInfoApi => Cow::Borrowed("export info api"), // TODO: mode diff --git a/crates/rspack_core/src/dependency/loader_import_dependency.rs b/crates/rspack_core/src/dependency/loader_import_dependency.rs new file mode 100644 index 00000000000..12822d2010f --- /dev/null +++ b/crates/rspack_core/src/dependency/loader_import_dependency.rs @@ -0,0 +1,53 @@ +use crate::{ + AsContextDependency, AsDependencyTemplate, Dependency, DependencyId, ModuleDependency, +}; + +#[derive(Debug, Hash, PartialEq, Eq, Clone)] +pub struct LoaderImportDependency { + request: String, + id: DependencyId, +} + +impl LoaderImportDependency { + pub fn new(request: String) -> Self { + Self { + request, + id: DependencyId::new(), + } + } +} + +impl AsDependencyTemplate for LoaderImportDependency {} +impl AsContextDependency for LoaderImportDependency {} + +impl Dependency for LoaderImportDependency { + fn dependency_debug_name(&self) -> &'static str { + "LoaderImportDependency" + } + + fn id(&self) -> &crate::DependencyId { + &self.id + } + + fn dependency_type(&self) -> &crate::DependencyType { + &crate::DependencyType::LoaderImport + } + + fn category(&self) -> &crate::DependencyCategory { + &crate::DependencyCategory::LoaderImport + } +} + +impl ModuleDependency for LoaderImportDependency { + fn request(&self) -> &str { + &self.request + } + + fn user_request(&self) -> &str { + &self.request + } + + fn set_request(&mut self, request: String) { + self.request = request + } +} diff --git a/crates/rspack_core/src/dependency/mod.rs b/crates/rspack_core/src/dependency/mod.rs index 835aa57eb87..debc305b7ff 100644 --- a/crates/rspack_core/src/dependency/mod.rs +++ b/crates/rspack_core/src/dependency/mod.rs @@ -9,6 +9,7 @@ mod dependency_trait; mod dependency_type; mod entry; mod import_dependency_trait; +mod loader_import_dependency; mod module_dependency; mod runtime_requirements_dependency; mod runtime_template; @@ -27,6 +28,7 @@ pub use dependency_trait::*; pub use dependency_type::DependencyType; pub use entry::*; pub use import_dependency_trait::ImportDependencyTrait; +pub use loader_import_dependency::LoaderImportDependency; pub use module_dependency::*; pub use runtime_requirements_dependency::RuntimeRequirementsDependency; pub use runtime_template::*; diff --git a/crates/rspack_core/src/loader/loader_runner.rs b/crates/rspack_core/src/loader/loader_runner.rs index e55b7679a20..2ce1a9d5928 100644 --- a/crates/rspack_core/src/loader/loader_runner.rs +++ b/crates/rspack_core/src/loader/loader_runner.rs @@ -2,8 +2,12 @@ use std::sync::Arc; pub use rspack_loader_runner::{run_loaders, Content, Loader, LoaderContext}; use rspack_util::source_map::SourceMapKind; +use rustc_hash::FxHashSet; -use crate::{CompilerOptions, Context, ModuleIdentifier, ResolverFactory}; +use crate::{ + cache::Cache, Compilation, CompilerOptions, Context, ModuleIdentifier, QueueHandler, + ResolverFactory, SharedPluginDriver, +}; #[derive(Debug, Clone)] pub struct CompilerContext { @@ -12,6 +16,49 @@ pub struct CompilerContext { pub module: ModuleIdentifier, // current module pub module_context: Option>, // current module context pub module_source_map_kind: SourceMapKind, + + pub queue_handler: Option, + pub plugin_driver: SharedPluginDriver, + pub cache: Arc, +} + +#[derive(Debug, Default)] +pub struct ExecuteModuleResult { + pub file_dependencies: FxHashSet, + pub context_dependencies: FxHashSet, + pub missing_dependencies: FxHashSet, + pub build_dependencies: FxHashSet, + pub assets: FxHashSet, + pub id: u32, +} + +impl CompilerContext { + pub async fn import_module( + &self, + request: String, + public_path: Option, + base_uri: Option, + ) -> rspack_error::Result { + if self.queue_handler.is_none() { + return Err(rspack_error::error!( + "use import_module without queue_handler" + )); + } + + Compilation::import_module_impl( + self.queue_handler.clone().expect("unreachable"), + self.resolver_factory.clone(), + self.options.clone(), + self.plugin_driver.clone(), + self.cache.clone(), + request, + public_path, + base_uri, + Some(self.module), + self.module_context.clone(), + ) + .await + } } pub type LoaderRunnerContext = CompilerContext; diff --git a/crates/rspack_core/src/logger.rs b/crates/rspack_core/src/logger.rs index b89354f2ce7..0ceeeff5c7f 100644 --- a/crates/rspack_core/src/logger.rs +++ b/crates/rspack_core/src/logger.rs @@ -270,6 +270,7 @@ impl StartTimeAggregate { } } +#[derive(Debug)] pub struct CacheCount { label: &'static str, hit: u32, diff --git a/crates/rspack_core/src/plugin/api.rs b/crates/rspack_core/src/plugin/api.rs index cbf993f1a84..8dbfa9fcf78 100644 --- a/crates/rspack_core/src/plugin/api.rs +++ b/crates/rspack_core/src/plugin/api.rs @@ -5,17 +5,18 @@ use rspack_error::{IntoTWithDiagnosticArray, Result, TWithDiagnosticArray}; use rspack_hash::RspackHashDigest; use rspack_loader_runner::{Content, LoaderContext, ResourceData}; use rspack_sources::BoxSource; +use tokio::sync::mpsc::UnboundedSender; use crate::{ AdditionalChunkRuntimeRequirementsArgs, AdditionalModuleRequirementsArgs, AssetEmittedArgs, - AssetInfo, BoxLoader, BoxModule, Chunk, ChunkAssetArgs, ChunkHashArgs, CodeGenerationResults, - Compilation, CompilationArgs, CompilationParams, CompilerOptions, ContentHashArgs, DoneArgs, - FactorizeArgs, JsChunkHashArgs, LoaderRunnerContext, MakeParam, Module, ModuleFactoryResult, - ModuleIdentifier, ModuleType, NormalModule, NormalModuleAfterResolveArgs, - NormalModuleBeforeResolveArgs, NormalModuleCreateData, OptimizeChunksArgs, ParserAndGenerator, - PluginContext, ProcessAssetsArgs, RenderArgs, RenderChunkArgs, RenderManifestArgs, - RenderModuleContentArgs, RenderStartupArgs, Resolver, RuntimeModule, - RuntimeRequirementsInTreeArgs, SourceType, ThisCompilationArgs, + AssetInfo, BoxLoader, BoxModule, BuildTimeExecutionOption, Chunk, ChunkAssetArgs, ChunkHashArgs, + CodeGenerationResults, Compilation, CompilationArgs, CompilationParams, CompilerOptions, + ContentHashArgs, DependencyId, DoneArgs, FactorizeArgs, JsChunkHashArgs, LoaderRunnerContext, + MakeParam, Module, ModuleFactoryResult, ModuleIdentifier, ModuleType, NormalModule, + NormalModuleAfterResolveArgs, NormalModuleBeforeResolveArgs, NormalModuleCreateData, + OptimizeChunksArgs, ParserAndGenerator, PluginContext, ProcessAssetsArgs, RenderArgs, + RenderChunkArgs, RenderManifestArgs, RenderModuleContentArgs, RenderStartupArgs, Resolver, + RuntimeModule, RuntimeRequirementsInTreeArgs, SourceType, ThisCompilationArgs, }; // use anyhow::{Context, Result}; @@ -525,13 +526,25 @@ pub trait Plugin: Debug + Send + Sync { Ok(()) } + fn prepare_execute_module( + &self, + _id: DependencyId, + _options: &BuildTimeExecutionOption, + _import_module_informer: UnboundedSender>, + ) -> Result<()> { + Ok(()) + } + fn execute_module( &self, _entry: ModuleIdentifier, + _request: &str, + _options: &BuildTimeExecutionOption, _runtime_modules: Vec, _codegen_results: &CodeGenerationResults, - ) -> Result> { - Ok(None) + _id: u32, + ) -> Result<()> { + Ok(()) } } diff --git a/crates/rspack_core/src/plugin/plugin_driver.rs b/crates/rspack_core/src/plugin/plugin_driver.rs index 8574eafdc73..c9856fea5d4 100644 --- a/crates/rspack_core/src/plugin/plugin_driver.rs +++ b/crates/rspack_core/src/plugin/plugin_driver.rs @@ -6,16 +6,17 @@ use std::{ use rspack_error::{Diagnostic, Result, TWithDiagnosticArray}; use rspack_loader_runner::{LoaderContext, ResourceData}; use rustc_hash::FxHashMap as HashMap; +use tokio::sync::mpsc::UnboundedSender; use tracing::instrument; use crate::{ AdditionalChunkRuntimeRequirementsArgs, AdditionalModuleRequirementsArgs, ApplyContext, - AssetEmittedArgs, BoxLoader, BoxModule, BoxedParserAndGeneratorBuilder, Chunk, ChunkAssetArgs, - ChunkContentHash, ChunkHashArgs, CodeGenerationResults, Compilation, CompilationArgs, - CompilationParams, CompilerOptions, Content, ContentHashArgs, DoneArgs, FactorizeArgs, - JsChunkHashArgs, LoaderRunnerContext, MakeParam, Module, ModuleIdentifier, ModuleType, - NormalModule, NormalModuleAfterResolveArgs, NormalModuleBeforeResolveArgs, - NormalModuleCreateData, OptimizeChunksArgs, Plugin, + AssetEmittedArgs, BoxLoader, BoxModule, BoxedParserAndGeneratorBuilder, BuildTimeExecutionOption, + Chunk, ChunkAssetArgs, ChunkContentHash, ChunkHashArgs, CodeGenerationResults, Compilation, + CompilationArgs, CompilationParams, CompilerOptions, Content, ContentHashArgs, DependencyId, + DoneArgs, FactorizeArgs, JsChunkHashArgs, LoaderRunnerContext, MakeParam, Module, + ModuleIdentifier, ModuleType, NormalModule, NormalModuleAfterResolveArgs, + NormalModuleBeforeResolveArgs, NormalModuleCreateData, OptimizeChunksArgs, Plugin, PluginAdditionalChunkRuntimeRequirementsOutput, PluginAdditionalModuleRequirementsOutput, PluginBuildEndHookOutput, PluginChunkHashHookOutput, PluginCompilationHookOutput, PluginContext, PluginFactorizeHookOutput, PluginJsChunkHashHookOutput, PluginMakeHookOutput, @@ -773,21 +774,40 @@ impl PluginDriver { Ok(()) } + pub fn prepare_execute_module( + &self, + dep: DependencyId, + options: &BuildTimeExecutionOption, + import_module_informer: UnboundedSender>, + ) -> Result<()> { + for plugin in &self.plugins { + plugin.prepare_execute_module(dep, options, import_module_informer.clone())?; + } + + Ok(()) + } + #[instrument(name = "plugin:execute_module", skip_all)] pub fn execute_module( &self, entry: ModuleIdentifier, + request: &str, + options: &BuildTimeExecutionOption, runtime_modules: Vec, codegen_results: &CodeGenerationResults, - ) -> Result> { + id: u32, + ) -> Result<()> { for plugin in &self.plugins { - if let Some(exports) = - plugin.execute_module(entry, runtime_modules.clone(), codegen_results)? - { - return Ok(Some(exports)); - } + plugin.execute_module( + entry, + request, + options, + runtime_modules.clone(), + codegen_results, + id, + )? } - Ok(None) + Ok(()) } } diff --git a/crates/rspack_core/src/tree_shaking/mod.rs b/crates/rspack_core/src/tree_shaking/mod.rs index 8d8e2856d61..2aebb0c29d4 100644 --- a/crates/rspack_core/src/tree_shaking/mod.rs +++ b/crates/rspack_core/src/tree_shaking/mod.rs @@ -68,6 +68,7 @@ bitflags::bitflags! { const DYNAMIC_IMPORT = 1 << 3; const CONTEXT_MODULE = 1 << 4; const CONTAINER_EXPOSED = 1 << 5; + const BUILDTIME_EXECUTION = 1 << 6; } } diff --git a/crates/rspack_database/src/database.rs b/crates/rspack_database/src/database.rs index 05d9d9a707c..afd6283f65b 100644 --- a/crates/rspack_database/src/database.rs +++ b/crates/rspack_database/src/database.rs @@ -114,9 +114,9 @@ impl Database { } impl Database { - pub fn add(&mut self, item: Item) { + pub fn add(&mut self, item: Item) -> &mut Item { debug_assert!(self.inner.get(&item.ukey()).is_none()); let ukey = item.ukey(); - self.inner.insert(ukey, item); + self.inner.entry(ukey).or_insert(item) } } diff --git a/crates/rspack_loader_swc/tests/fixtures.rs b/crates/rspack_loader_swc/tests/fixtures.rs index 09f3683d220..aae0a5234b9 100644 --- a/crates/rspack_loader_swc/tests/fixtures.rs +++ b/crates/rspack_loader_swc/tests/fixtures.rs @@ -6,8 +6,8 @@ use std::{ }; use rspack_core::{ - run_loaders, CompilerContext, CompilerOptions, Loader, LoaderRunnerContext, ResourceData, - SideEffectOption, + run_loaders, CompilerContext, CompilerOptions, Loader, LoaderRunnerContext, PluginDriver, + ResourceData, SideEffectOption, }; use rspack_loader_swc::{SwcLoader, SwcLoaderJsOptions}; use rspack_testing::{fixture, test_fixture}; @@ -27,6 +27,76 @@ async fn loader_test(actual: impl AsRef, expected: impl AsRef) { plugin_path.to_string_lossy().to_string(), json!(null), )]); + + let compiler_options = CompilerOptions { + bail: false, + context: rspack_core::Context::default(), + dev_server: rspack_core::DevServerOptions::default(), + mode: rspack_core::Mode::None, + output: rspack_core::OutputOptions { + clean: false, + path: Default::default(), + public_path: Default::default(), + filename: rspack_core::Filename::from_str("").expect("TODO:"), + asset_module_filename: rspack_core::Filename::from_str("").expect("TODO:"), + wasm_loading: rspack_core::WasmLoading::Disable, + webassembly_module_filename: rspack_core::Filename::from_str("").expect("TODO:"), + chunk_filename: rspack_core::Filename::from_str("").expect("TODO:"), + cross_origin_loading: rspack_core::CrossOriginLoading::Disable, + unique_name: Default::default(), + chunk_loading: rspack_core::ChunkLoading::Enable(rspack_core::ChunkLoadingType::Jsonp), + chunk_loading_global: "webpackChunkwebpack".to_string(), + css_chunk_filename: rspack_core::Filename::from_str("").expect("TODO:"), + css_filename: rspack_core::Filename::from_str("").expect("TODO:"), + hot_update_chunk_filename: rspack_core::Filename::from_str("").expect("Should exist"), + hot_update_main_filename: rspack_core::Filename::from_str("").expect("Should exist"), + hot_update_global: "webpackHotUpdate".to_string(), + library: None, + enabled_library_types: None, + strict_module_error_handling: false, + global_object: "self".to_string(), + import_function_name: "import".to_string(), + iife: true, + module: false, + trusted_types: None, + source_map_filename: rspack_core::Filename::from_str("./a.map.js").expect("TODO:"), + hash_function: rspack_core::HashFunction::Xxhash64, + hash_digest: rspack_core::HashDigest::Hex, + hash_digest_length: 16, + hash_salt: rspack_core::HashSalt::None, + async_chunks: true, + worker_chunk_loading: rspack_core::ChunkLoading::Enable( + rspack_core::ChunkLoadingType::ImportScripts, + ), + worker_wasm_loading: rspack_core::WasmLoading::Disable, + worker_public_path: String::new(), + script_type: String::from("false"), + }, + target: rspack_core::Target::new(&vec![String::from("web")]).expect("TODO:"), + resolve: rspack_core::Resolve::default(), + resolve_loader: rspack_core::Resolve::default(), + builtins: Default::default(), + module: Default::default(), + stats: Default::default(), + cache: Default::default(), + snapshot: Default::default(), + experiments: Default::default(), + node: Default::default(), + optimization: rspack_core::Optimization { + remove_available_modules: false, + side_effects: SideEffectOption::False, + provided_exports: Default::default(), + used_exports: Default::default(), + inner_graph: Default::default(), + mangle_exports: Default::default(), + concatenate_modules: Default::default(), + }, + profile: false, + }; + + let (plugin_driver, compiler_options) = + PluginDriver::new(compiler_options, vec![], Default::default()); + let (result, _) = run_loaders( &[Arc::new(SwcLoader::new(options)) as Arc>], &ResourceData::new(actual_path.to_string_lossy().to_string(), actual_path), @@ -101,6 +171,9 @@ async fn loader_test(actual: impl AsRef, expected: impl AsRef) { module: "".into(), module_context: None, module_source_map_kind: SourceMapKind::SourceMap, + queue_handler: Default::default(), + plugin_driver, + cache: Arc::new(rspack_core::cache::Cache::new(compiler_options)), }, ) .await diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs index 7796d80e82c..83ddb588779 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs @@ -70,7 +70,11 @@ impl HarmonyImportSpecifierDependency { // TODO move export_info pub fn check_used(&self, reference_mgm: &ModuleGraphModule, compilation: &Compilation) -> bool { - if compilation.options.builtins.tree_shaking.is_false() { + if compilation.options.builtins.tree_shaking.is_false() + || compilation + .bailout_module_identifiers + .contains_key(&reference_mgm.module_identifier) + { return true; } if !compilation diff --git a/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs b/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs index 4c664ec6242..69cfe84ce08 100644 --- a/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs +++ b/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs @@ -798,6 +798,9 @@ impl Plugin for ModuleConcatenationPlugin { module: new_module.id(), module_context: None, module_source_map_kind: rspack_util::source_map::SourceMapKind::None, + cache: compilation.cache.clone(), + queue_handler: compilation.queue_handle.clone(), + plugin_driver: compilation.plugin_driver.clone(), }, plugin_driver: compilation.plugin_driver.clone(), compiler_options: &compilation.options, diff --git a/packages/rspack/src/Compilation.ts b/packages/rspack/src/Compilation.ts index e195b1786da..29c4e66df9a 100644 --- a/packages/rspack/src/Compilation.ts +++ b/packages/rspack/src/Compilation.ts @@ -82,7 +82,7 @@ export interface KnownCreateStatsOptionsContext { } export interface ExecuteModuleArgument { - result: CodeGenerationResult; + codeGenerationResult: CodeGenerationResult; moduleObject: { id: string; exports: any; diff --git a/packages/rspack/src/Compiler.ts b/packages/rspack/src/Compiler.ts index 48ffb2bc207..5577a53c5e4 100644 --- a/packages/rspack/src/Compiler.ts +++ b/packages/rspack/src/Compiler.ts @@ -49,6 +49,7 @@ import { tryRunOrWebpackError } from "./lib/HookWebpackError"; import { CodeGenerationResult } from "./Module"; import { canInherentFromParent } from "./builtin-plugin/base"; import { CreateModuleData } from "@rspack/binding"; +import ExecuteModulePlugin from "./ExecuteModulePlugin"; class Compiler { #_instance?: binding.Rspack; @@ -121,6 +122,8 @@ class Compiler { #disabledHooks: string[]; parentCompilation?: Compilation; + #moduleExecutionResultsMap: Map; + constructor(context: string, options: RspackOptionsNormalized) { this.outputFileSystem = fs; this.options = options; @@ -180,6 +183,9 @@ class Compiler { this.modifiedFiles = undefined; this.removedFiles = undefined; this.#disabledHooks = []; + this.#moduleExecutionResultsMap = new Map(); + + new ExecuteModulePlugin().apply(this); } /** @@ -883,14 +889,20 @@ class Compiler { return; } - #executeModule({ + async #executeModule({ entry, + request, + options, runtimeModules, - codegenResults + codegenResults, + id }: { entry: string; + request: string; + options: { publicPath?: string; baseUri?: string }; runtimeModules: string[]; codegenResults: binding.JsCodegenerationResults; + id: number; }) { const __webpack_require__: any = (id: string) => { const cached = moduleCache[id]; @@ -922,7 +934,10 @@ class Compiler { tryRunOrWebpackError( () => this.compilation.hooks.executeModule.call( - { result: new CodeGenerationResult(result), moduleObject }, + { + codeGenerationResult: new CodeGenerationResult(result), + moduleObject + }, { __webpack_require__ } ), "Compilation.hooks.executeModule" @@ -945,9 +960,9 @@ class Compiler { __webpack_require__(runtimeModule); } - exports = __webpack_require__(entry); + const executeResult = __webpack_require__(entry); - return JSON.stringify(exports); + this.#moduleExecutionResultsMap.set(id, executeResult); } #compilation(native: binding.JsCompilation) { @@ -1158,6 +1173,10 @@ class Compiler { __internal__registerBuiltinPlugin(plugin: binding.BuiltinPlugin) { this.builtinPlugins.push(plugin); } + + __internal__getModuleExecutionResult(id: number) { + return this.#moduleExecutionResultsMap.get(id); + } } export { Compiler }; diff --git a/packages/rspack/src/ExecuteModulePlugin.ts b/packages/rspack/src/ExecuteModulePlugin.ts new file mode 100644 index 00000000000..aff8066892d --- /dev/null +++ b/packages/rspack/src/ExecuteModulePlugin.ts @@ -0,0 +1,57 @@ +import { RuntimeGlobals } from "."; +import type { Compiler } from "./Compiler"; +import vm from "node:vm"; + +export default class ExecuteModulePlugin { + constructor() {} + + apply(compiler: Compiler) { + compiler.hooks.compilation.tap("executeModule", compilation => { + compilation.hooks.executeModule.tap( + "executeModule", + (options, context) => { + const moduleObject = options.moduleObject; + const source = options.codeGenerationResult.get("javascript"); + try { + const fn = vm.runInThisContext( + `(function(module, __webpack_exports__, ${RuntimeGlobals.require}) {\n${source}\n})`, + { + filename: moduleObject.id + } + ); + + fn.call( + moduleObject.exports, + moduleObject, + moduleObject.exports, + context.__webpack_require__ + ); + } catch (e: any) { + let err = e instanceof Error ? e : new Error(e); + + err.stack += printGeneratedCodeForStack(moduleObject.id, source); + throw err; + } + } + ); + }); + } +} +const printGeneratedCodeForStack = (moduleId: string, code: string) => { + const lines = code.split("\n"); + const n = `${lines.length}`.length; + return `\n\nGenerated code for ${moduleId}\n${lines + .map( + /** + * @param {string} line the line + * @param {number} i the index + * @param {string[]} lines the lines + * @returns {string} the line with line number + */ + (line, i, lines) => { + const iStr = `${i + 1}`; + return `${" ".repeat(n - iStr.length)}${iStr} | ${line}`; + } + ) + .join("\n")}`; +}; diff --git a/packages/rspack/src/Module.ts b/packages/rspack/src/Module.ts index 583a376ca38..cf54cd8e11e 100644 --- a/packages/rspack/src/Module.ts +++ b/packages/rspack/src/Module.ts @@ -38,9 +38,14 @@ export class Module { export class CodeGenerationResult { #inner: JsCodegenerationResult; + constructor(result: JsCodegenerationResult) { this.#inner = result; } + + get(sourceType: string) { + return this.#inner.sources[sourceType]; + } } export class CodeGenerationResults { diff --git a/packages/rspack/src/config/adapterRuleUse.ts b/packages/rspack/src/config/adapterRuleUse.ts index 2a509a8c504..d891f80843f 100644 --- a/packages/rspack/src/config/adapterRuleUse.ts +++ b/packages/rspack/src/config/adapterRuleUse.ts @@ -140,6 +140,11 @@ export interface LoaderContext { getContextDependencies(): string[]; getMissingDependencies(): string[]; addBuildDependency(file: string): void; + importModule( + request: string, + options: { publicPath: string; baseUri: string }, + callback: (err?: Error, res?: any) => void + ): void; fs: any; utils: { absolutify: (context: string, request: string) => string; diff --git a/packages/rspack/src/loader-runner/index.ts b/packages/rspack/src/loader-runner/index.ts index a7354585554..cf4b7fb8559 100644 --- a/packages/rspack/src/loader-runner/index.ts +++ b/packages/rspack/src/loader-runner/index.ts @@ -239,6 +239,80 @@ export async function runLoaders( missingDependencies.length = 0; cacheable = true; }; + loaderContext.importModule = function importModule( + request, + options, + callback + ) { + const executeModuleKey = request + options.publicPath + options.baseUri; + if (!callback) { + return new Promise((resolve, reject) => { + compiler.compilation + .__internal_getInner() + .importModule( + request, + options.publicPath, + options.baseUri, + rawContext._moduleIdentifier, + loaderContext.context, + (err, res) => { + if (err) reject(err); + else { + for (const dep of res.buildDependencies) { + this.addBuildDependency(dep); + } + for (const dep of res.contextDependencies) { + this.addContextDependency(dep); + } + for (const dep of res.missingDependencies) { + this.addMissingDependency(dep); + } + for (const dep of res.fileDependencies) { + this.addDependency(dep); + } + assetFilenames.push(...res.assets); + + resolve(compiler.__internal__getModuleExecutionResult(res.id)); + } + } + ); + }); + } + return compiler.compilation + .__internal_getInner() + .importModule( + request, + options.publicPath, + options.baseUri, + rawContext._moduleIdentifier, + loaderContext.context, + (err, res) => { + if (err) { + callback(err, undefined); + } else { + for (const dep of res.buildDependencies) { + this.addBuildDependency(dep); + } + for (const dep of res.contextDependencies) { + this.addContextDependency(dep); + } + for (const dep of res.missingDependencies) { + this.addMissingDependency(dep); + } + for (const dep of res.fileDependencies) { + this.addDependency(dep); + } + assetFilenames.push(...res.assets); + + callback( + undefined, + compiler.__internal__getModuleExecutionResult(res.id) + ); + } + } + ); + }; + Object.defineProperty(loaderContext, "resource", { enumerable: true, get: function () { diff --git a/packages/rspack/tests/configCases/loader-import-module/css/colors.js b/packages/rspack/tests/configCases/loader-import-module/css/colors.js new file mode 100644 index 00000000000..d3da74ec7a1 --- /dev/null +++ b/packages/rspack/tests/configCases/loader-import-module/css/colors.js @@ -0,0 +1,2 @@ +export const red = "#f00"; +export const green = "#0f0"; diff --git a/packages/rspack/tests/configCases/loader-import-module/css/file.jpg b/packages/rspack/tests/configCases/loader-import-module/css/file.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fe5c6eefa791be68586c8eb05cd98a25265f15af GIT binary patch literal 6027 zcmbW5d0b5G+raN>T8U{Y64kUS%hM(bHB*#I+ElW{Ac{yE?KN!-ifN%xObenY5v@#{ z7NMe|O)Jx;W}0cXX6C#nJkRrfe((GK^IrFS=G>o|`?}8g-pjQd=qoe?WOnbe-31T` z0Bqm}pka_?6?)nW0DF6|9snQ-#1OIo3HK0i1Blf?>{lOv-H0{+^!Flk{)`a;fOr~+ z{uy%+zAg%Ec~Se%6`76r*J8HFKcmyLk^lA=)u5li#OajZdB31je*UVPboGJB4qJQC zMeDHq>TCV#%5*9x^^m}G(TNzTThp8!(NG;gOM-Nejzp*f5i|mcMnDY!13!fr;+OnV z0{|%^iVy?h5|UC&mcj$-WdH&R|ArJ57nKkfl@t#`hyWy7R8~#QKu%po+``#V{#by9 zM67Dcjukn3Hy{HY&et9Nl#sW| ze?;5L_~Z+>3-@o!D3E?SP`(-DAq|cC1+>JN{Qo}U1%LzM{6yI1L+EJo?HCaTDhib=YE1GZ`Cgj zV;E$YHH_l6ZxQCiNv}nopz{}k7nx$>6E6PdO1p9MH(K8PKXA`ql>XtXtN)wu^WTUg zqkkah{-NvxJAvpwA}%f_E-onni=?#FqDae0FPDZta%g!Ol#D!DMPbDX1r>GWHOlJi zwY1h785&tydO)xd#6LDf|DQ&Bfi&{h^r7L+#TLBuU2M7eZVG$OZc=D@*eBlS^LP8I zB&=q8F#a0nI(G!UmNA`waHNm?BB0)b7JgTJ2WNrRyWRJYuT5TrT@UrSgL~eXW_oAn zJlpyC}q5f4eKB1L1sMp(fw9nVWlShldnSIhX#u-dwSyr{!F3+`PKv<0`C8z$yqpK`)asS}@glSwnpT%N zcgw_v!i^o&{Bxu6pZd;LZs~m)b?L5dv#`6w`i}$Zk!LCbMH=YztWdc=3DrGHK-Rz!si1aCISJs*7FHl zwREm-?a*e-Ib7~fD{}Y@fkcy;^h;OJs>0KO>IPb$ZnCu@uyR^TeETbTc`V(@CiQ2k z?evXxCk{2qx>WvT=-TbFP;P&z+7x^<>U*Gf;GrUHjjU&B$e|A}pH)ov(0+OwXOdk8h!nW zHokZ7UX$y$#Wv^EwLsm^p1@;v3v1#|Sdl^r1W5ftZf`Vu#%`hMWFwXD{qo*yfX|F> z?yFZ-l!|JzS2+3tdGMdx_Cp|vWonFLkp?{>fT<8LN(Hn18SLw=*96t=9>IV9D$D$K zju8117s1vPZB@>5YY0FDC_S$Fnq0Qgu;xX55Cl%m^nM)qxv$Uc=_9wkrI&_m&z3IU zq)tvI^+L;lY|$1s-{8a#S1qf?*Jhk4{W^NBU>N;i_ujTsMf=ia?g)DR>d;7C(Ztrc z8&IKDr`2469Sd$fbY0V$|33YS%R%}0UIP7GM_q+d@HmNEmz<-Or%J?eo;y9b^-~tt zx%8T>nB`5ibyAo~%CD7&BLWUF&89~|;KKCE(@CQSmEuj&=kioA4Pks@hOv8^9_A7* zIh-&&kDtGG$#&FO%7s{zRNl%Ww{nGOz<4;+v#r2I&S(= z^DrSc!(Cc+bbPop{Jl_}dB3%w&i1*N5(FAsg%HTd7E18tFOWyc9J|iAaBNm=jUi>8 zIZnCwh0y)iHho8zOBV&TZTz#8S#CILh)`>NV6^^obF1;7;aTerftT->#zFd-OZ&sm z<ijah{jXcR3V8NoTyo)ACMLy3wL|_$rmjUIu0Q(ge&Ln1yT;CzhkM+v z5Xs|apzIS-sveY00~z9`^|K_dVE_5l#!U@2Xp?q-=6*T@SBam-{$6%C7l4l!^K)_q_z3at_a9iuFNK)&UGEoCO1E(++ ziNypT(0Y-7i5ndqOKMWQ*27R1W=2|NZM&XHCk0_R9SmWxT+-WyzI!G%)mfOfuH?Ka zVN_K4i3Cex_AXPRX3pMLlCs>IABmq&PEePXuiq>ni=JM$3%s)gW_F!t-<{03->~cB z$z*?}{dvxDFjUX_rs1H^&b-kUfUrc?r7WeE@(c=;QGd7#XaQ- zv4yxUlC5dOi7D->xfs6Mq&dm%7Q)=ToN|#Q5TO2>nLuRWB*HXh_2(x-fx~;jr!-K84W{-+;c!RdWa|ODW zCosh`8vJ|n67IG*1=l+yirZ=l8we=u^@-?Ef70S#V$syRN&LJL!YS9WrCY;!d_1CS z?e)ZVVR5ySqtk^E@{vxY`;CY39k+h&)pW1#!T4pq3)?VmO6>V6x*4sx}EuD9tIzN;lv|V^kIK@hv4mxzCQ6y+@;7BUsVe#FgB}L`#_xXti@8WKT zV=9u+Upl#{+mQ=BvJX(bEbHd55>e+*8(n=u?`)2`OKHIcJN{Jp6m@-6&`aTLda_}H zVZpMK8Bks8s9KC4OLzBWK4S9g7mCvpR_L+JyBK~q^OX=nS?)?n+lM##@!u!8X*RCK zqQ^089?y8-Xrz7xr5EpN zcWTg?zimv`2y&-%$r1@W7m}L%V|O;a$DT}m%#W|1*2^$lvSR#zKLqN`9*bU3@fk{C zm1wod>Wxm5N(8IVbE;i+@?Gaq-4JNOU9j7|J{omIFCLpc;YmD@51CTGV%R?Rs73=p^vFeQkx~>my6%!>^e|Qy(!m+Bz5s z{2mG3-Am?K_wheC=Lj6G%FmGIBAD0A4u=+g;l9&x-PXj}hAy<`>RwrKA&7M1+=|hk1iUc%AhY7d2oFC9fmn6ig98mhhc*c4>GPFe z`7J0wAf8DXmyPZ5h$Z8vA6yy3@qHzg%d(;&(BZajJ7K!MKv~Q17}tj#zwyX`D@s2D zXKy9yKwi*2!~7b{CM4xaMyL&o4!qW*Jk)pCeX=M(c6- zb7Yot<#JivOE1C$LYq|*=iK0AN|e|K5(NUwU13Mn7+J>YtF<3wZ-78;+=Bm8tVG73 zfWqH<75n}*;pN0~ypTRq(^@}A>AHtO3(ELyBnY55BpidOgF)6gp{J;cnFukxp~pM< zNjNM_D4K5dWuwg3v`d0Sxv7fw%iFdHDKrRVL14HE0z|$wnaf)tB)ntqy^FENYwa#f z;f4`{BTUQ0D@*dGFR-noS0`bpUA1K*)-~(C+$dKeT0sCk`M())Y!$ZN-~Jo|(r4c2 z+stVf6e@aoQd=1iAT)$iiupJyjuQai7uKf=qc0ND<{&`H9{eOE3sH2H;whMf+a^>Z za0m@y5FqCq4pz?9#6}SY+x;d8jxfrcM(k3_FUGKABl4;6jN$mI$Dbe=2YOYM1h4cv z>Ggu{7M&Em7V}*L>3G^cm1SWZ#yr7-@<4za;~}NEm3n*?4#vw7Y-%mT<89%jZ=)(TJqEnXM)>}f+=N?uEqXeAF@ z%jAk;;@)jzL*Njyld$`f=9&CR2ypUqCJ7-gs$l|yCjOph3H@UEYY7t85Qx(oJA#iR z_4L9|-E7hLUaydUGGjB3z_%ffnUplMam5fYD3NlK#&j(zWlR0VUD2cMBdHYt}Z@`(1(?f<1vf6zh&{zp%;r`BtVFW!EXTI7amdw zfL1e*flr-k04*gpr=+8HnEpQw-C~v~!(ZNjbBam4_{Oo(@C^M2To~-Zby19e2tufS zbKH;Q_uLoFJl(+&8A*-iyV1++m7U{4D0VX`{H|DuVGp5&j>4BGQNoN`%-D~Gr|0l% zK64G?f_W$^GtpV@nvsMTPp37R6UJ2wczZXYZS-Ud&6qDC2&?%J(^bA&h&&<|tzJ5z z5T8Yf$p1WFTud_BpA~4p;+dnu;ndV_w?P(#CYx?vSTOs6H~WRAJ%v9ov+d~6w>8uW zY+L5=CI5bjSAvi!j+}q06cL~CBQ$B$q+DP)b|pG$(C@zLenv}Q$-pzev`oGXRbq^) zS5Sjlwv;@~IDyh}Fp4@T&LMP_EyM{iw_V~B_SiJE-lpPx?`_`herPFc1wZPndBQIj+T&ONK9QZ|&Lmd@Zq_^R55Qnc@Y{Jf8I! z{t5p{2&R+&R9u)MJYgj#SHf5P@o{*B=iNON38Kw+NOOsptmYL>9geTqep?3>kkcjo z1Q&*OQ6!St6KHOF>n-+8c3&qyE7DlfC=B1<%^r`995)8=P;`Tlp{PV`8qvaj7l_l zj-kY~4{{w}7jw9xV zT-lFAI^W==hX~H41I4>|Ih+?Rw2_fyW~IqtmNt?bbS&Ce9rEO9C<-quc%FxK(`AKuwjJ6cYl2>UG9rCTCp&nld;Ha~aDNBLZJbAG6k} zj%k;w;6TjBH}OZCV>dX(ipHo$A?PRH1?HHpe)FwYTUW2?S=94|H&G0ixB*K?zo9la zj1+Ryc-xgXcI(@}N}{|8ZfMJdhVbl1OP+w;Z=NiKjappVcU|TR`7a32UaD(I)?rs} ztqvGw2v-|muhfv${oBVlI%^tvTf;Y2Wu#$dC@8!TBNXb)*6XLW8A`Ztb;K{-&;J;j zK(d!QpkG3}o_@l9@bLmxPn+Wh7rR6b{;#*2%XGqA*A(>})w>lyZ>u@MsmhMrSzmlK zAC9wL()^D{d0O-P(qmN5tKgTk4sB4d{wOA4_LGU9Q^7F!5@ZM{qG)dpT|P&2D#e~> zV~Owk{Td9^)#^eOCyp)O?XQrBq@y0b3bkLNpOySGCd5&12;H_aOf}-vo9o6@1VJX^ zk<0tFdOOW5;4p)OfEpZKa5ZrFpeI9M>30ZJIayk$Sf)IP3-4433wn&Yx=bUx^Jq}& zH_w98m~~Z}L@nyL!ac$)1jbBpR1OL!G{g)?QiRJWL)8Qh947fhA|C<>c&|0?492v!hvKZ6W`Tt<&@sac|eLboUzOVGBJDnE2iMBI5Jx zjqO$M<0$^)c}1+wuv|@SpCOhMU|W9f-*wEKB|=r5_Jk zU{@q>tC64anaC|F`sl}|!={H4FR^V|V^4IKOJZ$DT82#E@`8^pI!r{-y6ehPa^Mos z8Uo28BAObx`yW!tdh`*_mZcOVoXV=K;SDwkQyU|0!*MuBWTKWi#f1Q4>!TRkK;`#U zLKQ!~21z^KU?aB~-PAHvvhKrjjFBXO(>?;1_F%a*M>eO;f|!es>c};b#0fS*J^uxR Cke5#Y literal 0 HcmV?d00001 diff --git a/packages/rspack/tests/configCases/loader-import-module/css/file.png b/packages/rspack/tests/configCases/loader-import-module/css/file.png new file mode 100644 index 0000000000000000000000000000000000000000..fb53b9dedd3b409ea93b7db98a2c34454bf95fee GIT binary patch literal 14910 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
ci;+pI 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 { + expect(stylesheet).toBe( + 'body { background: url("my-schema://base/public/assets/file.png"); color: #f00; }' + ); + expect(otherStylesheet).toBe( + 'body { background: url("/other/assets/file.jpg"); color: #0f0; }' + ); +}); diff --git a/packages/rspack/tests/configCases/loader-import-module/css/loader.js b/packages/rspack/tests/configCases/loader-import-module/css/loader.js new file mode 100644 index 00000000000..118f7431844 --- /dev/null +++ b/packages/rspack/tests/configCases/loader-import-module/css/loader.js @@ -0,0 +1,13 @@ +/** @type {import("../../../../").PitchLoaderDefinitionFunction} */ +exports.pitch = async function (remaining) { + try { + const result = await this.importModule( + this.resourcePath + ".webpack[javascript/auto]" + "!=!" + remaining, + this.getOptions() + ); + return result.default || result; + } catch (e) { + console.error(e); + throw e; + } +}; diff --git a/packages/rspack/tests/configCases/loader-import-module/css/other-stylesheet.js b/packages/rspack/tests/configCases/loader-import-module/css/other-stylesheet.js new file mode 100644 index 00000000000..d3be25b7f28 --- /dev/null +++ b/packages/rspack/tests/configCases/loader-import-module/css/other-stylesheet.js @@ -0,0 +1,3 @@ +import { green } from "./colors.js"; +import file from "./file.jpg"; +export default `body { background: url("${file}"); color: ${green}; }`; diff --git a/packages/rspack/tests/configCases/loader-import-module/css/stylesheet.js b/packages/rspack/tests/configCases/loader-import-module/css/stylesheet.js new file mode 100644 index 00000000000..30116d4f4fb --- /dev/null +++ b/packages/rspack/tests/configCases/loader-import-module/css/stylesheet.js @@ -0,0 +1,4 @@ +import { red } from "./colors.js"; +export default `body { background: url("${ + new URL("./file.png", import.meta.url).href +}"); color: ${red}; }`; diff --git a/packages/rspack/tests/configCases/loader-import-module/css/webpack.config.js b/packages/rspack/tests/configCases/loader-import-module/css/webpack.config.js new file mode 100644 index 00000000000..2c03d3fc7a4 --- /dev/null +++ b/packages/rspack/tests/configCases/loader-import-module/css/webpack.config.js @@ -0,0 +1,77 @@ +/** @type {import("@rspack/core").Configuration} */ +module.exports = { + output: { + publicPath: "/public/" + }, + entry: "./index.js", + module: { + parser: { + javascript: { + // url: "relative" + } + }, + rules: [ + { + dependency: "url", + issuer: /stylesheet\.js$/, + type: "asset/resource", + generator: { + filename: "assets/[name][ext][query]" + } + }, + { + oneOf: [ + { + test: /other-stylesheet\.js$/, + loader: "./loader", + options: { + publicPath: "/other/", + baseUri: "my-schema://base" + }, + type: "asset/source" + }, + { + test: /stylesheet\.js$/, + loader: "./loader", + options: { + baseUri: "my-schema://base" + }, + type: "asset/source" + } + ] + }, + { + test: /\.jpg$/, + type: "asset/resource", + generator: { + filename: "assets/[name][ext]" + } + } + ] + }, + plugins: [ + compiler => + compiler.hooks.done.tap("test case", stats => { + try { + expect(stats.compilation.getAsset("assets/file.png")).toHaveProperty( + "info", + expect.objectContaining({ sourceFilename: "file.png" }) + ); + expect(stats.compilation.getAsset("assets/file.jpg")).toHaveProperty( + "info", + expect.objectContaining({ sourceFilename: "file.jpg" }) + ); + const { auxiliaryFiles } = stats.compilation.namedChunks.get("main"); + expect(auxiliaryFiles).toContain("assets/file.png"); + } catch (e) { + console.log(stats.toString({ colors: true, orphanModules: true })); + throw e; + } + }) + ], + experiments: { + rspackFuture: { + newTreeshaking: true + } + } +}; diff --git a/webpack-test/ConfigTestCases.template.js b/webpack-test/ConfigTestCases.template.js index 2b05905a353..4484a8275f6 100644 --- a/webpack-test/ConfigTestCases.template.js +++ b/webpack-test/ConfigTestCases.template.js @@ -21,15 +21,16 @@ const filterInfraStructureErrors = require("./helpers/infrastructureLogErrors"); const { normalizeFilteredTestName } = require('./lib/util/filterUtil') const casesPath = path.join(__dirname, "configCases"); -const categories = fs.readdirSync(casesPath).map(cat => { - return { - name: cat, - tests: fs - .readdirSync(path.join(casesPath, cat)) - .filter(folder => !folder.startsWith("_")) - .sort() - }; -}); +const categories = fs.readdirSync(casesPath) + .map(cat => { + return { + name: cat, + tests: fs + .readdirSync(path.join(casesPath, cat)) + .filter(folder => !folder.startsWith("_")) + .sort() + }; + }); const createLogger = appendTarget => { return { @@ -39,14 +40,14 @@ const createLogger = appendTarget => { info: l => appendTarget.push(l), warn: console.warn.bind(console), error: console.error.bind(console), - logTime: () => {}, - group: () => {}, - groupCollapsed: () => {}, - groupEnd: () => {}, - profile: () => {}, - profileEnd: () => {}, - clear: () => {}, - status: () => {} + logTime: () => { }, + group: () => { }, + groupCollapsed: () => { }, + groupEnd: () => { }, + profile: () => { }, + profileEnd: () => { }, + clear: () => { }, + status: () => { } }; }; @@ -78,7 +79,7 @@ const describeCases = config => { if (flag !== true) { let filteredName = normalizeFilteredTestName(flag, testName); describe.skip(testName, () => { - it(filteredName, () => {}); + it(filteredName, () => { }); }); return; } @@ -152,7 +153,7 @@ const describeCases = config => { // } }); testConfig = { - findBundle: function(i, options) { + findBundle: function (i, options) { const ext = path.extname( parseResource(options.output.filename).path ); @@ -274,27 +275,27 @@ const describeCases = config => { ) ); } -// const allModules = children -// ? children.reduce( -// (all, { modules }) => all.concat(modules), -// modules || [] -// ) -// : modules; -// if ( -// allModules.some( -// m => m.type !== "cached modules" && !m.cached -// ) -// ) { -// return done( -// new Error( -// `Some modules were not cached:\n${stats.toString({ -// all: false, -// modules: true, -// modulesSpace: 100 -// })}` -// ) -// ); -// } + // const allModules = children + // ? children.reduce( + // (all, { modules }) => all.concat(modules), + // modules || [] + // ) + // : modules; + // if ( + // allModules.some( + // m => m.type !== "cached modules" && !m.cached + // ) + // ) { + // return done( + // new Error( + // `Some modules were not cached:\n${stats.toString({ + // all: false, + // modules: true, + // modulesSpace: 100 + // })}` + // ) + // ); + // } } const infrastructureLogErrors = filterInfraStructureErrors( infraStructureLog, diff --git a/webpack-test/configCases/loader-import-module/css/test.filter.js b/webpack-test/configCases/loader-import-module/css/test.filter.js index 3be456dcd23..02971e02798 100644 --- a/webpack-test/configCases/loader-import-module/css/test.filter.js +++ b/webpack-test/configCases/loader-import-module/css/test.filter.js @@ -1 +1,8 @@ -module.exports = () => {return false} \ No newline at end of file +const { FilteredStatus } = require("../../../lib/util/filterUtil") + +module.exports = () => { + return [ + FilteredStatus.PARTIAL_PASS, + "https://github.com/web-infra-dev/rspack/issues/4923" + ] +}