Skip to content

Commit ef77182

Browse files
authored
fix: remove temp config file machinery re-introduced by PR #781 (#812)
oxlint >=1.53.0 and oxfmt >=0.38.0 natively support `-c vite.config.ts`, so the temp file creation/cleanup workaround is no longer needed. Pass config files directly via `-c` flag instead. Also relaxes `SubcommandResolver::resolve()` from `&mut self` to `&self` since removing temp file tracking eliminates the only mutation, cascading immutable borrows through all caller functions.
1 parent 27d3ed1 commit ef77182

3 files changed

Lines changed: 26 additions & 163 deletions

File tree

packages/cli/binding/src/cli.rs

Lines changed: 19 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
//! It handles argument parsing, command dispatching, and orchestration of the task execution.
55
66
use 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

1111
use clap::{
@@ -15,7 +15,6 @@ use clap::{
1515
use owo_colors::OwoColorize;
1616
use rustc_hash::FxHashMap;
1717
use serde::{Deserialize, Serialize};
18-
use tokio::fs::write;
1918
use vite_error::Error;
2019
use vite_path::{AbsolutePath, AbsolutePathBuf};
2120
use vite_shared::{PrependOptions, output, prepend_to_path_env};
@@ -185,144 +184,27 @@ impl ResolvedSubcommand {
185184
pub 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

192189
impl 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

202198
impl 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';\nimport viteConfig from './{config_basename}';\nexport 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`.
783651
async 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.
821689
async 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.
839707
async 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

870738
async 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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
[1]> vp check --fix
22
error: Formatting could not complete
3-
Failed to parse configuration.
4-
invalid type: string "invalid", expected struct Oxfmtrc
3+
Failed to load configuration file.
4+
<cwd>/vite.config.ts
5+
Ensure the file has a valid default export of a JSON-serializable configuration object.
56

67
Formatting failed during fix
78

89
[1]> vp check
910
error: Formatting could not start
10-
Failed to parse configuration.
11-
invalid type: string "invalid", expected struct Oxfmtrc
11+
Failed to load configuration file.
12+
<cwd>/vite.config.ts
13+
Ensure the file has a valid default export of a JSON-serializable configuration object.
1214

1315
Formatting failed before analysis started

packages/cli/snap-tests/check-fix-missing-stderr/steps.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"ignoredPlatforms": ["win32"],
23
"commands": [
34
"vp check --fix",
45
"vp check"

0 commit comments

Comments
 (0)