From 4a19cafe7c07e95d32f22d049a18987aa4539b47 Mon Sep 17 00:00:00 2001 From: Qingyu Wang <40660121+colinaaa@users.noreply.github.com> Date: Wed, 9 Jul 2025 00:49:51 +0800 Subject: [PATCH 1/7] fix: tree-shaking `RawModule` --- crates/rspack_core/src/raw_module.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/rspack_core/src/raw_module.rs b/crates/rspack_core/src/raw_module.rs index f4e2c1d7ebe8..aed9ad3269d1 100644 --- a/crates/rspack_core/src/raw_module.rs +++ b/crates/rspack_core/src/raw_module.rs @@ -4,7 +4,7 @@ use rspack_cacheable::{ cacheable, cacheable_dyn, with::{AsOption, AsPreset}, }; -use rspack_collections::Identifiable; +use rspack_collections::{Identifiable, IdentifierMap, IdentifierSet}; use rspack_error::{impl_empty_diagnosable_trait, Result}; use rspack_hash::{RspackHash, RspackHashDigest}; use rspack_macros::impl_source_map_config; @@ -13,9 +13,9 @@ use rspack_util::source_map::{ModuleSourceMapConfig, SourceMapKind}; use crate::{ dependencies_block::AsyncDependenciesBlockIdentifier, impl_module_meta_info, module_update_hash, - BuildInfo, BuildMeta, CodeGenerationResult, Compilation, ConcatenationScope, Context, - DependenciesBlock, DependencyId, FactoryMeta, Module, ModuleGraph, ModuleIdentifier, ModuleType, - RuntimeGlobals, RuntimeSpec, SourceType, + BuildInfo, BuildMeta, CodeGenerationResult, Compilation, ConcatenationScope, ConnectionState, + Context, DependenciesBlock, DependencyId, FactoryMeta, Module, ModuleGraph, + ModuleGraphCacheArtifact, ModuleIdentifier, ModuleType, RuntimeGlobals, RuntimeSpec, SourceType, }; #[impl_source_map_config] @@ -150,6 +150,16 @@ impl Module for RawModule { module_update_hash(self, &mut hasher, compilation, runtime); Ok(hasher.digest(&compilation.options.output.hash_digest)) } + + fn get_side_effects_connection_state( + &self, + _module_graph: &ModuleGraph, + _module_graph_cache: &ModuleGraphCacheArtifact, + _module_chain: &mut IdentifierSet, + _connection_state_cache: &mut IdentifierMap, + ) -> ConnectionState { + ConnectionState::Active(false) + } } impl_empty_diagnosable_trait!(RawModule); From c8f8d39a30d63d17f141344387fa96d9376f4196 Mon Sep 17 00:00:00 2001 From: Qingyu Wang <40660121+colinaaa@users.noreply.github.com> Date: Wed, 9 Jul 2025 01:24:24 +0800 Subject: [PATCH 2/7] test: port webpack-tests --- .../ignore/side-effects/ignored-module.js | 1 + .../configCases/ignore/side-effects/test.js | 8 ++++++++ .../ignore/side-effects/webpack.config.js | 15 +++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 tests/webpack-test/configCases/ignore/side-effects/ignored-module.js create mode 100644 tests/webpack-test/configCases/ignore/side-effects/test.js create mode 100644 tests/webpack-test/configCases/ignore/side-effects/webpack.config.js diff --git a/tests/webpack-test/configCases/ignore/side-effects/ignored-module.js b/tests/webpack-test/configCases/ignore/side-effects/ignored-module.js new file mode 100644 index 000000000000..4e015a52c59e --- /dev/null +++ b/tests/webpack-test/configCases/ignore/side-effects/ignored-module.js @@ -0,0 +1 @@ +module.exports = "ignored"; diff --git a/tests/webpack-test/configCases/ignore/side-effects/test.js b/tests/webpack-test/configCases/ignore/side-effects/test.js new file mode 100644 index 000000000000..5b8b6f1f2570 --- /dev/null +++ b/tests/webpack-test/configCases/ignore/side-effects/test.js @@ -0,0 +1,8 @@ +"use strict"; + +import "ignored-module"; +import "./ignored-module"; + +it("should remove all ignored moduels", function() { + expect(Object.keys(__webpack_modules__).length).toBe(1); +}); diff --git a/tests/webpack-test/configCases/ignore/side-effects/webpack.config.js b/tests/webpack-test/configCases/ignore/side-effects/webpack.config.js new file mode 100644 index 000000000000..a517fd2500c5 --- /dev/null +++ b/tests/webpack-test/configCases/ignore/side-effects/webpack.config.js @@ -0,0 +1,15 @@ +"use strict"; + +/** @type {import("../../../../").Configuration} */ +module.exports = { + entry: "./test.js", + resolve: { + alias: { + "ignored-module": false, + "./ignored-module": false + } + }, + optimization: { + sideEffects: true + }, +}; From 9efe874e771e89f715c7d2a2e412cf42c7f57a0b Mon Sep 17 00:00:00 2001 From: Qingyu Wang <40660121+colinaaa@users.noreply.github.com> Date: Wed, 9 Jul 2025 02:10:04 +0800 Subject: [PATCH 3/7] test: typo --- tests/webpack-test/configCases/ignore/side-effects/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/webpack-test/configCases/ignore/side-effects/test.js b/tests/webpack-test/configCases/ignore/side-effects/test.js index 5b8b6f1f2570..acd6cba8e732 100644 --- a/tests/webpack-test/configCases/ignore/side-effects/test.js +++ b/tests/webpack-test/configCases/ignore/side-effects/test.js @@ -3,6 +3,6 @@ import "ignored-module"; import "./ignored-module"; -it("should remove all ignored moduels", function() { +it("should remove all ignored modules", function() { expect(Object.keys(__webpack_modules__).length).toBe(1); }); From 29e59e447f65ab570537204365c50a16e5f9b13c Mon Sep 17 00:00:00 2001 From: Qingyu Wang <40660121+colinaaa@users.noreply.github.com> Date: Wed, 9 Jul 2025 02:10:23 +0800 Subject: [PATCH 4/7] fix: use `factory_meta.side_effect_free` --- .../rspack_core/src/context_module_factory.rs | 14 ++++++++--- crates/rspack_core/src/module.rs | 5 ++++ .../rspack_core/src/normal_module_factory.rs | 25 +++++++++++++------ crates/rspack_core/src/raw_module.rs | 18 +++---------- 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/crates/rspack_core/src/context_module_factory.rs b/crates/rspack_core/src/context_module_factory.rs index 5306676a8a5f..5ef5285ce0ba 100644 --- a/crates/rspack_core/src/context_module_factory.rs +++ b/crates/rspack_core/src/context_module_factory.rs @@ -11,9 +11,9 @@ use tracing::instrument; use crate::{ resolve, BoxDependency, CompilationId, ContextElementDependency, ContextModule, - ContextModuleOptions, DependencyCategory, DependencyId, DependencyType, ErrorSpan, ModuleExt, - ModuleFactory, ModuleFactoryCreateData, ModuleFactoryResult, ModuleIdentifier, RawModule, - ResolveArgs, ResolveContextModuleDependencies, ResolveInnerOptions, + ContextModuleOptions, DependencyCategory, DependencyId, DependencyType, ErrorSpan, FactoryMeta, + ModuleExt, ModuleFactory, ModuleFactoryCreateData, ModuleFactoryResult, ModuleIdentifier, + RawModule, ResolveArgs, ResolveContextModuleDependencies, ResolveInnerOptions, ResolveOptionsWithDependencyType, ResolveResult, Resolver, ResolverFactory, SharedPluginDriver, }; @@ -313,13 +313,19 @@ impl ContextModuleFactory { Ok(ResolveResult::Ignored) => { let ident = format!("{}/{}", data.context, specifier); let module_identifier = ModuleIdentifier::from(format!("ignored|{ident}")); - let raw_module = RawModule::new( + let mut raw_module = RawModule::new( "/* (ignored) */".to_owned(), module_identifier, format!("{specifier} (ignored)"), Default::default(), ) .boxed(); + + raw_module.set_factory_meta(FactoryMeta { + side_effect_free: Some(true), + ..Default::default() + }); + data.add_file_dependencies(file_dependencies); data.add_missing_dependencies(missing_dependencies); return Ok((ModuleFactoryResult::new_with_module(raw_module), None)); diff --git a/crates/rspack_core/src/module.rs b/crates/rspack_core/src/module.rs index b88bdcc75d82..f625e5ccf30c 100644 --- a/crates/rspack_core/src/module.rs +++ b/crates/rspack_core/src/module.rs @@ -383,6 +383,11 @@ pub trait Module: _module_chain: &mut IdentifierSet, _connection_state_cache: &mut IdentifierMap, ) -> ConnectionState { + if let Some(factory_meta) = self.factory_meta() { + if let Some(side_effect_free) = factory_meta.side_effect_free { + return ConnectionState::Active(!side_effect_free); + } + } ConnectionState::Active(true) } diff --git a/crates/rspack_core/src/normal_module_factory.rs b/crates/rspack_core/src/normal_module_factory.rs index 714a30b661b7..29520a9327a8 100644 --- a/crates/rspack_core/src/normal_module_factory.rs +++ b/crates/rspack_core/src/normal_module_factory.rs @@ -18,11 +18,12 @@ use crate::{ stringify_loaders_and_resource, AssetInlineGeneratorOptions, AssetResourceGeneratorOptions, BoxLoader, BoxModule, CompilerOptions, Context, CssAutoGeneratorOptions, CssAutoParserOptions, CssModuleGeneratorOptions, CssModuleParserOptions, Dependency, DependencyCategory, - DependencyRange, FuncUseCtx, GeneratorOptions, ModuleExt, ModuleFactory, ModuleFactoryCreateData, - ModuleFactoryResult, ModuleIdentifier, ModuleLayer, ModuleRuleEffect, ModuleRuleEnforce, - ModuleRuleUse, ModuleRuleUseLoader, ModuleType, NormalModule, ParserAndGenerator, ParserOptions, - RawModule, Resolve, ResolveArgs, ResolveOptionsWithDependencyType, ResolveResult, Resolver, - ResolverFactory, ResourceData, ResourceParsedData, RunnerContext, SharedPluginDriver, + DependencyRange, FactoryMeta, FuncUseCtx, GeneratorOptions, ModuleExt, ModuleFactory, + ModuleFactoryCreateData, ModuleFactoryResult, ModuleIdentifier, ModuleLayer, ModuleRuleEffect, + ModuleRuleEnforce, ModuleRuleUse, ModuleRuleUseLoader, ModuleType, NormalModule, + ParserAndGenerator, ParserOptions, RawModule, Resolve, ResolveArgs, + ResolveOptionsWithDependencyType, ResolveResult, Resolver, ResolverFactory, ResourceData, + ResourceParsedData, RunnerContext, SharedPluginDriver, }; define_hook!(NormalModuleFactoryBeforeResolve: SeriesBail(data: &mut ModuleFactoryCreateData) -> bool,tracing=false); @@ -346,7 +347,7 @@ impl NormalModuleFactory { let ident = format!("{}/{}", &data.context, resource); let module_identifier = ModuleIdentifier::from(format!("ignored|{ident}")); - let raw_module = RawModule::new( + let mut raw_module = RawModule::new( "/* (ignored) */".to_owned(), module_identifier, format!("{resource} (ignored)"), @@ -354,6 +355,11 @@ impl NormalModuleFactory { ) .boxed(); + raw_module.set_factory_meta(FactoryMeta { + side_effect_free: Some(true), + ..Default::default() + }); + return Ok(Some(ModuleFactoryResult::new_with_module(raw_module))); } Err(err) => { @@ -866,7 +872,7 @@ impl NormalModuleFactory { ); let module_identifier = ModuleIdentifier::from(format!("ignored|{ident}")); - let raw_module = RawModule::new( + let mut raw_module = RawModule::new( "/* (ignored) */".to_owned(), module_identifier, format!( @@ -877,6 +883,11 @@ impl NormalModuleFactory { ) .boxed(); + raw_module.set_factory_meta(FactoryMeta { + side_effect_free: Some(true), + ..Default::default() + }); + return Ok(ModuleFactoryResult::new_with_module(raw_module)); } } diff --git a/crates/rspack_core/src/raw_module.rs b/crates/rspack_core/src/raw_module.rs index aed9ad3269d1..f4e2c1d7ebe8 100644 --- a/crates/rspack_core/src/raw_module.rs +++ b/crates/rspack_core/src/raw_module.rs @@ -4,7 +4,7 @@ use rspack_cacheable::{ cacheable, cacheable_dyn, with::{AsOption, AsPreset}, }; -use rspack_collections::{Identifiable, IdentifierMap, IdentifierSet}; +use rspack_collections::Identifiable; use rspack_error::{impl_empty_diagnosable_trait, Result}; use rspack_hash::{RspackHash, RspackHashDigest}; use rspack_macros::impl_source_map_config; @@ -13,9 +13,9 @@ use rspack_util::source_map::{ModuleSourceMapConfig, SourceMapKind}; use crate::{ dependencies_block::AsyncDependenciesBlockIdentifier, impl_module_meta_info, module_update_hash, - BuildInfo, BuildMeta, CodeGenerationResult, Compilation, ConcatenationScope, ConnectionState, - Context, DependenciesBlock, DependencyId, FactoryMeta, Module, ModuleGraph, - ModuleGraphCacheArtifact, ModuleIdentifier, ModuleType, RuntimeGlobals, RuntimeSpec, SourceType, + BuildInfo, BuildMeta, CodeGenerationResult, Compilation, ConcatenationScope, Context, + DependenciesBlock, DependencyId, FactoryMeta, Module, ModuleGraph, ModuleIdentifier, ModuleType, + RuntimeGlobals, RuntimeSpec, SourceType, }; #[impl_source_map_config] @@ -150,16 +150,6 @@ impl Module for RawModule { module_update_hash(self, &mut hasher, compilation, runtime); Ok(hasher.digest(&compilation.options.output.hash_digest)) } - - fn get_side_effects_connection_state( - &self, - _module_graph: &ModuleGraph, - _module_graph_cache: &ModuleGraphCacheArtifact, - _module_chain: &mut IdentifierSet, - _connection_state_cache: &mut IdentifierMap, - ) -> ConnectionState { - ConnectionState::Active(false) - } } impl_empty_diagnosable_trait!(RawModule); From 5cddce4ed649a85be5ef629ac0340e254534eb9f Mon Sep 17 00:00:00 2001 From: Qingyu Wang <40660121+colinaaa@users.noreply.github.com> Date: Wed, 9 Jul 2025 02:19:33 +0800 Subject: [PATCH 5/7] fix: clippy --- crates/rspack_core/src/context_module_factory.rs | 1 - crates/rspack_core/src/normal_module_factory.rs | 2 -- 2 files changed, 3 deletions(-) diff --git a/crates/rspack_core/src/context_module_factory.rs b/crates/rspack_core/src/context_module_factory.rs index 5ef5285ce0ba..611722d4625d 100644 --- a/crates/rspack_core/src/context_module_factory.rs +++ b/crates/rspack_core/src/context_module_factory.rs @@ -323,7 +323,6 @@ impl ContextModuleFactory { raw_module.set_factory_meta(FactoryMeta { side_effect_free: Some(true), - ..Default::default() }); data.add_file_dependencies(file_dependencies); diff --git a/crates/rspack_core/src/normal_module_factory.rs b/crates/rspack_core/src/normal_module_factory.rs index 29520a9327a8..2094a3c60f63 100644 --- a/crates/rspack_core/src/normal_module_factory.rs +++ b/crates/rspack_core/src/normal_module_factory.rs @@ -357,7 +357,6 @@ impl NormalModuleFactory { raw_module.set_factory_meta(FactoryMeta { side_effect_free: Some(true), - ..Default::default() }); return Ok(Some(ModuleFactoryResult::new_with_module(raw_module))); @@ -885,7 +884,6 @@ impl NormalModuleFactory { raw_module.set_factory_meta(FactoryMeta { side_effect_free: Some(true), - ..Default::default() }); return Ok(ModuleFactoryResult::new_with_module(raw_module)); From a937ec511537074aeecabd1e28234f082028c951 Mon Sep 17 00:00:00 2001 From: Qingyu Wang <40660121+colinaaa@users.noreply.github.com> Date: Thu, 10 Jul 2025 22:05:07 +0800 Subject: [PATCH 6/7] test: update webpack-tests cherry-pick from https://github.com/webpack/webpack/pull/19671/commits/dd24db4c50661b54dd457ee072847f9364faa429 --- .../configCases/ignore/side-effects/locales/a.js | 1 + .../configCases/ignore/side-effects/locales/b.js | 1 + .../configCases/ignore/side-effects/test.js | 11 +++++++++-- .../configCases/ignore/side-effects/webpack.config.js | 5 ++++- 4 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 tests/webpack-test/configCases/ignore/side-effects/locales/a.js create mode 100644 tests/webpack-test/configCases/ignore/side-effects/locales/b.js diff --git a/tests/webpack-test/configCases/ignore/side-effects/locales/a.js b/tests/webpack-test/configCases/ignore/side-effects/locales/a.js new file mode 100644 index 000000000000..e94fef18587e --- /dev/null +++ b/tests/webpack-test/configCases/ignore/side-effects/locales/a.js @@ -0,0 +1 @@ +export default "a"; diff --git a/tests/webpack-test/configCases/ignore/side-effects/locales/b.js b/tests/webpack-test/configCases/ignore/side-effects/locales/b.js new file mode 100644 index 000000000000..eff703ff4657 --- /dev/null +++ b/tests/webpack-test/configCases/ignore/side-effects/locales/b.js @@ -0,0 +1 @@ +export default "b"; diff --git a/tests/webpack-test/configCases/ignore/side-effects/test.js b/tests/webpack-test/configCases/ignore/side-effects/test.js index acd6cba8e732..7975f3e1438f 100644 --- a/tests/webpack-test/configCases/ignore/side-effects/test.js +++ b/tests/webpack-test/configCases/ignore/side-effects/test.js @@ -3,6 +3,13 @@ import "ignored-module"; import "./ignored-module"; -it("should remove all ignored modules", function() { - expect(Object.keys(__webpack_modules__).length).toBe(1); +it("should remove all ignored modules", async function() { + // Current module + module with runtime code to load context modules + expect(Object.keys(__webpack_modules__)).toHaveLength(2); + + const x = "a"; + const locale = (await import("./locales/" + x)).default; + + expect(locale).toBe("a"); + expect(Object.keys(__webpack_modules__)).toHaveLength(3); }); diff --git a/tests/webpack-test/configCases/ignore/side-effects/webpack.config.js b/tests/webpack-test/configCases/ignore/side-effects/webpack.config.js index a517fd2500c5..d1ebecd62c25 100644 --- a/tests/webpack-test/configCases/ignore/side-effects/webpack.config.js +++ b/tests/webpack-test/configCases/ignore/side-effects/webpack.config.js @@ -1,5 +1,7 @@ "use strict"; +const webpack = require("@rspack/core"); + /** @type {import("../../../../").Configuration} */ module.exports = { entry: "./test.js", @@ -9,7 +11,8 @@ module.exports = { "./ignored-module": false } }, + plugins: [new webpack.IgnorePlugin({ resourceRegExp: /(b\.js|b)$/ })], optimization: { sideEffects: true - }, + } }; From 7551451ab0759b0eec0050d0804c9ecd889aae05 Mon Sep 17 00:00:00 2001 From: Qingyu Wang <40660121+colinaaa@users.noreply.github.com> Date: Thu, 10 Jul 2025 22:07:43 +0800 Subject: [PATCH 7/7] fix: do not change `Module` --- crates/rspack_core/src/module.rs | 5 ----- crates/rspack_core/src/raw_module.rs | 21 +++++++++++++++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/crates/rspack_core/src/module.rs b/crates/rspack_core/src/module.rs index f625e5ccf30c..b88bdcc75d82 100644 --- a/crates/rspack_core/src/module.rs +++ b/crates/rspack_core/src/module.rs @@ -383,11 +383,6 @@ pub trait Module: _module_chain: &mut IdentifierSet, _connection_state_cache: &mut IdentifierMap, ) -> ConnectionState { - if let Some(factory_meta) = self.factory_meta() { - if let Some(side_effect_free) = factory_meta.side_effect_free { - return ConnectionState::Active(!side_effect_free); - } - } ConnectionState::Active(true) } diff --git a/crates/rspack_core/src/raw_module.rs b/crates/rspack_core/src/raw_module.rs index f4e2c1d7ebe8..3500e33f4990 100644 --- a/crates/rspack_core/src/raw_module.rs +++ b/crates/rspack_core/src/raw_module.rs @@ -4,7 +4,7 @@ use rspack_cacheable::{ cacheable, cacheable_dyn, with::{AsOption, AsPreset}, }; -use rspack_collections::Identifiable; +use rspack_collections::{Identifiable, IdentifierMap, IdentifierSet}; use rspack_error::{impl_empty_diagnosable_trait, Result}; use rspack_hash::{RspackHash, RspackHashDigest}; use rspack_macros::impl_source_map_config; @@ -13,9 +13,9 @@ use rspack_util::source_map::{ModuleSourceMapConfig, SourceMapKind}; use crate::{ dependencies_block::AsyncDependenciesBlockIdentifier, impl_module_meta_info, module_update_hash, - BuildInfo, BuildMeta, CodeGenerationResult, Compilation, ConcatenationScope, Context, - DependenciesBlock, DependencyId, FactoryMeta, Module, ModuleGraph, ModuleIdentifier, ModuleType, - RuntimeGlobals, RuntimeSpec, SourceType, + BuildInfo, BuildMeta, CodeGenerationResult, Compilation, ConcatenationScope, ConnectionState, + Context, DependenciesBlock, DependencyId, FactoryMeta, Module, ModuleGraph, + ModuleGraphCacheArtifact, ModuleIdentifier, ModuleType, RuntimeGlobals, RuntimeSpec, SourceType, }; #[impl_source_map_config] @@ -150,6 +150,19 @@ impl Module for RawModule { module_update_hash(self, &mut hasher, compilation, runtime); Ok(hasher.digest(&compilation.options.output.hash_digest)) } + + fn get_side_effects_connection_state( + &self, + _module_graph: &ModuleGraph, + _module_graph_cache: &ModuleGraphCacheArtifact, + _module_chain: &mut IdentifierSet, + _connection_state_cache: &mut IdentifierMap, + ) -> ConnectionState { + if let Some(side_effect_free) = self.factory_meta().and_then(|m| m.side_effect_free) { + return ConnectionState::Active(!side_effect_free); + } + ConnectionState::Active(true) + } } impl_empty_diagnosable_trait!(RawModule);