1use std::{
2 hash::Hash,
3 ops::{Deref, DerefMut},
4};
5
6use anyhow::{Context, Result, bail};
7use bincode::{Decode, Encode};
8use either::Either;
9use indexmap::map::Entry;
10use roaring::RoaringBitmap;
11use rustc_hash::FxHashMap;
12use tracing::Instrument;
13use turbo_rcstr::RcStr;
14use turbo_tasks::{
15 FxIndexMap, FxIndexSet, NonLocalValue, ResolvedVc, TaskInput, TryJoinIterExt, ValueToString,
16 Vc, debug::ValueDebugFormat, trace::TraceRawVcs, turbofmt,
17};
18
19use crate::{
20 chunk::ChunkingType,
21 module::Module,
22 module_graph::{GraphTraversalAction, ModuleGraph, RefData},
23};
24
25#[derive(Clone, Debug, Default, PartialEq, TraceRawVcs, ValueDebugFormat, Encode, Decode)]
26#[repr(transparent)]
27pub struct RoaringBitmapWrapper(
28 #[turbo_tasks(trace_ignore)]
29 #[bincode(with_serde)]
30 pub RoaringBitmap,
31);
32
33impl TaskInput for RoaringBitmapWrapper {
34 fn is_transient(&self) -> bool {
35 false
36 }
37}
38
39impl RoaringBitmapWrapper {
40 pub fn is_proper_superset(&self, other: &Self) -> bool {
44 !self.is_subset(other)
45 }
46
47 pub fn into_inner(self) -> RoaringBitmap {
48 self.0
49 }
50}
51unsafe impl NonLocalValue for RoaringBitmapWrapper {}
52
53impl Eq for RoaringBitmapWrapper {}
57
58impl Deref for RoaringBitmapWrapper {
59 type Target = RoaringBitmap;
60 fn deref(&self) -> &Self::Target {
61 &self.0
62 }
63}
64impl DerefMut for RoaringBitmapWrapper {
65 fn deref_mut(&mut self) -> &mut Self::Target {
66 &mut self.0
67 }
68}
69impl Hash for RoaringBitmapWrapper {
70 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
71 struct HasherWriter<'a, H: std::hash::Hasher>(&'a mut H);
72 impl<H: std::hash::Hasher> std::io::Write for HasherWriter<'_, H> {
73 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
74 self.0.write(buf);
75 Ok(buf.len())
76 }
77 fn flush(&mut self) -> std::io::Result<()> {
78 Ok(())
79 }
80 }
81 self.0.serialize_into(HasherWriter(state)).unwrap();
82 }
83}
84
85#[turbo_tasks::value(transparent, cell = "keyed")]
86pub struct ModuleToChunkGroups(FxHashMap<ResolvedVc<Box<dyn Module>>, RoaringBitmapWrapper>);
87
88#[turbo_tasks::value]
89pub struct ChunkGroupInfo {
90 pub module_chunk_groups: ResolvedVc<ModuleToChunkGroups>,
91 #[turbo_tasks(trace_ignore)]
92 #[bincode(with = "turbo_bincode::indexset")]
93 pub chunk_groups: FxIndexSet<ChunkGroup>,
94 #[turbo_tasks(trace_ignore)]
95 #[bincode(with = "turbo_bincode::indexset")]
96 pub chunk_group_keys: FxIndexSet<ChunkGroupKey>,
97}
98
99#[turbo_tasks::value_impl]
100impl ChunkGroupInfo {
101 #[turbo_tasks::function]
102 pub fn module_chunk_groups(&self) -> Vc<ModuleToChunkGroups> {
103 *self.module_chunk_groups
104 }
105
106 #[turbo_tasks::function]
107 pub async fn get_index_of(&self, chunk_group: ChunkGroup) -> Result<Vc<usize>> {
108 if let Some(idx) = self.chunk_groups.get_index_of(&chunk_group) {
109 Ok(Vc::cell(idx))
110 } else {
111 if cfg!(debug_assertions) {
112 bail!(
113 "Couldn't find chunk group index for {} in {}",
114 chunk_group.debug_str(self).await?,
115 self.chunk_groups
116 .iter()
117 .map(|c| c.debug_str(self))
118 .try_join()
119 .await?
120 .join(", ")
121 );
122 } else {
123 bail!("Couldn't find chunk group index")
124 }
125 }
126 }
127}
128
129#[derive(
131 Debug, Clone, Hash, TaskInput, PartialEq, Eq, TraceRawVcs, NonLocalValue, Encode, Decode,
132)]
133pub enum ChunkGroupEntry {
134 Entry(Vec<ResolvedVc<Box<dyn Module>>>),
135 Async(ResolvedVc<Box<dyn Module>>),
136 Isolated(ResolvedVc<Box<dyn Module>>),
137 IsolatedMerged {
138 parent: Box<ChunkGroupEntry>,
139 merge_tag: RcStr,
140 entries: Vec<ResolvedVc<Box<dyn Module>>>,
141 },
142 Shared(ResolvedVc<Box<dyn Module>>),
143 SharedMultiple(Vec<ResolvedVc<Box<dyn Module>>>),
144 SharedMerged {
145 parent: Box<ChunkGroupEntry>,
146 merge_tag: RcStr,
147 entries: Vec<ResolvedVc<Box<dyn Module>>>,
148 },
149}
150impl ChunkGroupEntry {
151 pub fn entries(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn Module>>> + '_ {
152 match self {
153 Self::Async(e) | Self::Isolated(e) | Self::Shared(e) => {
154 Either::Left(std::iter::once(*e))
155 }
156 Self::Entry(entries)
157 | Self::IsolatedMerged { entries, .. }
158 | Self::SharedMultiple(entries)
159 | Self::SharedMerged { entries, .. } => Either::Right(entries.iter().copied()),
160 }
161 }
162}
163
164#[derive(Debug, Clone, Hash, TaskInput, PartialEq, Eq, TraceRawVcs, Encode, Decode)]
165pub enum ChunkGroup {
166 Entry(Vec<ResolvedVc<Box<dyn Module>>>),
169 Async(ResolvedVc<Box<dyn Module>>),
171 Isolated(ResolvedVc<Box<dyn Module>>),
174 IsolatedMerged {
177 parent: usize,
178 merge_tag: RcStr,
179 entries: Vec<ResolvedVc<Box<dyn Module>>>,
180 },
181 Shared(ResolvedVc<Box<dyn Module>>),
184 SharedMultiple(Vec<ResolvedVc<Box<dyn Module>>>),
186 SharedMerged {
189 parent: usize,
190 merge_tag: RcStr,
191 entries: Vec<ResolvedVc<Box<dyn Module>>>,
192 },
193}
194
195impl ChunkGroup {
196 pub fn get_merged_parent(&self) -> Option<usize> {
199 match self {
200 ChunkGroup::IsolatedMerged { parent, .. } | ChunkGroup::SharedMerged { parent, .. } => {
201 Some(*parent)
202 }
203 _ => None,
204 }
205 }
206
207 pub fn entries(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn Module>>> + Clone + '_ {
210 match self {
211 ChunkGroup::Async(e) | ChunkGroup::Isolated(e) | ChunkGroup::Shared(e) => {
212 Either::Left(std::iter::once(*e))
213 }
214 ChunkGroup::Entry(entries)
215 | ChunkGroup::IsolatedMerged { entries, .. }
216 | ChunkGroup::SharedMultiple(entries)
217 | ChunkGroup::SharedMerged { entries, .. } => Either::Right(entries.iter().copied()),
218 }
219 }
220
221 pub fn entries_count(&self) -> usize {
222 match self {
223 ChunkGroup::Async(_) | ChunkGroup::Isolated(_) | ChunkGroup::Shared(_) => 1,
224 ChunkGroup::Entry(entries)
225 | ChunkGroup::IsolatedMerged { entries, .. }
226 | ChunkGroup::SharedMultiple(entries)
227 | ChunkGroup::SharedMerged { entries, .. } => entries.len(),
228 }
229 }
230
231 pub async fn debug_str(&self, chunk_group_info: &ChunkGroupInfo) -> Result<String> {
232 Ok(match self {
233 ChunkGroup::Entry(entries) => format!(
234 "ChunkGroup::Entry({:?})",
235 entries
236 .iter()
237 .map(|m| m.ident().to_string())
238 .try_join()
239 .await?
240 ),
241 ChunkGroup::Async(entry) => turbofmt!("ChunkGroup::Async({:?})", entry.ident())
242 .await?
243 .to_string(),
244 ChunkGroup::Isolated(entry) => turbofmt!("ChunkGroup::Isolated({:?})", entry.ident())
245 .await?
246 .to_string(),
247 ChunkGroup::Shared(entry) => turbofmt!("ChunkGroup::Shared({:?})", entry.ident())
248 .await?
249 .to_string(),
250 ChunkGroup::SharedMultiple(entries) => format!(
251 "ChunkGroup::SharedMultiple({:?})",
252 entries
253 .iter()
254 .map(|m| m.ident().to_string())
255 .try_join()
256 .await?
257 ),
258 ChunkGroup::IsolatedMerged {
259 parent,
260 merge_tag,
261 entries,
262 } => {
263 format!(
264 "ChunkGroup::IsolatedMerged({}, {}, {:?})",
265 Box::pin(chunk_group_info.chunk_groups[*parent].debug_str(chunk_group_info))
266 .await?,
267 merge_tag,
268 entries
269 .iter()
270 .map(|m| m.ident().to_string())
271 .try_join()
272 .await?
273 )
274 }
275 ChunkGroup::SharedMerged {
276 parent,
277 merge_tag,
278 entries,
279 } => {
280 format!(
281 "ChunkGroup::SharedMerged({}, {}, {:?})",
282 Box::pin(chunk_group_info.chunk_groups[*parent].debug_str(chunk_group_info))
283 .await?,
284 merge_tag,
285 entries
286 .iter()
287 .map(|m| m.ident().to_string())
288 .try_join()
289 .await?
290 )
291 }
292 })
293 }
294}
295
296#[derive(Debug, Clone, Hash, PartialEq, Eq, Encode, Decode)]
298pub enum ChunkGroupKey {
299 Entry(Vec<ResolvedVc<Box<dyn Module>>>),
300 Async(ResolvedVc<Box<dyn Module>>),
301 Isolated(ResolvedVc<Box<dyn Module>>),
302 IsolatedMerged {
303 parent: ChunkGroupId,
304 merge_tag: RcStr,
305 },
306 Shared(ResolvedVc<Box<dyn Module>>),
307 SharedMultiple(Vec<ResolvedVc<Box<dyn Module>>>),
308 SharedMerged {
309 parent: ChunkGroupId,
310 merge_tag: RcStr,
311 },
312}
313
314impl ChunkGroupKey {
315 pub async fn debug_str(
316 &self,
317 keys: impl std::ops::Index<usize, Output = Self>,
318 ) -> Result<String> {
319 Ok(match self {
320 ChunkGroupKey::Entry(entries) => format!(
321 "Entry({:?})",
322 entries
323 .iter()
324 .map(|m| m.ident().to_string())
325 .try_join()
326 .await?
327 ),
328 ChunkGroupKey::Async(module) => {
329 turbofmt!("Async({:?})", module.ident()).await?.to_string()
330 }
331 ChunkGroupKey::Isolated(module) => turbofmt!("Isolated({:?})", module.ident())
332 .await?
333 .to_string(),
334 ChunkGroupKey::IsolatedMerged { parent, merge_tag } => {
335 format!(
336 "IsolatedMerged {{ parent: {}, merge_tag: {:?} }}",
337 Box::pin(keys.index(parent.0 as usize).clone().debug_str(keys)).await?,
338 merge_tag
339 )
340 }
341 ChunkGroupKey::Shared(module) => {
342 turbofmt!("Shared({:?})", module.ident()).await?.to_string()
343 }
344 ChunkGroupKey::SharedMultiple(entries) => format!(
345 "SharedMultiple({:?})",
346 entries
347 .iter()
348 .map(|m| m.ident().to_string())
349 .try_join()
350 .await?
351 ),
352 ChunkGroupKey::SharedMerged { parent, merge_tag } => {
353 format!(
354 "SharedMerged {{ parent: {}, merge_tag: {:?} }}",
355 Box::pin(keys.index(parent.0 as usize).clone().debug_str(keys)).await?,
356 merge_tag
357 )
358 }
359 })
360 }
361}
362
363#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encode, Decode)]
364pub struct ChunkGroupId(u32);
365
366impl From<usize> for ChunkGroupId {
367 fn from(id: usize) -> Self {
368 Self(id as u32)
369 }
370}
371
372impl Deref for ChunkGroupId {
373 type Target = u32;
374 fn deref(&self) -> &Self::Target {
375 &self.0
376 }
377}
378
379#[derive(Debug, Clone, PartialEq, Eq)]
380struct TraversalPriority {
381 depth: usize,
382 chunk_group_len: u64,
383}
384impl PartialOrd for TraversalPriority {
385 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
386 Some(self.cmp(other))
387 }
388}
389impl Ord for TraversalPriority {
390 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
391 let depth_order = self.depth.cmp(&other.depth).reverse();
395 let chunk_group_len_order = self.chunk_group_len.cmp(&other.chunk_group_len).reverse();
397
398 depth_order.then(chunk_group_len_order)
399 }
400}
401
402pub async fn compute_chunk_group_info(graph: &ModuleGraph) -> Result<Vc<ChunkGroupInfo>> {
403 let span_outer = tracing::info_span!(
404 "compute chunk group info",
405 module_count = tracing::field::Empty,
406 visit_count = tracing::field::Empty,
407 chunk_group_count = tracing::field::Empty
408 );
409
410 let span = span_outer.clone();
411 async move {
412 #[allow(clippy::type_complexity)]
413 let mut chunk_groups_map: FxIndexMap<
414 ChunkGroupKey,
415 (ChunkGroupId, FxIndexSet<ResolvedVc<Box<dyn Module>>>),
416 > = FxIndexMap::default();
417
418 let mut module_chunk_groups: FxHashMap<ResolvedVc<Box<dyn Module>>, RoaringBitmapWrapper> =
421 FxHashMap::default();
422
423 let module_count = graph
424 .graphs
425 .iter()
426 .map(|g| g.graph.node_count())
427 .sum::<usize>();
428 span.record("module_count", module_count);
429
430 let entries = graph
432 .graphs
433 .iter()
434 .flat_map(|g| g.entries.iter())
435 .collect::<Vec<_>>();
436
437 let module_depth: FxHashMap<ResolvedVc<Box<dyn Module>>, usize> = {
439 let mut module_depth =
440 FxHashMap::with_capacity_and_hasher(module_count, Default::default());
441 graph.traverse_edges_bfs(
442 entries.iter().flat_map(|e| e.entries()),
443 |parent, node| {
444 if let Some((parent, _)) = parent {
445 let parent_depth = *module_depth
446 .get(&parent)
447 .context("Module depth not found")?;
448 module_depth.entry(node).or_insert(parent_depth + 1);
449 } else {
450 module_depth.insert(node, 0);
451 };
452
453 module_chunk_groups.insert(node, RoaringBitmapWrapper::default());
454
455 Ok(GraphTraversalAction::Continue)
456 },
457 )?;
458 module_depth
459 };
460
461 #[allow(clippy::type_complexity)]
464 fn entry_to_chunk_group_id(
465 entry: ChunkGroupEntry,
466 chunk_groups_map: &mut FxIndexMap<
467 ChunkGroupKey,
468 (ChunkGroupId, FxIndexSet<ResolvedVc<Box<dyn Module>>>),
469 >,
470 ) -> ChunkGroupKey {
471 match entry {
472 ChunkGroupEntry::Entry(entries) => ChunkGroupKey::Entry(entries),
473 ChunkGroupEntry::Async(entry) => ChunkGroupKey::Async(entry),
474 ChunkGroupEntry::Isolated(entry) => ChunkGroupKey::Isolated(entry),
475 ChunkGroupEntry::Shared(entry) => ChunkGroupKey::Shared(entry),
476 ChunkGroupEntry::SharedMultiple(entries) => ChunkGroupKey::SharedMultiple(entries),
477 ChunkGroupEntry::IsolatedMerged {
478 parent,
479 merge_tag,
480 entries: _,
481 } => {
482 let parent = entry_to_chunk_group_id(*parent, chunk_groups_map);
483 let len = chunk_groups_map.len();
484 let parent = chunk_groups_map
485 .entry(parent)
486 .or_insert_with(|| (ChunkGroupId(len as u32), FxIndexSet::default()))
487 .0;
488
489 ChunkGroupKey::IsolatedMerged {
490 parent: ChunkGroupId(*parent),
491 merge_tag,
492 }
493 }
494 ChunkGroupEntry::SharedMerged {
495 parent,
496 merge_tag,
497 entries: _,
498 } => {
499 let parent = entry_to_chunk_group_id(*parent, chunk_groups_map);
500 let len = chunk_groups_map.len();
501 let parent = chunk_groups_map
502 .entry(parent)
503 .or_insert_with(|| (ChunkGroupId(len as u32), FxIndexSet::default()))
504 .0;
505
506 ChunkGroupKey::SharedMerged {
507 parent: ChunkGroupId(*parent),
508 merge_tag,
509 }
510 }
511 }
512 }
513
514 let entry_chunk_group_keys = graph
515 .graphs
516 .iter()
517 .flat_map(|g| g.entries.iter())
518 .flat_map(|chunk_group| {
519 let chunk_group_key =
520 entry_to_chunk_group_id(chunk_group.clone(), &mut chunk_groups_map);
521 chunk_group
522 .entries()
523 .map(move |e| (e, chunk_group_key.clone()))
524 })
525 .collect::<FxHashMap<_, _>>();
526
527 let visit_count = graph.traverse_edges_fixed_point_with_priority(
528 entries
529 .iter()
530 .flat_map(|e| e.entries())
531 .map(|e| {
532 Ok((
533 e,
534 TraversalPriority {
535 depth: *module_depth.get(&e).context("Module depth not found")?,
536 chunk_group_len: 0,
537 },
538 ))
539 })
540 .collect::<Result<Vec<_>>>()?,
541 &mut module_chunk_groups,
542 |parent_info: Option<(ResolvedVc<Box<dyn Module>>, &'_ RefData, _)>,
543 node: ResolvedVc<Box<dyn Module>>,
544 module_chunk_groups: &mut FxHashMap<
545 ResolvedVc<Box<dyn Module>>,
546 RoaringBitmapWrapper,
547 >|
548 -> Result<GraphTraversalAction> {
549 enum ChunkGroupInheritance<It: Iterator<Item = ChunkGroupKey>> {
550 Inherit(ResolvedVc<Box<dyn Module>>),
551 ChunkGroup(It),
552 }
553 let chunk_groups = if let Some((parent, ref_data, _)) = parent_info {
554 match &ref_data.chunking_type {
555 ChunkingType::Parallel { .. } => ChunkGroupInheritance::Inherit(parent),
556 ChunkingType::Async => ChunkGroupInheritance::ChunkGroup(Either::Left(
557 std::iter::once(ChunkGroupKey::Async(node)),
558 )),
559 ChunkingType::Isolated {
560 merge_tag: None, ..
561 } => ChunkGroupInheritance::ChunkGroup(Either::Left(std::iter::once(
562 ChunkGroupKey::Isolated(node),
563 ))),
564 ChunkingType::Shared {
565 merge_tag: None, ..
566 } => ChunkGroupInheritance::ChunkGroup(Either::Left(std::iter::once(
567 ChunkGroupKey::Shared(node),
568 ))),
569 ChunkingType::Isolated {
570 merge_tag: Some(merge_tag),
571 ..
572 } => {
573 let parents = module_chunk_groups
574 .get(&parent)
575 .context("Module chunk group not found")?;
576 let chunk_groups =
577 parents.iter().map(|parent| ChunkGroupKey::IsolatedMerged {
578 parent: ChunkGroupId(parent),
579 merge_tag: merge_tag.clone(),
580 });
581 ChunkGroupInheritance::ChunkGroup(Either::Right(Either::Left(
582 chunk_groups,
583 )))
584 }
585 ChunkingType::Shared {
586 merge_tag: Some(merge_tag),
587 ..
588 } => {
589 let parents = module_chunk_groups
590 .get(&parent)
591 .context("Module chunk group not found")?;
592 let chunk_groups =
593 parents.iter().map(|parent| ChunkGroupKey::SharedMerged {
594 parent: ChunkGroupId(parent),
595 merge_tag: merge_tag.clone(),
596 });
597 ChunkGroupInheritance::ChunkGroup(Either::Right(Either::Right(
598 chunk_groups,
599 )))
600 }
601 ChunkingType::Traced => {
602 return Ok(GraphTraversalAction::Skip);
604 }
605 }
606 } else {
607 ChunkGroupInheritance::ChunkGroup(Either::Left(std::iter::once(
608 entry_chunk_group_keys
610 .get(&node)
611 .context("Module chunk group not found")?
612 .clone(),
613 )))
614 };
615
616 Ok(match chunk_groups {
617 ChunkGroupInheritance::ChunkGroup(chunk_groups) => {
618 let chunk_group_ids = chunk_groups.map(|chunk_group| {
620 let len = chunk_groups_map.len();
621 let is_merged = matches!(
622 chunk_group,
623 ChunkGroupKey::IsolatedMerged { .. }
624 | ChunkGroupKey::SharedMerged { .. }
625 );
626 match chunk_groups_map.entry(chunk_group) {
627 Entry::Occupied(mut e) => {
628 let (id, merged_entries) = e.get_mut();
629 if is_merged {
630 merged_entries.insert(node);
631 }
632 **id
633 }
634 Entry::Vacant(e) => {
635 let chunk_group_id = len as u32;
636 let mut set = FxIndexSet::default();
637 if is_merged {
638 set.insert(node);
639 }
640 e.insert((ChunkGroupId(chunk_group_id), set));
641 chunk_group_id
642 }
643 }
644 });
645
646 let chunk_groups =
647 RoaringBitmapWrapper(RoaringBitmap::from_iter(chunk_group_ids));
648
649 let bitset = module_chunk_groups
651 .get_mut(&node)
652 .context("Module chunk group not found")?;
653 if chunk_groups.is_proper_superset(bitset) {
654 **bitset |= chunk_groups.into_inner();
656
657 GraphTraversalAction::Continue
658 } else {
659 GraphTraversalAction::Skip
661 }
662 }
663 ChunkGroupInheritance::Inherit(parent) => {
664 if parent == node {
668 GraphTraversalAction::Skip
670 } else {
671 let [Some(parent_chunk_groups), Some(current_chunk_groups)] =
672 module_chunk_groups.get_disjoint_mut([&parent, &node])
673 else {
674 bail!("Module chunk groups not found");
678 };
679
680 if current_chunk_groups.is_empty() {
681 *current_chunk_groups = parent_chunk_groups.clone();
683 GraphTraversalAction::Continue
684 } else if parent_chunk_groups.is_proper_superset(current_chunk_groups) {
685 **current_chunk_groups |= &**parent_chunk_groups;
687 GraphTraversalAction::Continue
688 } else {
689 GraphTraversalAction::Skip
691 }
692 }
693 }
694 })
695 },
696 |successor, module_chunk_groups| {
704 Ok(TraversalPriority {
705 depth: *module_depth
706 .get(&successor)
707 .context("Module depth not found")?,
708 chunk_group_len: module_chunk_groups
709 .get(&successor)
710 .context("Module chunk group not found")?
711 .len(),
712 })
713 },
714 )?;
715
716 span.record("visit_count", visit_count);
717 span.record("chunk_group_count", chunk_groups_map.len());
718
719 #[cfg(debug_assertions)]
720 {
721 use std::sync::LazyLock;
722 static PRINT_CHUNK_GROUP_INFO: LazyLock<bool> =
723 LazyLock::new(|| match std::env::var_os("TURBOPACK_PRINT_CHUNK_GROUPS") {
724 Some(v) => v == "1",
725 None => false,
726 });
727 if *PRINT_CHUNK_GROUP_INFO {
728 use std::{
729 collections::{BTreeMap, BTreeSet},
730 path::absolute,
731 };
732
733 let mut buckets = BTreeMap::default();
734 for (module, key) in &module_chunk_groups {
735 if !key.is_empty() {
736 buckets
737 .entry(key.iter().collect::<Vec<_>>())
738 .or_insert(BTreeSet::new())
739 .insert(module.ident().to_string().await?);
740 }
741 }
742
743 let mut result = vec![];
744 result.push("Chunk Groups:".to_string());
745 for (i, (key, _)) in chunk_groups_map.iter().enumerate() {
746 result.push(format!(
747 " {:?}: {}",
748 i,
749 key.debug_str(chunk_groups_map.keys()).await?
750 ));
751 }
752 result.push("# Module buckets:".to_string());
753 for (key, modules) in buckets.iter() {
754 result.push(format!("## {:?}:", key.iter().collect::<Vec<_>>()));
755 for module in modules {
756 result.push(format!(" {module}"));
757 }
758 result.push("".to_string());
759 }
760 let f = absolute("chunk_group_info.log")?;
761 println!("written to {}", f.display());
762 std::fs::write(f, result.join("\n"))?;
763 }
764 }
765
766 Ok(ChunkGroupInfo {
767 module_chunk_groups: ResolvedVc::cell(module_chunk_groups),
768 chunk_group_keys: chunk_groups_map.keys().cloned().collect(),
769 chunk_groups: chunk_groups_map
770 .into_iter()
771 .map(|(k, (_, merged_entries))| match k {
772 ChunkGroupKey::Entry(entries) => ChunkGroup::Entry(entries),
773 ChunkGroupKey::Async(module) => ChunkGroup::Async(module),
774 ChunkGroupKey::Isolated(module) => ChunkGroup::Isolated(module),
775 ChunkGroupKey::IsolatedMerged { parent, merge_tag } => {
776 ChunkGroup::IsolatedMerged {
777 parent: parent.0 as usize,
778 merge_tag,
779 entries: merged_entries.into_iter().collect(),
780 }
781 }
782 ChunkGroupKey::Shared(module) => ChunkGroup::Shared(module),
783 ChunkGroupKey::SharedMultiple(entries) => ChunkGroup::SharedMultiple(entries),
784 ChunkGroupKey::SharedMerged { parent, merge_tag } => ChunkGroup::SharedMerged {
785 parent: parent.0 as usize,
786 merge_tag,
787 entries: merged_entries.into_iter().collect(),
788 },
789 })
790 .collect(),
791 }
792 .cell())
793 }
794 .instrument(span_outer)
795 .await
796}