Skip to content

Commit 4ad8b49

Browse files
authored
fix: inline value for multiple runtime (#11983)
1 parent 3e23e41 commit 4ad8b49

File tree

28 files changed

+281
-42
lines changed

28 files changed

+281
-42
lines changed

crates/node_binding/napi-binding.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,7 @@ export declare enum BuiltinPluginName {
565565
SideEffectsFlagPlugin = 'SideEffectsFlagPlugin',
566566
FlagDependencyExportsPlugin = 'FlagDependencyExportsPlugin',
567567
FlagDependencyUsagePlugin = 'FlagDependencyUsagePlugin',
568+
InlineExportsPlugin = 'InlineExportsPlugin',
568569
MangleExportsPlugin = 'MangleExportsPlugin',
569570
ModuleConcatenationPlugin = 'ModuleConcatenationPlugin',
570571
CssModulesPlugin = 'CssModulesPlugin',

crates/rspack_binding_api/src/raw_options/raw_builtins/mod.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,10 @@ use rspack_plugin_hmr::HotModuleReplacementPlugin;
6464
use rspack_plugin_html::HtmlRspackPlugin;
6565
use rspack_plugin_ignore::IgnorePlugin;
6666
use rspack_plugin_javascript::{
67-
FlagDependencyExportsPlugin, FlagDependencyUsagePlugin, InferAsyncModulesPlugin, JsPlugin,
68-
MangleExportsPlugin, ModuleConcatenationPlugin, SideEffectsFlagPlugin, api_plugin::APIPlugin,
69-
define_plugin::DefinePlugin, provide_plugin::ProvidePlugin, url_plugin::URLPlugin,
67+
FlagDependencyExportsPlugin, FlagDependencyUsagePlugin, InferAsyncModulesPlugin,
68+
InlineExportsPlugin, JsPlugin, MangleExportsPlugin, ModuleConcatenationPlugin,
69+
SideEffectsFlagPlugin, api_plugin::APIPlugin, define_plugin::DefinePlugin,
70+
provide_plugin::ProvidePlugin, url_plugin::URLPlugin,
7071
};
7172
use rspack_plugin_json::JsonPlugin;
7273
use rspack_plugin_library::enable_library_plugin;
@@ -196,6 +197,7 @@ pub enum BuiltinPluginName {
196197
SideEffectsFlagPlugin,
197198
FlagDependencyExportsPlugin,
198199
FlagDependencyUsagePlugin,
200+
InlineExportsPlugin,
199201
MangleExportsPlugin,
200202
ModuleConcatenationPlugin,
201203
CssModulesPlugin,
@@ -609,6 +611,9 @@ impl<'a> BuiltinPlugin<'a> {
609611
)
610612
.boxed(),
611613
),
614+
BuiltinPluginName::InlineExportsPlugin => {
615+
plugins.push(InlineExportsPlugin::default().boxed())
616+
}
612617
BuiltinPluginName::MangleExportsPlugin => plugins.push(
613618
MangleExportsPlugin::new(
614619
downcast_into::<bool>(self.options)

crates/rspack_core/src/exports/export_info.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use super::{
99
};
1010
use crate::{
1111
CanInlineUse, DependencyId, EvaluatedInlinableValue, FindTargetResult, ModuleGraph,
12-
ModuleIdentifier, ResolveFilterFnTy, find_target_from_export_info,
12+
ModuleIdentifier, ResolveFilterFnTy, UsedNameItem, find_target_from_export_info,
1313
get_target_from_maybe_export_info, get_target_with_filter,
1414
};
1515

@@ -58,7 +58,7 @@ pub struct ExportInfoData {
5858
// the name could be `null` you could refer https://github.com/webpack/webpack/blob/ac7e531436b0d47cd88451f497cdfd0dad4153d/lib/ExportsInfo.js#L78
5959
name: Option<Atom>,
6060
/// this is mangled name, https://github.com/webpack/webpack/blob/1f99ad6367f2b8a6ef17cce0e058f7a67fb7db18/lib/ExportsInfo.js#L1181-L1188
61-
used_name: Option<Atom>,
61+
used_name: Option<UsedNameItem>,
6262
target: HashMap<Option<DependencyId>, ExportInfoTargetValue>,
6363
/// This is rspack only variable, it is used to flag if the target has been initialized
6464
target_is_set: bool,
@@ -150,7 +150,7 @@ impl ExportInfoData {
150150
self.name.as_ref()
151151
}
152152

153-
pub fn used_name(&self) -> Option<&Atom> {
153+
pub fn used_name(&self) -> Option<&UsedNameItem> {
154154
self.used_name.as_ref()
155155
}
156156

@@ -261,7 +261,7 @@ impl ExportInfoData {
261261
self.exports_info_owned = value;
262262
}
263263

264-
pub fn set_used_name(&mut self, name: Atom) {
264+
pub fn set_used_name(&mut self, name: UsedNameItem) {
265265
self.used_name = Some(name);
266266
}
267267

crates/rspack_core/src/exports/export_info_getter.rs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,14 @@ use rspack_util::atom::Atom;
55
use rustc_hash::FxHashMap as HashMap;
66

77
use super::{ExportInfoData, ExportInfoTargetValue, ExportProvided, UsageState, UsedNameItem};
8-
use crate::{CanInlineUse, DependencyId, EvaluatedInlinableValue, RuntimeSpec};
8+
use crate::{CanInlineUse, DependencyId, RuntimeSpec};
99

1010
impl ExportInfoData {
1111
pub fn get_used_name(
1212
&self,
1313
fallback_name: Option<&Atom>,
1414
runtime: Option<&RuntimeSpec>,
1515
) -> Option<UsedNameItem> {
16-
if let Some(inlined) = self.get_inline() {
17-
return Some(UsedNameItem::Inlined(inlined.clone()));
18-
}
1916
if self.has_use_in_runtime_info() {
2017
if let Some(usage) = self.global_used() {
2118
if matches!(usage, UsageState::Unused) {
@@ -34,7 +31,7 @@ impl ExportInfoData {
3431
}
3532
}
3633
if let Some(used_name) = self.used_name() {
37-
return Some(UsedNameItem::Str(used_name.clone()));
34+
return Some(used_name.clone());
3835
}
3936
if let Some(name) = self.name() {
4037
Some(UsedNameItem::Str(name.clone()))
@@ -91,8 +88,13 @@ impl ExportInfoData {
9188

9289
pub fn get_rename_info(&self) -> Cow<'_, str> {
9390
match (self.used_name(), self.name()) {
94-
(Some(used), Some(name)) if used != name => return format!("renamed to {used}").into(),
95-
(Some(used), None) => return format!("renamed to {used}").into(),
91+
(Some(UsedNameItem::Inlined(inlined)), _) => {
92+
return format!("inlined to {}", inlined.render()).into();
93+
}
94+
(Some(UsedNameItem::Str(used)), Some(name)) if used != name => {
95+
return format!("renamed to {used}").into();
96+
}
97+
(Some(UsedNameItem::Str(used)), None) => return format!("renamed to {used}").into(),
9698
_ => {}
9799
}
98100

@@ -185,13 +187,13 @@ impl ExportInfoData {
185187
}
186188
}
187189

188-
pub fn get_inline(&self) -> Option<&EvaluatedInlinableValue> {
189-
if let Some(provided) = self.can_inline_provide()
190-
&& self.can_inline_use() == Some(CanInlineUse::Yes)
191-
{
192-
return Some(provided);
190+
pub fn can_inline(&self) -> Option<bool> {
191+
match self.can_inline_provide() {
192+
Some(_) => self
193+
.can_inline_use()
194+
.map(|v| matches!(v, CanInlineUse::Yes)),
195+
None => None,
193196
}
194-
None
195197
}
196198

197199
pub fn has_used_name(&self) -> bool {

crates/rspack_core/src/exports/exports_info_getter.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,6 @@ impl<'a> PrefetchedExportsInfoWrapper<'a> {
406406
export_info.get_used(runtime).dyn_hash(hasher);
407407
export_info.provided().dyn_hash(hasher);
408408
export_info.terminal_binding().dyn_hash(hasher);
409-
export_info.get_inline().dyn_hash(hasher);
410409
}
411410

412411
let mut exports = self.exports.values().collect_vec();

crates/rspack_core/src/exports/utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ pub enum CanInlineUse {
109109
No,
110110
}
111111

112-
#[derive(Debug, Clone)]
112+
#[derive(Debug, Clone, Hash)]
113113
pub enum UsedNameItem {
114114
Str(Atom),
115115
Inlined(EvaluatedInlinableValue),

crates/rspack_core/src/module_graph/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use swc_core::ecma::atoms::Atom;
1010
use crate::{
1111
AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, Compilation, DependenciesBlock,
1212
Dependency, ExportInfo, ExportName, ImportedByDeferModulesArtifact, ModuleGraphCacheArtifact,
13-
RuntimeSpec,
13+
RuntimeSpec, UsedNameItem,
1414
};
1515
mod module;
1616
pub use module::*;
@@ -1224,7 +1224,7 @@ impl<'a> ModuleGraph<'a> {
12241224
}
12251225
}
12261226

1227-
pub fn batch_set_export_info_used_name(&mut self, tasks: Vec<(ExportInfo, Atom)>) {
1227+
pub fn batch_set_export_info_used_name(&mut self, tasks: Vec<(ExportInfo, UsedNameItem)>) {
12281228
if self.active.is_none() {
12291229
panic!("should have active partial");
12301230
}

crates/rspack_plugin_esm_library/src/link.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,12 +1054,16 @@ impl EsmLibraryPlugin {
10541054
continue;
10551055
}
10561056

1057+
let local = match export_info.used_name() {
1058+
Some(UsedNameItem::Inlined(inlined)) => inlined.render().into(),
1059+
Some(UsedNameItem::Str(name)) => {
1060+
required_interop.property_access(name, &mut chunk_link.used_names)
1061+
}
1062+
None => required_interop.property_access(name, &mut chunk_link.used_names),
1063+
};
10571064
Self::add_chunk_export(
10581065
current_chunk,
1059-
required_interop.property_access(
1060-
export_info.used_name().unwrap_or(name),
1061-
&mut chunk_link.used_names,
1062-
),
1066+
local,
10631067
name.clone(),
10641068
exports,
10651069
keep_export_name,
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
use itertools::Itertools;
2+
use rayon::prelude::*;
3+
use rspack_core::{
4+
Compilation, CompilationOptimizeDependencies, ExportProvided, ExportsInfo, ExportsInfoGetter,
5+
Plugin, PrefetchExportsInfoMode, UsageState, UsedNameItem, incremental::IncrementalPasses,
6+
};
7+
use rspack_error::Result;
8+
use rspack_hook::{plugin, plugin_hook};
9+
use rustc_hash::{FxHashMap, FxHashSet};
10+
11+
#[plugin]
12+
#[derive(Debug, Default)]
13+
pub struct InlineExportsPlugin;
14+
15+
// We put it to optimize_dependencies hook instead of optimize_code_generation hook like MangleExportsPlugin
16+
// because inline can affect side effects optimization (not the SideEffectsFlagPlugin does, the buildChunkGraph
17+
// does), buildChunkGraph can use dependency condition to determine if a dependency still active, if the dependency
18+
// imported export is inlined, then the dependency is inactive and will not be processed by buildChunkGraph, if a
19+
// module's all exports are all being inlined, then the module can be eliminated by buildChunkGraph
20+
#[plugin_hook(CompilationOptimizeDependencies for InlineExportsPlugin, stage = 100)]
21+
async fn optimize_dependencies(&self, compilation: &mut Compilation) -> Result<Option<bool>> {
22+
if let Some(diagnostic) = compilation.incremental.disable_passes(
23+
IncrementalPasses::MODULES_HASHES,
24+
"InlineExportsPlugin (optimization.inlineExports = true)",
25+
"it requires calculating the export names of all the modules, which is a global effect",
26+
) {
27+
if let Some(diagnostic) = diagnostic {
28+
compilation.push_diagnostic(diagnostic);
29+
}
30+
compilation.cgm_hash_artifact.clear();
31+
}
32+
33+
let mut mg = compilation.get_module_graph_mut();
34+
let modules = mg.modules();
35+
36+
let mut visited: FxHashSet<ExportsInfo> = FxHashSet::default();
37+
38+
let mut q = modules
39+
.keys()
40+
.filter_map(|mid| {
41+
let mgm = mg.module_graph_module_by_identifier(mid)?;
42+
Some(mgm.exports)
43+
})
44+
.collect_vec();
45+
46+
while !q.is_empty() {
47+
let items = std::mem::take(&mut q);
48+
let batch = items
49+
.par_iter()
50+
.filter_map(|exports_info| {
51+
let exports_info_data =
52+
ExportsInfoGetter::prefetch(exports_info, &mg, PrefetchExportsInfoMode::Default);
53+
let export_list = {
54+
// If there are other usage (e.g. `import { Kind } from './enum'; Kind;`) in any runtime,
55+
// then we cannot inline this export.
56+
if exports_info_data.other_exports_info().get_used(None) != UsageState::Unused {
57+
return None;
58+
}
59+
exports_info_data
60+
.exports()
61+
.map(|(_, export_info_data)| {
62+
let do_inline = !export_info_data.has_used_name()
63+
&& export_info_data.can_inline() == Some(true)
64+
&& matches!(export_info_data.provided(), Some(ExportProvided::Provided));
65+
66+
let nested_exports_info = if export_info_data.exports_info_owned() {
67+
let used = export_info_data.get_used(None);
68+
if used == UsageState::OnlyPropertiesUsed || used == UsageState::Unused {
69+
export_info_data.exports_info()
70+
} else {
71+
None
72+
}
73+
} else {
74+
None
75+
};
76+
(export_info_data.id(), nested_exports_info, do_inline)
77+
})
78+
.collect::<Vec<_>>()
79+
};
80+
81+
Some((*exports_info, export_list))
82+
})
83+
.collect::<FxHashMap<_, _>>();
84+
85+
visited.extend(batch.keys());
86+
for (_, export_list) in batch {
87+
q.extend(
88+
export_list
89+
.into_iter()
90+
.filter_map(|(export_info, nested_exports_info, do_inline)| {
91+
if do_inline {
92+
let data = export_info.as_data_mut(&mut mg);
93+
data.set_used_name(UsedNameItem::Inlined(
94+
data
95+
.can_inline_provide()
96+
.expect("should have provided inline value")
97+
.clone(),
98+
));
99+
}
100+
nested_exports_info
101+
})
102+
.filter(|e| !visited.contains(e)),
103+
);
104+
}
105+
}
106+
107+
Ok(None)
108+
}
109+
110+
impl Plugin for InlineExportsPlugin {
111+
fn apply(&self, ctx: &mut rspack_core::ApplyContext<'_>) -> Result<()> {
112+
ctx
113+
.compilation_hooks
114+
.optimize_dependencies
115+
.tap(optimize_dependencies::new(self));
116+
Ok(())
117+
}
118+
}

crates/rspack_plugin_javascript/src/plugin/mangle_exports_plugin.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use regex::Regex;
66
use rspack_core::{
77
BuildMetaExportsType, Compilation, CompilationOptimizeCodeGeneration, ExportInfo, ExportProvided,
88
ExportsInfo, ExportsInfoGetter, ModuleGraph, Plugin, PrefetchExportsInfoMode,
9-
PrefetchedExportsInfoWrapper, UsageState, incremental::IncrementalPasses,
9+
PrefetchedExportsInfoWrapper, UsageState, UsedNameItem, incremental::IncrementalPasses,
1010
};
1111
use rspack_error::Result;
1212
use rspack_hook::{plugin, plugin_hook};
@@ -71,8 +71,6 @@ async fn optimize_code_generation(&self, compilation: &mut Compilation) -> Resul
7171
compilation.cgm_hash_artifact.clear();
7272
}
7373

74-
// TODO: should bailout if compilation.moduleMemCache is enable, https://github.com/webpack/webpack/blob/1f99ad6367f2b8a6ef17cce0e058f7a67fb7db18/lib/optimize/MangleExportsPlugin.js#L160-L164
75-
// We don't do that cause we don't have this option
7674
let mut mg = compilation.get_module_graph_mut();
7775
let modules = mg.modules();
7876

@@ -232,7 +230,7 @@ fn mangle_exports_info(
232230
deterministic: bool,
233231
exports_info: ExportsInfo,
234232
exports_info_cache: &FxHashMap<ExportsInfo, Vec<ExportInfoCache>>,
235-
) -> (Vec<(ExportInfo, Atom)>, Vec<ExportsInfo>) {
233+
) -> (Vec<(ExportInfo, UsedNameItem)>, Vec<ExportsInfo>) {
236234
let mut changes = vec![];
237235
let mut nested_exports = vec![];
238236
let mut used_names = FxHashSet::default();
@@ -246,7 +244,7 @@ fn mangle_exports_info(
246244
for export_info in export_list {
247245
match &export_info.can_mangle {
248246
Manglable::CanNotMangle(name) => {
249-
changes.push((export_info.id.clone(), name.clone()));
247+
changes.push((export_info.id.clone(), UsedNameItem::Str(name.clone())));
250248
used_names.insert(name.to_string());
251249
}
252250
Manglable::CanMangle(name) => {
@@ -298,7 +296,7 @@ fn mangle_exports_info(
298296
0,
299297
);
300298
for (export_info, name) in export_info_used_name {
301-
changes.push((export_info, name.into()));
299+
changes.push((export_info, UsedNameItem::Str(name.into())));
302300
}
303301
} else {
304302
let mut used_exports = Vec::new();
@@ -337,7 +335,7 @@ fn mangle_exports_info(
337335
}
338336
i += 1;
339337
}
340-
changes.push((export_info, name.into()));
338+
changes.push((export_info, UsedNameItem::Str(name.into())));
341339
}
342340
}
343341
}

0 commit comments

Comments
 (0)