1use std::{
2 collections::{BinaryHeap, HashSet, VecDeque, hash_map::Entry},
3 future::Future,
4};
5
6use anyhow::{Context, Result, bail};
7use auto_hash_map::AutoSet;
8use petgraph::{
9 graph::{DiGraph, EdgeIndex, NodeIndex},
10 visit::{
11 Dfs, EdgeRef, IntoNeighbors, IntoNodeReferences, NodeIndexable, Reversed, VisitMap,
12 Visitable,
13 },
14};
15use rustc_hash::{FxHashMap, FxHashSet};
16use serde::{Deserialize, Serialize};
17use tracing::{Instrument, Span};
18use turbo_rcstr::RcStr;
19use turbo_tasks::{
20 CollectiblesSource, FxIndexMap, NonLocalValue, ReadRef, ResolvedVc, TryJoinIterExt,
21 ValueToString, Vc,
22 debug::ValueDebugFormat,
23 graph::{AdjacencyMap, GraphTraversal, Visit, VisitControlFlow},
24 trace::TraceRawVcs,
25};
26use turbo_tasks_fs::FileSystemPath;
27
28use crate::{
29 chunk::{AsyncModuleInfo, ChunkingContext, ChunkingType},
30 issue::{ImportTrace, ImportTracer, ImportTraces, Issue},
31 module::Module,
32 module_graph::{
33 async_module_info::{AsyncModulesInfo, compute_async_module_info},
34 chunk_group_info::{ChunkGroupEntry, ChunkGroupInfo, compute_chunk_group_info},
35 merged_modules::{MergedModuleInfo, compute_merged_modules},
36 module_batches::{ModuleBatchesGraph, compute_module_batches},
37 style_groups::{StyleGroups, StyleGroupsConfig, compute_style_groups},
38 traced_di_graph::{TracedDiGraph, iter_neighbors_rev},
39 },
40 reference::primary_chunkable_referenced_modules,
41 resolve::ExportUsage,
42};
43
44pub mod async_module_info;
45pub mod chunk_group_info;
46pub mod merged_modules;
47pub mod module_batch;
48pub(crate) mod module_batches;
49pub(crate) mod style_groups;
50mod traced_di_graph;
51
52pub use self::module_batches::BatchingConfig;
53
54#[derive(
55 Debug, Copy, Clone, Eq, PartialOrd, Ord, Hash, PartialEq, Serialize, Deserialize, TraceRawVcs,
56)]
57pub struct GraphNodeIndex {
58 #[turbo_tasks(trace_ignore)]
59 graph_idx: u32,
60 #[turbo_tasks(trace_ignore)]
61 node_idx: NodeIndex,
62}
63impl GraphNodeIndex {
64 #[inline(always)]
65 fn graph_idx(&self) -> usize {
66 self.graph_idx as usize
67 }
68}
69
70unsafe impl NonLocalValue for GraphNodeIndex {}
71
72#[turbo_tasks::value]
73#[derive(Clone, Debug)]
74pub struct VisitedModules {
75 pub modules: FxIndexMap<ResolvedVc<Box<dyn Module>>, GraphNodeIndex>,
76 next_graph_idx: u32,
77}
78
79#[turbo_tasks::value_impl]
80impl VisitedModules {
81 #[turbo_tasks::function]
82 pub fn empty() -> Vc<Self> {
83 Self {
84 modules: Default::default(),
85 next_graph_idx: 0,
86 }
87 .cell()
88 }
89
90 #[turbo_tasks::function]
91 pub async fn from_graph(graph: Vc<SingleModuleGraph>) -> Result<Vc<Self>> {
92 Ok(Self {
93 modules: graph
94 .await?
95 .enumerate_nodes()
96 .flat_map(|(node_idx, module)| match module {
97 SingleModuleGraphNode::Module(SingleModuleGraphModuleNode {
98 module, ..
99 }) => Some((
100 *module,
101 GraphNodeIndex {
102 graph_idx: 0,
103 node_idx,
104 },
105 )),
106 SingleModuleGraphNode::VisitedModule { .. } => None,
107 })
108 .collect(),
109 next_graph_idx: 1,
110 }
111 .cell())
112 }
113
114 #[turbo_tasks::function]
115 pub fn with_incremented_index(&self) -> Result<Vc<Self>> {
116 Ok(Self {
117 modules: self.modules.clone(),
118 next_graph_idx: self.next_graph_idx + 1,
119 }
120 .cell())
121 }
122
123 #[turbo_tasks::function]
124 pub async fn concatenate(&self, graph: Vc<SingleModuleGraph>) -> Result<Vc<Self>> {
125 let graph = graph.await?;
126 let iter = self
127 .modules
128 .iter()
129 .map(|(module, idx)| (*module, *idx))
130 .chain(
131 graph
132 .enumerate_nodes()
133 .flat_map(|(node_idx, module)| match module {
134 SingleModuleGraphNode::Module(SingleModuleGraphModuleNode {
135 module,
136 ..
137 }) => Some((
138 *module,
139 GraphNodeIndex {
140 graph_idx: self.next_graph_idx,
141 node_idx,
142 },
143 )),
144 SingleModuleGraphNode::VisitedModule { .. } => None,
145 }),
146 );
147
148 let mut map = FxIndexMap::with_capacity_and_hasher(
149 self.modules.len() + graph.number_of_modules,
150 Default::default(),
151 );
152 for (k, v) in iter {
153 map.entry(k).or_insert(v);
154 }
155 map.shrink_to_fit();
156
157 Ok(Self {
158 modules: map,
159 next_graph_idx: self.next_graph_idx + 1,
160 }
161 .cell())
162 }
163}
164
165pub type GraphEntriesT = Vec<ChunkGroupEntry>;
166
167#[turbo_tasks::value(transparent)]
168pub struct GraphEntries(GraphEntriesT);
169
170#[turbo_tasks::value_impl]
171impl GraphEntries {
172 #[turbo_tasks::function]
173 pub fn empty() -> Vc<Self> {
174 Vc::cell(Vec::new())
175 }
176}
177
178#[turbo_tasks::value(cell = "new", eq = "manual", into = "new")]
179#[derive(Clone, Default)]
180pub struct SingleModuleGraph {
181 pub graph: TracedDiGraph<SingleModuleGraphNode, RefData>,
182
183 pub number_of_modules: usize,
185
186 #[turbo_tasks(trace_ignore)]
193 modules: FxHashMap<ResolvedVc<Box<dyn Module>>, NodeIndex>,
194
195 #[turbo_tasks(trace_ignore)]
196 pub entries: GraphEntriesT,
197}
198
199#[derive(
200 Debug,
201 Clone,
202 Hash,
203 TraceRawVcs,
204 Serialize,
205 Deserialize,
206 Eq,
207 PartialEq,
208 ValueDebugFormat,
209 NonLocalValue,
210)]
211pub struct RefData {
212 pub chunking_type: ChunkingType,
213 pub export: ExportUsage,
214}
215
216impl SingleModuleGraph {
217 async fn new_inner(
221 entries: &GraphEntriesT,
222 visited_modules: &FxIndexMap<ResolvedVc<Box<dyn Module>>, GraphNodeIndex>,
223 include_traced: bool,
224 ) -> Result<Vc<Self>> {
225 let root_edges = entries
226 .iter()
227 .flat_map(|e| e.entries())
228 .map(|e| async move {
229 Ok(SingleModuleGraphBuilderEdge {
230 to: SingleModuleGraphBuilderNode::new_module(e).await?,
231 export: ExportUsage::All,
232 })
233 })
234 .try_join()
235 .await?;
236
237 let (children_nodes_iter, visited_nodes) = AdjacencyMap::new()
238 .skip_duplicates_with_key(|node: &(SingleModuleGraphBuilderNode, ExportUsage)| &node.0)
239 .visit(
240 root_edges,
241 SingleModuleGraphBuilder {
242 visited_modules,
243 include_traced,
244 },
245 )
246 .await
247 .completed()?
248 .into_inner_with_visited();
249 let node_count = visited_nodes.0.len();
250 drop(visited_nodes);
251
252 let mut graph: DiGraph<SingleModuleGraphNode, RefData> = DiGraph::with_capacity(
253 node_count,
254 node_count * 4,
257 );
258
259 let mut number_of_modules = 0;
260 let mut modules: FxHashMap<ResolvedVc<Box<dyn Module>>, NodeIndex> =
261 FxHashMap::with_capacity_and_hasher(node_count, Default::default());
262 {
263 let _span = tracing::info_span!("build module graph").entered();
264 for (parent, (current, export)) in children_nodes_iter.into_breadth_first_edges() {
265 let parent_edge = match parent.map(|v| v.0) {
266 Some(SingleModuleGraphBuilderNode::Module { module, .. }) => Some((
267 *modules.get(&module).unwrap(),
268 RefData {
269 chunking_type: COMMON_CHUNKING_TYPE,
270 export,
271 },
272 )),
273 Some(SingleModuleGraphBuilderNode::ChunkableReference { .. }) => {
274 continue;
276 }
277 Some(SingleModuleGraphBuilderNode::VisitedModule { .. }) => unreachable!(),
278 None => None,
279 };
280
281 match current {
282 SingleModuleGraphBuilderNode::Module { module, ident: _ } => {
283 let current_idx = if let Some(current_idx) = modules.get(&module) {
285 *current_idx
286 } else {
287 let idx = graph.add_node(SingleModuleGraphNode::Module(
288 SingleModuleGraphModuleNode { module },
289 ));
290 number_of_modules += 1;
291 modules.insert(module, idx);
292 idx
293 };
294 if let Some((parent_idx, ref_data)) = parent_edge {
296 graph.add_edge(parent_idx, current_idx, ref_data);
297 }
298 }
299 SingleModuleGraphBuilderNode::VisitedModule { module, idx } => {
300 let current_idx = if let Some(current_idx) = modules.get(&module) {
302 *current_idx
303 } else {
304 let idx = graph
305 .add_node(SingleModuleGraphNode::VisitedModule { idx, module });
306 modules.insert(module, idx);
307 idx
308 };
309 if let Some((parent_idx, data)) = parent_edge {
311 graph.add_edge(parent_idx, current_idx, data);
312 }
313 }
314 SingleModuleGraphBuilderNode::ChunkableReference {
315 source,
316 target,
317 ref_data,
318 ..
319 } => {
320 let target_idx = if let Some(target_idx) = modules.get(&target) {
322 *target_idx
323 } else {
324 let target_idx = visited_modules.get(&target);
325 let idx = graph.add_node(match target_idx {
326 Some(idx) => SingleModuleGraphNode::VisitedModule {
327 idx: *idx,
328 module: target,
329 },
330 None => {
331 SingleModuleGraphNode::Module(SingleModuleGraphModuleNode {
332 module: target,
333 })
334 }
335 });
336 modules.insert(target, idx);
337 idx
338 };
339 graph.add_edge(*modules.get(&source).unwrap(), target_idx, ref_data);
340 }
341 }
342 }
343 }
344
345 graph.shrink_to_fit();
346
347 #[cfg(debug_assertions)]
348 {
349 use once_cell::sync::Lazy;
350
351 static CHECK_FOR_DUPLICATE_MODULES: Lazy<bool> = Lazy::new(|| {
353 match std::env::var_os("TURBOPACK_TEMP_DISABLE_DUPLICATE_MODULES_CHECK") {
354 Some(v) => v != "1" && v != "true",
355 None => true,
356 }
357 });
358 if *CHECK_FOR_DUPLICATE_MODULES {
359 let mut duplicates = Vec::new();
360 let mut set = FxHashSet::default();
361 for &module in modules.keys() {
362 let ident = module.ident().to_string().await?;
363 if !set.insert(ident.clone()) {
364 duplicates.push(ident)
365 }
366 }
367 if !duplicates.is_empty() {
368 panic!("Duplicate module idents in graph: {duplicates:#?}");
369 }
370 }
371 }
372
373 let graph = SingleModuleGraph {
374 graph: TracedDiGraph::new(graph),
375 number_of_modules,
376 modules,
377 entries: entries.clone(),
378 }
379 .cell();
380
381 turbo_tasks::emit(ResolvedVc::upcast::<Box<dyn ImportTracer>>(
382 ModuleGraphImportTracer::new(graph).to_resolved().await?,
383 ));
384 Ok(graph)
385 }
386
387 fn get_module(&self, module: ResolvedVc<Box<dyn Module>>) -> Result<NodeIndex> {
388 self.modules
389 .get(&module)
390 .copied()
391 .context("Couldn't find module in graph")
392 }
393
394 pub fn iter_nodes(&self) -> impl Iterator<Item = &'_ SingleModuleGraphModuleNode> + '_ {
396 self.graph.node_weights().filter_map(|n| match n {
397 SingleModuleGraphNode::Module(node) => Some(node),
398 SingleModuleGraphNode::VisitedModule { .. } => None,
399 })
400 }
401
402 pub fn entry_modules(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn Module>>> + '_ {
404 self.entries.iter().flat_map(|e| e.entries())
405 }
406
407 pub fn enumerate_nodes(
409 &self,
410 ) -> impl Iterator<Item = (NodeIndex, &'_ SingleModuleGraphNode)> + '_ {
411 self.graph.node_references()
412 }
413
414 pub fn traverse_from_entry<'a>(
416 &'a self,
417 entry: ResolvedVc<Box<dyn Module>>,
418 mut visitor: impl FnMut(&'a SingleModuleGraphModuleNode),
419 ) -> Result<()> {
420 let entry_node = self.get_module(entry)?;
421
422 let mut dfs = Dfs::new(&*self.graph, entry_node);
423 while let Some(nx) = dfs.next(&*self.graph) {
424 let SingleModuleGraphNode::Module(weight) = self.graph.node_weight(nx).unwrap() else {
425 return Ok(());
426 };
427 visitor(weight);
429 }
430 Ok(())
431 }
432
433 pub fn traverse_edges_from_entries<'a>(
444 &'a self,
445 entries: impl IntoIterator<Item = ResolvedVc<Box<dyn Module>>>,
446 mut visitor: impl FnMut(
447 Option<(&'a SingleModuleGraphModuleNode, &'a RefData)>,
448 &'a SingleModuleGraphModuleNode,
449 ) -> GraphTraversalAction,
450 ) -> Result<()> {
451 let graph = &self.graph;
452 let entries = entries.into_iter().map(|e| self.get_module(e).unwrap());
453
454 let mut stack = entries.collect::<Vec<_>>();
455 let mut discovered = graph.visit_map();
456 for entry_node in &stack {
458 let SingleModuleGraphNode::Module(entry_weight) =
459 graph.node_weight(*entry_node).unwrap()
460 else {
461 continue;
462 };
463 visitor(None, entry_weight);
464 }
465
466 while let Some(node) = stack.pop() {
467 let SingleModuleGraphNode::Module(node_weight) = graph.node_weight(node).unwrap()
468 else {
469 continue;
470 };
471 if discovered.visit(node) {
472 let neighbors = {
473 let mut neighbors = vec![];
474 let mut walker = graph.neighbors(node).detach();
475 while let Some((edge, succ)) = walker.next(graph) {
476 neighbors.push((edge, succ));
477 }
478 neighbors
479 };
480
481 for (edge, succ) in neighbors {
482 let SingleModuleGraphNode::Module(succ_weight) =
483 graph.node_weight(succ).unwrap()
484 else {
485 continue;
486 };
487 let edge_weight = graph.edge_weight(edge).unwrap();
488 let action = visitor(Some((node_weight, edge_weight)), succ_weight);
489 if !discovered.is_visited(&succ) && action == GraphTraversalAction::Continue {
490 stack.push(succ);
491 }
492 }
493 }
494 }
495
496 Ok(())
497 }
498
499 pub fn traverse_edges<'a>(
504 &'a self,
505 mut visitor: impl FnMut(
506 (
507 Option<(&'a SingleModuleGraphModuleNode, &'a RefData)>,
508 &'a SingleModuleGraphModuleNode,
509 ),
510 ) -> GraphTraversalAction,
511 ) -> Result<()> {
512 let graph = &self.graph;
513 let mut stack: Vec<NodeIndex> = self
514 .entries
515 .iter()
516 .flat_map(|e| e.entries())
517 .map(|e| *self.modules.get(&e).unwrap())
518 .collect();
519 let mut discovered = graph.visit_map();
520 for entry_node in &stack {
521 let SingleModuleGraphNode::Module(entry_node) = graph.node_weight(*entry_node).unwrap()
522 else {
523 continue;
524 };
525 visitor((None, entry_node));
526 }
527
528 while let Some(node) = stack.pop() {
529 if discovered.visit(node) {
530 let SingleModuleGraphNode::Module(node_weight) = graph.node_weight(node).unwrap()
531 else {
532 continue;
533 };
534 for edge in graph.edges(node).collect::<Vec<_>>() {
535 let edge_weight = edge.weight();
536 let succ = edge.target();
537 let SingleModuleGraphNode::Module(succ_weight) =
538 graph.node_weight(succ).unwrap()
539 else {
540 continue;
541 };
542 let action = visitor((Some((node_weight, edge_weight)), succ_weight));
543 if !discovered.is_visited(&succ) && action == GraphTraversalAction::Continue {
544 stack.push(succ);
545 }
546 }
547 }
548 }
549
550 Ok(())
551 }
552
553 pub fn traverse_edges_from_entries_topological<'a, S>(
571 &'a self,
572 entries: impl IntoIterator<Item = ResolvedVc<Box<dyn Module>>>,
573 state: &mut S,
574 mut visit_preorder: impl FnMut(
575 Option<(&'a SingleModuleGraphModuleNode, &'a RefData)>,
576 &'a SingleModuleGraphNode,
577 &mut S,
578 ) -> Result<GraphTraversalAction>,
579 mut visit_postorder: impl FnMut(
580 Option<(&'a SingleModuleGraphModuleNode, &'a RefData)>,
581 &'a SingleModuleGraphNode,
582 &mut S,
583 ) -> Result<()>,
584 ) -> Result<()> {
585 let graph = &self.graph;
586 let entries = entries.into_iter().map(|e| self.get_module(e).unwrap());
587
588 enum TopologicalPass {
589 Visit,
590 ExpandAndVisit,
591 }
592
593 #[allow(clippy::type_complexity)] let mut stack: Vec<(TopologicalPass, Option<(NodeIndex, EdgeIndex)>, NodeIndex)> = entries
595 .map(|e| (TopologicalPass::ExpandAndVisit, None, e))
596 .collect();
597 let mut expanded = FxHashSet::default();
598 while let Some((pass, parent, current)) = stack.pop() {
599 let parent_arg = parent.map(|parent| {
600 (
601 match graph.node_weight(parent.0).unwrap() {
602 SingleModuleGraphNode::Module(node) => node,
603 SingleModuleGraphNode::VisitedModule { .. } => {
604 unreachable!()
605 }
606 },
607 graph.edge_weight(parent.1).unwrap(),
608 )
609 });
610 match pass {
611 TopologicalPass::Visit => {
612 visit_postorder(parent_arg, graph.node_weight(current).unwrap(), state)?;
613 }
614 TopologicalPass::ExpandAndVisit => match graph.node_weight(current).unwrap() {
615 current_node @ SingleModuleGraphNode::Module(_) => {
616 let action = visit_preorder(parent_arg, current_node, state)?;
617 if action == GraphTraversalAction::Exclude {
618 continue;
619 }
620 stack.push((TopologicalPass::Visit, parent, current));
621 if action == GraphTraversalAction::Continue && expanded.insert(current) {
622 stack.extend(iter_neighbors_rev(graph, current).map(
623 |(edge, child)| {
624 (
625 TopologicalPass::ExpandAndVisit,
626 Some((current, edge)),
627 child,
628 )
629 },
630 ));
631 }
632 }
633 current_node @ SingleModuleGraphNode::VisitedModule { .. } => {
634 visit_preorder(parent_arg, current_node, state)?;
635 visit_postorder(parent_arg, current_node, state)?;
636 }
637 },
638 }
639 }
640
641 Ok(())
642 }
643
644 pub fn traverse_cycles<'l>(
645 &'l self,
646 edge_filter: impl Fn(&'l RefData) -> bool,
647 mut visit_cycle: impl FnMut(&[&'l SingleModuleGraphModuleNode]),
648 ) {
649 #[derive(Clone)]
653 struct NodeState {
654 index: u32,
655 lowlink: u32,
656 on_stack: bool,
657 }
658 enum VisitStep {
659 UnvisitedNode(NodeIndex),
660 EdgeAfterVisit { parent: NodeIndex, child: NodeIndex },
661 AfterVisit(NodeIndex),
662 }
663 let mut node_states = vec![None; self.graph.node_bound()];
664 let mut stack = Vec::new();
665 let mut visit_stack = Vec::new();
666 let mut index = 0;
667 let mut scc = Vec::new();
668 for initial_index in self.graph.node_indices() {
669 if node_states[initial_index.index()].is_some() {
671 continue;
672 }
673 visit_stack.push(VisitStep::UnvisitedNode(initial_index));
674 while let Some(step) = visit_stack.pop() {
675 match step {
676 VisitStep::UnvisitedNode(node) => {
677 node_states[node.index()] = Some(NodeState {
678 index,
679 lowlink: index,
680 on_stack: true,
681 });
682 index += 1;
683 stack.push(node);
684 visit_stack.push(VisitStep::AfterVisit(node));
685 let mut neighbors = self.graph.neighbors(node).detach();
686 while let Some((edge, succ)) = neighbors.next(&self.graph) {
687 let edge_weight = self.graph.edge_weight(edge).unwrap();
688 if !edge_filter(edge_weight) {
689 continue;
690 }
691 let node_state = &node_states[succ.index()];
692 if let Some(node_state) = node_state {
693 if node_state.on_stack {
694 let index = node_state.index;
695 let parent_state = node_states[node.index()].as_mut().unwrap();
696 parent_state.lowlink = parent_state.lowlink.min(index);
697 }
698 } else {
699 visit_stack.push(VisitStep::EdgeAfterVisit {
700 parent: node,
701 child: succ,
702 });
703 visit_stack.push(VisitStep::UnvisitedNode(succ));
704 }
705 }
706 }
707 VisitStep::EdgeAfterVisit { parent, child } => {
708 let child_state = node_states[child.index()].as_ref().unwrap();
709 let lowlink = child_state.lowlink;
710
711 let parent_state = node_states[parent.index()].as_mut().unwrap();
712 parent_state.lowlink = parent_state.lowlink.min(lowlink);
713 }
714 VisitStep::AfterVisit(node) => {
715 let node_state = node_states[node.index()].as_ref().unwrap();
716 if node_state.lowlink == node_state.index {
717 loop {
718 let poppped = stack.pop().unwrap();
719 let popped_state = node_states[poppped.index()].as_mut().unwrap();
720 popped_state.on_stack = false;
721 if let SingleModuleGraphNode::Module(module) =
722 self.graph.node_weight(poppped).unwrap()
723 {
724 scc.push(module);
725 }
726 if poppped == node {
727 break;
728 }
729 }
730 if scc.len() > 1 {
731 visit_cycle(&scc);
732 }
733 scc.clear();
734 }
735 }
736 }
737 }
738 }
739 }
740
741 pub async fn compute_import_traces_for_issues(
749 &self,
750 issues: &AutoSet<ResolvedVc<Box<dyn Issue>>>,
751 ) -> Result<FxHashMap<ResolvedVc<Box<dyn Issue>>, Vec<ImportTrace>>> {
752 let issue_paths = issues
753 .iter()
754 .map(|issue| async move { Ok((*issue.file_path().await?).clone()) })
755 .try_join()
756 .await?;
757 let mut file_path_to_traces: FxHashMap<FileSystemPath, Vec<ImportTrace>> =
758 FxHashMap::with_capacity_and_hasher(issue_paths.len(), Default::default());
759 for issue in &issue_paths {
761 file_path_to_traces.entry(issue.clone()).or_default();
762 }
763
764 {
765 let modules = self
766 .modules
767 .iter()
768 .map(|(module, &index)| async move {
769 Ok(((*module.ident().path().await?).clone(), index))
770 })
771 .try_join()
772 .await?;
773 let reversed_graph = Reversed(&self.graph.0);
775 for (path, module_idx) in modules {
776 if let Entry::Occupied(mut entry) = file_path_to_traces.entry(path) {
777 let Some((_, path)) = petgraph::algo::astar(
779 &reversed_graph,
780 module_idx,
781 |n| reversed_graph.neighbors(n).next().is_none(),
782 |e| match e.weight().chunking_type {
784 ChunkingType::Parallel { .. } => 0,
786 _ => 1,
787 },
788 |_| 0,
802 ) else {
803 unreachable!("there must be a path to a root");
804 };
805 let path = path
809 .into_iter()
810 .map(async |n| {
811 Ok(self
812 .graph
813 .node_weight(n)
814 .unwrap()
815 .module()
816 .ident()
817 .await?
818 .clone())
819 })
820 .try_join()
821 .await?;
822 entry.get_mut().push(path);
823 }
824 }
825 }
826 let mut issue_to_traces: FxHashMap<ResolvedVc<Box<dyn Issue>>, Vec<ImportTrace>> =
827 FxHashMap::with_capacity_and_hasher(issues.len(), Default::default());
828 for (path, issue) in issue_paths.iter().zip(issues) {
832 if let Some(traces) = file_path_to_traces.get(path) {
833 issue_to_traces.insert(*issue, traces.clone());
834 }
835 }
836 Ok(issue_to_traces)
837 }
838}
839
840#[turbo_tasks::value]
841struct ModuleGraphImportTracer {
842 graph: ResolvedVc<SingleModuleGraph>,
843}
844
845#[turbo_tasks::value(shared)]
846struct PathToModulesMap {
847 map: FxHashMap<FileSystemPath, Vec<ResolvedVc<Box<dyn Module>>>>,
848}
849
850#[turbo_tasks::value_impl]
851impl ModuleGraphImportTracer {
852 #[turbo_tasks::function]
853 fn new(graph: ResolvedVc<SingleModuleGraph>) -> Vc<Self> {
854 Self::cell(Self { graph })
855 }
856
857 #[turbo_tasks::function]
859 async fn path_to_modules(&self) -> Result<Vc<PathToModulesMap>> {
860 let path_and_modules = self
861 .graph
862 .await?
863 .modules
864 .iter()
865 .map(|(&module, _)| async move { Ok((module.ident().path().owned().await?, module)) })
866 .try_join()
867 .await?;
868 let mut map: FxHashMap<FileSystemPath, Vec<ResolvedVc<Box<dyn Module>>>> =
869 FxHashMap::default();
870 for (path, module) in path_and_modules {
871 map.entry(path).or_default().push(module)
872 }
873 Ok(PathToModulesMap::cell(PathToModulesMap { map }))
874 }
875}
876
877#[turbo_tasks::value_impl]
878impl ImportTracer for ModuleGraphImportTracer {
879 #[turbo_tasks::function]
880 async fn get_traces(
881 self: Vc<Self>,
882 path: ResolvedVc<FileSystemPath>,
883 ) -> Result<Vc<ImportTraces>> {
884 let path_to_modules = self.path_to_modules().await?;
885 let Some(modules) = path_to_modules.map.get(&*path.await?) else {
886 return Ok(Vc::default()); };
889 debug_assert!(!modules.is_empty(), "modules should not be an empty vec");
890 let graph = &*self.await?.graph.await?;
891
892 let reversed_graph = Reversed(&graph.graph.0);
893 return Ok(ImportTraces::cell(ImportTraces(
894 modules
895 .iter()
896 .map(|m| async move {
897 let Some(&module_idx) = graph.modules.get(m) else {
898 bail!("inconsistent read?")
901 };
902 let Some((_, path)) = petgraph::algo::astar(
904 &reversed_graph,
905 module_idx,
906 |n| reversed_graph.neighbors(n).next().is_none(),
907 |e| match e.weight().chunking_type {
909 ChunkingType::Parallel { .. } => 0,
911 _ => 1,
912 },
913 |_| 0,
927 ) else {
928 unreachable!("there must be a path to a root");
929 };
930
931 let path = path
935 .into_iter()
936 .map(async |n| {
937 graph
938 .graph
939 .node_weight(n)
940 .unwrap() .module()
942 .ident()
943 .await
944 })
945 .try_join()
946 .await?;
947 Ok(path)
948 })
949 .try_join()
950 .await?,
951 )));
952 }
953}
954
955#[turbo_tasks::value(shared)]
956#[derive(Clone, Default)]
957pub struct ModuleGraph {
958 pub graphs: Vec<ResolvedVc<SingleModuleGraph>>,
959}
960
961#[turbo_tasks::value_impl]
962impl ModuleGraph {
963 #[turbo_tasks::function]
964 pub fn from_graphs(graphs: Vec<ResolvedVc<SingleModuleGraph>>) -> Vc<Self> {
965 Self { graphs }.cell()
966 }
967
968 #[turbo_tasks::function]
969 pub fn from_single_graph(graph: ResolvedVc<SingleModuleGraph>) -> Vc<Self> {
970 Self {
971 graphs: vec![graph],
972 }
973 .cell()
974 }
975
976 #[turbo_tasks::function]
977 pub fn from_entry_module(
978 module: ResolvedVc<Box<dyn Module>>,
979 include_traced: bool,
980 ) -> Vc<Self> {
981 Self::from_single_graph(SingleModuleGraph::new_with_entries(
982 Vc::cell(vec![ChunkGroupEntry::Entry(vec![module])]),
983 include_traced,
984 ))
985 }
986
987 #[turbo_tasks::function]
988 pub fn from_modules(modules: Vc<GraphEntries>, include_traced: bool) -> Vc<Self> {
989 Self::from_single_graph(SingleModuleGraph::new_with_entries(modules, include_traced))
990 }
991
992 #[turbo_tasks::function]
993 pub async fn chunk_group_info(&self) -> Result<Vc<ChunkGroupInfo>> {
994 compute_chunk_group_info(self).await
995 }
996
997 #[turbo_tasks::function]
998 pub async fn merged_modules(self: Vc<Self>) -> Result<Vc<MergedModuleInfo>> {
999 compute_merged_modules(self).await
1000 }
1001
1002 #[turbo_tasks::function]
1003 pub async fn module_batches(
1004 self: Vc<Self>,
1005 config: Vc<BatchingConfig>,
1006 ) -> Result<Vc<ModuleBatchesGraph>> {
1007 compute_module_batches(self, &*config.await?).await
1008 }
1009
1010 #[turbo_tasks::function]
1011 pub async fn style_groups(
1012 self: Vc<Self>,
1013 chunking_context: Vc<Box<dyn ChunkingContext>>,
1014 config: StyleGroupsConfig,
1015 ) -> Result<Vc<StyleGroups>> {
1016 compute_style_groups(self, chunking_context, &config).await
1017 }
1018
1019 #[turbo_tasks::function]
1020 pub async fn async_module_info(self: Vc<Self>) -> Result<Vc<AsyncModulesInfo>> {
1021 async move {
1024 let result_op = compute_async_module_info(self.to_resolved().await?);
1025 let result_vc = result_op.resolve_strongly_consistent().await?;
1026 let _issues = result_op.take_collectibles::<Box<dyn Issue>>();
1027 anyhow::Ok(*result_vc)
1028 }
1029 .instrument(tracing::info_span!("compute async module info"))
1030 .await
1031 }
1032
1033 #[turbo_tasks::function]
1034 pub async fn referenced_async_modules(
1035 self: Vc<Self>,
1036 module: ResolvedVc<Box<dyn Module>>,
1037 ) -> Result<Vc<AsyncModuleInfo>> {
1038 let this = self.await?;
1039 let graphs = this.get_graphs().await?;
1040 let async_modules_info = self.async_module_info().await?;
1041
1042 let entry = ModuleGraph::get_entry(&graphs, module).await?;
1043 let referenced_modules =
1044 iter_neighbors_rev(&graphs[entry.graph_idx()].graph, entry.node_idx)
1045 .filter(|(edge_idx, _)| {
1046 let ty = graphs[entry.graph_idx()]
1047 .graph
1048 .edge_weight(*edge_idx)
1049 .unwrap();
1050 ty.chunking_type.is_inherit_async()
1051 })
1052 .map(|(_, child_idx)| {
1053 anyhow::Ok(
1054 get_node!(
1055 graphs,
1056 GraphNodeIndex {
1057 graph_idx: entry.graph_idx,
1058 node_idx: child_idx
1059 }
1060 )?
1061 .module,
1062 )
1063 })
1064 .collect::<Result<Vec<_>>>()?
1065 .into_iter()
1066 .rev()
1067 .filter(|m| async_modules_info.contains(m))
1068 .map(|m| *m)
1069 .collect();
1070
1071 Ok(AsyncModuleInfo::new(referenced_modules))
1072 }
1073}
1074
1075macro_rules! get_node {
1080 ($graphs:expr, $node:expr) => {{
1081 let node_idx = $node;
1082 match $graphs[node_idx.graph_idx()]
1083 .graph
1084 .node_weight(node_idx.node_idx)
1085 {
1086 Some(SingleModuleGraphNode::Module(node)) => ::anyhow::Ok(node),
1087 Some(SingleModuleGraphNode::VisitedModule { idx, .. }) => {
1088 match $graphs[idx.graph_idx()].graph.node_weight(idx.node_idx) {
1089 Some(SingleModuleGraphNode::Module(node)) => ::anyhow::Ok(node),
1090 Some(SingleModuleGraphNode::VisitedModule { .. }) => Err(::anyhow::anyhow!(
1091 "Expected visited target node to be module"
1092 )),
1093 None => Err(::anyhow::anyhow!("Expected visited target node")),
1094 }
1095 }
1096 None => Err(::anyhow::anyhow!("Expected graph node")),
1097 }
1098 }};
1099}
1100pub(crate) use get_node;
1101macro_rules! get_node_idx {
1102 ($graphs:expr, $node:expr) => {{
1103 let node_idx = $node;
1104 match $graphs[node_idx.graph_idx()]
1105 .graph
1106 .node_weight(node_idx.node_idx)
1107 {
1108 Some(SingleModuleGraphNode::Module(node)) => ::anyhow::Ok((node, node_idx)),
1109 Some(SingleModuleGraphNode::VisitedModule { idx, .. }) => {
1110 match $graphs[idx.graph_idx()].graph.node_weight(idx.node_idx) {
1111 Some(SingleModuleGraphNode::Module(node)) => ::anyhow::Ok((node, *idx)),
1112 Some(SingleModuleGraphNode::VisitedModule { .. }) => Err(::anyhow::anyhow!(
1113 "Expected visited target node to be module"
1114 )),
1115 None => Err(::anyhow::anyhow!("Expected visited target node")),
1116 }
1117 }
1118 None => Err(::anyhow::anyhow!("Expected graph node")),
1119 }
1120 }};
1121}
1122pub(crate) use get_node_idx;
1123
1124impl ModuleGraph {
1125 pub async fn get_graphs(&self) -> Result<Vec<ReadRef<SingleModuleGraph>>> {
1126 self.graphs.iter().try_join().await
1127 }
1128
1129 async fn get_entry(
1130 graphs: &[ReadRef<SingleModuleGraph>],
1131 entry: ResolvedVc<Box<dyn Module>>,
1132 ) -> Result<GraphNodeIndex> {
1133 let Some(idx) = graphs.iter().enumerate().find_map(|(graph_idx, graph)| {
1134 graph.modules.get(&entry).map(|node_idx| GraphNodeIndex {
1135 graph_idx: u32::try_from(graph_idx).unwrap(),
1136 node_idx: *node_idx,
1137 })
1138 }) else {
1139 bail!(
1140 "Couldn't find entry module {} in module graph (potential entries: {:?})",
1141 entry.ident().to_string().await?,
1142 graphs
1143 .iter()
1144 .flat_map(|g| g.entries.iter())
1145 .flat_map(|e| e.entries())
1146 .map(|e| e.ident().to_string())
1147 .try_join()
1148 .await?
1149 );
1150 };
1151 Ok(idx)
1152 }
1153
1154 pub async fn traverse_edges_from_entries_bfs(
1165 &self,
1166 entries: impl IntoIterator<Item = ResolvedVc<Box<dyn Module>>>,
1167 mut visitor: impl FnMut(
1168 Option<(&'_ SingleModuleGraphModuleNode, &'_ RefData)>,
1169 &'_ SingleModuleGraphModuleNode,
1170 ) -> Result<GraphTraversalAction>,
1171 ) -> Result<()> {
1172 let graphs = self.get_graphs().await?;
1173
1174 let mut queue = VecDeque::from(
1175 entries
1176 .into_iter()
1177 .map(|e| ModuleGraph::get_entry(&graphs, e))
1178 .try_join()
1179 .await?,
1180 );
1181 let mut visited = HashSet::new();
1182 for entry_node in &queue {
1183 visitor(None, get_node!(graphs, entry_node)?)?;
1184 }
1185 while let Some(node) = queue.pop_front() {
1186 let graph = &graphs[node.graph_idx()].graph;
1187 let node_weight = get_node!(graphs, node)?;
1188 if visited.insert(node) {
1189 let neighbors = iter_neighbors_rev(graph, node.node_idx);
1190
1191 for (edge, succ) in neighbors {
1192 let succ = GraphNodeIndex {
1193 graph_idx: node.graph_idx,
1194 node_idx: succ,
1195 };
1196 let succ_weight = get_node!(graphs, succ)?;
1197 let edge_weight = graph.edge_weight(edge).unwrap();
1198 let action = visitor(Some((node_weight, edge_weight)), succ_weight)?;
1199 if !visited.contains(&succ) && action == GraphTraversalAction::Continue {
1200 queue.push_back(succ);
1201 }
1202 }
1203 }
1204 }
1205
1206 Ok(())
1207 }
1208
1209 pub async fn traverse_edges_from_entry(
1220 &self,
1221 entries: impl IntoIterator<Item = ResolvedVc<Box<dyn Module>>>,
1222 mut visitor: impl FnMut(
1223 Option<(&'_ SingleModuleGraphModuleNode, &'_ RefData)>,
1224 &'_ SingleModuleGraphModuleNode,
1225 ) -> GraphTraversalAction,
1226 ) -> Result<()> {
1227 let graphs = self.get_graphs().await?;
1228
1229 let entries = entries.into_iter();
1230 let mut stack = Vec::with_capacity(entries.size_hint().0);
1231 for entry in entries {
1232 stack.push(ModuleGraph::get_entry(&graphs, entry).await?);
1233 }
1234 let mut visited = HashSet::new();
1235 for entry_node in &stack {
1236 visitor(None, get_node!(graphs, entry_node)?);
1237 }
1238 while let Some(node) = stack.pop() {
1239 let graph = &graphs[node.graph_idx()].graph;
1240 let node_weight = get_node!(graphs, node)?;
1241 if visited.insert(node) {
1242 let neighbors = iter_neighbors_rev(graph, node.node_idx);
1243
1244 for (edge, succ) in neighbors {
1245 let succ = GraphNodeIndex {
1246 graph_idx: node.graph_idx,
1247 node_idx: succ,
1248 };
1249 let succ_weight = get_node!(graphs, succ)?;
1250 let edge_weight = graph.edge_weight(edge).unwrap();
1251 let action = visitor(Some((node_weight, edge_weight)), succ_weight);
1252 if !visited.contains(&succ) && action == GraphTraversalAction::Continue {
1253 stack.push(succ);
1254 }
1255 }
1256 }
1257 }
1258
1259 Ok(())
1260 }
1261
1262 pub async fn traverse_all_edges_unordered(
1271 &self,
1272 mut visitor: impl FnMut(
1273 (&'_ SingleModuleGraphModuleNode, &'_ RefData),
1274 &'_ SingleModuleGraphModuleNode,
1275 ) -> Result<()>,
1276 ) -> Result<()> {
1277 let graphs = self.get_graphs().await?;
1278
1279 for graph in &graphs {
1280 let graph = &graph.graph;
1281 for edge in graph.edge_references() {
1282 let source = match graph.node_weight(edge.source()).unwrap() {
1283 SingleModuleGraphNode::Module(node) => node,
1284 SingleModuleGraphNode::VisitedModule { .. } => unreachable!(),
1285 };
1286 let target = match graph.node_weight(edge.target()).unwrap() {
1287 SingleModuleGraphNode::Module(node) => node,
1288 SingleModuleGraphNode::VisitedModule { idx, .. } => get_node!(graphs, idx)?,
1289 };
1290 visitor((source, edge.weight()), target)?;
1291 }
1292 }
1293
1294 Ok(())
1295 }
1296
1297 pub async fn traverse_edges_from_entries_topological<S>(
1316 &self,
1317 entries: impl IntoIterator<Item = ResolvedVc<Box<dyn Module>>>,
1318 state: &mut S,
1319 mut visit_preorder: impl FnMut(
1320 Option<(&'_ SingleModuleGraphModuleNode, &'_ RefData)>,
1321 &'_ SingleModuleGraphModuleNode,
1322 &mut S,
1323 ) -> Result<GraphTraversalAction>,
1324 mut visit_postorder: impl FnMut(
1325 Option<(&'_ SingleModuleGraphModuleNode, &'_ RefData)>,
1326 &'_ SingleModuleGraphModuleNode,
1327 &mut S,
1328 ) -> Result<()>,
1329 ) -> Result<()> {
1330 let graphs = self.get_graphs().await?;
1331
1332 let entries = entries.into_iter().collect::<Vec<_>>();
1333
1334 enum TopologicalPass {
1335 Visit,
1336 ExpandAndVisit,
1337 }
1338 #[allow(clippy::type_complexity)] let mut stack: Vec<(
1340 TopologicalPass,
1341 Option<(GraphNodeIndex, EdgeIndex)>,
1342 GraphNodeIndex,
1343 )> = Vec::with_capacity(entries.len());
1344 for entry in entries.into_iter().rev() {
1345 stack.push((
1346 TopologicalPass::ExpandAndVisit,
1347 None,
1348 ModuleGraph::get_entry(&graphs, entry).await?,
1349 ));
1350 }
1351 let mut expanded = HashSet::new();
1352 while let Some((pass, parent, current)) = stack.pop() {
1353 let parent_arg = match parent {
1354 Some((parent_node, parent_edge)) => Some((
1355 get_node!(graphs, parent_node)?,
1356 graphs[parent_node.graph_idx()]
1357 .graph
1358 .edge_weight(parent_edge)
1359 .unwrap(),
1360 )),
1361 None => None,
1362 };
1363 let current_node = get_node!(graphs, current)?;
1364 match pass {
1365 TopologicalPass::Visit => {
1366 visit_postorder(parent_arg, current_node, state)?;
1367 }
1368 TopologicalPass::ExpandAndVisit => {
1369 let action = visit_preorder(parent_arg, current_node, state)?;
1370 if action == GraphTraversalAction::Exclude {
1371 continue;
1372 }
1373 stack.push((TopologicalPass::Visit, parent, current));
1374 if action == GraphTraversalAction::Continue && expanded.insert(current) {
1375 let graph = &graphs[current.graph_idx()].graph;
1376 let (neighbors_rev, current) = match graph
1377 .node_weight(current.node_idx)
1378 .unwrap()
1379 {
1380 SingleModuleGraphNode::Module(_) => {
1381 (iter_neighbors_rev(graph, current.node_idx), current)
1382 }
1383 SingleModuleGraphNode::VisitedModule { idx, .. } => (
1384 iter_neighbors_rev(&graphs[idx.graph_idx()].graph, idx.node_idx),
1386 *idx,
1387 ),
1388 };
1389 stack.extend(neighbors_rev.map(|(edge, child)| {
1390 (
1391 TopologicalPass::ExpandAndVisit,
1392 Some((current, edge)),
1393 GraphNodeIndex {
1394 graph_idx: current.graph_idx,
1395 node_idx: child,
1396 },
1397 )
1398 }));
1399 }
1400 }
1401 }
1402 }
1403
1404 Ok(())
1405 }
1406
1407 pub async fn traverse_cycles(
1410 &self,
1411 edge_filter: impl Fn(&RefData) -> bool,
1412 mut visit_cycle: impl FnMut(&[&SingleModuleGraphModuleNode]),
1413 ) -> Result<()> {
1414 for graph in &self.graphs {
1415 graph.await?.traverse_cycles(&edge_filter, &mut visit_cycle);
1416 }
1417 Ok(())
1418 }
1419
1420 pub async fn traverse_edges_fixed_point_with_priority<S, P: Ord>(
1442 &self,
1443 entries: impl IntoIterator<Item = (ResolvedVc<Box<dyn Module>>, P)>,
1444 state: &mut S,
1445 mut visit: impl FnMut(
1446 Option<(&'_ SingleModuleGraphModuleNode, &'_ RefData)>,
1447 &'_ SingleModuleGraphModuleNode,
1448 &mut S,
1449 ) -> Result<GraphTraversalAction>,
1450 priority: impl Fn(&'_ SingleModuleGraphModuleNode, &mut S) -> Result<P>,
1451 ) -> Result<usize> {
1452 let graphs = self.get_graphs().await?;
1453
1454 #[derive(PartialEq, Eq)]
1455 struct NodeWithPriority<T: Ord> {
1456 node: GraphNodeIndex,
1457 priority: T,
1458 }
1459 impl<T: Ord> PartialOrd for NodeWithPriority<T> {
1460 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1461 Some(self.cmp(other))
1462 }
1463 }
1464 impl<T: Ord> Ord for NodeWithPriority<T> {
1465 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1466 self.priority
1469 .cmp(&other.priority)
1470 .then(other.node.cmp(&self.node))
1472 }
1473 }
1474
1475 let mut queue_set = FxHashSet::default();
1476 let mut queue = BinaryHeap::from_iter(
1477 entries
1478 .into_iter()
1479 .map(async |(m, priority)| {
1480 Ok(NodeWithPriority {
1481 node: ModuleGraph::get_entry(&graphs, m).await?,
1482 priority,
1483 })
1484 })
1485 .try_join()
1486 .await?,
1487 );
1488 for entry_node in &queue {
1489 visit(None, get_node!(graphs, entry_node.node)?, state)?;
1490 }
1491
1492 let mut visit_count = 0usize;
1493 while let Some(NodeWithPriority { node, .. }) = queue.pop() {
1494 queue_set.remove(&node);
1495 let (node_weight, node) = get_node_idx!(graphs, node)?;
1496 let graph = &graphs[node.graph_idx()].graph;
1497 let neighbors = iter_neighbors_rev(graph, node.node_idx);
1498
1499 visit_count += 1;
1500
1501 for (edge, succ) in neighbors {
1502 let succ = GraphNodeIndex {
1503 graph_idx: node.graph_idx,
1504 node_idx: succ,
1505 };
1506 let (succ_weight, succ) = get_node_idx!(graphs, succ)?;
1507 let edge_weight = graph.edge_weight(edge).unwrap();
1508 let action = visit(Some((node_weight, edge_weight)), succ_weight, state)?;
1509
1510 if action == GraphTraversalAction::Continue && queue_set.insert(succ) {
1511 queue.push(NodeWithPriority {
1512 node: succ,
1513 priority: priority(succ_weight, state)?,
1514 });
1515 }
1516 }
1517 }
1518
1519 Ok(visit_count)
1520 }
1521}
1522
1523#[turbo_tasks::value_impl]
1524impl SingleModuleGraph {
1525 #[turbo_tasks::function]
1526 pub async fn new_with_entries(
1527 entries: Vc<GraphEntries>,
1528 include_traced: bool,
1529 ) -> Result<Vc<Self>> {
1530 SingleModuleGraph::new_inner(&*entries.await?, &Default::default(), include_traced).await
1531 }
1532
1533 #[turbo_tasks::function]
1534 pub async fn new_with_entries_visited(
1535 entries: Vc<GraphEntries>,
1536 visited_modules: Vc<VisitedModules>,
1537 include_traced: bool,
1538 ) -> Result<Vc<Self>> {
1539 SingleModuleGraph::new_inner(
1540 &*entries.await?,
1541 &visited_modules.await?.modules,
1542 include_traced,
1543 )
1544 .await
1545 }
1546
1547 #[turbo_tasks::function]
1548 pub async fn new_with_entries_visited_intern(
1549 entries: GraphEntriesT,
1551 visited_modules: Vc<VisitedModules>,
1552 include_traced: bool,
1553 ) -> Result<Vc<Self>> {
1554 SingleModuleGraph::new_inner(&entries, &visited_modules.await?.modules, include_traced)
1555 .await
1556 }
1557}
1558
1559#[derive(Clone, Debug, Serialize, Deserialize, TraceRawVcs, NonLocalValue)]
1560pub struct SingleModuleGraphModuleNode {
1561 pub module: ResolvedVc<Box<dyn Module>>,
1562}
1563
1564#[derive(Clone, Debug, Serialize, Deserialize, TraceRawVcs, NonLocalValue)]
1565pub enum SingleModuleGraphNode {
1566 Module(SingleModuleGraphModuleNode),
1567 VisitedModule {
1569 idx: GraphNodeIndex,
1570 module: ResolvedVc<Box<dyn Module>>,
1571 },
1572}
1573
1574impl SingleModuleGraphNode {
1575 pub fn module(&self) -> ResolvedVc<Box<dyn Module>> {
1576 match self {
1577 SingleModuleGraphNode::Module(SingleModuleGraphModuleNode { module }) => *module,
1578 SingleModuleGraphNode::VisitedModule { module, .. } => *module,
1579 }
1580 }
1581}
1582
1583#[derive(PartialEq, Eq, Debug)]
1584pub enum GraphTraversalAction {
1585 Continue,
1587 Skip,
1589 Exclude,
1591}
1592
1593#[derive(Clone, Hash, PartialEq, Eq)]
1596enum SingleModuleGraphBuilderNode {
1597 ChunkableReference {
1599 ref_data: RefData,
1600 source: ResolvedVc<Box<dyn Module>>,
1601 target: ResolvedVc<Box<dyn Module>>,
1602 source_ident: ReadRef<RcStr>,
1605 target_ident: ReadRef<RcStr>,
1606 },
1607 Module {
1609 module: ResolvedVc<Box<dyn Module>>,
1610 ident: ReadRef<RcStr>,
1612 },
1613 VisitedModule {
1615 module: ResolvedVc<Box<dyn Module>>,
1616 idx: GraphNodeIndex,
1617 },
1618}
1619
1620impl SingleModuleGraphBuilderNode {
1621 async fn new_module(module: ResolvedVc<Box<dyn Module>>) -> Result<Self> {
1622 let ident = module.ident();
1623 Ok(Self::Module {
1624 module,
1625 ident: ident.to_string().await?,
1626 })
1627 }
1628 async fn new_chunkable_ref(
1629 source: ResolvedVc<Box<dyn Module>>,
1630 target: ResolvedVc<Box<dyn Module>>,
1631 ref_data: RefData,
1632 ) -> Result<Self> {
1633 Ok(Self::ChunkableReference {
1634 ref_data,
1635 source,
1636 source_ident: source.ident().to_string().await?,
1637 target,
1638 target_ident: target.ident().to_string().await?,
1639 })
1640 }
1641 fn new_visited_module(module: ResolvedVc<Box<dyn Module>>, idx: GraphNodeIndex) -> Self {
1642 Self::VisitedModule { module, idx }
1643 }
1644}
1645struct SingleModuleGraphBuilderEdge {
1646 to: SingleModuleGraphBuilderNode,
1647 export: ExportUsage,
1648}
1649
1650const COMMON_CHUNKING_TYPE: ChunkingType = ChunkingType::Parallel {
1653 inherit_async: true,
1654 hoisted: true,
1655};
1656
1657struct SingleModuleGraphBuilder<'a> {
1658 visited_modules: &'a FxIndexMap<ResolvedVc<Box<dyn Module>>, GraphNodeIndex>,
1659 include_traced: bool,
1661}
1662impl Visit<(SingleModuleGraphBuilderNode, ExportUsage)> for SingleModuleGraphBuilder<'_> {
1663 type Edge = SingleModuleGraphBuilderEdge;
1664 type EdgesIntoIter = Vec<Self::Edge>;
1665 type EdgesFuture = impl Future<Output = Result<Self::EdgesIntoIter>>;
1666
1667 fn visit(
1668 &mut self,
1669 edge: Self::Edge,
1670 ) -> VisitControlFlow<(SingleModuleGraphBuilderNode, ExportUsage)> {
1671 match edge.to {
1672 SingleModuleGraphBuilderNode::Module { .. } => {
1673 VisitControlFlow::Continue((edge.to, edge.export))
1674 }
1675 SingleModuleGraphBuilderNode::ChunkableReference { ref ref_data, .. } => {
1676 match &ref_data.chunking_type {
1677 ChunkingType::Traced => VisitControlFlow::Skip((edge.to, edge.export)),
1678 _ => VisitControlFlow::Continue((edge.to, edge.export)),
1679 }
1680 }
1681 SingleModuleGraphBuilderNode::VisitedModule { .. } => {
1683 VisitControlFlow::Skip((edge.to, edge.export))
1684 }
1685 }
1686 }
1687
1688 fn edges(
1689 &mut self,
1690 (node, _): &(SingleModuleGraphBuilderNode, ExportUsage),
1693 ) -> Self::EdgesFuture {
1694 let (module, chunkable_ref_target) = match node {
1696 SingleModuleGraphBuilderNode::Module { module, .. } => (Some(*module), None),
1697 SingleModuleGraphBuilderNode::ChunkableReference {
1698 target, ref_data, ..
1699 } => (None, Some((*target, ref_data.export.clone()))),
1700 SingleModuleGraphBuilderNode::VisitedModule { .. } => unreachable!(),
1702 };
1703 let visited_modules = self.visited_modules;
1704 let include_traced = self.include_traced;
1705 async move {
1706 Ok(match (module, chunkable_ref_target) {
1707 (Some(module), None) => {
1708 let refs_cell = primary_chunkable_referenced_modules(*module, include_traced);
1709 let refs = match refs_cell.await {
1710 Ok(refs) => refs,
1711 Err(e) => {
1712 return Err(e.context(module.ident().to_string().await?));
1713 }
1714 };
1715
1716 refs.iter()
1717 .flat_map(|(ty, export, modules)| {
1718 modules.iter().map(|m| (ty.clone(), export.clone(), *m))
1719 })
1720 .map(async |(ty, export, target)| {
1721 let to = if ty == COMMON_CHUNKING_TYPE {
1722 if let Some(idx) = visited_modules.get(&target) {
1723 SingleModuleGraphBuilderNode::new_visited_module(target, *idx)
1724 } else {
1725 SingleModuleGraphBuilderNode::new_module(target).await?
1726 }
1727 } else {
1728 SingleModuleGraphBuilderNode::new_chunkable_ref(
1729 module,
1730 target,
1731 RefData {
1732 chunking_type: ty,
1733 export: export.clone(),
1734 },
1735 )
1736 .await?
1737 };
1738 Ok(SingleModuleGraphBuilderEdge { to, export })
1739 })
1740 .try_join()
1741 .await?
1742 }
1743 (None, Some((chunkable_ref_target, export))) => {
1744 vec![SingleModuleGraphBuilderEdge {
1745 to: if let Some(idx) = visited_modules.get(&chunkable_ref_target) {
1746 SingleModuleGraphBuilderNode::new_visited_module(
1747 chunkable_ref_target,
1748 *idx,
1749 )
1750 } else {
1751 SingleModuleGraphBuilderNode::new_module(chunkable_ref_target).await?
1752 },
1753 export,
1754 }]
1755 }
1756 _ => unreachable!(),
1757 })
1758 }
1759 }
1760
1761 fn span(&mut self, (node, _): &(SingleModuleGraphBuilderNode, ExportUsage)) -> tracing::Span {
1762 match node {
1763 SingleModuleGraphBuilderNode::Module { ident, .. } => {
1764 tracing::info_span!("module", name = display(ident))
1765 }
1766
1767 SingleModuleGraphBuilderNode::ChunkableReference {
1768 ref_data,
1769 source_ident,
1770 target_ident,
1771 ..
1772 } => match &ref_data.chunking_type {
1773 ChunkingType::Parallel {
1774 inherit_async: false,
1775 ..
1776 } => Span::current(),
1777 _ => {
1778 tracing::info_span!(
1779 "chunkable reference",
1780 ty = debug(&ref_data.chunking_type),
1781 source = display(source_ident),
1782 target = display(target_ident)
1783 )
1784 }
1785 },
1786 SingleModuleGraphBuilderNode::VisitedModule { .. } => {
1787 tracing::info_span!("visited module")
1788 }
1789 }
1790 }
1791}