@@ -74,6 +74,72 @@ enum ExternalImportBinding {
7474}
7575
7676impl EsmLibraryPlugin {
77+ fn module_external_fragment_content (
78+ init_fragment : Box < dyn rspack_core:: InitFragment < ChunkRenderContext > > ,
79+ ) -> Option < String > {
80+ if !matches ! ( init_fragment. key( ) , InitFragmentKey :: ModuleExternal ( _) ) {
81+ return None ;
82+ }
83+
84+ if let Ok ( fragment) = init_fragment
85+ . clone ( )
86+ . into_any ( )
87+ . downcast :: < ConditionalInitFragment > ( )
88+ {
89+ Some ( fragment. content ( ) . to_owned ( ) )
90+ } else {
91+ init_fragment
92+ . clone ( )
93+ . contents ( & mut ChunkRenderContext { } )
94+ . ok ( )
95+ . map ( |contents| contents. start )
96+ }
97+ }
98+
99+ fn collect_module_external_fragments_in_render_order < ' a > (
100+ init_fragment_groups : impl IntoIterator < Item = & ' a ChunkInitFragments > ,
101+ ) -> Vec < Box < dyn rspack_core:: InitFragment < ChunkRenderContext > > > {
102+ let mut ordered_fragments = Vec :: new ( ) ;
103+
104+ for init_fragments in init_fragment_groups {
105+ for init_fragment in init_fragments {
106+ if !matches ! ( init_fragment. key( ) , InitFragmentKey :: ModuleExternal ( _) ) {
107+ continue ;
108+ }
109+
110+ ordered_fragments. push ( (
111+ init_fragment. stage ( ) ,
112+ init_fragment. position ( ) ,
113+ ordered_fragments. len ( ) ,
114+ init_fragment. key ( ) . clone ( ) ,
115+ init_fragment. clone ( ) ,
116+ ) ) ;
117+ }
118+ }
119+
120+ ordered_fragments. sort_by ( |a, b| {
121+ let stage = a. 0 . cmp ( & b. 0 ) ;
122+ if !stage. is_eq ( ) {
123+ return stage;
124+ }
125+ let position = a. 1 . cmp ( & b. 1 ) ;
126+ if !position. is_eq ( ) {
127+ return position;
128+ }
129+ a. 2 . cmp ( & b. 2 )
130+ } ) ;
131+
132+ let mut rendered_keys = FxHashSet :: default ( ) ;
133+ let mut rendered_fragments = Vec :: with_capacity ( ordered_fragments. len ( ) ) ;
134+ for ( _, _, _, key, init_fragment) in ordered_fragments {
135+ if rendered_keys. insert ( key) {
136+ rendered_fragments. push ( init_fragment) ;
137+ }
138+ }
139+
140+ rendered_fragments
141+ }
142+
77143 fn parse_module_external_namespace_import ( content : & str ) -> Option < ( RawImportSource , Atom ) > {
78144 let content = content. trim_start ( ) ;
79145 let content = content. strip_prefix ( "import * as " ) ?;
@@ -119,54 +185,10 @@ impl EsmLibraryPlugin {
119185 fn collect_module_external_namespace_imports_in_render_order < ' a > (
120186 init_fragment_groups : impl IntoIterator < Item = & ' a ChunkInitFragments > ,
121187 ) -> Vec < ( RawImportSource , Atom ) > {
122- let mut ordered_imports = Vec :: new ( ) ;
123-
124- for init_fragments in init_fragment_groups {
125- for init_fragment in init_fragments {
126- if !matches ! ( init_fragment. key( ) , InitFragmentKey :: ModuleExternal ( _) ) {
127- continue ;
128- }
129-
130- let content = if let Ok ( fragment) = init_fragment
131- . clone ( )
132- . into_any ( )
133- . downcast :: < ConditionalInitFragment > ( )
134- {
135- fragment. content ( ) . to_owned ( )
136- } else {
137- let Ok ( contents) = init_fragment. clone ( ) . contents ( & mut ChunkRenderContext { } ) else {
138- continue ;
139- } ;
140- contents. start
141- } ;
142-
143- if let Some ( ( source, local_name) ) = Self :: parse_module_external_namespace_import ( & content) {
144- ordered_imports. push ( (
145- init_fragment. stage ( ) ,
146- init_fragment. position ( ) ,
147- ordered_imports. len ( ) ,
148- source,
149- local_name,
150- ) ) ;
151- }
152- }
153- }
154-
155- ordered_imports. sort_by ( |a, b| {
156- let stage = a. 0 . cmp ( & b. 0 ) ;
157- if !stage. is_eq ( ) {
158- return stage;
159- }
160- let position = a. 1 . cmp ( & b. 1 ) ;
161- if !position. is_eq ( ) {
162- return position;
163- }
164- a. 2 . cmp ( & b. 2 )
165- } ) ;
166-
167- ordered_imports
188+ Self :: collect_module_external_fragments_in_render_order ( init_fragment_groups)
168189 . into_iter ( )
169- . map ( |( _, _, _, source, local_name) | ( source, local_name) )
190+ . filter_map ( Self :: module_external_fragment_content)
191+ . filter_map ( |content| Self :: parse_module_external_namespace_import ( & content) )
170192 . collect ( )
171193 }
172194
@@ -191,6 +213,25 @@ impl EsmLibraryPlugin {
191213 }
192214 }
193215
216+ #[ cfg( test) ]
217+ fn reserve_module_external_top_level_decls (
218+ init_fragments : & ChunkInitFragments ,
219+ used_names : & mut FxHashSet < Atom > ,
220+ ) {
221+ Self :: reserve_module_external_top_level_decls_in_render_order ( [ init_fragments] , used_names) ;
222+ }
223+
224+ fn reserve_module_external_top_level_decls_in_render_order < ' a > (
225+ init_fragment_groups : impl IntoIterator < Item = & ' a ChunkInitFragments > ,
226+ used_names : & mut FxHashSet < Atom > ,
227+ ) {
228+ for init_fragment in
229+ Self :: collect_module_external_fragments_in_render_order ( init_fragment_groups)
230+ {
231+ used_names. extend ( init_fragment. top_level_decl_symbols ( ) . iter ( ) . cloned ( ) ) ;
232+ }
233+ }
234+
194235 fn assign_external_candidate_name (
195236 readable_identifier : & str ,
196237 candidate_used_names : & mut FxHashSet < Atom > ,
@@ -971,10 +1012,14 @@ var {} = {{}};
9711012 }
9721013 }
9731014 Self :: reserve_module_external_namespace_import_locals_in_render_order (
974- module_external_init_fragment_groups,
1015+ module_external_init_fragment_groups. iter ( ) . copied ( ) ,
9751016 & mut all_used_names,
9761017 Some ( & mut chunk_link. module_external_namespace_imports ) ,
9771018 ) ;
1019+ Self :: reserve_module_external_top_level_decls_in_render_order (
1020+ module_external_init_fragment_groups. iter ( ) . copied ( ) ,
1021+ & mut all_used_names,
1022+ ) ;
9781023
9791024 // deconflict top level symbols
9801025 for id in chunk_link
@@ -3572,6 +3617,95 @@ mod tests {
35723617 assert ! ( chunk_used_names. is_empty( ) ) ;
35733618 }
35743619
3620+ #[ test]
3621+ fn module_external_non_namespace_init_fragment_claims_top_level_decls ( ) {
3622+ let init_fragments: ChunkInitFragments = vec ! [ Box :: new( rspack_core:: NormalInitFragment :: new(
3623+ "import { createRequire as __rspack_createRequire } from \" node:module\" ;\n const __rspack_createRequire_require = __rspack_createRequire(import.meta.url);\n "
3624+ . into( ) ,
3625+ rspack_core:: InitFragmentStage :: StageESMImports ,
3626+ 0 ,
3627+ InitFragmentKey :: ModuleExternal ( "node-commonjs" . into( ) ) ,
3628+ None ,
3629+ )
3630+ . with_top_level_decl_symbols( vec![
3631+ "__rspack_createRequire" . into( ) ,
3632+ "__rspack_createRequire_require" . into( ) ,
3633+ ] ) ) ] ;
3634+ let mut chunk_used_names = FxHashSet :: default ( ) ;
3635+
3636+ EsmLibraryPlugin :: reserve_module_external_top_level_decls (
3637+ & init_fragments,
3638+ & mut chunk_used_names,
3639+ ) ;
3640+
3641+ assert ! ( chunk_used_names. contains( & Atom :: from( "__rspack_createRequire" ) ) ) ;
3642+ assert ! ( chunk_used_names. contains( & Atom :: from( "__rspack_createRequire_require" ) ) ) ;
3643+ }
3644+
3645+ #[ test]
3646+ fn module_external_top_level_decls_keep_first_rendered_fragment ( ) {
3647+ let init_fragments: ChunkInitFragments = vec ! [
3648+ Box :: new( rspack_core:: NormalInitFragment :: new(
3649+ "import { createRequire as __rspack_createRequire_1 } from \" node:module\" ;\n const __rspack_createRequire_require_1 = __rspack_createRequire_1(import.meta.url);\n "
3650+ . into( ) ,
3651+ rspack_core:: InitFragmentStage :: StageESMImports ,
3652+ 1 ,
3653+ InitFragmentKey :: ModuleExternal ( "node-commonjs" . into( ) ) ,
3654+ None ,
3655+ )
3656+ . with_top_level_decl_symbols( vec![
3657+ "__rspack_createRequire_1" . into( ) ,
3658+ "__rspack_createRequire_require_1" . into( ) ,
3659+ ] ) ) ,
3660+ Box :: new( rspack_core:: NormalInitFragment :: new(
3661+ "import { createRequire as __rspack_createRequire_0 } from \" node:module\" ;\n const __rspack_createRequire_require_0 = __rspack_createRequire_0(import.meta.url);\n "
3662+ . into( ) ,
3663+ rspack_core:: InitFragmentStage :: StageESMImports ,
3664+ 0 ,
3665+ InitFragmentKey :: ModuleExternal ( "node-commonjs" . into( ) ) ,
3666+ None ,
3667+ )
3668+ . with_top_level_decl_symbols( vec![
3669+ "__rspack_createRequire_0" . into( ) ,
3670+ "__rspack_createRequire_require_0" . into( ) ,
3671+ ] ) ) ,
3672+ ] ;
3673+ let mut chunk_used_names = FxHashSet :: default ( ) ;
3674+
3675+ EsmLibraryPlugin :: reserve_module_external_top_level_decls (
3676+ & init_fragments,
3677+ & mut chunk_used_names,
3678+ ) ;
3679+
3680+ assert ! ( chunk_used_names. contains( & Atom :: from( "__rspack_createRequire_0" ) ) ) ;
3681+ assert ! ( chunk_used_names. contains( & Atom :: from( "__rspack_createRequire_require_0" ) ) ) ;
3682+ assert ! ( !chunk_used_names. contains( & Atom :: from( "__rspack_createRequire_1" ) ) ) ;
3683+ assert ! ( !chunk_used_names. contains( & Atom :: from( "__rspack_createRequire_require_1" ) ) ) ;
3684+ }
3685+
3686+ #[ test]
3687+ fn module_external_var_init_fragment_claims_top_level_decl ( ) {
3688+ let init_fragments: ChunkInitFragments = vec ! [ Box :: new(
3689+ rspack_core:: NormalInitFragment :: new(
3690+ "/* provided dependency */ var provided_identifier = __webpack_require__(\" ./dep\" );\n "
3691+ . into( ) ,
3692+ rspack_core:: InitFragmentStage :: StageProvides ,
3693+ 1 ,
3694+ InitFragmentKey :: ModuleExternal ( "provided provided_identifier" . into( ) ) ,
3695+ None ,
3696+ )
3697+ . with_top_level_decl_symbols( vec![ "provided_identifier" . into( ) ] ) ,
3698+ ) ] ;
3699+ let mut chunk_used_names = FxHashSet :: default ( ) ;
3700+
3701+ EsmLibraryPlugin :: reserve_module_external_top_level_decls (
3702+ & init_fragments,
3703+ & mut chunk_used_names,
3704+ ) ;
3705+
3706+ assert ! ( chunk_used_names. contains( & Atom :: from( "provided_identifier" ) ) ) ;
3707+ }
3708+
35753709 #[ test]
35763710 fn external_candidate_name_does_not_claim_chunk_top_level_name ( ) {
35773711 let module = ModuleIdentifier :: from ( "test_module" ) ;
0 commit comments