Skip to content

Commit 733261f

Browse files
committed
fix: export interop default symbol and ensure import required chunks
1 parent 34aec48 commit 733261f

File tree

19 files changed

+314
-75
lines changed

19 files changed

+314
-75
lines changed

crates/rspack_plugin_esm_library/src/chunk_link.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub struct ExternalInterop {
7070
pub from_module: IdentifierSet,
7171
pub required_symbol: Option<Atom>,
7272
pub default_access: Option<Atom>,
73+
pub default_exported: Option<Atom>,
7374
pub namespace_object: Option<Atom>,
7475
pub namespace_object2: Option<Atom>,
7576
pub property_access: FxIndexMap<Atom, Atom>,
@@ -152,6 +153,24 @@ impl ExternalInterop {
152153
}
153154
}
154155

156+
pub fn default_exported(&mut self, used_names: &mut FxHashSet<Atom>) -> Atom {
157+
if self.required_symbol.is_none() {
158+
let new_name = find_new_name("", used_names, &vec![]);
159+
used_names.insert(new_name.clone());
160+
self.required_symbol = Some(new_name);
161+
}
162+
163+
if let Some(default_exported) = &self.default_exported {
164+
return default_exported.clone();
165+
}
166+
167+
let default_access_symbol = self.default_access(used_names);
168+
let default_exported_symbol = find_new_name(&default_access_symbol, used_names, &vec![]);
169+
used_names.insert(default_exported_symbol.clone());
170+
self.default_exported = Some(default_exported_symbol.clone());
171+
default_exported_symbol
172+
}
173+
155174
pub fn property_access(&mut self, atom: &Atom, used_names: &mut FxHashSet<Atom>) -> Atom {
156175
self.property_access.get(atom).cloned().unwrap_or_else(|| {
157176
let local_name = find_new_name(atom, used_names, &vec![]);
@@ -207,6 +226,12 @@ impl ExternalInterop {
207226
RuntimeGlobals::COMPAT_GET_DEFAULT_EXPORT,
208227
name
209228
)));
229+
230+
if let Some(default_exported_symbol) = &self.default_exported {
231+
source.add(RawStringSource::from(format!(
232+
"var {default_exported_symbol} = {default_access}();\n",
233+
)));
234+
}
210235
}
211236

