@@ -8,7 +8,7 @@ use std::{ffi::OsStr, fmt::Debug, sync::Arc};
88
99use cache:: ExecutionCache ;
1010pub use cache:: { CacheMiss , FingerprintMismatch } ;
11- use clap:: { Parser , Subcommand } ;
11+ use clap:: Parser ;
1212pub use event:: ExecutionEvent ;
1313use once_cell:: sync:: OnceCell ;
1414pub use reporter:: { LabeledReporter , Reporter } ;
@@ -22,10 +22,7 @@ use vite_task_plan::{
2222} ;
2323use vite_workspace:: { WorkspaceRoot , find_workspace_root} ;
2424
25- use crate :: {
26- cli:: { ParsedTaskCLIArgs , TaskCLIArgs } ,
27- collections:: HashMap ,
28- } ;
25+ use crate :: { cli:: BuiltInCommand , collections:: HashMap } ;
2926
3027#[ derive( Debug ) ]
3128enum LazyTaskGraph < ' a > {
@@ -52,87 +49,76 @@ impl TaskGraphLoader for LazyTaskGraph<'_> {
5249 }
5350}
5451
55- pub struct SessionCallbacks < ' a , CustomSubcommand > {
56- pub task_synthesizer : & ' a mut ( dyn TaskSynthesizer < CustomSubcommand > + ' a ) ,
52+ pub struct SessionCallbacks < ' a > {
53+ pub task_synthesizer : & ' a mut ( dyn TaskSynthesizer + ' a ) ,
5754 pub user_config_loader : & ' a mut ( dyn UserConfigLoader + ' a ) ,
5855}
5956
57+ /// Handles synthesizing task plan requests from commands found in task scripts.
58+ ///
59+ /// When a task's command references a known program (e.g., `vite lint` in a script),
60+ /// the synthesizer converts it into a `SyntheticPlanRequest` for execution.
6061#[ async_trait:: async_trait( ?Send ) ]
61- pub trait TaskSynthesizer < CustomSubcommand > : Debug {
62- fn should_synthesize_for_program ( & self , program : & str ) -> bool ;
63-
64- /// Synthesize a synthetic task plan request for the given parsed custom subcommand.
62+ pub trait TaskSynthesizer : Debug {
63+ /// Called for every command in task scripts to determine if it should be synthesized.
6564 ///
65+ /// - `program` is the program name (e.g., `"vite"`).
66+ /// - `args` is all arguments after the program (e.g., `["lint", "--fix"]`).
6667 /// - `envs` is the current environment variables where the task is being planned.
6768 /// - `cwd` is the current working directory where the task is being planned.
6869 ///
69- /// The implementor can return a different `envs` in `SyntheticPlanRequest` to customize
70- /// environment variables for the synthetic task .
70+ /// Returns `Ok(Some(request))` if the command is recognized and should be synthesized,
71+ /// `Ok(None)` if the command should be executed as a normal process .
7172 async fn synthesize_task (
7273 & mut self ,
73- subcommand : CustomSubcommand ,
74+ program : & str ,
75+ args : & [ Str ] ,
7476 envs : & Arc < HashMap < Arc < OsStr > , Arc < OsStr > > > ,
7577 cwd : & Arc < AbsolutePath > ,
76- ) -> anyhow:: Result < SyntheticPlanRequest > ;
78+ ) -> anyhow:: Result < Option < SyntheticPlanRequest > > ;
7779}
7880
7981#[ derive( derive_more:: Debug ) ]
80- #[ debug( bound( ) ) ] // Avoid requiring CustomSubcommand: Debug
81- struct PlanRequestParser < ' a , CustomSubcommand > {
82- task_synthesizer : & ' a mut ( dyn TaskSynthesizer < CustomSubcommand > + ' a ) ,
83- }
84-
85- impl < CustomSubcommand : clap:: Subcommand > PlanRequestParser < ' _ , CustomSubcommand > {
86- async fn get_plan_request_from_cli_args (
87- & mut self ,
88- cli_args : ParsedTaskCLIArgs < CustomSubcommand > ,
89- envs : & Arc < HashMap < Arc < OsStr > , Arc < OsStr > > > ,
90- cwd : & Arc < AbsolutePath > ,
91- ) -> anyhow:: Result < PlanRequest > {
92- match cli_args {
93- ParsedTaskCLIArgs :: BuiltIn ( vite_task_subcommand) => {
94- Ok ( vite_task_subcommand. into_plan_request ( cwd) ?)
95- }
96- ParsedTaskCLIArgs :: Custom ( custom_subcommand) => {
97- let synthetic_plan_request =
98- self . task_synthesizer . synthesize_task ( custom_subcommand, envs, cwd) . await ?;
99- Ok ( PlanRequest :: Synthetic ( synthetic_plan_request) )
100- }
101- }
102- }
82+ struct PlanRequestParser < ' a > {
83+ task_synthesizer : & ' a mut ( dyn TaskSynthesizer + ' a ) ,
10384}
10485
10586#[ async_trait:: async_trait( ?Send ) ]
106- impl < CustomSubcommand : clap:: Subcommand > vite_task_plan:: PlanRequestParser
107- for PlanRequestParser < ' _ , CustomSubcommand >
108- {
87+ impl vite_task_plan:: PlanRequestParser for PlanRequestParser < ' _ > {
10988 async fn get_plan_request (
11089 & mut self ,
11190 program : & str ,
11291 args : & [ Str ] ,
11392 envs : & Arc < HashMap < Arc < OsStr > , Arc < OsStr > > > ,
11493 cwd : & Arc < AbsolutePath > ,
11594 ) -> anyhow:: Result < Option < PlanRequest > > {
116- Ok (
117- if self . task_synthesizer . should_synthesize_for_program ( program)
118- && let Some ( subcommand) = args. first ( )
119- && ParsedTaskCLIArgs :: < CustomSubcommand > :: has_subcommand ( subcommand)
120- {
121- let cli_args = ParsedTaskCLIArgs :: < CustomSubcommand > :: try_parse_from (
122- std:: iter:: once ( program) . chain ( args. iter ( ) . map ( Str :: as_str) ) ,
123- ) ?;
124- Some ( self . get_plan_request_from_cli_args ( cli_args, envs, cwd) . await ?)
125- } else {
126- None
127- } ,
128- )
95+ // Try task synthesizer first (handles e.g. "vite lint" in scripts)
96+ if let Some ( synthetic) =
97+ self . task_synthesizer . synthesize_task ( program, args, envs, cwd) . await ?
98+ {
99+ return Ok ( Some ( PlanRequest :: Synthetic ( synthetic) ) ) ;
100+ }
101+
102+ // Try built-in "run" command (handles "vite run build" in scripts)
103+ #[ derive( Parser ) ]
104+ enum BuiltInParser {
105+ #[ clap( flatten) ]
106+ Command ( BuiltInCommand ) ,
107+ }
108+ if let Ok ( BuiltInParser :: Command ( built_in) ) = BuiltInParser :: try_parse_from (
109+ std:: iter:: once ( program) . chain ( args. iter ( ) . map ( Str :: as_str) ) ,
110+ ) {
111+ return Ok ( Some ( built_in. into_plan_request ( cwd) ?) ) ;
112+ }
113+
114+ Ok ( None )
129115 }
130116}
131117
132118/// Represents a vite task session for planning and executing tasks. A process typically has one session.
133119///
134120/// A session manages task graph loading internally and provides non-consuming methods to plan and/or execute tasks (allows multiple plans/executions per session).
135- pub struct Session < ' a , CustomSubcommand > {
121+ pub struct Session < ' a > {
136122 workspace_path : Arc < AbsolutePath > ,
137123 /// A session doesn't necessarily load the task graph immediately.
138124 /// The task graph is loaded on-demand and cached for future use.
@@ -141,7 +127,7 @@ pub struct Session<'a, CustomSubcommand> {
141127 envs : Arc < HashMap < Arc < OsStr > , Arc < OsStr > > > ,
142128 cwd : Arc < AbsolutePath > ,
143129
144- plan_request_parser : PlanRequestParser < ' a , CustomSubcommand > ,
130+ plan_request_parser : PlanRequestParser < ' a > ,
145131
146132 /// Cache is lazily initialized to avoid SQLite race conditions when multiple
147133 /// processes (e.g., parallel `vite lib` commands) start simultaneously.
@@ -157,9 +143,9 @@ fn get_cache_path_of_workspace(workspace_root: &AbsolutePath) -> AbsolutePathBuf
157143 }
158144}
159145
160- impl < ' a , CustomSubcommand > Session < ' a , CustomSubcommand > {
146+ impl < ' a > Session < ' a > {
161147 /// Initialize a session with real environment variables and cwd
162- pub fn init ( callbacks : SessionCallbacks < ' a , CustomSubcommand > ) -> anyhow:: Result < Self > {
148+ pub fn init ( callbacks : SessionCallbacks < ' a > ) -> anyhow:: Result < Self > {
163149 let envs = std:: env:: vars_os ( )
164150 . map ( |( k, v) | ( Arc :: < OsStr > :: from ( k. as_os_str ( ) ) , Arc :: < OsStr > :: from ( v. as_os_str ( ) ) ) )
165151 . collect ( ) ;
@@ -176,7 +162,7 @@ impl<'a, CustomSubcommand> Session<'a, CustomSubcommand> {
176162 pub fn init_with (
177163 mut envs : HashMap < Arc < OsStr > , Arc < OsStr > > ,
178164 cwd : Arc < AbsolutePath > ,
179- callbacks : SessionCallbacks < ' a , CustomSubcommand > ,
165+ callbacks : SessionCallbacks < ' a > ,
180166 ) -> anyhow:: Result < Self > {
181167 let ( workspace_root, _) = find_workspace_root ( & cwd) ?;
182168 let cache_path = get_cache_path_of_workspace ( & workspace_root. path ) ;
@@ -217,9 +203,7 @@ impl<'a, CustomSubcommand> Session<'a, CustomSubcommand> {
217203 _ => None ,
218204 }
219205 }
220- }
221206
222- impl < ' a , CustomSubcommand : clap:: Subcommand > Session < ' a , CustomSubcommand > {
223207 pub async fn plan_synthetic_task (
224208 & mut self ,
225209 synthetic_plan_request : SyntheticPlanRequest ,
@@ -239,21 +223,17 @@ impl<'a, CustomSubcommand: clap::Subcommand> Session<'a, CustomSubcommand> {
239223 pub async fn plan_from_cli (
240224 & mut self ,
241225 cwd : Arc < AbsolutePath > ,
242- cli_args : TaskCLIArgs < CustomSubcommand > ,
226+ command : BuiltInCommand ,
243227 ) -> Result < ExecutionPlan , vite_task_plan:: Error > {
244- let plan_request = self
245- . plan_request_parser
246- . get_plan_request_from_cli_args ( cli_args. parsed , & self . envs , & cwd)
247- . await
248- . map_err ( |error| {
249- TaskPlanErrorKind :: ParsePlanRequestError {
250- error,
251- program : cli_args. original [ 0 ] . clone ( ) ,
252- args : cli_args. original . iter ( ) . skip ( 1 ) . cloned ( ) . collect ( ) ,
253- cwd : Arc :: clone ( & cwd) ,
254- }
255- . with_empty_call_stack ( )
256- } ) ?;
228+ let plan_request = command. into_plan_request ( & cwd) . map_err ( |error| {
229+ TaskPlanErrorKind :: ParsePlanRequestError {
230+ error : error. into ( ) ,
231+ program : Str :: from ( "vite" ) ,
232+ args : Default :: default ( ) ,
233+ cwd : Arc :: clone ( & cwd) ,
234+ }
235+ . with_empty_call_stack ( )
236+ } ) ?;
257237 let plan = ExecutionPlan :: plan (
258238 plan_request,
259239 & self . workspace_path ,
0 commit comments