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 bail!(
112 "Couldn't find chunk group index for {} in {}",
113 chunk_group.debug_str(self).await?,
114 self.chunk_groups
115 .iter()
116 .map(|c| c.debug_str(self))
117 .try_join()
118 .await?
119 .join(", ")
120 );
121 }
122 }
123}
124
125#[derive(
127 Debug, Clone, Hash, TaskInput, PartialEq, Eq, TraceRawVcs, NonLocalValue, Encode, Decode,
128)]
129pub enum ChunkGroupEntry {
130 Entry(Vec<ResolvedVc<Box<dyn Module>>>),
131 Async(ResolvedVc<Box<dyn Module>>),
132 Isolated(ResolvedVc<Box<dyn Module>>),
133 IsolatedMerged {
134 parent: Box<ChunkGroupEntry>,
135 merge_tag: RcStr,
136 entries: Vec<ResolvedVc<Box<dyn Module>>>,
137 },
138 Shared(ResolvedVc<Box<dyn Module>>),
139 SharedMultiple(Vec<ResolvedVc<Box<dyn Module>>>),
140 SharedMerged {
141 parent: Box<ChunkGroupEntry>,
142 merge_tag: RcStr,
143 entries: Vec<ResolvedVc<Box<dyn Module>>>,
144 },
145}
146impl ChunkGroupEntry {
147 pub fn entries(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn Module>>> + '_ {
148 match self {
149 Self::Async(e) | Self::Isolated(e) | Self::Shared(e) => {
150 Either::Left(std::iter::once(*e))
151 }
152 Self::Entry(entries)
153 | Self::IsolatedMerged { entries, .. }
154 | Self::SharedMultiple(entries)
155 | Self::SharedMerged { entries, .. } => Either::Right(entries.iter().copied()),
156 }
157 }
158}
159
160#[derive(Debug, Clone, Hash, TaskInput, PartialEq, Eq, TraceRawVcs, Encode, Decode)]
161pub enum ChunkGroup {
162 Entry(Vec<ResolvedVc<Box<dyn Module>>>),
165 Async(ResolvedVc<Box<dyn Module>>),
167 Isolated(ResolvedVc<Box<dyn Module>>),
170 IsolatedMerged {
173 parent: usize,
174 merge_tag: RcStr,
175 entries: Vec<ResolvedVc<Box<dyn Module>>>,
176 },
177 Shared(ResolvedVc<Box<dyn Module>>),
180 SharedMultiple(Vec<ResolvedVc<Box<dyn Module>>>),
182 SharedMerged {
185 parent: usize,
186 merge_tag: RcStr,
187 entries: Vec<ResolvedVc<Box<dyn Module>>>,
188 },
189}
190
191impl ChunkGroup {
192 pub fn get_merged_parent(&self) -> Option<usize> {
195 match self {
196 ChunkGroup::IsolatedMerged { parent, .. } | ChunkGroup::SharedMerged { parent, .. } => {
197 Some(*parent)
198 }
199 _ => None,
200 }
201 }
202
203 pub fn entries(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn Module>>> + Clone + '_ {
206 match self {
207 ChunkGroup::Async(e) | ChunkGroup::Isolated(e) | ChunkGroup::Shared(e) => {
208 Either::Left(std::iter::once(*e))
209 }
210 ChunkGroup::Entry(entries)
211 | ChunkGroup::IsolatedMerged { entries, .. }
212 | ChunkGroup::SharedMultiple(entries)
213 | ChunkGroup::SharedMerged { entries, .. } => Either::Right(entries.iter().copied()),
214 }
215 }
216
217 pub fn entries_count(&self) -> usize {
218 match self {
219 ChunkGroup::Async(_) | ChunkGroup::Isolated(_) | ChunkGroup::Shared(_) => 1,
220 ChunkGroup::Entry(entries)
221 | ChunkGroup::IsolatedMerged { entries, .. }
222 | ChunkGroup::SharedMultiple(entries)
223 | ChunkGroup::SharedMerged { entries, .. } => entries.len(),
224 }
225 }
226
227 pub async fn debug_str(&self, chunk_group_info: &ChunkGroupInfo) -> Result<String> {
228 Ok(match self {
229 ChunkGroup::Entry(entries) => format!(
230 "ChunkGroup::Entry({:?})",
231 entries
232 .iter()
233 .map(|m| m.ident().to_string())
234 .try_join()
235 .await?
236 ),
237 ChunkGroup::Async(entry) => turbofmt!("ChunkGroup::Async({:?})", entry.ident())
238 .await?
239 .to_string(),
240 ChunkGroup::Isolated(entry) => turbofmt!("ChunkGroup::Isolated({:?})", entry.ident())
241 .await?
242 .to_string(),
243 ChunkGroup::Shared(entry) => turbofmt!("ChunkGroup::Shared({:?})", entry.ident())
244 .await?
245 .to_string(),
246 ChunkGroup::SharedMultiple(entries) => format!(
247 "ChunkGroup::SharedMultiple({:?})",
248 entries
249 .iter()
250 .map(|m| m.ident().to_string())
251 .try_join()
252 .await?
253 ),
254 ChunkGroup::IsolatedMerged {
255 parent,
256 merge_tag,
257 entries,
258 } => {
259 format!(
260 "ChunkGroup::IsolatedMerged({}, {}, {:?})",
261 Box::pin(chunk_group_info.chunk_groups[*parent].debug_str(chunk_group_info))
262 .await?,
263 merge_tag,
264 entries
265 .iter()
266 .map(|m| m.ident().to_string())
267 .try_join()
268 .await?
269 )
270 }
271 ChunkGroup::SharedMerged {
272 parent,
273 merge_tag,
274 entries,
275 } => {
276 format!(
277 "ChunkGroup::SharedMerged({}, {}, {:?})",
278 Box::pin(chunk_group_info.chunk_groups[*parent].debug_str(chunk_group_info))
279 .await?,
280 merge_tag,
281 entries
282 .iter()
283 .map(|m| m.ident().to_string())
284 .try_join()
285 .await?
286 )
287 }
288 })
289 }
290}
291
292#[derive(Debug, Clone, Hash, PartialEq, Eq, Encode, Decode)]
294pub enum ChunkGroupKey {
295 Entry(Vec<ResolvedVc<Box<dyn Module>>>),
296 Async(ResolvedVc<Box<dyn Module>>),
297 Isolated(ResolvedVc<Box<dyn Module>>),
298 IsolatedMerged {
299 parent: ChunkGroupId,
300 merge_tag: RcStr,
301 },
302 Shared(ResolvedVc<Box<dyn Module>>),
303 SharedMultiple(Vec<ResolvedVc<Box<dyn Module>>>),
304 SharedMerged {
305 parent: ChunkGroupId,
306 merge_tag: RcStr,
307 },
308}
309
310impl ChunkGroupKey {
311 pub async fn debug_str(
312 &self,
313 keys: impl std::ops::Index<usize, Output = Self>,
314 ) -> Result<String> {
315 Ok(match self {
316 ChunkGroupKey::Entry(entries) => format!(
317 "Entry({:?})",
318 entries
319 .iter()
320 .map(|m| m.ident().to_string())
321 .try_join()
322 .await?
323 ),
324 ChunkGroupKey::Async(module) => {
325 turbofmt!("Async({:?})", module.ident()).await?.to_string()
326 }
327 ChunkGroupKey::Isolated(module) => turbofmt!("Isolated({:?})", module.ident())
328 .await?
329 .to_string(),
330 ChunkGroupKey::IsolatedMerged { parent, merge_tag } => {
331 format!(
332 "IsolatedMerged {{ parent: {}, merge_tag: {:?} }}",
333 Box::pin(keys.index(parent.0 as usize).clone().debug_str(keys)).await?,
334 merge_tag
335 )
336 }
337 ChunkGroupKey::Shared(module) => {
338 turbofmt!("Shared({:?})", module.ident()).await?.to_string()
339 }
340 ChunkGroupKey::SharedMultiple(entries) => format!(
341 "SharedMultiple({:?})",
342 entries
343 .iter()
344 .map(|m| m.ident().to_string())
345 .try_join()
346 .await?
347 ),
348 ChunkGroupKey::SharedMerged { parent, merge_tag } => {
349 format!(
350 "SharedMerged {{ parent: {}, merge_tag: {:?} }}",
351 Box::pin(keys.index(parent.0 as usize).clone().debug_str(keys)).await?,
352 merge_tag
353 )
354 }
355 })
356 }
357}
358
359#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encode, Decode)]
360pub struct ChunkGroupId(u32);
361
362impl From<usize> for ChunkGroupId {
363 fn from(id: usize) -> Self {
364 Self(id as u32)
365 }
366}
367
368impl Deref for ChunkGroupId {
369 type Target = u32;
370 fn deref(&self) -> &Self::Target {
371 &self.0
372 }
373}
374
375#[derive(Debug, Clone, PartialEq, Eq)]
376struct TraversalPriority {
377 depth: usize,
378 chunk_group_len: u64,
379}
380impl PartialOrd for TraversalPriority {
381 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
382 Some(self.cmp(other))
383 }
384}
385impl Ord for TraversalPriority {
386 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
387 let depth_order = self.depth.cmp(&other.depth).reverse();
391 let chunk_group_len_order = self.chunk_group_len.cmp(&other.chunk_group_len).reverse();
393
394 depth_order.then(chunk_group_len_order)
395 }
396}
397
398pub async fn compute_chunk_group_info(graph: &ModuleGraph) -> Result<Vc<ChunkGroupInfo>> {
399 let span_outer = tracing::info_span!(
400 "compute chunk group info",
401 module_count = tracing::field::Empty,
402 visit_count = tracing::field::Empty,
403 chunk_group_count = tracing::field::Empty
404 );
405
406 let span = span_outer.clone();
407 async move {
408 #[allow(clippy::type_complexity)]
409 let mut chunk_groups_map: FxIndexMap<
410 ChunkGroupKey,
411 (ChunkGroupId, FxIndexSet<ResolvedVc<Box<dyn Module>>>),
412 > = FxIndexMap::default();
413
414 let mut module_chunk_groups: FxHashMap<ResolvedVc<Box<dyn Module>>, RoaringBitmapWrapper> =
417 FxHashMap::default();
418
419 let module_count = graph
420 .graphs
421 .iter()
422 .map(|g| g.graph.node_count())
423 .sum::<usize>();
424 span.record("module_count", module_count);
425
426 let entries = graph
428 .graphs
429 .iter()
430 .flat_map(|g| g.entries.iter())
431 .collect::<Vec<_>>();
432
433 let module_depth: FxHashMap<ResolvedVc<Box<dyn Module>>, usize> = {
435 let mut module_depth =
436 FxHashMap::with_capacity_and_hasher(module_count, Default::default());
437 graph.traverse_edges_bfs(
438 entries.iter().flat_map(|e| e.entries()),
439 |parent, node| {
440 if let Some((parent, _)) = parent {
441 let parent_depth = *module_depth
442 .get(&parent)
443 .context("Module depth not found")?;
444 module_depth.entry(node).or_insert(parent_depth + 1);
445 } else {
446 module_depth.insert(node, 0);
447 };
448
449 module_chunk_groups.insert(node, RoaringBitmapWrapper::default());
450
451 Ok(GraphTraversalAction::Continue)
452 },
453 )?;
454 module_depth
455 };
456
457 #[allow(clippy::type_complexity)]
460 fn entry_to_chunk_group_id(
461 entry: ChunkGroupEntry,
462 chunk_groups_map: &mut FxIndexMap<
463 ChunkGroupKey,
464 (ChunkGroupId, FxIndexSet<ResolvedVc<Box<dyn Module>>>),
465 >,
466 ) -> ChunkGroupKey {
467 match entry {
468 ChunkGroupEntry::Entry(entries) => ChunkGroupKey::Entry(entries),
469 ChunkGroupEntry::Async(entry) => ChunkGroupKey::Async(entry),
470 ChunkGroupEntry::Isolated(entry) => ChunkGroupKey::Isolated(entry),
471 ChunkGroupEntry::Shared(entry) => ChunkGroupKey::Shared(entry),
472 ChunkGroupEntry::SharedMultiple(entries) => ChunkGroupKey::SharedMultiple(entries),
473 ChunkGroupEntry::IsolatedMerged {
474 parent,
475 merge_tag,
476 entries: _,
477 } => {
478 let parent = entry_to_chunk_group_id(*parent, chunk_groups_map);
479 let len = chunk_groups_map.len();
480 let parent = chunk_groups_map
481 .entry(parent)
482 .or_insert_with(|| (ChunkGroupId(len as u32), FxIndexSet::default()))
483 .0;
484
485 ChunkGroupKey::IsolatedMerged {
486 parent: ChunkGroupId(*parent as u32),
487 merge_tag,
488 }
489 }
490 ChunkGroupEntry::SharedMerged {
491 parent,
492 merge_tag,
493 entries: _,
494 } => {
495 let parent = entry_to_chunk_group_id(*parent, chunk_groups_map);
496 let len = chunk_groups_map.len();
497 let parent = chunk_groups_map
498 .entry(parent)
499 .or_insert_with(|| (ChunkGroupId(len as u32), FxIndexSet::default()))
500 .0;
501
502 ChunkGroupKey::SharedMerged {
503 parent: ChunkGroupId(*parent as u32),
504 merge_tag,
505 }
506 }
507 }
508 }
509
510 let entry_chunk_group_keys = graph
511 .graphs
512 .iter()
513 .flat_map(|g| g.entries.iter())
514 .flat_map(|chunk_group| {
515 let chunk_group_key =
516 entry_to_chunk_group_id(chunk_group.clone(), &mut chunk_groups_map);
517 chunk_group
518 .entries()
519 .map(move |e| (e, chunk_group_key.clone()))
520 })
521 .collect::<FxHashMap<_, _>>();
522
523 let visit_count = graph.traverse_edges_fixed_point_with_priority(
524 entries
525 .iter()
526 .flat_map(|e| e.entries())
527 .map(|e| {
528 Ok((
529 e,
530 TraversalPriority {
531 depth: *module_depth.get(&e).context("Module depth not found")?,
532 chunk_group_len: 0,
533 },
534 ))
535 })
536 .collect::<Result<Vec<_>>>()?,
537 &mut module_chunk_groups,
538 |parent_info: Option<(ResolvedVc<Box<dyn Module>>, &'_ RefData, _)>,
539 node: ResolvedVc<Box<dyn Module>>,
540 module_chunk_groups: &mut FxHashMap<
541 ResolvedVc<Box<dyn Module>>,
542 RoaringBitmapWrapper,
543 >|
544 -> Result<GraphTraversalAction> {
545 enum ChunkGroupInheritance<It: Iterator<Item = ChunkGroupKey>> {
546 Inherit(ResolvedVc<Box<dyn Module>>),
547 ChunkGroup(It),
548 }
549 let chunk_groups = if let Some((parent, ref_data, _)) = parent_info {
550 match &ref_data.chunking_type {
551 ChunkingType::Parallel { .. } => ChunkGroupInheritance::Inherit(parent),
552 ChunkingType::Async => ChunkGroupInheritance::ChunkGroup(Either::Left(
553 std::iter::once(ChunkGroupKey::Async(node)),
554 )),
555 ChunkingType::Isolated {
556 merge_tag: None, ..
557 } => ChunkGroupInheritance::ChunkGroup(Either::Left(std::iter::once(
558 ChunkGroupKey::Isolated(node),
559 ))),
560 ChunkingType::Shared {
561 merge_tag: None, ..
562 } => ChunkGroupInheritance::ChunkGroup(Either::Left(std::iter::once(
563 ChunkGroupKey::Shared(node),
564 ))),
565 ChunkingType::Isolated {
566 merge_tag: Some(merge_tag),
567 ..
568 } => {
569 let parents = module_chunk_groups
570 .get(&parent)
571 .context("Module chunk group not found")?;
572 let chunk_groups =
573 parents.iter().map(|parent| ChunkGroupKey::IsolatedMerged {
574 parent: ChunkGroupId(parent),
575 merge_tag: merge_tag.clone(),
576 });
577 ChunkGroupInheritance::ChunkGroup(Either::Right(Either::Left(
578 chunk_groups,
579 )))
580 }
581 ChunkingType::Shared {
582 merge_tag: Some(merge_tag),
583 ..
584 } => {
585 let parents = module_chunk_groups
586 .get(&parent)
587 .context("Module chunk group not found")?;
588 let chunk_groups =
589 parents.iter().map(|parent| ChunkGroupKey::SharedMerged {
590 parent: ChunkGroupId(parent),
591 merge_tag: merge_tag.clone(),
592 });
593 ChunkGroupInheritance::ChunkGroup(Either::Right(Either::Right(
594 chunk_groups,
595 )))
596 }
597 ChunkingType::Traced => {
598 return Ok(GraphTraversalAction::Skip);
600 }
601 }
602 } else {
603 ChunkGroupInheritance::ChunkGroup(Either::Left(std::iter::once(
604 entry_chunk_group_keys
606 .get(&node)
607 .context("Module chunk group not found")?
608 .clone(),
609 )))
610 };
611
612 Ok(match chunk_groups {
613 ChunkGroupInheritance::ChunkGroup(chunk_groups) => {
614 let chunk_group_ids = chunk_groups.map(|chunk_group| {
616 let len = chunk_groups_map.len();
617 let is_merged = matches!(
618 chunk_group,
619 ChunkGroupKey::IsolatedMerged { .. }
620 | ChunkGroupKey::SharedMerged { .. }
621 );
622 match chunk_groups_map.entry(chunk_group) {
623 Entry::Occupied(mut e) => {
624 let (id, merged_entries) = e.get_mut();
625 if is_merged {
626 merged_entries.insert(node);
627 }
628 **id
629 }
630 Entry::Vacant(e) => {
631 let chunk_group_id = len as u32;
632 let mut set = FxIndexSet::default();
633 if is_merged {
634 set.insert(node);
635 }
636 e.insert((ChunkGroupId(chunk_group_id), set));
637 chunk_group_id
638 }
639 }
640 });
641
642 let chunk_groups =
643 RoaringBitmapWrapper(RoaringBitmap::from_iter(chunk_group_ids));
644
645 let bitset = module_chunk_groups
647 .get_mut(&node)
648 .context("Module chunk group not found")?;
649 if chunk_groups.is_proper_superset(bitset) {
650 **bitset |= chunk_groups.into_inner();
652
653 GraphTraversalAction::Continue
654 } else {
655 GraphTraversalAction::Skip
657 }
658 }
659 ChunkGroupInheritance::Inherit(parent) => {
660 if parent == node {
664 GraphTraversalAction::Skip
666 } else {
667 let [Some(parent_chunk_groups), Some(current_chunk_groups)] =
668 module_chunk_groups.get_disjoint_mut([&parent, &node])
669 else {
670 bail!("Module chunk groups not found");
674 };
675
676 if current_chunk_groups.is_empty() {
677 *current_chunk_groups = parent_chunk_groups.clone();
679 GraphTraversalAction::Continue
680 } else if parent_chunk_groups.is_proper_superset(current_chunk_groups) {
681 **current_chunk_groups |= &**parent_chunk_groups;
683 GraphTraversalAction::Continue
684 } else {
685 GraphTraversalAction::Skip
687 }
688 }
689 }
690 })
691 },
692 |successor, module_chunk_groups| {
700 Ok(TraversalPriority {
701 depth: *module_depth
702 .get(&successor)
703 .context("Module depth not found")?,
704 chunk_group_len: module_chunk_groups
705 .get(&successor)
706 .context("Module chunk group not found")?
707 .len(),
708 })
709 },
710 )?;
711
712 span.record("visit_count", visit_count);
713 span.record("chunk_group_count", chunk_groups_map.len());
714
715 #[cfg(debug_assertions)]
716 {
717 use once_cell::sync::Lazy;
718 static PRINT_CHUNK_GROUP_INFO: Lazy<bool> =
719 Lazy::new(|| match std::env::var_os("TURBOPACK_PRINT_CHUNK_GROUPS") {
720 Some(v) => v == "1",
721 None => false,
722 });
723 if *PRINT_CHUNK_GROUP_INFO {
724 use std::{
725 collections::{BTreeMap, BTreeSet},
726 path::absolute,
727 };
728
729 let mut buckets = BTreeMap::default();
730 for (module, key) in &module_chunk_groups {
731 if !key.is_empty() {
732 buckets
733 .entry(key.iter().collect::<Vec<_>>())
734 .or_insert(BTreeSet::new())
735 .insert(module.ident().to_string().await?);
736 }
737 }
738
739 let mut result = vec![];
740 result.push("Chunk Groups:".to_string());
741 for (i, (key, _)) in chunk_groups_map.iter().enumerate() {
742 result.push(format!(
743 " {:?}: {}",
744 i,
745 key.debug_str(chunk_groups_map.keys()).await?
746 ));
747 }
748 result.push("# Module buckets:".to_string());
749 for (key, modules) in buckets.iter() {
750 result.push(format!("## {:?}:", key.iter().collect::<Vec<_>>()));
751 for module in modules {
752 result.push(format!(" {module}"));
753 }
754 result.push("".to_string());
755 }
756 let f = absolute("chunk_group_info.log")?;
757 println!("written to {}", f.display());
758 std::fs::write(f, result.join("\n"))?;
759 }
760 }
761
762 Ok(ChunkGroupInfo {
763 module_chunk_groups: ResolvedVc::cell(module_chunk_groups),
764 chunk_group_keys: chunk_groups_map.keys().cloned().collect(),
765 chunk_groups: chunk_groups_map
766 .into_iter()
767 .map(|(k, (_, merged_entries))| match k {
768 ChunkGroupKey::Entry(entries) => ChunkGroup::Entry(entries),
769 ChunkGroupKey::Async(module) => ChunkGroup::Async(module),
770 ChunkGroupKey::Isolated(module) => ChunkGroup::Isolated(module),
771 ChunkGroupKey::IsolatedMerged { parent, merge_tag } => {
772 ChunkGroup::IsolatedMerged {
773 parent: parent.0 as usize,
774 merge_tag,
775 entries: merged_entries.into_iter().collect(),
776 }
777 }
778 ChunkGroupKey::Shared(module) => ChunkGroup::Shared(module),
779 ChunkGroupKey::SharedMultiple(entries) => ChunkGroup::SharedMultiple(entries),
780 ChunkGroupKey::SharedMerged { parent, merge_tag } => ChunkGroup::SharedMerged {
781 parent: parent.0 as usize,
782 merge_tag,
783 entries: merged_entries.into_iter().collect(),
784 },
785 })
786 .collect(),
787 }
788 .cell())
789 }
790 .instrument(span_outer)
791 .await
792}