@@ -2,10 +2,12 @@ use std::{
22 collections:: { BTreeSet , BinaryHeap , VecDeque } ,
33 future:: Future ,
44 ops:: Deref ,
5+ pin:: Pin ,
56} ;
67
78use anyhow:: { Context , Result , bail} ;
89use bincode:: { Decode , Encode } ;
10+ use futures:: { StreamExt , stream:: FuturesUnordered } ;
911use petgraph:: {
1012 Direction ,
1113 graph:: { DiGraph , EdgeIndex , NodeIndex } ,
@@ -19,13 +21,14 @@ use turbo_tasks::{
1921 CollectiblesSource , FxIndexMap , NonLocalValue , OperationVc , ReadRef , ResolvedVc , TaskInput ,
2022 TryFlatJoinIterExt , TryJoinIterExt , ValueToString , Vc ,
2123 debug:: ValueDebugFormat ,
22- graph:: { AdjacencyMap , GraphTraversal , Visit , VisitControlFlow } ,
24+ graph:: { AdjacencyMap , GraphStore , Visit , VisitControlFlow } ,
2325 trace:: TraceRawVcs ,
2426} ;
2527use turbo_tasks_fs:: FileSystemPath ;
2628
2729use crate :: {
2830 chunk:: { AsyncModuleInfo , ChunkingContext , ChunkingType } ,
31+ emit_collect:: CollectingModule ,
2932 issue:: { ImportTracer , ImportTraces , Issue } ,
3033 module:: Module ,
3134 module_graph:: {
@@ -317,19 +320,17 @@ impl SingleModuleGraph {
317320 . try_join ( )
318321 . await ?;
319322
320- let children_nodes_iter = AdjacencyMap :: new ( )
323+ let children_nodes_iter = DeferringAdjacencyMap :: new ( collecting_mode )
321324 . visit (
322325 root_nodes,
323326 SingleModuleGraphBuilder {
324327 visited_modules,
325328 emit_spans,
326329 include_traced,
327330 include_binding_usage,
328- collecting_mode,
329331 } ,
330332 )
331- . await
332- . completed ( ) ?;
333+ . await ?;
333334 let node_count = children_nodes_iter. len ( ) ;
334335
335336 let mut graph: DiGraph < SingleModuleGraphNode , RefData > = DiGraph :: with_capacity (
@@ -1618,6 +1619,222 @@ impl SingleModuleGraphBuilderNode {
16181619 }
16191620}
16201621
1622+ type EdgesFutureOutput = (
1623+ SingleModuleGraphBuilderNode ,
1624+ Span ,
1625+ Result < Vec < ( SingleModuleGraphBuilderNode , RefData ) > > ,
1626+ ) ;
1627+
1628+ /// A graph traversal wrapper that defers visiting `ChunkingType::Emitted` references based on
1629+ /// [`GraphCollectingMode`]:
1630+ /// - `CompleteGraph`: defers emitted references until a [`CollectingModule`] with matching
1631+ /// `namespace()` is discovered. Unmatched emitted references are discarded.
1632+ /// - `IncompleteGraph`: excludes emitted references whose `merge_tag` is in
1633+ /// `ignored_collected_namespace`. Other emitted references are traversed normally.
1634+ struct DeferringAdjacencyMap {
1635+ collecting_mode : GraphCollectingMode ,
1636+ }
1637+
1638+ impl DeferringAdjacencyMap {
1639+ fn new ( collecting_mode : GraphCollectingMode ) -> Self {
1640+ Self { collecting_mode }
1641+ }
1642+
1643+ async fn visit < ' a > (
1644+ self ,
1645+ root_nodes : impl IntoIterator < Item = SingleModuleGraphBuilderNode > ,
1646+ mut visit : SingleModuleGraphBuilder < ' a > ,
1647+ ) -> Result < AdjacencyMap < SingleModuleGraphBuilderNode , RefData > > {
1648+ use turbo_tasks:: graph:: { GraphStore , VisitControlFlow } ;
1649+
1650+ let mut store = AdjacencyMap :: < SingleModuleGraphBuilderNode , RefData > :: new ( ) ;
1651+ let mut futures: FuturesUnordered <
1652+ Pin < Box < dyn Future < Output = EdgesFutureOutput > + Send + ' a > > ,
1653+ > = FuturesUnordered :: new ( ) ;
1654+
1655+ // Deferred emitted references: (parent_handle, target_node, edge_data)
1656+ let mut deferred: Vec < (
1657+ SingleModuleGraphBuilderNode ,
1658+ SingleModuleGraphBuilderNode ,
1659+ RefData ,
1660+ ) > = Vec :: new ( ) ;
1661+ let mut discovered_namespaces: FxHashSet < RcStr > = FxHashSet :: default ( ) ;
1662+
1663+ // Process root nodes
1664+ for node in root_nodes {
1665+ match visit. visit ( & node, None ) {
1666+ VisitControlFlow :: Continue => {
1667+ if let Some ( handle) = store. try_enter ( & node) {
1668+ let span = visit. span ( & node, None ) ;
1669+ let edges_future = visit. edges ( & node) ;
1670+ futures. push ( Box :: pin ( async move {
1671+ let result = edges_future. await ;
1672+ ( handle, span, result)
1673+ } ) ) ;
1674+ }
1675+ self . check_collecting_module (
1676+ & node,
1677+ & mut discovered_namespaces,
1678+ & mut deferred,
1679+ & mut store,
1680+ & mut visit,
1681+ & mut futures,
1682+ )
1683+ . await ?;
1684+ store. insert ( None , node) ;
1685+ }
1686+ VisitControlFlow :: Skip => {
1687+ store. insert ( None , node) ;
1688+ }
1689+ VisitControlFlow :: Exclude => {
1690+ // do nothing
1691+ }
1692+ }
1693+ }
1694+
1695+ let mut result = Ok ( ( ) ) ;
1696+ loop {
1697+ match futures. next ( ) . await {
1698+ Some ( ( parent_handle, span, Ok ( edges) ) ) => {
1699+ let _guard = span. enter ( ) ;
1700+ for ( node, edge) in edges {
1701+ match visit. visit ( & node, Some ( & edge) ) {
1702+ VisitControlFlow :: Continue => {
1703+ // Potentially defer visiting emitted references
1704+ if let ChunkingType :: Emitted { merge_tag, .. } = & edge. chunking_type
1705+ {
1706+ match & self . collecting_mode {
1707+ GraphCollectingMode :: CompleteGraph => {
1708+ if !discovered_namespaces. contains ( merge_tag) {
1709+ deferred. push ( ( parent_handle. clone ( ) , node, edge) ) ;
1710+ continue ;
1711+ }
1712+ }
1713+ GraphCollectingMode :: IncompleteGraph {
1714+ ignored_collected_namespace,
1715+ } => {
1716+ if ignored_collected_namespace. contains ( merge_tag) {
1717+ continue ;
1718+ }
1719+ }
1720+ }
1721+ }
1722+
1723+ if let Some ( handle) = store. try_enter ( & node) {
1724+ let span = visit. span ( & node, Some ( & edge) ) ;
1725+ let edges_future = visit. edges ( & node) ;
1726+ futures. push ( Box :: pin ( async move {
1727+ let result = edges_future. await ;
1728+ ( handle, span, result)
1729+ } ) ) ;
1730+ }
1731+ self . check_collecting_module (
1732+ & node,
1733+ & mut discovered_namespaces,
1734+ & mut deferred,
1735+ & mut store,
1736+ & mut visit,
1737+ & mut futures,
1738+ )
1739+ . await ?;
1740+ store. insert ( Some ( ( & parent_handle, edge) ) , node) ;
1741+ }
1742+ VisitControlFlow :: Skip => {
1743+ store. insert ( Some ( ( & parent_handle, edge) ) , node) ;
1744+ }
1745+ VisitControlFlow :: Exclude => {
1746+ // do nothing
1747+ }
1748+ }
1749+ }
1750+ }
1751+ Some ( ( _, _, Err ( err) ) ) => {
1752+ result = Err ( err) ;
1753+ }
1754+ None => break ,
1755+ }
1756+ }
1757+
1758+ result. map ( |( ) | store)
1759+ }
1760+
1761+ /// If the node is a [`CollectingModule`], discover its namespace and release any matching
1762+ /// deferred emitted references. Handles cascading: a released deferred node might itself be a
1763+ /// `CollectingModule`.
1764+ #[ allow( clippy:: too_many_arguments) ]
1765+ async fn check_collecting_module < ' a > (
1766+ & self ,
1767+ node : & SingleModuleGraphBuilderNode ,
1768+ discovered_namespaces : & mut FxHashSet < RcStr > ,
1769+ deferred : & mut Vec < (
1770+ SingleModuleGraphBuilderNode ,
1771+ SingleModuleGraphBuilderNode ,
1772+ RefData ,
1773+ ) > ,
1774+ store : & mut AdjacencyMap < SingleModuleGraphBuilderNode , RefData > ,
1775+ visit : & mut SingleModuleGraphBuilder < ' a > ,
1776+ futures : & mut FuturesUnordered <
1777+ Pin < Box < dyn Future < Output = EdgesFutureOutput > + Send + ' a > > ,
1778+ > ,
1779+ ) -> Result < ( ) > {
1780+ let SingleModuleGraphBuilderNode :: Module { module, .. } = node else {
1781+ return Ok ( ( ) ) ;
1782+ } ;
1783+ let Some ( module) = ResolvedVc :: try_downcast :: < Box < dyn CollectingModule > > ( * module) else {
1784+ return Ok ( ( ) ) ;
1785+ } ;
1786+
1787+ let mut collecting_modules = vec ! [ module] ;
1788+ while let Some ( module) = collecting_modules. pop ( ) {
1789+ let ns = module. namespace ( ) . await ?;
1790+ if discovered_namespaces. contains ( & * ns) {
1791+ continue ;
1792+ }
1793+ discovered_namespaces. insert ( ( * ns) . clone ( ) ) ;
1794+
1795+ // Drain matching deferred entries
1796+ let mut i = 0 ;
1797+ while i < deferred. len ( ) {
1798+ if matches ! (
1799+ & deferred[ i] . 2 . chunking_type,
1800+ ChunkingType :: Emitted { merge_tag, .. } if merge_tag == & * ns
1801+ ) {
1802+ let ( parent_handle, def_node, edge) = deferred. swap_remove ( i) ;
1803+ match visit. visit ( & def_node, Some ( & edge) ) {
1804+ VisitControlFlow :: Continue => {
1805+ if let Some ( handle) = store. try_enter ( & def_node) {
1806+ let span = visit. span ( & def_node, Some ( & edge) ) ;
1807+ let edges_future = visit. edges ( & def_node) ;
1808+ futures. push ( Box :: pin ( async move {
1809+ let result = edges_future. await ;
1810+ ( handle, span, result)
1811+ } ) ) ;
1812+ }
1813+ if let SingleModuleGraphBuilderNode :: Module { module, .. } = & def_node
1814+ && let Some ( module) =
1815+ ResolvedVc :: try_downcast :: < Box < dyn CollectingModule > > ( * module)
1816+ {
1817+ collecting_modules. push ( module) ;
1818+ }
1819+ store. insert ( Some ( ( & parent_handle, edge) ) , def_node) ;
1820+ }
1821+ VisitControlFlow :: Skip => {
1822+ store. insert ( Some ( ( & parent_handle, edge) ) , def_node) ;
1823+ }
1824+ VisitControlFlow :: Exclude => {
1825+ // do nothing
1826+ }
1827+ }
1828+ } else {
1829+ i += 1 ;
1830+ }
1831+ }
1832+ }
1833+
1834+ Ok ( ( ) )
1835+ }
1836+ }
1837+
16211838struct SingleModuleGraphBuilder < ' a > {
16221839 visited_modules : & ' a FxIndexMap < ResolvedVc < Box < dyn Module > > , GraphNodeIndex > ,
16231840
@@ -1628,8 +1845,6 @@ struct SingleModuleGraphBuilder<'a> {
16281845
16291846 /// Whether to read ModuleReference::binding_usage()
16301847 include_binding_usage : bool ,
1631-
1632- collecting_mode : GraphCollectingMode ,
16331848}
16341849impl Visit < SingleModuleGraphBuilderNode , RefData > for SingleModuleGraphBuilder < ' _ > {
16351850 type EdgesIntoIter = Vec < ( SingleModuleGraphBuilderNode , RefData ) > ;
0 commit comments