@@ -48,77 +48,88 @@ pub(crate) fn extract_tla_shared_modules(compilation: &mut Compilation) -> bool
4848 return false ;
4949 }
5050
51- // 2. For each async chunk, find ancestor chunks and scan for cross-chunk deps.
52- // Collect modules to extract keyed by module id → set of source chunks they
53- // need to be removed from. This ensures each module is only extracted once
54- // even if it appears in multiple ancestor chunks.
55- let mut modules_to_extract : IdentifierMap < FxHashSet < ChunkUkey > > = IdentifierMap :: default ( ) ;
56-
57- for async_chunk_ukey in & async_chunks {
58- // Get all ancestor chunk group ukeys
51+ // 2. Pre-compute: for each async chunk, its ancestor chunks; also build a
52+ // reverse lookup from chunk → which async chunks consider it an ancestor.
53+ // Then do a single BFS across all async chunks sharing one visited set.
54+ let mut chunk_to_ancestor_of : FxHashMap < ChunkUkey , Vec < ChunkUkey > > = FxHashMap :: default ( ) ;
55+ let mut module_to_async_chunk : IdentifierMap < ChunkUkey > = IdentifierMap :: default ( ) ;
56+ let mut queue : VecDeque < ModuleIdentifier > = VecDeque :: new ( ) ;
57+
58+ for & async_chunk_ukey in & async_chunks {
5959 let chunk = compilation
6060 . build_chunk_graph_artifact
6161 . chunk_by_ukey
62- . expect_get ( async_chunk_ukey) ;
62+ . expect_get ( & async_chunk_ukey) ;
6363 let mut ancestor_group_ukeys = FxHashSet :: default ( ) ;
6464 for group_ukey in chunk. groups ( ) {
6565 let group = chunk_group_by_ukey. expect_get ( group_ukey) ;
6666 ancestor_group_ukeys. extend ( group. ancestors ( chunk_group_by_ukey) ) ;
6767 }
68+ for g in & ancestor_group_ukeys {
69+ for & ancestor_chunk in & chunk_group_by_ukey. expect_get ( g) . chunks {
70+ chunk_to_ancestor_of
71+ . entry ( ancestor_chunk)
72+ . or_default ( )
73+ . push ( async_chunk_ukey) ;
74+ }
75+ }
6876
69- // Collect ancestor chunk ukeys from ancestor groups
70- let ancestor_chunks: FxHashSet < ChunkUkey > = ancestor_group_ukeys
71- . iter ( )
72- . flat_map ( |g| chunk_group_by_ukey. expect_get ( g) . chunks . iter ( ) . copied ( ) )
73- . collect ( ) ;
77+ // Seed BFS with all modules in this async chunk
78+ for & m in chunk_graph. get_chunk_modules_identifier ( & async_chunk_ukey) {
79+ module_to_async_chunk. insert ( m, async_chunk_ukey) ;
80+ queue. push_back ( m) ;
81+ }
82+ }
83+
84+ // Single BFS across all async chunks with a shared visited set — O(N + E).
85+ // Each module is visited at most once. When a target is in an ancestor chunk
86+ // of the originating async chunk, we mark it for extraction.
87+ let mut modules_to_extract: IdentifierMap < FxHashSet < ChunkUkey > > = IdentifierMap :: default ( ) ;
88+ let mut visited = IdentifierSet :: default ( ) ;
7489
75- if ancestor_chunks. is_empty ( ) {
90+ while let Some ( module_id) = queue. pop_front ( ) {
91+ if !visited. insert ( module_id) {
7692 continue ;
7793 }
7894
79- let chunk_modules = chunk_graph. get_chunk_modules_identifier ( async_chunk_ukey) ;
80-
81- // BFS through all outgoing dependencies of modules in async chunk
82- let mut visited = IdentifierSet :: default ( ) ;
83- let mut queue: VecDeque < ModuleIdentifier > = chunk_modules. iter ( ) . copied ( ) . collect ( ) ;
95+ // Determine which async chunk this module belongs to
96+ let Some ( & origin_async_chunk) = module_to_async_chunk. get ( & module_id) else {
97+ continue ;
98+ } ;
8499
85- while let Some ( module_id) = queue. pop_front ( ) {
86- if !visited. insert ( module_id) {
100+ for conn in module_graph. get_outgoing_connections ( & module_id) {
101+ if !conn. is_target_active (
102+ module_graph,
103+ None ,
104+ & compilation. module_graph_cache_artifact ,
105+ & compilation. exports_info_artifact ,
106+ ) {
87107 continue ;
88108 }
89109
90- for conn in module_graph. get_outgoing_connections ( & module_id) {
91- // Skip inactive connections
92- if !conn. is_target_active (
93- module_graph,
94- None ,
95- & compilation. module_graph_cache_artifact ,
96- & compilation. exports_info_artifact ,
97- ) {
98- continue ;
99- }
100-
101- let Some ( target) = module_graph. module_identifier_by_dependency_id ( & conn. dependency_id )
102- else {
103- continue ;
104- } ;
110+ let Some ( target) = module_graph. module_identifier_by_dependency_id ( & conn. dependency_id )
111+ else {
112+ continue ;
113+ } ;
105114
106- let target_chunks = chunk_graph. get_module_chunks ( * target) ;
115+ let target_chunks = chunk_graph. get_module_chunks ( * target) ;
107116
108- // Check if target is in an ancestor chunk → circular dep
109- for & target_chunk in target_chunks {
110- if ancestor_chunks. contains ( & target_chunk) {
117+ // Check if target is in an ancestor chunk of origin_async_chunk → circular dep
118+ for & target_chunk in target_chunks {
119+ if let Some ( ancestor_of) = chunk_to_ancestor_of. get ( & target_chunk) {
120+ if ancestor_of. contains ( & origin_async_chunk) {
111121 modules_to_extract
112122 . entry ( * target)
113123 . or_default ( )
114124 . insert ( target_chunk) ;
115125 }
116126 }
127+ }
117128
118- // Continue BFS if target is in the same async chunk
119- if target_chunks. contains ( async_chunk_ukey ) {
120- queue . push_back ( * target) ;
121- }
129+ // Continue BFS if target is in the same async chunk
130+ if target_chunks. contains ( & origin_async_chunk ) {
131+ module_to_async_chunk . insert ( * target, origin_async_chunk ) ;
132+ queue . push_back ( * target ) ;
122133 }
123134 }
124135 }
0 commit comments