11use std:: {
22 collections:: { self } ,
33 hash:: BuildHasher ,
4- sync:: Arc ,
4+ sync:: { Arc , LazyLock } ,
55} ;
66
77use rayon:: prelude:: * ;
@@ -11,18 +11,19 @@ use rspack_collections::{
1111use rspack_core:: {
1212 BuildMetaDefaultObject , BuildMetaExportsType , ChunkGraph , ChunkInitFragments , ChunkUkey ,
1313 CodeGenerationPublicPathAutoReplace , Compilation , ConcatenatedModuleIdent , DependencyType ,
14- ExportMode , ExportProvided , ExportsInfoGetter , ExportsType , FindTargetResult , GetUsedNameParam ,
15- IdentCollector , InitFragmentKey , InitFragmentStage , MaybeDynamicTargetExportInfoHashKey ,
16- ModuleGraph , ModuleGraphCacheArtifact , ModuleIdentifier , ModuleInfo , NAMESPACE_OBJECT_EXPORT ,
17- NormalInitFragment , PathData , PrefetchExportsInfoMode , RuntimeGlobals , SourceType , URLStaticMode ,
18- UsageState , UsedName , UsedNameItem , escape_name, find_new_name, get_cached_readable_identifier,
19- get_js_chunk_filename_template, property_access, property_name, reserved_names:: RESERVED_NAMES ,
20- returning_function, rspack_sources:: ReplaceSource , split_readable_identifier, to_normal_comment,
14+ ExportMode , ExportProvided , ExportsInfoGetter , ExportsType , ExternalModule , FindTargetResult ,
15+ GetUsedNameParam , IdentCollector , InitFragmentKey , InitFragmentStage ,
16+ MaybeDynamicTargetExportInfoHashKey , ModuleGraph , ModuleGraphCacheArtifact , ModuleIdentifier ,
17+ ModuleInfo , NAMESPACE_OBJECT_EXPORT , NormalInitFragment , PathData , PrefetchExportsInfoMode ,
18+ RuntimeGlobals , SourceType , URLStaticMode , UsageState , UsedName , UsedNameItem , escape_name,
19+ find_new_name, get_cached_readable_identifier, get_js_chunk_filename_template, property_access,
20+ property_name, reserved_names:: RESERVED_NAMES , returning_function, rspack_sources:: ReplaceSource ,
21+ split_readable_identifier, to_normal_comment,
2122} ;
2223use rspack_error:: { Diagnostic , Result } ;
2324use rspack_javascript_compiler:: ast:: Ast ;
2425use rspack_plugin_javascript:: {
25- JsPlugin , RenderSource , dependency:: ESMExportImportedSpecifierDependency ,
26+ JS_DEFAULT_KEYWORD , JsPlugin , RenderSource , dependency:: ESMExportImportedSpecifierDependency ,
2627 visitors:: swc_visitor:: resolver,
2728} ;
2829use rspack_util:: {
@@ -63,6 +64,8 @@ impl<V> GetMut<ModuleIdentifier, V> for IdentifierIndexMap<V> {
6364 }
6465}
6566
67+ static START_EXPORTS : LazyLock < Atom > = LazyLock :: new ( || "*" . into ( ) ) ;
68+
6669#[ derive( Default ) ]
6770struct ExportsContext {
6871 pub exports : FxHashMap < Atom , FxIndexSet < Atom > > ,
@@ -1056,6 +1059,22 @@ impl EsmLibraryPlugin {
10561059 continue ;
10571060 }
10581061
1062+ if export_info. is_reexport ( )
1063+ && let Some ( ( Some ( dep) , _) ) = export_info. target ( ) . iter ( ) . next ( )
1064+ {
1065+ let module_id = module_graph
1066+ . module_identifier_by_dependency_id ( dep)
1067+ . expect ( "should be ESMExportImportedSpecifierDependency" ) ;
1068+ let module = module_graph
1069+ . module_by_identifier ( module_id)
1070+ . expect ( "should have module" ) ;
1071+ if module. as_external_module ( ) . is_some ( ) {
1072+ // ignore re-exported symbol from external module
1073+ // we have special handle for external module re-exports
1074+ continue ;
1075+ }
1076+ }
1077+
10591078 let local = match export_info. used_name ( ) {
10601079 Some ( UsedNameItem :: Inlined ( inlined) ) => inlined. render ( ) . into ( ) ,
10611080 Some ( UsedNameItem :: Str ( name) ) => {
@@ -1091,6 +1110,83 @@ impl EsmLibraryPlugin {
10911110 errors
10921111 }
10931112
1113+ fn re_export_from_external_module (
1114+ module : & ExternalModule ,
1115+ current_chunk : ChunkUkey ,
1116+ mode : & ExportMode ,
1117+ link : & mut UkeyMap < ChunkUkey , ChunkLinkContext > ,
1118+ export_dep : & ESMExportImportedSpecifierDependency ,
1119+ ) {
1120+ match mode {
1121+ // render export * from 'external module'
1122+ ExportMode :: DynamicReexport ( _) | ExportMode :: EmptyStar ( _) => {
1123+ let chunk_link = link. get_mut_unwrap ( & current_chunk) ;
1124+ chunk_link
1125+ . raw_star_exports
1126+ . entry ( export_dep. request . to_string ( ) )
1127+ . or_default ( )
1128+ . insert ( START_EXPORTS . clone ( ) ) ;
1129+ }
1130+
1131+ ExportMode :: Unused ( mode) if mode. name == "*" => {
1132+ let chunk_link = link. get_mut_unwrap ( & current_chunk) ;
1133+ chunk_link
1134+ . raw_star_exports
1135+ . entry ( export_dep. request . to_string ( ) )
1136+ . or_default ( )
1137+ . insert ( START_EXPORTS . clone ( ) ) ;
1138+ }
1139+
1140+ ExportMode :: ReexportUndefined ( _)
1141+ | ExportMode :: Missing
1142+ | ExportMode :: LazyMake
1143+ | ExportMode :: Unused ( _) => { }
1144+
1145+ ExportMode :: ReexportDynamicDefault ( _) => {
1146+ let chunk_link = link. get_mut_unwrap ( & current_chunk) ;
1147+ chunk_link. add_re_export_from_request (
1148+ module. user_request ( ) . to_string ( ) ,
1149+ JS_DEFAULT_KEYWORD . clone ( ) ,
1150+ JS_DEFAULT_KEYWORD . clone ( ) ,
1151+ ) ;
1152+ }
1153+ ExportMode :: ReexportNamedDefault ( mode) => {
1154+ let chunk_link = link. get_mut_unwrap ( & current_chunk) ;
1155+ chunk_link. add_re_export_from_request (
1156+ module. user_request ( ) . to_string ( ) ,
1157+ JS_DEFAULT_KEYWORD . clone ( ) ,
1158+ mode. name . clone ( ) ,
1159+ ) ;
1160+ }
1161+ ExportMode :: ReexportNamespaceObject ( mode) => {
1162+ let chunk_link = link. get_mut_unwrap ( & current_chunk) ;
1163+ chunk_link
1164+ . raw_star_exports
1165+ . entry ( module. user_request ( ) . to_string ( ) )
1166+ . or_default ( )
1167+ . insert ( mode. name . clone ( ) ) ;
1168+ }
1169+ ExportMode :: ReexportFakeNamespaceObject ( mode) => {
1170+ let chunk_link = link. get_mut_unwrap ( & current_chunk) ;
1171+ chunk_link
1172+ . raw_star_exports
1173+ . entry ( module. user_request ( ) . to_string ( ) )
1174+ . or_default ( )
1175+ . insert ( mode. name . clone ( ) ) ;
1176+ }
1177+ ExportMode :: NormalReexport ( normal) => {
1178+ let chunk_link = link. get_mut_unwrap ( & current_chunk) ;
1179+ for item in & normal. items {
1180+ chunk_link. add_re_export_from_request (
1181+ module. user_request ( ) . into ( ) ,
1182+ item. ids . first ( ) . unwrap_or ( & item. name ) . clone ( ) ,
1183+ item. name . clone ( ) ,
1184+ ) ;
1185+ }
1186+ }
1187+ }
1188+ }
1189+
10941190 // export * from 'target'
10951191 // export * as n from 'target'
10961192 #[ allow( clippy:: too_many_arguments) ]
@@ -1119,8 +1215,15 @@ impl EsmLibraryPlugin {
11191215 . and_then ( |dep| dep. downcast_ref :: < ESMExportImportedSpecifierDependency > ( ) )
11201216 . and_then ( |dep| {
11211217 module_graph
1122- . get_module_by_dependency_id ( & dep. id )
1123- . map ( |module| ( dep, module) )
1218+ . connection_by_dependency_id ( & dep. id )
1219+ . map ( |conn| {
1220+ (
1221+ dep,
1222+ module_graph
1223+ . module_by_identifier ( conn. module_identifier ( ) )
1224+ . expect ( "should have module" ) ,
1225+ )
1226+ } )
11241227 } )
11251228 } )
11261229 . map ( |( export_imported_dep, module) | {
@@ -1148,33 +1251,28 @@ impl EsmLibraryPlugin {
11481251 for ( export_dep, re_exports) in deps_and_modes {
11491252 // reset ref_module for each dep
11501253 let ref_module = orig_ref_module;
1151- // optimize `export * from 'external module'`
1152- let possible_to_optimize_mode = matches ! (
1153- & re_exports,
1154- ExportMode :: DynamicReexport ( _) | ExportMode :: EmptyStar ( _)
1155- ) || matches ! ( & re_exports, ExportMode :: Unused ( mode) if & mode. name == "*" ) ;
11561254
11571255 let ref_box_module = module_graph
11581256 . module_by_identifier ( & ref_module)
11591257 . expect ( "should have mode" ) ;
11601258
1161- let optimize_reexport_star = possible_to_optimize_mode
1162- && export_dep. name . is_none ( )
1163- && ref_box_module
1164- . as_external_module ( )
1165- . is_some_and ( |m| matches ! ( m. get_external_type( ) . as_str( ) , "module-import" | "module" ) ) ;
1166-
1167- if optimize_reexport_star {
1168- // render export * from 'external module'
1169- let chunk_link = link. get_mut_unwrap ( & current_chunk) ;
1170- chunk_link
1171- . raw_star_exports
1172- . insert ( export_dep. request . to_string ( ) ) ;
1259+ if let Some ( external_module) = ref_box_module. as_external_module ( )
1260+ && matches ! (
1261+ external_module. get_external_type( ) . as_str( ) ,
1262+ "module-import" | "module"
1263+ )
1264+ {
1265+ Self :: re_export_from_external_module (
1266+ external_module,
1267+ current_chunk,
1268+ & re_exports,
1269+ link,
1270+ export_dep,
1271+ ) ;
11731272 continue ;
11741273 }
11751274
11761275 let chunk_link = link. get_mut_unwrap ( & current_chunk) ;
1177-
11781276 match re_exports {
11791277 rspack_core:: ExportMode :: Missing
11801278 | rspack_core:: ExportMode :: LazyMake
@@ -1388,6 +1486,27 @@ impl EsmLibraryPlugin {
13881486 export_info = target_export_info;
13891487 }
13901488
1489+ if ref_module != orig_ref_module
1490+ && let Some ( external_module) = module_graph
1491+ . module_by_identifier ( & ref_module)
1492+ . expect ( "should have module" )
1493+ . as_external_module ( )
1494+ && matches ! (
1495+ external_module. get_external_type( ) . as_str( ) ,
1496+ "module-import" | "module"
1497+ )
1498+ {
1499+ // handle external module
1500+ Self :: re_export_from_external_module (
1501+ external_module,
1502+ current_chunk,
1503+ & rspack_core:: ExportMode :: NormalReexport ( mode. clone ( ) ) ,
1504+ link,
1505+ export_dep,
1506+ ) ;
1507+ continue ;
1508+ }
1509+
13911510 let chunk_link = link. get_mut_unwrap ( & current_chunk) ;
13921511
13931512 let used_name = if unknown_export_info {
@@ -1974,7 +2093,7 @@ impl EsmLibraryPlugin {
19742093 we can remove the empty raw_import if there is star reexport.
19752094 */
19762095 for chunk_link in link. values_mut ( ) {
1977- for source in & chunk_link. raw_star_exports {
2096+ for ( source, _ ) in & chunk_link. raw_star_exports {
19782097 let key = ( source. clone ( ) , None ) ;
19792098 if let Some ( import_spec) = chunk_link. raw_import_stmts . get ( & key)
19802099 && import_spec. atoms . is_empty ( )
0 commit comments