212237
for (s, local) in &self.property_access {

crates/rspack_plugin_esm_library/src/link.rs

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,7 @@ impl EsmLibraryPlugin {
871871
from_module: Default::default(),
872872
required_symbol: None,
873873
default_access: None,
874+
default_exported: None,
874875
namespace_object: None,
875876
namespace_object2: None,
876877
property_access: Default::default(),
@@ -905,7 +906,7 @@ impl EsmLibraryPlugin {
905906
current: ModuleIdentifier,
906907
current_chunk: ChunkUkey,
907908
compilation: &Compilation,
908-
concate_modules_map: &IdentifierIndexMap<ModuleInfo>,
909+
concate_modules_map: &mut IdentifierIndexMap<ModuleInfo>,
909910
required: &mut IdentifierIndexMap<ExternalInterop>,
910911
link: &mut UkeyMap<ChunkUkey, ChunkLinkContext>,
911912
module_graph: &ModuleGraph,
@@ -1002,14 +1003,17 @@ impl EsmLibraryPlugin {
10021003
required,
10031004
);
10041005
let ref_chunk = Self::get_module_chunk(ref_module, compilation);
1005-
1006-
let default_access_symbol = interop_info.default_access(&mut chunk_link.used_names);
1006+
let info = concate_modules_map
1007+
.get_mut(&ref_module)
1008+
.expect("should have info");
1009+
info.set_interop_default_access_used(true);
1010+
let default_exported = interop_info.default_exported(&mut chunk_link.used_names);
10071011
// ensure we import this chunk
10081012
entry_imports.entry(ref_module).or_default();
10091013

10101014
let exported = Self::add_chunk_export(
10111015
ref_chunk,
1012-
default_access_symbol,
1016+
default_exported.clone(),
10131017
mode.name.clone(),
10141018
exports,
10151019
ref_chunk == current_chunk,
@@ -1029,10 +1033,11 @@ impl EsmLibraryPlugin {
10291033
rspack_core::ExportMode::ReexportNamedDefault(mode) => {
10301034
// export { default as n } from './foo.cjs'
10311035
let ref_info = concate_modules_map
1032-
.get(&ref_module)
1036+
.get_mut(&ref_module)
10331037
.expect("should have info");
10341038
match ref_info {
10351039
ModuleInfo::External(ref_info) => {
1040+
ref_info.interop_default_access_used = true;
10361041
// var foo_default = /*#__PURE__*/ __webpack_require__.n(m3);
10371042
let interop_info = Self::add_require(
10381043
ref_module,
@@ -1042,14 +1047,15 @@ impl EsmLibraryPlugin {
10421047
required,
10431048
);
10441049

1045-
let default_access_symbol = interop_info.default_access(&mut chunk_link.used_names);
1050+
let default_exported_symbol =
1051+
interop_info.default_exported(&mut chunk_link.used_names);
10461052

10471053
// ensure we import this chunk
10481054
entry_imports.entry(ref_module).or_default();
10491055

10501056
Self::add_chunk_export(
10511057
current_chunk,
1052-
default_access_symbol,
1058+
default_exported_symbol,
10531059
mode.name.clone(),
10541060
exports,
10551061
true,
@@ -1157,7 +1163,7 @@ impl EsmLibraryPlugin {
11571163

11581164
let chunk_link = link.get_mut(&current_chunk).expect("should have link");
11591165

1160-
let mut export_info = item
1166+
let export_info = item
11611167
.ids
11621168
.first()
11631169
.and_then(|id| exports_info.as_data(module_graph).named_exports(id))
@@ -1178,18 +1184,17 @@ impl EsmLibraryPlugin {
11781184
continue;
11791185
};
11801186
ref_module = *module;
1181-
1182-
let exports_info = module_graph.get_exports_info(module).as_data(module_graph);
1183-
if let Some(target) =
1184-
exports_info.named_exports(item.ids.first().unwrap_or(&item.name))
1185-
{
1186-
export_info = target;
1187-
}
11881187
}
11891188

1190-
let Some(used_name) = export_info.get_used_name(None, None) else {
1191-
continue;
1192-
};
1189+
let exports_info = module_graph
1190+
.get_prefetched_exports_info(&ref_module, PrefetchExportsInfoMode::Default);
1191+
let export_info =
1192+
exports_info.get_read_only_export_info(item.ids.first().unwrap_or(&item.name));
1193+
1194+
let used_name = export_info.get_used_name(None, None).unwrap_or_else(|| {
1195+
// dynamic export
1196+
UsedNameItem::Str(item.name.clone())
1197+
});
11931198

11941199
if let UsedNameItem::Inlined(inlined) = used_name {
11951200
let new_name = find_new_name(&item.name, &chunk_link.used_names, &vec![]);
@@ -1882,9 +1887,9 @@ impl EsmLibraryPlugin {
18821887
ExportsType::Dynamic => match export_name.first().map(|atom| atom.as_str()) {
18831888
Some("default") => {
18841889
// shadowing the previous immutable ref to avoid violating rustc borrow rules
1890+
info.set_interop_default_access_used(true);
18851891
let symbol = match info {
18861892
ModuleInfo::External(info) => {
1887-
info.interop_default_access_used = true;
18881893
let required_info = Self::add_require(
18891894
*info_id,
18901895
from,
@@ -1894,13 +1899,10 @@ impl EsmLibraryPlugin {
18941899
);
18951900
required_info.default_access(all_used_names)
18961901
}
1897-
ModuleInfo::Concatenated(info) => {
1898-
info.interop_default_access_used = true;
1899-
info
1900-
.interop_default_access_name
1901-
.clone()
1902-
.expect("should already set interop namespace")
1903-
}
1902+
ModuleInfo::Concatenated(info) => info
1903+
.interop_default_access_name
1904+
.clone()
1905+
.expect("should already set interop namespace"),
19041906
};
19051907

19061908
export_name = export_name[1..].to_vec();

crates/rspack_plugin_esm_library/src/render.rs

Lines changed: 54 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::{borrow::Cow, sync::Arc};
22

33
use regex::Regex;
4-
use rspack_collections::{IdentifierSet, UkeyIndexMap, UkeySet};
4+
use rspack_collections::{IdentifierIndexSet, UkeyIndexMap, UkeySet};
55
use rspack_core::{
66
AssetInfo, Chunk, ChunkGraph, ChunkRenderContext, ChunkUkey, CodeGenerationDataFilename,
77
Compilation, ConcatenatedModuleInfo, DependencyId, InitFragment, ModuleIdentifier, PathData,
@@ -181,6 +181,8 @@ impl EsmLibraryPlugin {
181181
let mut export_specifiers: FxIndexSet<Cow<str>> = Default::default();
182182
let mut export_default = None;
183183
let mut imported_chunks = UkeyIndexMap::<ChunkUkey, FxHashMap<Atom, Atom>>::default();
184+
let mut runtime_requirements =
185+
*ChunkGraph::get_chunk_runtime_requirements(compilation, chunk_ukey);
184186

185187
// render webpack runtime
186188
if chunk.has_runtime(&compilation.chunk_group_by_ukey) {
@@ -210,7 +212,7 @@ impl EsmLibraryPlugin {
210212
render_source.add(RawStringSource::from(format!("{namespace}\n")));
211213
}
212214

213-
let mut already_rendered = IdentifierSet::default();
215+
let mut already_required = IdentifierIndexSet::default();
214216
for m in &chunk_link.hoisted_modules {
215217
let info = concatenated_modules_map
216218
.get(m)
@@ -245,7 +247,7 @@ impl EsmLibraryPlugin {
245247
*m,
246248
compilation,
247249
chunk_link,
248-
&mut already_rendered,
250+
&mut already_required,
249251
));
250252
render_source.add(source);
251253
render_source.add(RawSource::from_static("\n"));
@@ -299,7 +301,8 @@ impl EsmLibraryPlugin {
299301
}
300302

301303
for (m, required_info) in &chunk_link.required {
302-
if already_rendered.insert(*m) {
304+
if already_required.insert(*m) {
305+
runtime_requirements.insert(RuntimeGlobals::REQUIRE);
303306
render_source.add(required_info.render(compilation));
304307
render_source.add(RawSource::from_static("\n"));
305308
}
@@ -308,7 +311,14 @@ impl EsmLibraryPlugin {
308311
// render imports and exports to other chunks
309312
let mut final_source = ConcatSource::default();
310313

311-
let runtime_requirements = ChunkGraph::get_chunk_runtime_requirements(compilation, chunk_ukey);
314+
for required_module in already_required {
315+
runtime_requirements.insert(RuntimeGlobals::REQUIRE);
316+
let target_chunk = Self::get_module_chunk(required_module, compilation);
317+
if &target_chunk != chunk_ukey {
318+
imported_chunks.entry(target_chunk).or_default();
319+
}
320+
}
321+
312322
if !runtime_requirements.is_empty() {
313323
let runtime_chunk = Self::get_runtime_chunk(*chunk_ukey, compilation);
314324
if &runtime_chunk != chunk_ukey && runtime_requirements.contains(RuntimeGlobals::REQUIRE) {
@@ -355,49 +365,49 @@ impl EsmLibraryPlugin {
355365
final_source.add(RawStringSource::from(import_str));
356366
}
357367

358-
if !chunk_link.imports.is_empty() {
359-
for (id, imports) in &chunk_link.imports {
360-
let chunk = Self::get_module_chunk(*id, compilation);
361-
if &chunk == chunk_ukey {
362-
// ignore self import
363-
continue;
364-
}
365-
366-
let imported_symbols = imported_chunks.entry(chunk).or_default();
367-
if imports.is_empty() {
368-
continue;
369-
}
368+
for (id, imports) in &chunk_link.imports {
369+
let chunk = Self::get_module_chunk(*id, compilation);
370+
if &chunk == chunk_ukey {
371+
// ignore self import
372+
continue;
373+
}
370374

371-
for (imported, local) in imports {
372-
imported_symbols.insert(imported.clone(), local.clone());
373-
}
375+
let imported_symbols = imported_chunks.entry(chunk).or_default();
376+
if imports.is_empty() {
377+
continue;
374378
}
375379

376-
for (chunk, imported) in &imported_chunks {
377-
final_source.add(RawStringSource::from(format!(
378-
"import {}\"__RSPACK_ESM_CHUNK_{}\";\n",
379-
if imported.is_empty() {
380-
String::new()
381-
} else {
382-
format!(
383-
"{{ {} }} from ",
384-
imported
385-
.iter()
386-
.map(|(imported, local)| {
387-
if imported == local {
388-
Cow::Borrowed(imported.as_str())
389-
} else {
390-
Cow::Owned(format!("{imported} as {local}"))
391-
}
392-
})
393-
.collect::<Vec<_>>()
394-
.join(", ")
395-
)
396-
},
397-
chunk.as_u32()
398-
)));
380+
for (imported, local) in imports {
381+
imported_symbols.insert(imported.clone(), local.clone());
399382
}
383+
}
384+
385+
for (chunk, imported) in &imported_chunks {
386+
final_source.add(RawStringSource::from(format!(
387+
"import {}\"__RSPACK_ESM_CHUNK_{}\";\n",
388+
if imported.is_empty() {
389+
String::new()
390+
} else {
391+
format!(
392+
"{{ {} }} from ",
393+
imported
394+
.iter()
395+
.map(|(imported, local)| {
396+
if imported == local {
397+
Cow::Borrowed(imported.as_str())
398+
} else {
399+
Cow::Owned(format!("{imported} as {local}"))
400+
}
401+
})
402+
.collect::<Vec<_>>()
403+
.join(", ")
404+
)
405+
},
406+
chunk.as_u32()
407+
)));
408+
}
400409

410+
if !imported_chunks.is_empty() || !chunk_link.raw_import_stmts.is_empty() {
401411
final_source.add(RawSource::from_static("\n"));
402412
}
403413

@@ -695,7 +705,7 @@ impl EsmLibraryPlugin {
695705
root: ModuleIdentifier,
696706
compilation: &Compilation,
697707
chunk_link: &ChunkLinkContext,
698-
already_required: &mut IdentifierSet,
708+
already_required: &mut IdentifierIndexSet,
699709
) -> ConcatSource {
700710
let mut source = ConcatSource::default();
701711

crates/rspack_plugin_remove_empty_chunks/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ impl RemoveEmptyChunksPlugin {
2222
chunk_graph.get_number_of_chunk_modules(&chunk.ukey()) == 0
2323
&& !chunk.has_runtime(&compilation.chunk_group_by_ukey)
2424
&& chunk_graph.get_number_of_entry_modules(&chunk.ukey()) == 0
25+
&& chunk
26+
.get_entry_options(&compilation.chunk_group_by_ukey)
27+
.is_none()
2528
})
2629
.map(|chunk| chunk.ukey())
2730
.collect::<Vec<_>>();

tests/rspack-test/esmOutputCases/dynamic-import/basic/__snapshots__/esm.snap.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ __webpack_require__.d(__webpack_exports__, {
1919

2020
```mjs title=main.mjs
2121
import { __webpack_require__ } from "./runtime.mjs";
22-
2322
__webpack_require__.add({
2423
"./index.js":
2524
/*!******************!*\

tests/rspack-test/esmOutputCases/interop/correct-order/__snapshots__/esm.snap.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
```mjs title=main.mjs
22
import { __webpack_require__ } from "./runtime.mjs";
3-
43
__webpack_require__.add({
54
"./bar.cjs":
65
/*!*****************!*\

0 commit comments

Comments
 (0)