diff --git a/Cargo.lock b/Cargo.lock index 0a89c1e4f272..1a43fcfd281f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4123,7 +4123,9 @@ dependencies = [ "rspack_binding_builder_macros", "rspack_core", "rspack_error", + "rspack_hook", "rspack_napi", + "rspack_plugin_javascript", ] [[package]] diff --git a/crates/rspack_binding_api/src/plugins/interceptor.rs b/crates/rspack_binding_api/src/plugins/interceptor.rs index d3cd987cabdd..da32e5134410 100644 --- a/crates/rspack_binding_api/src/plugins/interceptor.rs +++ b/crates/rspack_binding_api/src/plugins/interceptor.rs @@ -15,7 +15,7 @@ use napi::{ use rspack_collections::{Identifier, IdentifierMap, IdentifierSet}; use rspack_core::{ AfterResolveResult, AssetEmittedInfo, AsyncModulesArtifact, BeforeResolveResult, BindingCell, - BoxModule, ChunkGraph, ChunkUkey, CircularModulesInfo, Compilation, + BoxDependency, BoxModule, ChunkGraph, ChunkUkey, CircularModulesInfo, Compilation, CompilationAdditionalTreeRuntimeRequirements, CompilationAdditionalTreeRuntimeRequirementsHook, CompilationAfterOptimizeModules, CompilationAfterOptimizeModulesHook, CompilationAfterProcessAssets, CompilationAfterProcessAssetsHook, CompilationAfterSeal, @@ -1291,6 +1291,7 @@ impl CompilationSucceedModule for CompilationSucceedModuleTap { compiler_id: CompilerId, _compilation_id: CompilationId, module: &mut BoxModule, + _dependencies: &mut [BoxDependency], ) -> rspack_error::Result<()> { #[allow(clippy::unwrap_used)] let _ = self diff --git a/crates/rspack_binding_builder_testing/Cargo.toml b/crates/rspack_binding_builder_testing/Cargo.toml index f1688ef318db..01cfef5baeb7 100644 --- a/crates/rspack_binding_builder_testing/Cargo.toml +++ b/crates/rspack_binding_builder_testing/Cargo.toml @@ -27,9 +27,11 @@ plugin = ["rspack_binding_builder/plugin"] rspack_binding_builder = { workspace = true } rspack_binding_builder_macros = { workspace = true } -rspack_core = { workspace = true } -rspack_error = { workspace = true } -rspack_napi = { workspace = true } +rspack_core = { workspace = true } +rspack_error = { workspace = true } +rspack_hook = { workspace = true } +rspack_napi = { workspace = true } +rspack_plugin_javascript = { workspace = true } napi = { workspace = true, features = ["async", "tokio_rt", "serde-json", "anyhow", "napi9", "compat-mode"] } napi-derive = { workspace = true, features = ["compat-mode"] } diff --git a/crates/rspack_binding_builder_testing/src/lib.rs b/crates/rspack_binding_builder_testing/src/lib.rs index eef1a2ca0942..8264fbd4b456 100644 --- a/crates/rspack_binding_builder_testing/src/lib.rs +++ b/crates/rspack_binding_builder_testing/src/lib.rs @@ -3,15 +3,83 @@ extern crate napi_derive; extern crate rspack_binding_builder; use rspack_binding_builder_macros::register_plugin; -use rspack_core::{BoxPlugin, Plugin}; +use rspack_core::{ + BoxDependency, BoxModule, BoxPlugin, CompilationId, CompilationSucceedModule, CompilerId, + ModuleDependency, Plugin, +}; +use rspack_hook::{plugin, plugin_hook}; use rspack_napi::{napi, napi::bindgen_prelude::*}; +use rspack_plugin_javascript::dependency::{ + ESMExportImportedSpecifierDependency, ESMImportSpecifierDependency, +}; +#[plugin] #[derive(Debug)] #[allow(unused)] -struct BindingBuilderTestingPlugin; +struct BindingBuilderTestingPlugin { + reroute_specifiers: bool, +} + +impl BindingBuilderTestingPlugin { + fn new(reroute_specifiers: bool) -> Self { + Self::new_inner(reroute_specifiers) + } +} + +fn rerouted_request(request: &str, imported: Option<&str>) -> Option<&'static str> { + if request != "pkg" { + return None; + } + + match imported { + Some("A") => Some("pkg/A"), + Some("B") => Some("pkg/B"), + _ => None, + } +} + +#[plugin_hook(CompilationSucceedModule for BindingBuilderTestingPlugin, tracing = false)] +async fn succeed_module( + &self, + _compiler_id: CompilerId, + _compilation_id: CompilationId, + _module: &mut BoxModule, + dependencies: &mut [BoxDependency], +) -> rspack_error::Result<()> { + for dependency in dependencies { + let dependency = dependency.as_mut(); + + if let Some(dependency) = dependency.downcast_mut::() { + if let Some(request) = rerouted_request( + dependency.request(), + dependency.ids().first().map(|id| id.as_str()), + ) { + dependency.set_request(request.into()); + } + continue; + } + + if let Some(dependency) = dependency.downcast_mut::() + && let Some(request) = rerouted_request( + dependency.request(), + dependency.ids().first().map(|id| id.as_str()), + ) + { + dependency.set_request(request.into()); + } + } + + Ok(()) +} impl Plugin for BindingBuilderTestingPlugin { - fn apply(&self, _ctx: &mut rspack_core::ApplyContext<'_>) -> rspack_error::Result<()> { + fn apply(&self, ctx: &mut rspack_core::ApplyContext<'_>) -> rspack_error::Result<()> { + if self.reroute_specifiers { + ctx + .compilation_hooks + .succeed_module + .tap(succeed_module::new(self)); + } Ok(()) } } @@ -22,7 +90,10 @@ fn get_binding_plugin(_env: Env, options: Unknown<'_>) -> Result { #[allow(clippy::disallowed_names, clippy::unwrap_used)] let foo = options.get::("foo")?.unwrap(); assert_eq!(foo, "bar".to_string()); - Ok(Box::new(BindingBuilderTestingPlugin) as BoxPlugin) + let reroute_specifiers = options + .get::("rerouteSpecifiers")? + .unwrap_or_default(); + Ok(Box::new(BindingBuilderTestingPlugin::new(reroute_specifiers)) as BoxPlugin) } register_plugin!("BindingBuilderTestingPlugin", get_binding_plugin); diff --git a/crates/rspack_core/src/compilation/build_module_graph/graph_updater/repair/build.rs b/crates/rspack_core/src/compilation/build_module_graph/graph_updater/repair/build.rs index 6e9425017071..eb14aad909a6 100644 --- a/crates/rspack_core/src/compilation/build_module_graph/graph_updater/repair/build.rs +++ b/crates/rspack_core/src/compilation/build_module_graph/graph_updater/repair/build.rs @@ -93,7 +93,7 @@ impl Task for BuildResultTask { } async fn main_run(self: Box, context: &mut TaskContext) -> TaskResult { let BuildResultTask { - build_result, + mut build_result, plugin_driver, mut forwarded_ids, } = *self; @@ -102,7 +102,12 @@ impl Task for BuildResultTask { plugin_driver .compilation_hooks .succeed_module - .call(context.compiler_id, context.compilation_id, &mut module) + .call( + context.compiler_id, + context.compilation_id, + &mut module, + &mut build_result.dependencies, + ) .await?; let build_info = module.build_info(); diff --git a/crates/rspack_core/src/compilation/mod.rs b/crates/rspack_core/src/compilation/mod.rs index 16b1c215604d..b58d35d62301 100644 --- a/crates/rspack_core/src/compilation/mod.rs +++ b/crates/rspack_core/src/compilation/mod.rs @@ -70,17 +70,17 @@ pub use self::{ runtime_requirements::RuntimeRequirementsPass, }; use crate::{ - AsyncModulesArtifact, BindingCell, BoxModule, BuildChunkGraphArtifact, CacheCount, CacheOptions, - CgcRuntimeRequirementsArtifact, CgmHashArtifact, CgmRuntimeRequirementsArtifact, Chunk, - ChunkByUkey, ChunkContentHash, ChunkGraph, ChunkGroupByUkey, ChunkGroupUkey, ChunkHashesArtifact, - ChunkKind, ChunkNamedIdArtifact, ChunkRenderArtifact, ChunkRenderCacheArtifact, - ChunkRenderResult, ChunkUkey, CircularModulesInfo, CodeGenerateCacheArtifact, CodeGenerationJob, - CodeGenerationResult, CodeGenerationResultBuilder, CodeGenerationResults, CompilationLogger, - CompilationLogging, CompilerOptions, CompilerPlatform, ConcatenationScope, - DependenciesDiagnosticsArtifact, Dependency, DependencyId, DependencyRef, DependencyTemplate, - DependencyTemplateType, DependencyType, Entry, EntryData, EntryOptions, EntryRuntime, Entrypoint, - ExecuteModuleId, ExportsInfoArtifact, ExternalModuleChunkConditionHook, Filename, ImportPhase, - ImportVarMap, ImportedByDeferModulesArtifact, ModuleFactory, ModuleGraph, + AsyncModulesArtifact, BindingCell, BoxDependency, BoxModule, BuildChunkGraphArtifact, CacheCount, + CacheOptions, CgcRuntimeRequirementsArtifact, CgmHashArtifact, CgmRuntimeRequirementsArtifact, + Chunk, ChunkByUkey, ChunkContentHash, ChunkGraph, ChunkGroupByUkey, ChunkGroupUkey, + ChunkHashesArtifact, ChunkKind, ChunkNamedIdArtifact, ChunkRenderArtifact, + ChunkRenderCacheArtifact, ChunkRenderResult, ChunkUkey, CircularModulesInfo, + CodeGenerateCacheArtifact, CodeGenerationJob, CodeGenerationResult, CodeGenerationResultBuilder, + CodeGenerationResults, CompilationLogger, CompilationLogging, CompilerOptions, CompilerPlatform, + ConcatenationScope, DependenciesDiagnosticsArtifact, Dependency, DependencyId, DependencyRef, + DependencyTemplate, DependencyTemplateType, DependencyType, Entry, EntryData, EntryOptions, + EntryRuntime, Entrypoint, ExecuteModuleId, ExportsInfoArtifact, ExternalModuleChunkConditionHook, + Filename, ImportPhase, ImportVarMap, ImportedByDeferModulesArtifact, ModuleFactory, ModuleGraph, ModuleGraphCacheArtifact, ModuleIdentifier, ModuleIdsArtifact, ModuleStaticCache, PathData, ProcessRuntimeRequirementsCacheArtifact, ReferencedExport, ResolverFactory, RuntimeGlobals, RuntimeKeyMap, RuntimeMode, RuntimeModule, RuntimeProxyMetadataArtifact, RuntimeSpec, @@ -104,7 +104,7 @@ define_hook!(CompilationAddEntry: Series(entry_name: Option<&str>, options: &mut define_hook!(CompilationBuildModule: Series(compiler_id: CompilerId, compilation_id: CompilationId, module: &mut BoxModule),tracing=false); define_hook!(CompilationRevokedModules: Series(compilation: &Compilation, revoked_modules: &IdentifierSet)); define_hook!(CompilationStillValidModule: Series(compiler_id: CompilerId, compilation_id: CompilationId, module: &mut BoxModule)); -define_hook!(CompilationSucceedModule: Series(compiler_id: CompilerId, compilation_id: CompilationId, module: &mut BoxModule),tracing=false); +define_hook!(CompilationSucceedModule: Series(compiler_id: CompilerId, compilation_id: CompilationId, module: &mut BoxModule, dependencies: &mut [BoxDependency]),tracing=false); define_hook!(CompilationExecuteModule: Series(module: &ModuleIdentifier, runtime_modules: &[Identifier], code_generation_results: &BindingCell, execute_module_id: &ExecuteModuleId)); define_hook!(CompilationFinishModules: Series(compilation: &Compilation, async_modules_artifact: &mut AsyncModulesArtifact, exports_info_artifact: &mut ExportsInfoArtifact, side_effects_state_artifact: &mut SideEffectsStateArtifact)); diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/esm_export_imported_specifier_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/esm_export_imported_specifier_dependency.rs index f41c7b3066aa..af9aa2f8a0f6 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/esm_export_imported_specifier_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/esm_export_imported_specifier_dependency.rs @@ -135,6 +135,16 @@ impl ESMExportImportedSpecifierDependency { .map_or_else(|| self.ids.as_slice(), |meta| meta.ids.as_slice()) } + pub fn ids(&self) -> &[Atom] { + &self.ids + } + + pub fn set_request(&mut self, request: Atom) { + self.resource_identifier = + create_resource_identifier_for_esm_dependency(&request, self.phase, self.attributes.as_ref()); + self.request = request; + } + pub fn get_mode( &self, module_graph: &ModuleGraph, diff --git a/crates/rspack_plugin_javascript/src/dependency/esm/esm_import_specifier_dependency.rs b/crates/rspack_plugin_javascript/src/dependency/esm/esm_import_specifier_dependency.rs index d77fe8d811ed..548c29aa6750 100644 --- a/crates/rspack_plugin_javascript/src/dependency/esm/esm_import_specifier_dependency.rs +++ b/crates/rspack_plugin_javascript/src/dependency/esm/esm_import_specifier_dependency.rs @@ -114,6 +114,16 @@ impl ESMImportSpecifierDependency { .map_or_else(|| self.ids.as_slice(), |meta| meta.ids.as_slice()) } + pub fn ids(&self) -> &[Atom] { + &self.ids + } + + pub fn set_request(&mut self, request: Atom) { + self.resource_identifier = + create_resource_identifier_for_esm_dependency(&request, self.phase, self.attributes.as_ref()); + self.request = request; + } + pub fn imported_name(&self) -> &Atom { self.ids.first().unwrap_or(&self.name) } diff --git a/crates/rspack_plugin_progress/src/lib.rs b/crates/rspack_plugin_progress/src/lib.rs index f4869be1afaa..42e98be13945 100644 --- a/crates/rspack_plugin_progress/src/lib.rs +++ b/crates/rspack_plugin_progress/src/lib.rs @@ -12,8 +12,8 @@ use futures::future::BoxFuture; use indicatif::{MultiProgress, ProgressBar, ProgressDrawTarget, ProgressStyle}; use rspack_collections::IdentifierMap; use rspack_core::{ - AsyncModulesArtifact, BoxModule, ChunkByUkey, ChunkNamedIdArtifact, CircularModulesInfo, - Compilation, CompilationAfterOptimizeModules, CompilationAfterProcessAssets, + AsyncModulesArtifact, BoxDependency, BoxModule, ChunkByUkey, ChunkNamedIdArtifact, + CircularModulesInfo, Compilation, CompilationAfterOptimizeModules, CompilationAfterProcessAssets, CompilationBuildModule, CompilationChunkIds, CompilationFinishModules, CompilationId, CompilationModuleIds, CompilationOptimizeChunkModules, CompilationOptimizeChunks, CompilationOptimizeCodeGeneration, CompilationOptimizeDependencies, CompilationOptimizeModules, @@ -387,6 +387,7 @@ async fn succeed_module( _compiler_id: CompilerId, _compilation_id: CompilationId, module: &mut BoxModule, + _dependencies: &mut [BoxDependency], ) -> Result<()> { self.modules_done.fetch_add(1, Relaxed); self diff --git a/packages/rspack-test-tools/src/helper/setup-env.ts b/packages/rspack-test-tools/src/helper/setup-env.ts index 6165d092f80d..0b45cbcc09c4 100644 --- a/packages/rspack-test-tools/src/helper/setup-env.ts +++ b/packages/rspack-test-tools/src/helper/setup-env.ts @@ -1,12 +1,9 @@ // @ts-nocheck -const path = require('node:path'); - // Setup environment variable for binding testing if (process.env.RSPACK_BINDING_BUILDER_TESTING) { - process.env.RSPACK_BINDING = path.resolve( - __dirname, - '../../node_modules/@rspack/binding-testing', - ); + process.env.RSPACK_BINDING = require.resolve('@rspack/binding-testing', { + paths: [process.cwd()], + }); } if (process.env.RSTEST) { diff --git a/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/index.js b/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/index.js new file mode 100644 index 000000000000..2c8603c40339 --- /dev/null +++ b/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/index.js @@ -0,0 +1,6 @@ +import { A, B } from "pkg"; + +it("should reroute individual ESM specifiers from a native plugin", () => { + expect(A).toBe("A from shim"); + expect(B).toBe("B from shim"); +}); diff --git a/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/node_modules/pkg/A.js b/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/node_modules/pkg/A.js new file mode 100644 index 000000000000..a4373fb47210 --- /dev/null +++ b/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/node_modules/pkg/A.js @@ -0,0 +1 @@ +export const A = "A from shim"; diff --git a/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/node_modules/pkg/B.js b/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/node_modules/pkg/B.js new file mode 100644 index 000000000000..f1e54c96ac94 --- /dev/null +++ b/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/node_modules/pkg/B.js @@ -0,0 +1 @@ +export const B = "B from shim"; diff --git a/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/node_modules/pkg/index.js b/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/node_modules/pkg/index.js new file mode 100644 index 000000000000..9aab0d1444cc --- /dev/null +++ b/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/node_modules/pkg/index.js @@ -0,0 +1,4 @@ +throw new Error("The original package entry should be tree-shaken"); + +export const A = "A from package"; +export const B = "B from package"; diff --git a/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/node_modules/pkg/package.json b/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/node_modules/pkg/package.json new file mode 100644 index 000000000000..820cc7714df6 --- /dev/null +++ b/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/node_modules/pkg/package.json @@ -0,0 +1,4 @@ +{ + "name": "pkg", + "sideEffects": false +} diff --git a/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/rspack.config.js b/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/rspack.config.js new file mode 100644 index 000000000000..978bdabbc118 --- /dev/null +++ b/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/rspack.config.js @@ -0,0 +1,24 @@ +/** @type {import("@rspack/core").experiments} */ +const experiments = require('@rspack/core').experiments; + +const binding = require(process.env.RSPACK_BINDING); +binding.registerBindingBuilderTestingPlugin(); + +const BindingBuilderTestingPlugin = experiments.createNativePlugin( + 'BindingBuilderTestingPlugin', + (options) => options, +); + +/** @type {import("@rspack/core").Configuration} */ +module.exports = { + mode: 'development', + optimization: { + sideEffects: true, + }, + plugins: [ + new BindingBuilderTestingPlugin({ + foo: 'bar', + rerouteSpecifiers: true, + }), + ], +}; diff --git a/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/test.filter.js b/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/test.filter.js new file mode 100644 index 000000000000..ac7a4c622426 --- /dev/null +++ b/tests/rspack-test/configCases/plugins/binding-builder-succeed-module/test.filter.js @@ -0,0 +1 @@ +module.exports = () => Boolean(process.env.RSPACK_BINDING);