@@ -7,7 +7,8 @@ use futures_util::FutureExt;
77use tokio:: io:: AsyncWriteExt as _;
88use vite_path:: AbsolutePath ;
99use vite_task_plan:: {
10- ExecutionGraph , ExecutionItemKind , LeafExecutionKind , SpawnCommand , SpawnExecution ,
10+ ExecutionGraph , ExecutionItemDisplay , ExecutionItemKind , LeafExecutionKind , SpawnCommand ,
11+ SpawnExecution ,
1112} ;
1213
1314use self :: {
@@ -21,8 +22,8 @@ use super::{
2122 ExecutionError ,
2223 } ,
2324 reporter:: {
24- ExitStatus , GraphExecutionReporter , GraphExecutionReporterBuilder , LeafExecutionPath ,
25- LeafExecutionReporter , StdioSuggestion ,
25+ ExitStatus , GraphExecutionReporter , GraphExecutionReporterBuilder , LeafExecutionReporter ,
26+ StdioSuggestion ,
2627 } ,
2728} ;
2829use crate :: { Session , session:: execute:: spawn:: SpawnTrackResult } ;
@@ -64,16 +65,18 @@ impl ExecutionContext<'_> {
6465 /// We compute a topological order and iterate in reverse to get execution order
6566 /// (dependencies before dependents).
6667 ///
67- /// The `path_prefix` tracks our position within nested execution graphs. For the
68- /// root call this is an empty path; for nested `Expanded` items it carries the
69- /// path so far.
68+ /// `all_ancestors_single_node` tracks whether every graph in the ancestry chain
69+ /// (from the root down to this level) contains exactly one node. The initial call
70+ /// passes `graph.node_count() == 1`; recursive calls AND with the nested graph's
71+ /// node count.
72+ ///
7073 /// Leaf-level errors are reported through the reporter and do not abort the graph.
7174 /// Cycle detection is handled at plan time, so this function cannot encounter cycles.
7275 #[ expect( clippy:: future_not_send, reason = "uses !Send types internally" ) ]
7376 async fn execute_expanded_graph (
7477 & mut self ,
7578 graph : & ExecutionGraph ,
76- path_prefix : & LeafExecutionPath ,
79+ all_ancestors_single_node : bool ,
7780 ) {
7881 // `compute_topological_order()` returns nodes in topological order: for every
7982 // edge A→B, A appears before B. Since our edges mean "A depends on B",
@@ -86,18 +89,24 @@ impl ExecutionContext<'_> {
8689 for & node_ix in topo_order. iter ( ) . rev ( ) {
8790 let task_execution = & graph[ node_ix] ;
8891
89- for ( item_idx, item) in task_execution. items . iter ( ) . enumerate ( ) {
90- // Build the path for this item by appending to the prefix
91- let mut item_path = path_prefix. clone ( ) ;
92- item_path. push ( node_ix, item_idx) ;
93-
92+ for item in & task_execution. items {
9493 match & item. kind {
9594 ExecutionItemKind :: Leaf ( leaf_kind) => {
96- self . execute_leaf ( & item_path, leaf_kind) . boxed_local ( ) . await ;
95+ self . execute_leaf (
96+ & item. execution_item_display ,
97+ leaf_kind,
98+ all_ancestors_single_node,
99+ )
100+ . boxed_local ( )
101+ . await ;
97102 }
98103 ExecutionItemKind :: Expanded ( nested_graph) => {
99- // Recurse into the nested graph, carrying the path prefix forward.
100- self . execute_expanded_graph ( nested_graph, & item_path) . boxed_local ( ) . await ;
104+ self . execute_expanded_graph (
105+ nested_graph,
106+ all_ancestors_single_node && nested_graph. node_count ( ) == 1 ,
107+ )
108+ . boxed_local ( )
109+ . await ;
101110 }
102111 }
103112 }
@@ -111,12 +120,14 @@ impl ExecutionContext<'_> {
111120 #[ expect( clippy:: future_not_send, reason = "uses !Send types internally" ) ]
112121 async fn execute_leaf (
113122 & mut self ,
114- path : & LeafExecutionPath ,
115- leaf_execution_kind : & LeafExecutionKind ,
123+ display : & ExecutionItemDisplay ,
124+ leaf_kind : & LeafExecutionKind ,
125+ all_ancestors_single_node : bool ,
116126 ) {
117- let mut leaf_reporter = self . reporter . new_leaf_execution ( path) ;
127+ let mut leaf_reporter =
128+ self . reporter . new_leaf_execution ( display, leaf_kind, all_ancestors_single_node) ;
118129
119- match leaf_execution_kind {
130+ match leaf_kind {
120131 LeafExecutionKind :: InProcess ( in_process_execution) => {
121132 // In-process (built-in) commands: caching is disabled, execute synchronously
122133 let mut stdio_config = leaf_reporter
@@ -401,10 +412,7 @@ impl Session<'_> {
401412 }
402413 } ;
403414
404- // Wrap the graph in Arc so both the reporter and execution can reference it.
405- // The reporter clones the Arc internally for display lookups.
406- let graph = Arc :: new ( execution_graph) ;
407- let mut reporter = builder. build ( & graph) ;
415+ let mut reporter = builder. build ( ) ;
408416
409417 let mut execution_context = ExecutionContext {
410418 reporter : & mut * reporter,
@@ -414,7 +422,8 @@ impl Session<'_> {
414422
415423 // Execute the graph. Leaf-level errors are reported through the reporter
416424 // and do not abort the graph. Cycle detection is handled at plan time.
417- execution_context. execute_expanded_graph ( & graph, & LeafExecutionPath :: default ( ) ) . await ;
425+ let all_single_node = execution_graph. node_count ( ) == 1 ;
426+ execution_context. execute_expanded_graph ( & execution_graph, all_single_node) . await ;
418427
419428 // Leaf-level errors and non-zero exit statuses are tracked internally
420429 // by the reporter.
0 commit comments