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#[turbo_tasks::task_input]
131#[derive(Debug, Clone, Hash, PartialEq, Eq, TraceRawVcs, Encode, Decode)]
132pub enum ChunkGroupEntry {
133 Entry(Vec<ResolvedVc<Box<dyn Module>>>),
134 Async(ResolvedVc<Box<dyn Module>>),
135 Isolated(ResolvedVc<Box<dyn Module>>),
136 IsolatedMerged {
137 parent: Box<ChunkGroupEntry>,
138 merge_tag: RcStr,
139 entries: Vec<ResolvedVc<Box<dyn Module>>>,
140 },
141 Shared(ResolvedVc<Box<dyn Module>>),
142 SharedMultiple(Vec<ResolvedVc<Box<dyn Module>>>),
143 SharedMerged {
144 parent: Box<ChunkGroupEntry>,
145 merge_tag: RcStr,
146 entries: Vec<ResolvedVc<Box<dyn Module>>>,
147 },
148}
149impl ChunkGroupEntry {
150 pub fn entries(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn Module>>> + '_ {
151 match self {
152 Self::Async(e) | Self::Isolated(e) | Self::Shared(e) => {
153 Either::Left(std::iter::once(*e))
154 }
155 Self::Entry(entries)
156 | Self::IsolatedMerged { entries, .. }
157 | Self::SharedMultiple(entries)
158 | Self::SharedMerged { entries, .. } => Either::Right(entries.iter().copied()),
159 }
160 }
161}
162
163#[turbo_tasks::task_input]
164#[derive(Debug, Clone, Hash, 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.all_chunk_group_entries().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),
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),
504 merge_tag,
505 }
506 }
507 }
508 }
509
510 let entry_chunk_group_keys = entries
511 .iter()
512 .flat_map(|&chunk_group| {
513 let chunk_group_key =
514 entry_to_chunk_group_id(chunk_group.clone(), &mut chunk_groups_map);
515 chunk_group
516 .entries()
517 .map(move |e| (e, chunk_group_key.clone()))
518 })
519 .collect::<FxHashMap<_, _>>();
520
521 let visit_count = graph.traverse_edges_fixed_point_with_priority(
522 entries
523 .iter()
524 .flat_map(|e| e.entries())
525 .map(|e| {
526 Ok((
527 e,
528 TraversalPriority {
529 depth: *module_depth.get(&e).context("Module depth not found")?,
530 chunk_group_len: 0,
531 },
532 ))
533 })
534 .collect::<Result<Vec<_>>>()?,
535 &mut module_chunk_groups,
536 |parent_info: Option<(ResolvedVc<Box<dyn Module>>, &'_ RefData, _)>,
537 node: ResolvedVc<Box<dyn Module>>,
538 module_chunk_groups: &mut FxHashMap<
539 ResolvedVc<Box<dyn Module>>,
540 RoaringBitmapWrapper,
541 >|
542 -> Result<GraphTraversalAction> {
543 enum ChunkGroupInheritance<It: Iterator<Item = ChunkGroupKey>> {
544 Inherit(ResolvedVc<Box<dyn Module>>),
545 ChunkGroup(It),
546 }
547 let chunk_groups = if let Some((parent, ref_data, _)) = parent_info {
548 match &ref_data.chunking_type {
549 ChunkingType::Parallel { .. } => ChunkGroupInheritance::Inherit(parent),
550 ChunkingType::Async => ChunkGroupInheritance::ChunkGroup(Either::Left(
551 std::iter::once(ChunkGroupKey::Async(node)),
552 )),
553 ChunkingType::Isolated {
554 merge_tag: None, ..
555 } => ChunkGroupInheritance::ChunkGroup(Either::Left(std::iter::once(
556 ChunkGroupKey::Isolated(node),
557 ))),
558 ChunkingType::Shared {
559 merge_tag: None, ..
560 } => ChunkGroupInheritance::ChunkGroup(Either::Left(std::iter::once(
561 ChunkGroupKey::Shared(node),
562 ))),
563 ChunkingType::Isolated {
564 merge_tag: Some(merge_tag),
565 ..
566 } => {
567 let parents = module_chunk_groups
568 .get(&parent)
569 .context("Module chunk group not found")?;
570 let chunk_groups =
571 parents.iter().map(|parent| ChunkGroupKey::IsolatedMerged {
572 parent: ChunkGroupId(parent),
573 merge_tag: merge_tag.clone(),
574 });
575 ChunkGroupInheritance::ChunkGroup(Either::Right(Either::Left(
576 chunk_groups,
577 )))
578 }
579 ChunkingType::Shared {
580 merge_tag: Some(merge_tag),
581 ..
582 } => {
583 let parents = module_chunk_groups
584 .get(&parent)
585 .context("Module chunk group not found")?;
586 let chunk_groups =
587 parents.iter().map(|parent| ChunkGroupKey::SharedMerged {
588 parent: ChunkGroupId(parent),
589 merge_tag: merge_tag.clone(),
590 });
591 ChunkGroupInheritance::ChunkGroup(Either::Right(Either::Right(
592 chunk_groups,
593 )))
594 }
595 ChunkingType::Traced { .. } => {
596 return Ok(GraphTraversalAction::Skip);
598 }
599 }
600 } else {
601 ChunkGroupInheritance::ChunkGroup(Either::Left(std::iter::once(
602 entry_chunk_group_keys
604 .get(&node)
605 .context("Module chunk group not found")?
606 .clone(),
607 )))
608 };
609
610 Ok(match chunk_groups {
611 ChunkGroupInheritance::ChunkGroup(chunk_groups) => {
612 let chunk_group_ids = chunk_groups.map(|chunk_group| {
614 let len = chunk_groups_map.len();
615 let is_merged = matches!(
616 chunk_group,
617 ChunkGroupKey::IsolatedMerged { .. }
618 | ChunkGroupKey::SharedMerged { .. }
619 );
620 match chunk_groups_map.entry(chunk_group) {
621 Entry::Occupied(mut e) => {
622 let (id, merged_entries) = e.get_mut();
623 if is_merged {
624 merged_entries.insert(node);
625 }
626 **id
627 }
628 Entry::Vacant(e) => {
629 let chunk_group_id = len as u32;
630 let mut set = FxIndexSet::default();
631 if is_merged {
632 set.insert(node);
633 }
634 e.insert((ChunkGroupId(chunk_group_id), set));
635 chunk_group_id
636 }
637 }
638 });
639
640 let chunk_groups =
641 RoaringBitmapWrapper(RoaringBitmap::from_iter(chunk_group_ids));
642
643 let bitset = module_chunk_groups
645 .get_mut(&node)
646 .context("Module chunk group not found")?;
647 if chunk_groups.is_proper_superset(bitset) {
648 **bitset |= chunk_groups.into_inner();
650
651 GraphTraversalAction::Continue
652 } else {
653 GraphTraversalAction::Skip
655 }
656 }
657 ChunkGroupInheritance::Inherit(parent) => {
658 if parent == node {
662 GraphTraversalAction::Skip
664 } else {
665 let [Some(parent_chunk_groups), Some(current_chunk_groups)] =
666 module_chunk_groups.get_disjoint_mut([&parent, &node])
667 else {
668 bail!("Module chunk groups not found");
672 };
673
674 if current_chunk_groups.is_empty() {
675 *current_chunk_groups = parent_chunk_groups.clone();
677 GraphTraversalAction::Continue
678 } else if parent_chunk_groups.is_proper_superset(current_chunk_groups) {
679 **current_chunk_groups |= &**parent_chunk_groups;
681 GraphTraversalAction::Continue
682 } else {
683 GraphTraversalAction::Skip
685 }
686 }
687 }
688 })
689 },
690 |successor, module_chunk_groups| {
698 Ok(TraversalPriority {
699 depth: *module_depth
700 .get(&successor)
701 .context("Module depth not found")?,
702 chunk_group_len: module_chunk_groups
703 .get(&successor)
704 .context("Module chunk group not found")?
705 .len(),
706 })
707 },
708 )?;
709
710 span.record("visit_count", visit_count);
711 span.record("chunk_group_count", chunk_groups_map.len());
712
713 #[cfg(debug_assertions)]
714 {
715 use std::sync::LazyLock;
716 static PRINT_CHUNK_GROUP_INFO: LazyLock<bool> =
717 LazyLock::new(|| match std::env::var_os("TURBOPACK_PRINT_CHUNK_GROUPS") {
718 Some(v) => v == "1",
719 None => false,
720 });
721 if *PRINT_CHUNK_GROUP_INFO {
722 use std::{
723 collections::{BTreeMap, BTreeSet},
724 path::absolute,
725 };
726
727 let mut buckets = BTreeMap::default();
728 for (module, key) in &module_chunk_groups {
729 if !key.is_empty() {
730 buckets
731 .entry(key.iter().collect::<Vec<_>>())
732 .or_insert(BTreeSet::new())
733 .insert(module.ident().to_string().await?);
734 }
735 }
736
737 let mut result = vec![];
738 result.push("Chunk Groups:".to_string());
739 for (i, (key, _)) in chunk_groups_map.iter().enumerate() {
740 result.push(format!(
741 " {:?}: {}",
742 i,
743 key.debug_str(chunk_groups_map.keys()).await?
744 ));
745 }
746 result.push("# Module buckets:".to_string());
747 for (key, modules) in buckets.iter() {
748 result.push(format!("## {:?}:", key.iter().collect::<Vec<_>>()));
749 for module in modules {
750 result.push(format!(" {module}"));
751 }
752 result.push("".to_string());
753 }
754 let f = absolute("chunk_group_info.log")?;
755 println!("written to {}", f.display());
756 std::fs::write(f, result.join("\n"))?;
757 }
758 }
759
760 Ok(ChunkGroupInfo {
761 module_chunk_groups: ResolvedVc::cell(module_chunk_groups),
762 chunk_group_keys: chunk_groups_map.keys().cloned().collect(),
763 chunk_groups: chunk_groups_map
764 .into_iter()
765 .map(|(k, (_, merged_entries))| match k {
766 ChunkGroupKey::Entry(entries) => ChunkGroup::Entry(entries),
767 ChunkGroupKey::Async(module) => ChunkGroup::Async(module),
768 ChunkGroupKey::Isolated(module) => ChunkGroup::Isolated(module),
769 ChunkGroupKey::IsolatedMerged { parent, merge_tag } => {
770 ChunkGroup::IsolatedMerged {
771 parent: parent.0 as usize,
772 merge_tag,
773 entries: merged_entries.into_iter().collect(),
774 }
775 }
776 ChunkGroupKey::Shared(module) => ChunkGroup::Shared(module),
777 ChunkGroupKey::SharedMultiple(entries) => ChunkGroup::SharedMultiple(entries),
778 ChunkGroupKey::SharedMerged { parent, merge_tag } => ChunkGroup::SharedMerged {
779 parent: parent.0 as usize,
780 merge_tag,
781 entries: merged_entries.into_iter().collect(),
782 },
783 })
784 .collect(),
785 }
786 .cell())
787 }
788 .instrument(span_outer)
789 .await
790}