44//! It handles argument parsing, command dispatching, and orchestration of the task execution.
55
66use std:: {
7- borrow:: Cow , env, ffi:: OsStr , future:: Future , io:: IsTerminal , iter, path :: PathBuf , pin :: Pin ,
8- process :: Stdio , sync:: Arc , time:: Instant ,
7+ borrow:: Cow , env, ffi:: OsStr , future:: Future , io:: IsTerminal , iter, pin :: Pin , process :: Stdio ,
8+ sync:: Arc , time:: Instant ,
99} ;
1010
1111use clap:: {
@@ -15,7 +15,6 @@ use clap::{
1515use owo_colors:: OwoColorize ;
1616use rustc_hash:: FxHashMap ;
1717use serde:: { Deserialize , Serialize } ;
18- use tokio:: fs:: write;
1918use vite_error:: Error ;
2019use vite_path:: { AbsolutePath , AbsolutePathBuf } ;
2120use vite_shared:: { PrependOptions , output, prepend_to_path_env} ;
@@ -185,144 +184,27 @@ impl ResolvedSubcommand {
185184pub struct SubcommandResolver {
186185 cli_options : Option < CliOptions > ,
187186 workspace_path : Arc < AbsolutePath > ,
188- /// Track temporary config files created during resolution for cleanup
189- temp_config_files : Vec < AbsolutePathBuf > ,
190187}
191188
192189impl std:: fmt:: Debug for SubcommandResolver {
193190 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
194191 f. debug_struct ( "SubcommandResolver" )
195192 . field ( "has_cli_options" , & self . cli_options . is_some ( ) )
196193 . field ( "workspace_path" , & self . workspace_path )
197- . field ( "temp_config_files_count" , & self . temp_config_files . len ( ) )
198194 . finish ( )
199195 }
200196}
201197
202198impl SubcommandResolver {
203199 pub fn new ( workspace_path : Arc < AbsolutePath > ) -> Self {
204- Self { cli_options : None , workspace_path, temp_config_files : Vec :: new ( ) }
200+ Self { cli_options : None , workspace_path }
205201 }
206202
207203 pub fn with_cli_options ( mut self , cli_options : CliOptions ) -> Self {
208204 self . cli_options = Some ( cli_options) ;
209205 self
210206 }
211207
212- /// Clean up temporary config files created during resolution.
213- /// Should be called after command execution completes (success or failure).
214- pub async fn cleanup_temp_files ( & mut self ) {
215- for path in self . temp_config_files . drain ( ..) {
216- if let Err ( e) = tokio:: fs:: remove_file ( & path) . await {
217- if e. kind ( ) != std:: io:: ErrorKind :: NotFound {
218- tracing:: warn!(
219- "Failed to cleanup temp config file {}: {}" ,
220- path. as_path( ) . display( ) ,
221- e
222- ) ;
223- }
224- }
225- }
226- }
227-
228- /// Write a temporary TS config file that re-exports a field from vite.config.
229- /// The temp file imports the vite config and re-exports the specified field,
230- /// so the tool (e.g. oxlint) picks it up via `-c <path>`.
231- /// The `config_file_path` must be an absolute path.
232- async fn write_temp_ts_config_import (
233- & mut self ,
234- config_file_path : & str ,
235- temp_filename : & str ,
236- field_name : & str ,
237- args : & mut Vec < String > ,
238- ) -> anyhow:: Result < ( ) > {
239- let path = PathBuf :: from ( config_file_path) ;
240- if !path. is_absolute ( ) {
241- anyhow:: bail!( "config_file_path must be an absolute path, got: {config_file_path}" ) ;
242- }
243-
244- let config_basename = path
245- . file_name ( )
246- . and_then ( |n| n. to_str ( ) )
247- . ok_or_else ( || {
248- anyhow:: anyhow!( "Failed to get file name of config file: {config_file_path}" )
249- } ) ?
250- . to_string ( ) ;
251-
252- let config_dir = AbsolutePathBuf :: new ( path)
253- . and_then ( |p| p. parent ( ) . map ( |p| p. to_absolute_path_buf ( ) ) )
254- . ok_or_else ( || {
255- anyhow:: anyhow!( "Failed to get parent directory of config file: {config_file_path}" )
256- } ) ?;
257-
258- let config_path = config_dir. join ( temp_filename) ;
259- let content = format ! (
260- "import {{ defineConfig }} from 'vite-plus/lint';\n import viteConfig from './{config_basename}';\n export default defineConfig(viteConfig.{field_name} as any);\n "
261- ) ;
262- write ( & config_path, content) . await ?;
263-
264- self . temp_config_files . push ( config_path. clone ( ) ) ;
265-
266- let config_path_str = config_path
267- . as_path ( )
268- . to_str ( )
269- . ok_or_else ( || anyhow:: anyhow!( "config path is not valid UTF-8" ) ) ?;
270- args. insert ( 0 , config_path_str. to_string ( ) ) ;
271- args. insert ( 0 , "-c" . to_string ( ) ) ;
272- // Prevent oxlint from linting the temp config file itself
273- args. push ( "--ignore-pattern" . to_string ( ) ) ;
274- args. push ( temp_filename. to_string ( ) ) ;
275- Ok ( ( ) )
276- }
277-
278- /// Write a temporary JSON config file and prepend `-c <path>` to args.
279- /// The file will be tracked for cleanup after command execution.
280- /// The `config_file_path` must be an absolute path.
281- async fn write_temp_json_config_file (
282- & mut self ,
283- config : & serde_json:: Value ,
284- config_file_path : & str ,
285- temp_filename : & str ,
286- args : & mut Vec < String > ,
287- ) -> anyhow:: Result < ( ) > {
288- let mut config = config. clone ( ) ;
289-
290- // Add temp file to ignorePatterns to prevent self-checking
291- if let Some ( obj) = config. as_object_mut ( ) {
292- if let Some ( patterns) = obj. get_mut ( "ignorePatterns" ) {
293- if let Some ( array) = patterns. as_array_mut ( ) {
294- array. push ( serde_json:: json!( temp_filename) ) ;
295- }
296- } else {
297- obj. insert ( "ignorePatterns" . to_string ( ) , serde_json:: json!( [ temp_filename] ) ) ;
298- }
299- }
300-
301- let path = PathBuf :: from ( config_file_path) ;
302- if !path. is_absolute ( ) {
303- anyhow:: bail!( "config_file_path must be an absolute path, got: {config_file_path}" ) ;
304- }
305-
306- let config_dir = AbsolutePathBuf :: new ( path)
307- . and_then ( |p| p. parent ( ) . map ( |p| p. to_absolute_path_buf ( ) ) )
308- . ok_or_else ( || {
309- anyhow:: anyhow!( "Failed to get parent directory of config file: {config_file_path}" )
310- } ) ?;
311-
312- let config_path = config_dir. join ( temp_filename) ;
313- write ( & config_path, serde_json:: to_string ( & config) ?) . await ?;
314-
315- self . temp_config_files . push ( config_path. clone ( ) ) ;
316-
317- let config_path_str = config_path
318- . as_path ( )
319- . to_str ( )
320- . ok_or_else ( || anyhow:: anyhow!( "config path is not valid UTF-8" ) ) ?;
321- args. insert ( 0 , config_path_str. to_string ( ) ) ;
322- args. insert ( 0 , "-c" . to_string ( ) ) ;
323- Ok ( ( ) )
324- }
325-
326208 async fn resolve_universal_vite_config ( & self ) -> anyhow:: Result < ResolvedUniversalViteConfig > {
327209 let cli_options = self
328210 . cli_options
@@ -343,7 +225,7 @@ impl SubcommandResolver {
343225
344226 /// Resolve a synthesizable subcommand to a concrete program, args, cache config, and envs.
345227 async fn resolve (
346- & mut self ,
228+ & self ,
347229 subcommand : SynthesizableSubcommand ,
348230 resolved_vite_config : Option < & ResolvedUniversalViteConfig > ,
349231 envs : & Arc < FxHashMap < Arc < OsStr > , Arc < OsStr > > > ,
@@ -371,13 +253,8 @@ impl SubcommandResolver {
371253 if let ( Some ( _) , Some ( config_file) ) =
372254 ( & resolved_vite_config. lint , & resolved_vite_config. config_file )
373255 {
374- self . write_temp_ts_config_import (
375- config_file,
376- ".vite-plus-lint.tmp.mts" ,
377- "lint" ,
378- & mut args,
379- )
380- . await ?;
256+ args. insert ( 0 , "-c" . to_string ( ) ) ;
257+ args. insert ( 1 , config_file. clone ( ) ) ;
381258 }
382259
383260 Ok ( ResolvedSubcommand {
@@ -412,16 +289,11 @@ impl SubcommandResolver {
412289 & owned_resolved_vite_config
413290 } ;
414291
415- if let ( Some ( fmt_config ) , Some ( config_file) ) =
292+ if let ( Some ( _ ) , Some ( config_file) ) =
416293 ( & resolved_vite_config. fmt , & resolved_vite_config. config_file )
417294 {
418- self . write_temp_json_config_file (
419- fmt_config,
420- config_file,
421- ".vite-plus-fmt.tmp.json" ,
422- & mut args,
423- )
424- . await ?;
295+ args. insert ( 0 , "-c" . to_string ( ) ) ;
296+ args. insert ( 1 , config_file. clone ( ) ) ;
425297 }
426298
427299 Ok ( ResolvedSubcommand {
@@ -656,10 +528,6 @@ impl VitePlusCommandHandler {
656528 pub fn new ( resolver : SubcommandResolver ) -> Self {
657529 Self { resolver }
658530 }
659-
660- pub async fn cleanup_temp_files ( & mut self ) {
661- self . resolver . cleanup_temp_files ( ) . await ;
662- }
663531}
664532
665533#[ async_trait:: async_trait( ?Send ) ]
@@ -781,7 +649,7 @@ impl UserConfigLoader for VitePlusConfigLoader {
781649
782650/// Resolve a subcommand into a prepared `tokio::process::Command`.
783651async fn resolve_and_build_command (
784- resolver : & mut SubcommandResolver ,
652+ resolver : & SubcommandResolver ,
785653 subcommand : SynthesizableSubcommand ,
786654 resolved_vite_config : Option < & ResolvedUniversalViteConfig > ,
787655 envs : & Arc < FxHashMap < Arc < OsStr > , Arc < OsStr > > > ,
@@ -819,7 +687,7 @@ async fn resolve_and_build_command(
819687
820688/// Resolve a single subcommand and execute it, returning its exit status.
821689async fn resolve_and_execute (
822- resolver : & mut SubcommandResolver ,
690+ resolver : & SubcommandResolver ,
823691 subcommand : SynthesizableSubcommand ,
824692 resolved_vite_config : Option < & ResolvedUniversalViteConfig > ,
825693 envs : & Arc < FxHashMap < Arc < OsStr > , Arc < OsStr > > > ,
@@ -837,7 +705,7 @@ async fn resolve_and_execute(
837705/// Like `resolve_and_execute`, but captures stdout, applies a text filter,
838706/// and writes the result to real stdout. Stderr remains inherited.
839707async fn resolve_and_execute_with_stdout_filter (
840- resolver : & mut SubcommandResolver ,
708+ resolver : & SubcommandResolver ,
841709 subcommand : SynthesizableSubcommand ,
842710 resolved_vite_config : Option < & ResolvedUniversalViteConfig > ,
843711 envs : & Arc < FxHashMap < Arc < OsStr > , Arc < OsStr > > > ,
@@ -868,7 +736,7 @@ struct CapturedCommandOutput {
868736}
869737
870738async fn resolve_and_capture_output (
871- resolver : & mut SubcommandResolver ,
739+ resolver : & SubcommandResolver ,
872740 subcommand : SynthesizableSubcommand ,
873741 resolved_vite_config : Option < & ResolvedUniversalViteConfig > ,
874742 envs : & Arc < FxHashMap < Arc < OsStr > , Arc < OsStr > > > ,
@@ -1125,7 +993,7 @@ async fn execute_direct_subcommand(
1125993 let ( workspace_root, _) = vite_workspace:: find_workspace_root ( cwd) ?;
1126994 let workspace_path: Arc < AbsolutePath > = workspace_root. path . into ( ) ;
1127995
1128- let mut resolver = if let Some ( options) = options {
996+ let resolver = if let Some ( options) = options {
1129997 SubcommandResolver :: new ( Arc :: clone ( & workspace_path) ) . with_cli_options ( options)
1130998 } else {
1131999 SubcommandResolver :: new ( Arc :: clone ( & workspace_path) )
@@ -1145,7 +1013,6 @@ async fn execute_direct_subcommand(
11451013 print_summary_line (
11461014 "`vp check` did not run because both `--no-fmt` and `--no-lint` were set" ,
11471015 ) ;
1148- resolver. cleanup_temp_files ( ) . await ;
11491016 return Ok ( ExitStatus ( 1 ) ) ;
11501017 }
11511018
@@ -1166,7 +1033,7 @@ async fn execute_direct_subcommand(
11661033 fmt_fix_started = Some ( fmt_start) ;
11671034 }
11681035 let captured = resolve_and_capture_output (
1169- & mut resolver,
1036+ & resolver,
11701037 SynthesizableSubcommand :: Fmt { args } ,
11711038 Some ( & resolved_vite_config) ,
11721039 & envs,
@@ -1231,7 +1098,6 @@ async fn execute_direct_subcommand(
12311098 "Formatting failed during fix" ,
12321099 ) ;
12331100 }
1234- resolver. cleanup_temp_files ( ) . await ;
12351101 return Ok ( status) ;
12361102 }
12371103 }
@@ -1247,7 +1113,7 @@ async fn execute_direct_subcommand(
12471113 args. extend ( paths. iter ( ) . cloned ( ) ) ;
12481114 }
12491115 let captured = resolve_and_capture_output (
1250- & mut resolver,
1116+ & resolver,
12511117 SynthesizableSubcommand :: Lint { args } ,
12521118 Some ( & resolved_vite_config) ,
12531119 & envs,
@@ -1309,7 +1175,6 @@ async fn execute_direct_subcommand(
13091175 }
13101176 }
13111177 if status != ExitStatus :: SUCCESS {
1312- resolver. cleanup_temp_files ( ) . await ;
13131178 return Ok ( status) ;
13141179 }
13151180 }
@@ -1323,7 +1188,7 @@ async fn execute_direct_subcommand(
13231188 args. extend ( paths. into_iter ( ) ) ;
13241189 }
13251190 let captured = resolve_and_capture_output (
1326- & mut resolver,
1191+ & resolver,
13271192 SynthesizableSubcommand :: Fmt { args } ,
13281193 Some ( & resolved_vite_config) ,
13291194 & envs,
@@ -1346,7 +1211,6 @@ async fn execute_direct_subcommand(
13461211 & combined_output,
13471212 "Formatting failed after lint fixes were applied" ,
13481213 ) ;
1349- resolver. cleanup_temp_files ( ) . await ;
13501214 return Ok ( status) ;
13511215 }
13521216 if let Some ( started) = fmt_fix_started {
@@ -1365,7 +1229,7 @@ async fn execute_direct_subcommand(
13651229 other => {
13661230 if should_suppress_subcommand_stdout ( & other) {
13671231 resolve_and_execute_with_stdout_filter (
1368- & mut resolver,
1232+ & resolver,
13691233 other,
13701234 None ,
13711235 & envs,
@@ -1375,13 +1239,11 @@ async fn execute_direct_subcommand(
13751239 )
13761240 . await ?
13771241 } else {
1378- resolve_and_execute ( & mut resolver, other, None , & envs, cwd, & cwd_arc) . await ?
1242+ resolve_and_execute ( & resolver, other, None , & envs, cwd, & cwd_arc) . await ?
13791243 }
13801244 }
13811245 } ;
13821246
1383- resolver. cleanup_temp_files ( ) . await ;
1384-
13851247 Ok ( status)
13861248}
13871249
@@ -1427,8 +1289,6 @@ async fn execute_vite_task_command(
14271289 // Main execution (consumes session)
14281290 let result = session. main ( command) . await . map_err ( |e| Error :: Anyhow ( e) ) ;
14291291
1430- command_handler. cleanup_temp_files ( ) . await ;
1431-
14321292 result
14331293}
14341294
0 commit comments