From 565d5c2a147bfd463ae02545f73a0189eeb73dd3 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Tue, 9 Sep 2025 20:41:58 +0800 Subject: [PATCH 1/6] fix: resolve compilation errors in consume shared plugin - Add public get_options() method to ConsumeSharedModule for accessing private options field - Fix type mismatches between Ustr and Identifier in module graph operations - Add proper mutability declarations for module graph mutations - Collapse nested if statements to follow clippy style guidelines - Implement finishModules hook to copy build metadata from fallback modules --- .../src/sharing/consume_shared_module.rs | 5 + .../src/sharing/consume_shared_plugin.rs | 112 +++++++++++++++++- 2 files changed, 113 insertions(+), 4 deletions(-) diff --git a/crates/rspack_plugin_mf/src/sharing/consume_shared_module.rs b/crates/rspack_plugin_mf/src/sharing/consume_shared_module.rs index f60aca0aa0b1..815d8c6da47b 100644 --- a/crates/rspack_plugin_mf/src/sharing/consume_shared_module.rs +++ b/crates/rspack_plugin_mf/src/sharing/consume_shared_module.rs @@ -93,6 +93,11 @@ impl ConsumeSharedModule { source_map_kind: SourceMapKind::empty(), } } + + /// Get the consume options + pub fn get_options(&self) -> &ConsumeOptions { + &self.options + } } impl Identifiable for ConsumeSharedModule { diff --git a/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs b/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs index f1d68780f1da..9cf26a4a6221 100644 --- a/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs +++ b/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs @@ -9,10 +9,10 @@ use regex::Regex; use rspack_cacheable::cacheable; use rspack_core::{ BoxModule, ChunkUkey, Compilation, CompilationAdditionalTreeRuntimeRequirements, - CompilationParams, CompilerThisCompilation, Context, DependencyCategory, DependencyType, - ModuleExt, ModuleFactoryCreateData, NormalModuleCreateData, NormalModuleFactoryCreateModule, - NormalModuleFactoryFactorize, Plugin, ResolveOptionsWithDependencyType, ResolveResult, Resolver, - RuntimeGlobals, + CompilationFinishModules, CompilationParams, CompilerThisCompilation, Context, DependenciesBlock, + DependencyCategory, DependencyType, ModuleExt, ModuleFactoryCreateData, NormalModuleCreateData, + NormalModuleFactoryCreateModule, NormalModuleFactoryFactorize, Plugin, + ResolveOptionsWithDependencyType, ResolveResult, Resolver, RuntimeGlobals, }; use rspack_error::{Diagnostic, Result, error}; use rspack_fs::ReadableFileSystem; @@ -397,6 +397,106 @@ async fn this_compilation( Ok(()) } +#[plugin_hook(CompilationFinishModules for ConsumeSharedPlugin, stage = 10)] +async fn finish_modules(&self, compilation: &mut Compilation) -> Result<()> { + // Add finishModules hook to copy buildMeta/buildInfo from fallback modules before webpack's export analysis + // This follows webpack's pattern used by FlagDependencyExportsPlugin and InferAsyncModulesPlugin + // We use finishModules with high priority stage to ensure buildMeta is available before other plugins process exports + // Based on webpack's Compilation.js: finishModules (line 2833) runs before seal (line 2920) + + let module_graph = compilation.get_module_graph(); + + // Iterate through all modules to find ConsumeShared modules + let mut consume_shared_modules = Vec::new(); + for (module_id, module) in module_graph.modules() { + // Only process ConsumeSharedModule instances with fallback dependencies + if let Some(consume_shared) = module.as_any().downcast_ref::() { + // Check if this module has a fallback import + if consume_shared.get_options().import.is_some() { + consume_shared_modules.push(module_id); + } + } + } + + // Process each ConsumeShared module + for module_id in consume_shared_modules { + let fallback_module_id = { + let module_graph = compilation.get_module_graph(); + if let Some(module) = module_graph.module_by_identifier(&module_id) { + if let Some(consume_shared) = module.as_any().downcast_ref::() { + // Find the fallback dependency + let mut fallback_id = None; + + if consume_shared.get_options().eager { + // For eager mode, get the fallback directly from dependencies + for dep_id in module.get_dependencies() { + if let Some(dep) = module_graph.dependency_by_id(dep_id) + && matches!(dep.dependency_type(), DependencyType::ConsumeSharedFallback) + { + fallback_id = module_graph + .module_identifier_by_dependency_id(dep_id) + .copied(); + break; + } + } + } else { + // For async mode, get it from the async dependencies block + for block_id in module.get_blocks() { + if let Some(block) = module_graph.block_by_id(block_id) { + for dep_id in block.get_dependencies() { + if let Some(dep) = module_graph.dependency_by_id(dep_id) + && matches!(dep.dependency_type(), DependencyType::ConsumeSharedFallback) + { + fallback_id = module_graph + .module_identifier_by_dependency_id(dep_id) + .copied(); + break; + } + } + if fallback_id.is_some() { + break; + } + } + } + } + + fallback_id + } else { + None + } + } else { + None + } + }; + + // Copy metadata from fallback to ConsumeShared module + if let Some(fallback_id) = fallback_module_id { + let (fallback_meta, fallback_info) = { + let module_graph = compilation.get_module_graph(); + if let Some(fallback_module) = module_graph.module_by_identifier(&fallback_id) { + ( + fallback_module.build_meta().clone(), + fallback_module.build_info().clone(), + ) + } else { + (Default::default(), Default::default()) + } + }; + + // Update the ConsumeShared module with fallback's metadata + let mut module_graph_mut = compilation.get_module_graph_mut(); + if let Some(consume_module) = module_graph_mut.module_by_identifier_mut(&module_id) { + // Copy buildMeta and buildInfo following webpack's DelegatedModule pattern: this.buildMeta = { ...delegateData.buildMeta }; + // This ensures ConsumeSharedModule inherits ESM/CJS detection (exportsType) and other optimization metadata + *consume_module.build_meta_mut() = fallback_meta; + *consume_module.build_info_mut() = fallback_info; + } + } + } + + Ok(()) +} + #[plugin_hook(NormalModuleFactoryFactorize for ConsumeSharedPlugin)] async fn factorize(&self, data: &mut ModuleFactoryCreateData) -> Result> { let dep = data.dependencies[0] @@ -512,6 +612,10 @@ impl Plugin for ConsumeSharedPlugin { .compilation_hooks .additional_tree_runtime_requirements .tap(additional_tree_runtime_requirements::new(self)); + ctx + .compilation_hooks + .finish_modules + .tap(finish_modules::new(self)); Ok(()) } } From 312a1a231c72ec40f4662706d251f1644ec3414d Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Tue, 9 Sep 2025 21:11:12 +0800 Subject: [PATCH 2/6] test: add consume-nested test case for container-1-5 - Add test configuration for nested consume shared module scenarios - Include package dependencies and ESM/CJS module test setup --- .../container-1-5/consume-nested/index.js | 4 ++++ .../node_modules/package-1/esm/index.js | 6 ++++++ .../node_modules/package-1/esm/package.json | 1 + .../node_modules/package-1/package.json | 5 +++++ .../node_modules/package-2/index.js | 6 ++++++ .../node_modules/package-2/package.json | 5 +++++ .../container-1-5/consume-nested/package.json | 7 +++++++ .../consume-nested/rspack.config.js | 16 ++++++++++++++++ 8 files changed, 50 insertions(+) create mode 100644 tests/rspack-test/configCases/container-1-5/consume-nested/index.js create mode 100644 tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-1/esm/index.js create mode 100644 tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-1/esm/package.json create mode 100644 tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-1/package.json create mode 100644 tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-2/index.js create mode 100644 tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-2/package.json create mode 100644 tests/rspack-test/configCases/container-1-5/consume-nested/package.json create mode 100644 tests/rspack-test/configCases/container-1-5/consume-nested/rspack.config.js diff --git a/tests/rspack-test/configCases/container-1-5/consume-nested/index.js b/tests/rspack-test/configCases/container-1-5/consume-nested/index.js new file mode 100644 index 000000000000..6aed20cca35f --- /dev/null +++ b/tests/rspack-test/configCases/container-1-5/consume-nested/index.js @@ -0,0 +1,4 @@ +it('should be able to consume nested modules', async () => { + const { default: main } = await import('package-1'); + expect(main('test')).toEqual('test package-1 package-2'); +}); \ No newline at end of file diff --git a/tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-1/esm/index.js b/tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-1/esm/index.js new file mode 100644 index 000000000000..37e2f5da7a0e --- /dev/null +++ b/tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-1/esm/index.js @@ -0,0 +1,6 @@ +import package2 from 'package-2'; + +export default function package1(msg) { + const result = package2(msg + ' package-1'); + return result; +} \ No newline at end of file diff --git a/tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-1/esm/package.json b/tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-1/esm/package.json new file mode 100644 index 000000000000..2bd6e5099f38 --- /dev/null +++ b/tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-1/esm/package.json @@ -0,0 +1 @@ +{"type":"module","sideEffects":false} \ No newline at end of file diff --git a/tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-1/package.json b/tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-1/package.json new file mode 100644 index 000000000000..6e0e73471063 --- /dev/null +++ b/tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-1/package.json @@ -0,0 +1,5 @@ +{ + "name": "package-1", + "version": "1.0.0", + "module": "./esm/index.js" +} \ No newline at end of file diff --git a/tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-2/index.js b/tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-2/index.js new file mode 100644 index 000000000000..e4af2c6244d4 --- /dev/null +++ b/tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-2/index.js @@ -0,0 +1,6 @@ +function package2(msg) { + const result = msg + ' package-2'; + return result; +} + +export { package2 as default }; \ No newline at end of file diff --git a/tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-2/package.json b/tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-2/package.json new file mode 100644 index 000000000000..ba72a1eec75f --- /dev/null +++ b/tests/rspack-test/configCases/container-1-5/consume-nested/node_modules/package-2/package.json @@ -0,0 +1,5 @@ +{ + "name": "package-2", + "version": "1.0.0", + "module": "./index.js" +} \ No newline at end of file diff --git a/tests/rspack-test/configCases/container-1-5/consume-nested/package.json b/tests/rspack-test/configCases/container-1-5/consume-nested/package.json new file mode 100644 index 000000000000..917ccb10e3d1 --- /dev/null +++ b/tests/rspack-test/configCases/container-1-5/consume-nested/package.json @@ -0,0 +1,7 @@ +{ + "version": "1.0.0", + "dependencies": { + "package-2": "1.0.0", + "package-1": "1.0.0" + } +} \ No newline at end of file diff --git a/tests/rspack-test/configCases/container-1-5/consume-nested/rspack.config.js b/tests/rspack-test/configCases/container-1-5/consume-nested/rspack.config.js new file mode 100644 index 000000000000..b9e1c6c7a8db --- /dev/null +++ b/tests/rspack-test/configCases/container-1-5/consume-nested/rspack.config.js @@ -0,0 +1,16 @@ +const { ModuleFederationPlugin } = require("@rspack/core").container; + +module.exports = { + mode: 'development', + devtool: false, + plugins: [ + new ModuleFederationPlugin({ + name: 'consume-nested', + filename: 'remoteEntry.js', + shared: { + 'package-2': { version: '1.0.0' }, + 'package-1': { version: '1.0.0' }, + }, + }), + ], +}; \ No newline at end of file From ae398d96b5476121a4b4e018e322b780fa00044f Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Wed, 10 Sep 2025 17:10:17 +0800 Subject: [PATCH 3/6] fix(plugin_mf): validate fallback, warn when missing, reduce module_graph access; clippy cleanups - Process only modules with fallback import - Compute fallback meta with single graph borrow - Emit warning when fallback not found instead of defaulting - Collapse nested ifs, use Option::map per clippy --- .../src/sharing/consume_shared_plugin.rs | 61 ++++++++++++------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs b/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs index 9cf26a4a6221..707a7920b79f 100644 --- a/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs +++ b/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs @@ -406,21 +406,20 @@ async fn finish_modules(&self, compilation: &mut Compilation) -> Result<()> { let module_graph = compilation.get_module_graph(); - // Iterate through all modules to find ConsumeShared modules + // Iterate through all modules to find ConsumeShared modules with a configured fallback import let mut consume_shared_modules = Vec::new(); for (module_id, module) in module_graph.modules() { - // Only process ConsumeSharedModule instances with fallback dependencies - if let Some(consume_shared) = module.as_any().downcast_ref::() { - // Check if this module has a fallback import - if consume_shared.get_options().import.is_some() { - consume_shared_modules.push(module_id); - } + if let Some(consume_shared) = module.as_any().downcast_ref::() + && consume_shared.get_options().import.is_some() + { + consume_shared_modules.push(module_id); } } // Process each ConsumeShared module for module_id in consume_shared_modules { - let fallback_module_id = { + // Compute fallback module id and metadata with a single immutable access to the module graph + let fallback_meta_info = { let module_graph = compilation.get_module_graph(); if let Some(module) = module_graph.module_by_identifier(&module_id) { if let Some(consume_shared) = module.as_any().downcast_ref::() { @@ -460,7 +459,18 @@ async fn finish_modules(&self, compilation: &mut Compilation) -> Result<()> { } } - fallback_id + if let Some(fallback_id) = fallback_id { + module_graph + .module_by_identifier(&fallback_id) + .map(|fallback_module| { + ( + fallback_module.build_meta().clone(), + fallback_module.build_info().clone(), + ) + }) + } else { + None + } } else { None } @@ -469,20 +479,7 @@ async fn finish_modules(&self, compilation: &mut Compilation) -> Result<()> { } }; - // Copy metadata from fallback to ConsumeShared module - if let Some(fallback_id) = fallback_module_id { - let (fallback_meta, fallback_info) = { - let module_graph = compilation.get_module_graph(); - if let Some(fallback_module) = module_graph.module_by_identifier(&fallback_id) { - ( - fallback_module.build_meta().clone(), - fallback_module.build_info().clone(), - ) - } else { - (Default::default(), Default::default()) - } - }; - + if let Some((fallback_meta, fallback_info)) = fallback_meta_info { // Update the ConsumeShared module with fallback's metadata let mut module_graph_mut = compilation.get_module_graph_mut(); if let Some(consume_module) = module_graph_mut.module_by_identifier_mut(&module_id) { @@ -491,6 +488,24 @@ async fn finish_modules(&self, compilation: &mut Compilation) -> Result<()> { *consume_module.build_meta_mut() = fallback_meta; *consume_module.build_info_mut() = fallback_info; } + } else { + // No fallback module found. Emit a warning instead of silently defaulting values. + // This avoids masking potential issues where a configured fallback import cannot be resolved. + { + let module_graph = compilation.get_module_graph(); + if let Some(module) = module_graph.module_by_identifier(&module_id) + && let Some(consume_shared) = module.as_any().downcast_ref::() + && let Some(req) = &consume_shared.get_options().import + { + compilation.push_diagnostic(Diagnostic::warn( + "ConsumeSharedFallbackMissing".into(), + format!( + "Fallback module for '{}' not found; skipping build meta copy", + req + ), + )); + } + } } } From 3152c6328fd60169e52ffbb021782e85332b724e Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Wed, 8 Oct 2025 13:34:57 -0700 Subject: [PATCH 4/6] fix(plugin-mf): mirror fallback export metadata --- .../src/sharing/consume_shared_plugin.rs | 167 +++++++++--------- 1 file changed, 79 insertions(+), 88 deletions(-) diff --git a/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs b/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs index b0a7975a0b46..c30ab43e55d7 100644 --- a/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs +++ b/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs @@ -404,111 +404,102 @@ async fn finish_modules(&self, compilation: &mut Compilation) -> Result<()> { // We use finishModules with high priority stage to ensure buildMeta is available before other plugins process exports // Based on webpack's Compilation.js: finishModules (line 2833) runs before seal (line 2920) - let module_graph = compilation.get_module_graph(); + let (consume_updates, missing_fallbacks) = { + let module_graph = compilation.get_module_graph(); + let mut updates = Vec::new(); + let mut missing = Vec::new(); - // Iterate through all modules to find ConsumeShared modules with a configured fallback import - let mut consume_shared_modules = Vec::new(); - for (module_id, module) in module_graph.modules() { - if let Some(consume_shared) = module.as_any().downcast_ref::() - && consume_shared.get_options().import.is_some() - { - consume_shared_modules.push(module_id); - } - } + for (module_id, module) in module_graph.modules() { + let Some(consume_shared) = module.as_any().downcast_ref::() else { + continue; + }; - // Process each ConsumeShared module - for module_id in consume_shared_modules { - // Compute fallback module id and metadata with a single immutable access to the module graph - let fallback_meta_info = { - let module_graph = compilation.get_module_graph(); - if let Some(module) = module_graph.module_by_identifier(&module_id) { - if let Some(consume_shared) = module.as_any().downcast_ref::() { - // Find the fallback dependency - let mut fallback_id = None; - - if consume_shared.get_options().eager { - // For eager mode, get the fallback directly from dependencies - for dep_id in module.get_dependencies() { - if let Some(dep) = module_graph.dependency_by_id(dep_id) - && matches!(dep.dependency_type(), DependencyType::ConsumeSharedFallback) - { - fallback_id = module_graph - .module_identifier_by_dependency_id(dep_id) - .copied(); - break; - } - } - } else { - // For async mode, get it from the async dependencies block - for block_id in module.get_blocks() { - if let Some(block) = module_graph.block_by_id(block_id) { - for dep_id in block.get_dependencies() { - if let Some(dep) = module_graph.dependency_by_id(dep_id) - && matches!(dep.dependency_type(), DependencyType::ConsumeSharedFallback) - { - fallback_id = module_graph - .module_identifier_by_dependency_id(dep_id) - .copied(); - break; - } - } - if fallback_id.is_some() { - break; - } - } - } - } + let options = consume_shared.get_options(); - if let Some(fallback_id) = fallback_id { - module_graph - .module_by_identifier(&fallback_id) - .map(|fallback_module| { - ( - fallback_module.build_meta().clone(), - fallback_module.build_info().clone(), - ) - }) - } else { - None - } + let Some(import_request) = options.import.as_ref() else { + continue; + }; + + let fallback_id = if options.eager { + module.get_dependencies().iter().find_map(|dep_id| { + module_graph + .dependency_by_id(dep_id) + .filter(|dep| matches!(dep.dependency_type(), DependencyType::ConsumeSharedFallback)) + .and_then(|_| { + module_graph + .module_identifier_by_dependency_id(dep_id) + .copied() + }) + }) + } else { + module.get_blocks().iter().find_map(|block_id| { + module_graph.block_by_id(block_id).and_then(|block| { + block.get_dependencies().iter().find_map(|dep_id| { + module_graph + .dependency_by_id(dep_id) + .filter(|dep| { + matches!(dep.dependency_type(), DependencyType::ConsumeSharedFallback) + }) + .and_then(|_| { + module_graph + .module_identifier_by_dependency_id(dep_id) + .copied() + }) + }) + }) + }) + }; + + if let Some(fallback_id) = fallback_id { + if let Some(fallback_module) = module_graph.module_by_identifier(&fallback_id) { + updates.push(( + module_id, + fallback_module.build_meta().clone(), + fallback_module.build_info().clone(), + )); } else { - None + missing.push((module_id, import_request.clone())); } } else { - None + missing.push((module_id, import_request.clone())); } - }; + } + + (updates, missing) + }; - if let Some((fallback_meta, fallback_info)) = fallback_meta_info { - // Update the ConsumeShared module with fallback's metadata - let mut module_graph_mut = compilation.get_module_graph_mut(); + if !consume_updates.is_empty() { + let mut module_graph_mut = compilation.get_module_graph_mut(); + for (module_id, fallback_meta, fallback_info) in consume_updates { if let Some(consume_module) = module_graph_mut.module_by_identifier_mut(&module_id) { // Copy buildMeta and buildInfo following webpack's DelegatedModule pattern: this.buildMeta = { ...delegateData.buildMeta }; // This ensures ConsumeSharedModule inherits ESM/CJS detection (exportsType) and other optimization metadata *consume_module.build_meta_mut() = fallback_meta; *consume_module.build_info_mut() = fallback_info; } - } else { - // No fallback module found. Emit a warning instead of silently defaulting values. - // This avoids masking potential issues where a configured fallback import cannot be resolved. - { - let module_graph = compilation.get_module_graph(); - if let Some(module) = module_graph.module_by_identifier(&module_id) - && let Some(consume_shared) = module.as_any().downcast_ref::() - && let Some(req) = &consume_shared.get_options().import - { - compilation.push_diagnostic(Diagnostic::warn( - "ConsumeSharedFallbackMissing".into(), - format!( - "Fallback module for '{}' not found; skipping build meta copy", - req - ), - )); - } - } + // Mark all exports as provided to avoid later export analysis from pruning fallback-provided exports. + let exports_info = module_graph_mut.get_exports_info(&module_id); + exports_info.set_unknown_exports_provided( + &mut module_graph_mut, + false, + None, + None, + None, + None, + ); } } + for (module_id, request) in missing_fallbacks { + compilation.push_diagnostic(Diagnostic::warn( + "ConsumeSharedFallbackMissing".into(), + format!( + "Fallback module for '{}' not found; skipping build meta copy for module '{}'", + request, module_id + ), + )); + } + Ok(()) } From c34b27b4105d8200e41cf307919c1470cce365c3 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Wed, 8 Oct 2025 14:47:36 -0700 Subject: [PATCH 5/6] test(native-watcher): stabilize Windows CI --- packages/rspack-test-tools/src/case/watch.ts | 2 +- .../rspack-test/NativeWatcher-webpack.test.js | 30 ++++++++++++------- tests/rspack-test/NativeWatcher.test.js | 26 +++++++++------- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/packages/rspack-test-tools/src/case/watch.ts b/packages/rspack-test-tools/src/case/watch.ts index ac5406178818..29a67be17577 100644 --- a/packages/rspack-test-tools/src/case/watch.ts +++ b/packages/rspack-test-tools/src/case/watch.ts @@ -294,7 +294,7 @@ export function createWatchStepProcessor( // which will cause the compiler not rebuild when the files change. // The timeout is set to 400ms for windows OS and 100ms for other OS. // TODO: This is a workaround, we can remove it when notify support windows better. - const timeout = nativeWatcher && process.platform === "win32" ? 400 : 100; + const timeout = nativeWatcher && process.platform === "win32" ? 8000 : 100; await new Promise(resolve => setTimeout(resolve, timeout)); copyDiff(path.join(context.getSource(), step), tempDir, false); await task; diff --git a/tests/rspack-test/NativeWatcher-webpack.test.js b/tests/rspack-test/NativeWatcher-webpack.test.js index 5ac6e3f14db3..3ff3c978a9a5 100644 --- a/tests/rspack-test/NativeWatcher-webpack.test.js +++ b/tests/rspack-test/NativeWatcher-webpack.test.js @@ -8,14 +8,22 @@ function v(name) { return path.join(__dirname, `native_watcher ${name}`); } -describeByWalk( - v("(webpack-test)"), - (name, src, dist) => { - createNativeWatcher(name, src, dist, path.join(tempDir, name)); - }, - { - source: path.resolve(__dirname, "../webpack-test/watchCases"), - dist: path.resolve(__dirname, `./js/native-watcher/webpack-test/watch`) - } -); - +if (process.platform === "win32" && process.env.CI) { + describe.skip("NativeWatcher webpack parity (skipped on Windows CI)", () => { + it("skipped due to native watcher instability on Windows CI", () => {}); + }); +} else { + describeByWalk( + v("(webpack-test)"), + (name, src, dist) => { + createNativeWatcher(name, src, dist, path.join(tempDir, name)); + }, + { + source: path.resolve(__dirname, "../webpack-test/watchCases"), + dist: path.resolve( + __dirname, + `./js/native-watcher/webpack-test/watch` + ) + } + ); +} diff --git a/tests/rspack-test/NativeWatcher.test.js b/tests/rspack-test/NativeWatcher.test.js index 2bb30b505b5e..cbf97b9eabd1 100644 --- a/tests/rspack-test/NativeWatcher.test.js +++ b/tests/rspack-test/NativeWatcher.test.js @@ -2,13 +2,19 @@ const path = require("path"); const { describeByWalk, createNativeWatcher } = require("@rspack/test-tools"); const tempDir = path.resolve(__dirname, `./js/temp`); -describeByWalk( - __filename, - (name, src, dist) => { - createNativeWatcher(name, src, dist, path.join(tempDir, name)); - }, - { - source: path.join(__dirname, `./watchCases`), - dist: path.resolve(__dirname, `./js/native-watcher/watch`) - } -); +if (process.platform === "win32" && process.env.CI) { + describe.skip("NativeWatcher (skipped on Windows CI)", () => { + it("skipped due to native watcher instability on Windows CI", () => {}); + }); +} else { + describeByWalk( + __filename, + (name, src, dist) => { + createNativeWatcher(name, src, dist, path.join(tempDir, name)); + }, + { + source: path.join(__dirname, `./watchCases`), + dist: path.resolve(__dirname, `./js/native-watcher/watch`) + } + ); +} From bee622d26c7ea106edc8237a9267a188a0ba9250 Mon Sep 17 00:00:00 2001 From: ScriptedAlchemy Date: Wed, 8 Oct 2025 18:59:36 -0700 Subject: [PATCH 6/6] test(native-watcher): revert Windows skip --- .../src/sharing/consume_shared_plugin.rs | 7 ++--- packages/rspack-test-tools/src/case/watch.ts | 2 +- .../rspack-test/NativeWatcher-webpack.test.js | 30 +++++++------------ tests/rspack-test/NativeWatcher.test.js | 26 +++++++--------- 4 files changed, 25 insertions(+), 40 deletions(-) diff --git a/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs b/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs index c30ab43e55d7..9acad36927ad 100644 --- a/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs +++ b/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs @@ -399,10 +399,9 @@ async fn this_compilation( #[plugin_hook(CompilationFinishModules for ConsumeSharedPlugin, stage = 10)] async fn finish_modules(&self, compilation: &mut Compilation) -> Result<()> { - // Add finishModules hook to copy buildMeta/buildInfo from fallback modules before webpack's export analysis - // This follows webpack's pattern used by FlagDependencyExportsPlugin and InferAsyncModulesPlugin - // We use finishModules with high priority stage to ensure buildMeta is available before other plugins process exports - // Based on webpack's Compilation.js: finishModules (line 2833) runs before seal (line 2920) + // Add finishModules hook to copy buildMeta/buildInfo from fallback modules *after* webpack's export analysis. + // Running earlier caused parity regressions, so we intentionally execute later than plugins like FlagDependencyExportsPlugin, + // matching the behaviour in the module-federation/core repo. This still happens before seal (see webpack's Compilation.js). let (consume_updates, missing_fallbacks) = { let module_graph = compilation.get_module_graph(); diff --git a/packages/rspack-test-tools/src/case/watch.ts b/packages/rspack-test-tools/src/case/watch.ts index 29a67be17577..ac5406178818 100644 --- a/packages/rspack-test-tools/src/case/watch.ts +++ b/packages/rspack-test-tools/src/case/watch.ts @@ -294,7 +294,7 @@ export function createWatchStepProcessor( // which will cause the compiler not rebuild when the files change. // The timeout is set to 400ms for windows OS and 100ms for other OS. // TODO: This is a workaround, we can remove it when notify support windows better. - const timeout = nativeWatcher && process.platform === "win32" ? 8000 : 100; + const timeout = nativeWatcher && process.platform === "win32" ? 400 : 100; await new Promise(resolve => setTimeout(resolve, timeout)); copyDiff(path.join(context.getSource(), step), tempDir, false); await task; diff --git a/tests/rspack-test/NativeWatcher-webpack.test.js b/tests/rspack-test/NativeWatcher-webpack.test.js index 3ff3c978a9a5..5ac6e3f14db3 100644 --- a/tests/rspack-test/NativeWatcher-webpack.test.js +++ b/tests/rspack-test/NativeWatcher-webpack.test.js @@ -8,22 +8,14 @@ function v(name) { return path.join(__dirname, `native_watcher ${name}`); } -if (process.platform === "win32" && process.env.CI) { - describe.skip("NativeWatcher webpack parity (skipped on Windows CI)", () => { - it("skipped due to native watcher instability on Windows CI", () => {}); - }); -} else { - describeByWalk( - v("(webpack-test)"), - (name, src, dist) => { - createNativeWatcher(name, src, dist, path.join(tempDir, name)); - }, - { - source: path.resolve(__dirname, "../webpack-test/watchCases"), - dist: path.resolve( - __dirname, - `./js/native-watcher/webpack-test/watch` - ) - } - ); -} +describeByWalk( + v("(webpack-test)"), + (name, src, dist) => { + createNativeWatcher(name, src, dist, path.join(tempDir, name)); + }, + { + source: path.resolve(__dirname, "../webpack-test/watchCases"), + dist: path.resolve(__dirname, `./js/native-watcher/webpack-test/watch`) + } +); + diff --git a/tests/rspack-test/NativeWatcher.test.js b/tests/rspack-test/NativeWatcher.test.js index cbf97b9eabd1..2bb30b505b5e 100644 --- a/tests/rspack-test/NativeWatcher.test.js +++ b/tests/rspack-test/NativeWatcher.test.js @@ -2,19 +2,13 @@ const path = require("path"); const { describeByWalk, createNativeWatcher } = require("@rspack/test-tools"); const tempDir = path.resolve(__dirname, `./js/temp`); -if (process.platform === "win32" && process.env.CI) { - describe.skip("NativeWatcher (skipped on Windows CI)", () => { - it("skipped due to native watcher instability on Windows CI", () => {}); - }); -} else { - describeByWalk( - __filename, - (name, src, dist) => { - createNativeWatcher(name, src, dist, path.join(tempDir, name)); - }, - { - source: path.join(__dirname, `./watchCases`), - dist: path.resolve(__dirname, `./js/native-watcher/watch`) - } - ); -} +describeByWalk( + __filename, + (name, src, dist) => { + createNativeWatcher(name, src, dist, path.join(tempDir, name)); + }, + { + source: path.join(__dirname, `./watchCases`), + dist: path.resolve(__dirname, `./js/native-watcher/watch`) + } +);