@@ -40,7 +40,7 @@ use swc_core::{
4040
4141use crate :: {
4242 EsmLibraryPlugin ,
43- chunk_link:: { ChunkLinkContext , ExternalInterop , Ref , SymbolRef } ,
43+ chunk_link:: { ChunkLinkContext , ExternalInterop , ReExportFrom , Ref , SymbolRef } ,
4444} ;
4545
4646pub ( crate ) trait GetMut < K , V > {
@@ -66,9 +66,10 @@ impl<V> GetMut<ModuleIdentifier, V> for IdentifierIndexMap<V> {
6666static START_EXPORTS : LazyLock < Atom > = LazyLock :: new ( || "*" . into ( ) ) ;
6767
6868#[ derive( Default , Debug ) ]
69- struct ExportsContext {
70- pub exports : FxHashMap < Atom , FxIndexSet < Atom > > ,
71- pub exported_symbols : FxHashMap < Atom , Atom > ,
69+ pub ( crate ) struct ExportsContext {
70+ exports : FxHashMap < Atom , FxIndexSet < Atom > > ,
71+ exported_symbols : FxHashSet < Atom > ,
72+ re_exports : FxIndexMap < ReExportFrom , FxHashMap < Atom , FxHashSet < Atom > > > ,
7273}
7374
7475impl EsmLibraryPlugin {
@@ -94,33 +95,92 @@ impl EsmLibraryPlugin {
9495 }
9596
9697 // we've not exported this local symbol, check if we've already exported this symbol
97- if ctx. exported_symbols . contains_key ( & exported) {
98+ if ctx. exported_symbols . contains ( & exported) {
9899 // the name is already exported and we know the exported_local is not the same
99100 if strict_exports {
100101 return None ;
101102 }
102103
103- let already_exported_names = ctx. exports . entry ( local. clone ( ) ) . or_default ( ) ;
104+ let already_exported_names = ctx. exports . entry ( local) . or_default ( ) ;
104105
105106 // we find another name to export this symbol
106107 let mut idx = 0 ;
107108 let mut new_export = Atom :: new ( format ! ( "{exported}_{idx}" ) ) ;
108- while ctx. exported_symbols . contains_key ( & new_export) {
109+ while ctx. exported_symbols . contains ( & new_export) {
109110 idx += 1 ;
110111 new_export = format ! ( "{exported}_{idx}" ) . into ( ) ;
111112 }
112113
113- ctx. exported_symbols . insert ( new_export. clone ( ) , local ) ;
114+ ctx. exported_symbols . insert ( new_export. clone ( ) ) ;
114115 already_exported_names. insert ( new_export. clone ( ) ) ;
115116 already_exported_names. get ( & new_export) . cloned ( )
116117 } else {
117- let already_exported_names = ctx. exports . entry ( local. clone ( ) ) . or_default ( ) ;
118- ctx. exported_symbols . insert ( exported. clone ( ) , local ) ;
118+ let already_exported_names = ctx. exports . entry ( local) . or_default ( ) ;
119+ ctx. exported_symbols . insert ( exported. clone ( ) ) ;
119120 already_exported_names. insert ( exported. clone ( ) ) ;
120121 already_exported_names. get ( & exported) . cloned ( )
121122 }
122123 }
123124
125+ // // orig_chunk
126+ // export { local_name as export_name } from 'ref_chunk'
127+ fn add_chunk_re_export (
128+ orig_chunk : ChunkUkey ,
129+ ref_chunk : ChunkUkey ,
130+ local_name : Atom ,
131+ export_name : Atom ,
132+ chunk_exports : & mut UkeyMap < ChunkUkey , ExportsContext > ,
133+ strict_exports : bool ,
134+ ) -> Option < & Atom > {
135+ let exports_context = chunk_exports. get_mut_unwrap ( & orig_chunk) ;
136+
137+ let export_name = if !exports_context. exported_symbols . contains ( & export_name) {
138+ export_name
139+ } else {
140+ if strict_exports {
141+ return None ;
142+ }
143+ let mut idx = 0 ;
144+ let mut new_export = Atom :: new ( format ! ( "{export_name}_{idx}" ) ) ;
145+ while exports_context. exported_symbols . contains ( & new_export) {
146+ idx += 1 ;
147+ new_export = format ! ( "{export_name}_{idx}" ) . into ( ) ;
148+ }
149+ new_export
150+ } ;
151+
152+ exports_context. exported_symbols . insert ( export_name. clone ( ) ) ;
153+
154+ let set = exports_context
155+ . re_exports
156+ . entry ( ReExportFrom :: Chunk ( ref_chunk) )
157+ . or_default ( )
158+ . entry ( local_name)
159+ . or_default ( ) ;
160+
161+ set. insert ( export_name. clone ( ) ) ;
162+ Some ( set. get ( & export_name) . expect ( "should have inserted" ) )
163+ }
164+
165+ fn add_re_export_from_request (
166+ chunk : ChunkUkey ,
167+ request : String ,
168+ imported_name : Atom ,
169+ export_name : Atom ,
170+ chunk_exports : & mut UkeyMap < ChunkUkey , ExportsContext > ,
171+ ) {
172+ let ctx = chunk_exports. get_mut_unwrap ( & chunk) ;
173+ ctx. exported_symbols . insert ( export_name. clone ( ) ) ;
174+
175+ ctx
176+ . re_exports
177+ . entry ( ReExportFrom :: Request ( request) )
178+ . or_default ( )
179+ . entry ( imported_name)
180+ . or_default ( )
181+ . insert ( export_name) ;
182+ }
183+
124184 pub ( crate ) async fn link (
125185 & self ,
126186 compilation : & Compilation ,
@@ -1042,8 +1102,14 @@ var {} = {{}};
10421102 if entry_chunk != current_chunk
10431103 && let Some ( exported) = exported
10441104 {
1045- let entry_chunk_link = link. get_mut_unwrap ( & entry_chunk) ;
1046- entry_chunk_link. add_re_export ( current_chunk, exported, "default" . to_string ( ) . into ( ) ) ;
1105+ Self :: add_chunk_re_export (
1106+ entry_chunk,
1107+ current_chunk,
1108+ exported,
1109+ "default" . to_string ( ) . into ( ) ,
1110+ exports,
1111+ true ,
1112+ ) ;
10471113 }
10481114 }
10491115 ModuleInfo :: External ( info) => {
@@ -1237,8 +1303,14 @@ var {} = {{}};
12371303 if ref_chunk != entry_chunk
12381304 && let Some ( exported) = exported
12391305 {
1240- let entry_chunk_link = link. get_mut_unwrap ( & entry_chunk) ;
1241- entry_chunk_link. add_re_export ( ref_chunk, exported. clone ( ) , name. clone ( ) ) ;
1306+ Self :: add_chunk_re_export (
1307+ entry_chunk,
1308+ ref_chunk,
1309+ exported. clone ( ) ,
1310+ name. clone ( ) ,
1311+ exports,
1312+ true ,
1313+ ) ;
12421314 }
12431315 }
12441316 }
@@ -1567,19 +1639,38 @@ var {} = {{}};
15671639
15681640 if let Ref :: Symbol ( symbol_binding) = & mut binding {
15691641 let module_id = symbol_binding. module ;
1570- let ref_chunk = Self :: get_module_chunk ( module_id, compilation) ;
1642+ let ref_module_chunk = Self :: get_module_chunk ( module_id, compilation) ;
15711643 let ref_external = concate_modules_map[ ref_module] . is_external ( ) ;
15721644
15731645 if from_other_chunk && !ref_external {
15741646 let exported = Self :: add_chunk_export (
1575- ref_chunk ,
1647+ ref_module_chunk ,
15761648 symbol_binding. symbol . clone ( ) ,
15771649 symbol_binding. symbol . clone ( ) ,
15781650 & mut exports,
15791651 false ,
15801652 ) ;
15811653
1582- symbol_binding. symbol = exported. expect ( "should have exported" ) ;
1654+ let mut exported = exported. expect ( "should have exported" ) ;
1655+
1656+ if ref_module_chunk != ref_chunk {
1657+ // special case
1658+ // const { foo, bar } = await import('./re-exports')
1659+ // there is a chance that foo is from another chunk, and bar is from re-exports chunk
1660+ // so should make sure foo is from another chunk
1661+ exported = Self :: add_chunk_re_export (
1662+ ref_chunk,
1663+ ref_module_chunk,
1664+ exported. clone ( ) ,
1665+ exported. clone ( ) ,
1666+ & mut exports,
1667+ false ,
1668+ )
1669+ . expect ( "should have name" )
1670+ . clone ( ) ;
1671+ }
1672+
1673+ symbol_binding. symbol = exported;
15831674 }
15841675 }
15851676
@@ -1788,34 +1879,39 @@ var {} = {{}};
17881879 }
17891880 }
17901881
1791- let exports = exports
1792- . get_mut ( & chunk_link. chunk )
1793- . expect ( "should have exports" ) ;
1794-
17951882 for ( key, locals) in removed_import_stmts {
17961883 chunk_link. raw_import_stmts . swap_remove ( & key) ;
17971884
17981885 // change it from normal export to re-export
17991886 for ( local, imported) in locals {
1887+ let chunk_exports = exports
1888+ . get_mut ( & chunk_link. chunk )
1889+ . expect ( "should have exports" ) ;
1890+
18001891 // remove local from exported names
1801- let Some ( export_names) = exports . exports . remove ( & local) else {
1892+ let Some ( export_names) = chunk_exports . exports . remove ( & local) else {
18021893 continue ;
18031894 } ;
18041895
18051896 // add local to re-export
18061897 for export_name in export_names {
1807- chunk_link. add_re_export_from_request ( key. 0 . clone ( ) , imported. clone ( ) , export_name) ;
1898+ Self :: add_re_export_from_request (
1899+ chunk_link. chunk ,
1900+ key. 0 . clone ( ) ,
1901+ imported. clone ( ) ,
1902+ export_name,
1903+ & mut exports,
1904+ ) ;
18081905 }
18091906 }
18101907 }
18111908 }
18121909
18131910 // put result into chunk_link context
18141911 for ( chunk, exports) in exports {
1815- * link
1816- . get_mut ( & chunk)
1817- . expect ( "should have chunk" )
1818- . exports_mut ( ) = exports. exports ;
1912+ let link = link. get_mut ( & chunk) . expect ( "should have chunk" ) ;
1913+ * link. exports_mut ( ) = exports. exports ;
1914+ * link. re_exports_mut ( ) = exports. re_exports ;
18191915 }
18201916 for ( chunk, imports) in imports {
18211917 link. get_mut ( & chunk) . expect ( "should have chunk" ) . imports = imports;
0 commit comments