Skip to content

Commit 7a71faa

Browse files
authored
feat: add HarmonyImportSideEffectDependency (#4010)
1 parent 6a921f7 commit 7a71faa

File tree

11 files changed

+305
-21
lines changed

11 files changed

+305
-21
lines changed

crates/rspack_core/src/dependency/mod.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::sync::atomic::Ordering::Relaxed;
66
pub use entry::*;
77
use once_cell::sync::Lazy;
88
use rspack_util::ext::AsAny;
9+
use rustc_hash::FxHashSet as HashSet;
910
use serde::Serialize;
1011
pub use span::SpanExt;
1112
mod runtime_template;
@@ -29,7 +30,7 @@ use dyn_clone::{clone_trait_object, DynClone};
2930

3031
use crate::{
3132
ChunkGroupOptions, ConnectionState, Context, ContextMode, ContextOptions, ErrorSpan, ModuleGraph,
32-
ModuleGraphConnection, ReferencedExport, RuntimeSpec,
33+
ModuleGraphConnection, ModuleIdentifier, ReferencedExport, RuntimeSpec,
3334
};
3435

3536
// Used to describe dependencies' types, see webpack's `type` getter in `Dependency`
@@ -43,6 +44,7 @@ pub enum DependencyType {
4344
// Harmony import
4445
EsmImport,
4546
EsmImportSpecifier,
47+
EsmImportSideEffect,
4648
// Harmony export
4749
EsmExport,
4850
EsmExportImportedSpecifier,
@@ -94,6 +96,7 @@ impl Display for DependencyType {
9496
DependencyType::Unknown => write!(f, "unknown"),
9597
DependencyType::Entry => write!(f, "entry"),
9698
DependencyType::EsmImport => write!(f, "esm import"),
99+
DependencyType::EsmImportSideEffect => write!(f, "esm import side effect"),
97100
DependencyType::EsmExport => write!(f, "esm export"),
98101
DependencyType::EsmExportSpecifier => write!(f, "esm export specifier"),
99102
DependencyType::EsmExportImportedSpecifier => write!(f, "esm export import specifier"),
@@ -311,6 +314,14 @@ pub trait ModuleDependency: Dependency {
311314
None
312315
}
313316

317+
fn get_module_evaluation_side_effects_state(
318+
&self,
319+
_module_graph: &ModuleGraph,
320+
_module_chain: &mut HashSet<ModuleIdentifier>,
321+
) -> ConnectionState {
322+
ConnectionState::Bool(true)
323+
}
324+
314325
fn get_referenced_exports(
315326
&self,
316327
_module_graph: &ModuleGraph,

crates/rspack_core/src/module.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ use rustc_hash::FxHashSet as HashSet;
1414
use crate::tree_shaking::visitor::OptimizeAnalyzeResult;
1515
use crate::{
1616
BoxDependency, ChunkUkey, CodeGenerationResult, Compilation, CompilerContext, CompilerOptions,
17-
Context, ContextModule, DependencyTemplate, ExternalModule, ModuleDependency, ModuleType,
18-
NormalModule, RawModule, Resolve, SharedPluginDriver, SourceType,
17+
ConnectionState, Context, ContextModule, DependencyTemplate, ExternalModule, ModuleDependency,
18+
ModuleGraph, ModuleType, NormalModule, RawModule, Resolve, SharedPluginDriver, SourceType,
1919
};
2020

2121
pub struct BuildContext<'a> {
@@ -105,8 +105,7 @@ pub struct BuildMeta {
105105
pub default_object: BuildMetaDefaultObject,
106106
pub module_argument: ModuleArgument,
107107
pub exports_argument: ExportsArgument,
108-
// TODO: Don't delete this comment, it will be used in near future
109-
// pub side_effect_free: bool,
108+
pub side_effect_free: Option<bool>,
110109
}
111110

112111
// webpack build info
@@ -223,6 +222,14 @@ pub trait Module: Debug + Send + Sync + AsAny + DynHash + DynEq + Identifiable {
223222
fn chunk_condition(&self, _chunk_key: &ChunkUkey, _compilation: &Compilation) -> bool {
224223
true
225224
}
225+
226+
fn get_side_effects_connection_state(
227+
&self,
228+
_module_graph: &ModuleGraph,
229+
_module_chain: &mut HashSet<ModuleIdentifier>,
230+
) -> ConnectionState {
231+
ConnectionState::Bool(true)
232+
}
226233
}
227234

228235
pub trait ModuleExt {

crates/rspack_core/src/module_graph/connection.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,22 @@ impl ConnectionState {
142142
!matches!(self, ConnectionState::Bool(false))
143143
}
144144
}
145+
146+
pub fn add_connection_states(a: ConnectionState, b: ConnectionState) -> ConnectionState {
147+
if matches!(a, ConnectionState::Bool(true)) || matches!(b, ConnectionState::Bool(true)) {
148+
return ConnectionState::Bool(true);
149+
}
150+
if matches!(a, ConnectionState::Bool(false)) {
151+
return ConnectionState::Bool(false);
152+
}
153+
if matches!(b, ConnectionState::Bool(false)) {
154+
return ConnectionState::Bool(false);
155+
}
156+
if matches!(a, ConnectionState::TransitiveOnly) {
157+
return b;
158+
}
159+
if matches!(b, ConnectionState::TransitiveOnly) {
160+
return a;
161+
}
162+
a
163+
}

crates/rspack_core/src/module_graph/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rspack_identifier::IdentifierMap;
88
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
99

1010
mod connection;
11-
pub use connection::{ConnectionId, ConnectionState, ModuleGraphConnection};
11+
pub use connection::*;
1212

1313
use crate::{
1414
to_identifier, BoxDependency, BoxModule, BuildDependency, BuildInfo, BuildMeta,

crates/rspack_core/src/normal_module.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,16 @@ use rspack_sources::{
2121
BoxSource, CachedSource, OriginalSource, RawSource, SourceExt, SourceMap, SourceMapSource,
2222
WithoutOriginalOptions,
2323
};
24+
use rustc_hash::FxHashSet as HashSet;
2425
use rustc_hash::FxHasher;
2526
use serde_json::json;
2627

2728
use crate::{
28-
contextify, get_context, BoxLoader, BoxModule, BuildContext, BuildInfo, BuildMeta, BuildResult,
29-
CodeGenerationResult, Compilation, CompilerOptions, Context, DependencyTemplate, GenerateContext,
30-
GeneratorOptions, LibIdentOptions, LoaderRunnerPluginProcessResource, Module, ModuleDependency,
31-
ModuleGraph, ModuleIdentifier, ModuleType, ParseContext, ParseResult, ParserAndGenerator,
32-
ParserOptions, Resolve, SourceType,
29+
add_connection_states, contextify, get_context, BoxLoader, BoxModule, BuildContext, BuildInfo,
30+
BuildMeta, BuildResult, CodeGenerationResult, Compilation, CompilerOptions, ConnectionState,
31+
Context, DependencyTemplate, GenerateContext, GeneratorOptions, LibIdentOptions,
32+
LoaderRunnerPluginProcessResource, Module, ModuleDependency, ModuleGraph, ModuleIdentifier,
33+
ModuleType, ParseContext, ParseResult, ParserAndGenerator, ParserOptions, Resolve, SourceType,
3334
};
3435

3536
bitflags! {
@@ -455,6 +456,40 @@ impl Module for NormalModule {
455456
fn get_context(&self) -> Option<&Context> {
456457
Some(&self.context)
457458
}
459+
460+
// Port from https://github.com/webpack/webpack/blob/main/lib/NormalModule.js#L1120
461+
fn get_side_effects_connection_state(
462+
&self,
463+
module_graph: &ModuleGraph,
464+
module_chain: &mut HashSet<ModuleIdentifier>,
465+
) -> ConnectionState {
466+
if let Some(mgm) = module_graph.module_graph_module_by_identifier(&self.identifier()) {
467+
if let Some(side_effect) = mgm.factory_meta.as_ref().and_then(|m| m.side_effects) {
468+
return ConnectionState::Bool(side_effect);
469+
}
470+
if let Some(side_effect_free) = mgm.build_meta.as_ref().and_then(|m| m.side_effect_free) && side_effect_free {
471+
// use module chain instead of is_evaluating_side_effects to mut module graph
472+
if module_chain.contains(&self.identifier()) {
473+
return ConnectionState::CircularConnection;
474+
}
475+
module_chain.insert(self.identifier());
476+
let mut current = ConnectionState::Bool(false);
477+
for dependency_id in mgm.dependencies.iter() {
478+
if let Some(dependency) = module_graph.dependency_by_id(dependency_id).expect("should have dependency").as_module_dependency() {
479+
let state = dependency.get_module_evaluation_side_effects_state(module_graph, module_chain);
480+
if matches!(state, ConnectionState::Bool(true)) {
481+
// TODO add optimization bailout
482+
return ConnectionState::Bool(true);
483+
} else if matches!(state, ConnectionState::CircularConnection) {
484+
current = add_connection_states(current, state);
485+
}
486+
}
487+
}
488+
return current;
489+
}
490+
}
491+
ConnectionState::Bool(true)
492+
}
458493
}
459494

460495
impl PartialEq for NormalModule {

crates/rspack_plugin_javascript/src/dependency/esm/harmony_export_imported_specifier_dependency.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use rspack_core::{
2-
export_from_import, get_exports_type, Dependency, DependencyCategory, DependencyId,
3-
DependencyTemplate, DependencyType, ErrorSpan, ExportsType, InitFragment, InitFragmentStage,
4-
ModuleDependency, RuntimeGlobals, TemplateContext, TemplateReplaceSource,
2+
export_from_import, get_exports_type, ConnectionState, Dependency, DependencyCategory,
3+
DependencyId, DependencyTemplate, DependencyType, ErrorSpan, ExportsType, InitFragment,
4+
InitFragmentStage, ModuleDependency, ModuleGraph, ModuleIdentifier, RuntimeGlobals,
5+
TemplateContext, TemplateReplaceSource,
56
};
7+
use rustc_hash::FxHashSet as HashSet;
68
use swc_core::ecma::atoms::JsWord;
79

810
use super::{create_resource_identifier_for_esm_dependency, format_exports};
@@ -135,6 +137,14 @@ impl ModuleDependency for HarmonyExportImportedSpecifierDependency {
135137
fn resource_identifier(&self) -> Option<&str> {
136138
Some(&self.resource_identifier)
137139
}
140+
141+
fn get_module_evaluation_side_effects_state(
142+
&self,
143+
_module_graph: &ModuleGraph,
144+
_module_chain: &mut HashSet<ModuleIdentifier>,
145+
) -> ConnectionState {
146+
ConnectionState::Bool(false)
147+
}
138148
}
139149

140150
#[allow(unused)]
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
use rspack_core::{AsDependencyTemplate, ModuleGraph};
2+
use rspack_core::{
3+
ConnectionState, Dependency, DependencyCategory, DependencyCondition, DependencyId,
4+
DependencyType, ErrorSpan, ModuleDependency, ModuleIdentifier,
5+
};
6+
use rustc_hash::FxHashSet as HashSet;
7+
use swc_core::ecma::atoms::JsWord;
8+
9+
use super::create_resource_identifier_for_esm_dependency;
10+
11+
#[derive(Debug, Clone)]
12+
pub struct HarmonyImportSideEffectDependency {
13+
pub request: JsWord,
14+
pub id: DependencyId,
15+
resource_identifier: String,
16+
}
17+
18+
impl HarmonyImportSideEffectDependency {
19+
pub fn new(request: JsWord) -> Self {
20+
let resource_identifier = create_resource_identifier_for_esm_dependency(&request);
21+
Self {
22+
id: DependencyId::new(),
23+
request,
24+
resource_identifier,
25+
}
26+
}
27+
}
28+
29+
impl Dependency for HarmonyImportSideEffectDependency {
30+
fn id(&self) -> &DependencyId {
31+
&self.id
32+
}
33+
34+
fn category(&self) -> &DependencyCategory {
35+
&DependencyCategory::Esm
36+
}
37+
38+
fn dependency_type(&self) -> &DependencyType {
39+
&DependencyType::EsmImportSideEffect
40+
}
41+
}
42+
43+
impl ModuleDependency for HarmonyImportSideEffectDependency {
44+
fn request(&self) -> &str {
45+
&self.request
46+
}
47+
48+
fn user_request(&self) -> &str {
49+
&self.request
50+
}
51+
52+
fn span(&self) -> Option<&ErrorSpan> {
53+
None
54+
}
55+
56+
fn set_request(&mut self, request: String) {
57+
self.request = request.into();
58+
}
59+
60+
fn resource_identifier(&self) -> Option<&str> {
61+
Some(&self.resource_identifier)
62+
}
63+
64+
fn get_condition(&self, _module_graph: &ModuleGraph) -> Option<DependencyCondition> {
65+
let id = self.id;
66+
Some(DependencyCondition::Fn(Box::new(
67+
move |_, _, module_graph| {
68+
if let Some(module) = module_graph
69+
.parent_module_by_dependency_id(&id)
70+
.and_then(|module_identifier| module_graph.module_by_identifier(&module_identifier))
71+
{
72+
module.get_side_effects_connection_state(module_graph, &mut HashSet::default())
73+
} else {
74+
ConnectionState::Bool(true)
75+
}
76+
},
77+
)))
78+
}
79+
80+
fn get_module_evaluation_side_effects_state(
81+
&self,
82+
module_graph: &ModuleGraph,
83+
module_chain: &mut HashSet<ModuleIdentifier>,
84+
) -> ConnectionState {
85+
if let Some(module) = module_graph
86+
.parent_module_by_dependency_id(&self.id)
87+
.and_then(|module_identifier| module_graph.module_by_identifier(&module_identifier))
88+
{
89+
module.get_side_effects_connection_state(module_graph, module_chain)
90+
} else {
91+
ConnectionState::Bool(true)
92+
}
93+
}
94+
}
95+
96+
impl AsDependencyTemplate for HarmonyImportSideEffectDependency {}

crates/rspack_plugin_javascript/src/dependency/esm/harmony_import_specifier_dependency.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use rspack_core::{
22
export_from_import, get_dependency_used_by_exports_condition,
3-
tree_shaking::symbol::DEFAULT_JS_WORD, Compilation, Dependency, DependencyCategory,
4-
DependencyCondition, DependencyId, DependencyTemplate, DependencyType, ErrorSpan,
5-
ExportsReferencedType, ModuleDependency, ModuleGraph, ModuleGraphModule, ReferencedExport,
6-
RuntimeSpec, TemplateContext, TemplateReplaceSource, UsedByExports,
3+
tree_shaking::symbol::DEFAULT_JS_WORD, Compilation, ConnectionState, Dependency,
4+
DependencyCategory, DependencyCondition, DependencyId, DependencyTemplate, DependencyType,
5+
ErrorSpan, ExportsReferencedType, ModuleDependency, ModuleGraph, ModuleGraphModule,
6+
ModuleIdentifier, ReferencedExport, RuntimeSpec, TemplateContext, TemplateReplaceSource,
7+
UsedByExports,
78
};
89
use rustc_hash::FxHashSet as HashSet;
910
use swc_core::ecma::atoms::JsWord;
@@ -200,6 +201,14 @@ impl ModuleDependency for HarmonyImportSpecifierDependency {
200201
get_dependency_used_by_exports_condition(&self.id, &self.used_by_exports, module_graph)
201202
}
202203

204+
fn get_module_evaluation_side_effects_state(
205+
&self,
206+
_module_graph: &ModuleGraph,
207+
_module_chain: &mut HashSet<ModuleIdentifier>,
208+
) -> ConnectionState {
209+
ConnectionState::Bool(false)
210+
}
211+
203212
fn get_referenced_exports(
204213
&self,
205214
_module_graph: &ModuleGraph,

crates/rspack_plugin_javascript/src/dependency/esm/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ pub use harmony_import_specifier_dependency::*;
1717
mod import_dependency;
1818
pub use import_dependency::*;
1919
use rspack_core::DependencyCategory;
20-
20+
mod harmony_import_side_effect_dependency;
21+
pub use harmony_import_side_effect_dependency::*;
2122
pub fn create_resource_identifier_for_esm_dependency(request: &str) -> String {
2223
format!("{}|{}", DependencyCategory::Esm, &request)
2324
}

crates/rspack_plugin_javascript/src/visitors/dependency/harmony_import_dependency_scanner.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use swc_core::{
1818

1919
use crate::dependency::{
2020
HarmonyExportImportedSpecifierDependency, HarmonyImportDependency,
21-
HarmonyImportSpecifierDependency, Specifier,
21+
HarmonyImportSideEffectDependency, HarmonyImportSpecifierDependency, Specifier,
2222
};
2323

2424
pub struct ImporterReferenceInfo {
@@ -114,12 +114,16 @@ impl Visit for HarmonyImportDependencyScanner<'_> {
114114
self
115115
.dependencies
116116
.push(Box::new(HarmonyImportDependency::new(
117-
request,
117+
request.clone(),
118118
Some(importer_info.span.into()),
119119
importer_info.specifiers,
120120
dependency_type,
121121
importer_info.exports_all,
122122
)));
123+
// TODO merge HarmonyImportSideEffectDependency and HarmonyImportDependency
124+
self
125+
.dependencies
126+
.push(Box::new(HarmonyImportSideEffectDependency::new(request)));
123127
}
124128

125129
// collect import reference info

0 commit comments

Comments
 (0)