From 9a51223a0c1d5fcba59ab1109163c70ab66ff376 Mon Sep 17 00:00:00 2001 From: unvalley Date: Tue, 24 Mar 2026 01:54:05 +0900 Subject: [PATCH 1/4] Add vite-task runner support --- README.md | 1 + src/detect.rs | 8 +++++- src/exec.rs | 13 +++++++++ src/parser.rs | 2 ++ src/parser/vite_task.rs | 60 +++++++++++++++++++++++++++++++++++++++++ src/tasks.rs | 1 + 6 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/parser/vite_task.rs diff --git a/README.md b/README.md index 5859d10..7d60f6e 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Inspired by [antfu/ni](https://github.com/antfu/ni). - make: `Makefile` - just: `justfile` / `Justfile` - task: `Taskfile.yml` / `Taskfile.yaml` ... +- vite-task: `vite-task.json` - cargo-make: `Makefile.toml` - mise: `mise.toml` - mask: `maskfile.md` diff --git a/src/detect.rs b/src/detect.rs index 9d2d994..017a52c 100644 --- a/src/detect.rs +++ b/src/detect.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use crate::RtError; -const RUNNER_CANDIDATES: [(&str, Runner); 15] = [ +const RUNNER_CANDIDATES: [(&str, Runner); 16] = [ ("Justfile", Runner::Justfile), ("justfile", Runner::Justfile), ("Taskfile.yml", Runner::Taskfile), @@ -15,6 +15,7 @@ const RUNNER_CANDIDATES: [(&str, Runner); 15] = [ ("taskfile.dist.yaml", Runner::Taskfile), ("maskfile.md", Runner::Maskfile), ("Maskfile.md", Runner::Maskfile), + ("vite-task.json", Runner::ViteTask), ("mise.toml", Runner::Mise), ("Makefile.toml", Runner::CargoMake), ("Makefile", Runner::Makefile), @@ -25,6 +26,7 @@ pub enum Runner { Justfile, Taskfile, Maskfile, + ViteTask, Mise, CargoMake, Makefile, @@ -87,6 +89,7 @@ pub fn runner_command(runner: Runner) -> &'static str { Runner::Justfile => "just", Runner::Taskfile => "task", Runner::Maskfile => "mask", + Runner::ViteTask => "vt", Runner::Mise => "mise", // cargo-make is a subcommand of cargo, so we need to check cargo Runner::CargoMake => "cargo", @@ -157,6 +160,7 @@ mod tests { assert_eq!(runner_command(Runner::Justfile), "just"); assert_eq!(runner_command(Runner::Taskfile), "task"); assert_eq!(runner_command(Runner::Maskfile), "mask"); + assert_eq!(runner_command(Runner::ViteTask), "vt"); assert_eq!(runner_command(Runner::Mise), "mise"); assert_eq!(runner_command(Runner::CargoMake), "cargo"); assert_eq!(runner_command(Runner::Makefile), "make"); @@ -168,6 +172,7 @@ mod tests { touch(dir.path(), "Makefile"); touch(dir.path(), "Makefile.toml"); touch(dir.path(), "mise.toml"); + touch(dir.path(), "vite-task.json"); touch(dir.path(), "maskfile.md"); touch(dir.path(), "Taskfile.yml"); touch(dir.path(), "justfile"); @@ -181,6 +186,7 @@ mod tests { Runner::Justfile, Runner::Taskfile, Runner::Maskfile, + Runner::ViteTask, Runner::Mise, Runner::CargoMake, Runner::Makefile, diff --git a/src/exec.rs b/src/exec.rs index e47b4ce..7f76130 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -22,6 +22,9 @@ pub fn run( if runner == Runner::CargoMake { args.push("make".to_string()); } + if runner == Runner::ViteTask { + args.push("run".to_string()); + } if runner == Runner::Mise { args.push("run".to_string()); } @@ -70,6 +73,9 @@ pub fn base_command(runner: Runner) -> Result { if runner == Runner::CargoMake { command.arg("make"); } + if runner == Runner::ViteTask { + command.arg("run"); + } Ok(command) } @@ -86,6 +92,9 @@ pub fn preview_command(runner: Runner, task: &str, passthrough: &[String]) -> St if runner == Runner::CargoMake { parts.push("make".to_string()); } + if runner == Runner::ViteTask { + parts.push("run".to_string()); + } if runner == Runner::Mise { parts.push("run".to_string()); } @@ -172,6 +181,10 @@ mod tests { preview_command(Runner::Mise, "build", &[]), "mise run build" ); + assert_eq!( + preview_command(Runner::ViteTask, "build", &[]), + "vt run build" + ); assert_eq!( preview_command(Runner::CargoMake, "build", &[]), "cargo make build" diff --git a/src/parser.rs b/src/parser.rs index 09616f5..fdb5ff7 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -7,6 +7,7 @@ mod makefile; mod mask; mod mise; mod taskfile; +mod vite_task; /// Returns parsed tasks from the output of the given runner's list command. pub fn parse_tasks(runner: Runner, output: &str) -> Vec { @@ -14,6 +15,7 @@ pub fn parse_tasks(runner: Runner, output: &str) -> Vec { Runner::Justfile => justfile::parse(output), Runner::Taskfile => taskfile::parse(output), Runner::Maskfile => mask::parse(output), + Runner::ViteTask => vite_task::parse(output), Runner::Mise => mise::parse(output), Runner::CargoMake => cargo_make::parse(output), Runner::Makefile => makefile::parse(output), diff --git a/src/parser/vite_task.rs b/src/parser/vite_task.rs new file mode 100644 index 0000000..c65ef30 --- /dev/null +++ b/src/parser/vite_task.rs @@ -0,0 +1,60 @@ +use crate::tasks::TaskItem; + +pub(super) fn parse(output: &str) -> Vec { + output + .lines() + .filter_map(|line| { + let line = line.trim(); + if line.is_empty() { + return None; + } + + let (name, description) = line.split_once(':')?; + let name = name.trim(); + if name.is_empty() { + return None; + } + + let description = description.trim(); + Some(TaskItem { + name: name.to_string(), + description: (!description.is_empty()).then(|| description.to_string()), + }) + }) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_vite_task_list() { + let output = "\ + check: echo check root + app#build: echo build app +"; + let tasks = parse(output); + assert_eq!( + tasks, + vec![ + TaskItem { + name: "check".to_string(), + description: Some("echo check root".to_string()), + }, + TaskItem { + name: "app#build".to_string(), + description: Some("echo build app".to_string()), + }, + ] + ); + } + + #[test] + fn parse_vite_task_ignores_blank_lines() { + let output = "\n\n build: echo build\n"; + let tasks = parse(output); + assert_eq!(tasks.len(), 1); + assert_eq!(tasks[0].name, "build"); + } +} diff --git a/src/tasks.rs b/src/tasks.rs index 456fc52..9bfc6f4 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -142,6 +142,7 @@ fn list_command_variants(runner: Runner) -> Vec> { Runner::Justfile => vec![vec!["--list", "--unsorted"]], Runner::Taskfile => vec![vec!["--list-all"]], Runner::Maskfile => vec![vec!["--introspect"]], + Runner::ViteTask => vec![vec!["run"]], Runner::Mise => vec![vec!["tasks", "ls", "--json"]], Runner::CargoMake => vec![ vec!["make", "--list-all-steps"], From 3575939ad649e47d86d694fba504786dbc66ebf4 Mon Sep 17 00:00:00 2001 From: unvalley Date: Fri, 27 Mar 2026 15:54:58 +0900 Subject: [PATCH 2/4] Align Vite support with Vite+ docs --- README.md | 2 +- src/detect.rs | 31 ++++++++++++++++++----- src/exec.rs | 10 ++++---- src/parser.rs | 4 +-- src/parser/{vite_task.rs => vite_plus.rs} | 4 +-- src/tasks.rs | 2 +- 6 files changed, 35 insertions(+), 18 deletions(-) rename src/parser/{vite_task.rs => vite_plus.rs} (94%) diff --git a/README.md b/README.md index 7d60f6e..ec7e680 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Inspired by [antfu/ni](https://github.com/antfu/ni). - make: `Makefile` - just: `justfile` / `Justfile` - task: `Taskfile.yml` / `Taskfile.yaml` ... -- vite-task: `vite-task.json` +- vite+: `vite.config.ts` / `vite.config.js` ... - cargo-make: `Makefile.toml` - mise: `mise.toml` - mask: `maskfile.md` diff --git a/src/detect.rs b/src/detect.rs index 017a52c..e23a5db 100644 --- a/src/detect.rs +++ b/src/detect.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use crate::RtError; -const RUNNER_CANDIDATES: [(&str, Runner); 16] = [ +const RUNNER_CANDIDATES: [(&str, Runner); 21] = [ ("Justfile", Runner::Justfile), ("justfile", Runner::Justfile), ("Taskfile.yml", Runner::Taskfile), @@ -15,7 +15,12 @@ const RUNNER_CANDIDATES: [(&str, Runner); 16] = [ ("taskfile.dist.yaml", Runner::Taskfile), ("maskfile.md", Runner::Maskfile), ("Maskfile.md", Runner::Maskfile), - ("vite-task.json", Runner::ViteTask), + ("vite.config.ts", Runner::VitePlus), + ("vite.config.mts", Runner::VitePlus), + ("vite.config.cts", Runner::VitePlus), + ("vite.config.js", Runner::VitePlus), + ("vite.config.mjs", Runner::VitePlus), + ("vite.config.cjs", Runner::VitePlus), ("mise.toml", Runner::Mise), ("Makefile.toml", Runner::CargoMake), ("Makefile", Runner::Makefile), @@ -26,7 +31,7 @@ pub enum Runner { Justfile, Taskfile, Maskfile, - ViteTask, + VitePlus, Mise, CargoMake, Makefile, @@ -89,7 +94,7 @@ pub fn runner_command(runner: Runner) -> &'static str { Runner::Justfile => "just", Runner::Taskfile => "task", Runner::Maskfile => "mask", - Runner::ViteTask => "vt", + Runner::VitePlus => "vp", Runner::Mise => "mise", // cargo-make is a subcommand of cargo, so we need to check cargo Runner::CargoMake => "cargo", @@ -160,7 +165,7 @@ mod tests { assert_eq!(runner_command(Runner::Justfile), "just"); assert_eq!(runner_command(Runner::Taskfile), "task"); assert_eq!(runner_command(Runner::Maskfile), "mask"); - assert_eq!(runner_command(Runner::ViteTask), "vt"); + assert_eq!(runner_command(Runner::VitePlus), "vp"); assert_eq!(runner_command(Runner::Mise), "mise"); assert_eq!(runner_command(Runner::CargoMake), "cargo"); assert_eq!(runner_command(Runner::Makefile), "make"); @@ -172,7 +177,7 @@ mod tests { touch(dir.path(), "Makefile"); touch(dir.path(), "Makefile.toml"); touch(dir.path(), "mise.toml"); - touch(dir.path(), "vite-task.json"); + touch(dir.path(), "vite.config.ts"); touch(dir.path(), "maskfile.md"); touch(dir.path(), "Taskfile.yml"); touch(dir.path(), "justfile"); @@ -186,7 +191,7 @@ mod tests { Runner::Justfile, Runner::Taskfile, Runner::Maskfile, - Runner::ViteTask, + Runner::VitePlus, Runner::Mise, Runner::CargoMake, Runner::Makefile, @@ -194,6 +199,18 @@ mod tests { ); } + #[test] + fn detect_runners_deduplicates_vite_plus_config_variants() { + let dir = tempdir().unwrap(); + touch(dir.path(), "vite.config.ts"); + touch(dir.path(), "vite.config.mjs"); + + let detections = detect_runners(dir.path()).unwrap(); + let runners: Vec = detections.into_iter().map(|d| d.runner).collect(); + + assert_eq!(runners, vec![Runner::VitePlus]); + } + #[test] fn detect_runners_deduplicates_case_variants() { let dir = tempdir().unwrap(); diff --git a/src/exec.rs b/src/exec.rs index 7f76130..a8d1eeb 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -22,7 +22,7 @@ pub fn run( if runner == Runner::CargoMake { args.push("make".to_string()); } - if runner == Runner::ViteTask { + if runner == Runner::VitePlus { args.push("run".to_string()); } if runner == Runner::Mise { @@ -73,7 +73,7 @@ pub fn base_command(runner: Runner) -> Result { if runner == Runner::CargoMake { command.arg("make"); } - if runner == Runner::ViteTask { + if runner == Runner::VitePlus { command.arg("run"); } Ok(command) @@ -92,7 +92,7 @@ pub fn preview_command(runner: Runner, task: &str, passthrough: &[String]) -> St if runner == Runner::CargoMake { parts.push("make".to_string()); } - if runner == Runner::ViteTask { + if runner == Runner::VitePlus { parts.push("run".to_string()); } if runner == Runner::Mise { @@ -182,8 +182,8 @@ mod tests { "mise run build" ); assert_eq!( - preview_command(Runner::ViteTask, "build", &[]), - "vt run build" + preview_command(Runner::VitePlus, "build", &[]), + "vp run build" ); assert_eq!( preview_command(Runner::CargoMake, "build", &[]), diff --git a/src/parser.rs b/src/parser.rs index fdb5ff7..75f8163 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -7,7 +7,7 @@ mod makefile; mod mask; mod mise; mod taskfile; -mod vite_task; +mod vite_plus; /// Returns parsed tasks from the output of the given runner's list command. pub fn parse_tasks(runner: Runner, output: &str) -> Vec { @@ -15,7 +15,7 @@ pub fn parse_tasks(runner: Runner, output: &str) -> Vec { Runner::Justfile => justfile::parse(output), Runner::Taskfile => taskfile::parse(output), Runner::Maskfile => mask::parse(output), - Runner::ViteTask => vite_task::parse(output), + Runner::VitePlus => vite_plus::parse(output), Runner::Mise => mise::parse(output), Runner::CargoMake => cargo_make::parse(output), Runner::Makefile => makefile::parse(output), diff --git a/src/parser/vite_task.rs b/src/parser/vite_plus.rs similarity index 94% rename from src/parser/vite_task.rs rename to src/parser/vite_plus.rs index c65ef30..f0cae53 100644 --- a/src/parser/vite_task.rs +++ b/src/parser/vite_plus.rs @@ -29,7 +29,7 @@ mod tests { use super::*; #[test] - fn parse_vite_task_list() { + fn parse_vite_plus_task_list() { let output = "\ check: echo check root app#build: echo build app @@ -51,7 +51,7 @@ mod tests { } #[test] - fn parse_vite_task_ignores_blank_lines() { + fn parse_vite_plus_ignores_blank_lines() { let output = "\n\n build: echo build\n"; let tasks = parse(output); assert_eq!(tasks.len(), 1); diff --git a/src/tasks.rs b/src/tasks.rs index 9bfc6f4..ab57997 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -142,7 +142,7 @@ fn list_command_variants(runner: Runner) -> Vec> { Runner::Justfile => vec![vec!["--list", "--unsorted"]], Runner::Taskfile => vec![vec!["--list-all"]], Runner::Maskfile => vec![vec!["--introspect"]], - Runner::ViteTask => vec![vec!["run"]], + Runner::VitePlus => vec![vec!["run"]], Runner::Mise => vec![vec!["tasks", "ls", "--json"]], Runner::CargoMake => vec![ vec!["make", "--list-all-steps"], From ed3361f5d0f17c64c02334d78901daa644b77139 Mon Sep 17 00:00:00 2001 From: unvalley Date: Fri, 27 Mar 2026 16:05:02 +0900 Subject: [PATCH 3/4] test: cover Vite+ runner command wiring --- src/exec.rs | 55 ++++++++++++++++++++++++++++------------------------ src/tasks.rs | 5 +++++ 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/exec.rs b/src/exec.rs index a8d1eeb..20b17ec 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -18,16 +18,7 @@ pub fn run( cwd: &Path, ) -> Result { let program = runner_command(runner).to_string(); - let mut args = Vec::new(); - if runner == Runner::CargoMake { - args.push("make".to_string()); - } - if runner == Runner::VitePlus { - args.push("run".to_string()); - } - if runner == Runner::Mise { - args.push("run".to_string()); - } + let mut args = runner_prefix_args(runner); args.push(task.to_string()); args.extend(passthrough.iter().cloned()); @@ -70,11 +61,8 @@ pub fn base_command(runner: Runner) -> Result { let program = runner_command(runner); ensure_tool(program)?; let mut command = Command::new(program); - if runner == Runner::CargoMake { - command.arg("make"); - } - if runner == Runner::VitePlus { - command.arg("run"); + for arg in runner_prefix_args(runner) { + command.arg(arg); } Ok(command) } @@ -87,23 +75,22 @@ pub fn ensure_tool(tool: &'static str) -> Result<(), RtError> { } pub fn preview_command(runner: Runner, task: &str, passthrough: &[String]) -> String { - let mut parts = Vec::new(); let program = runner_command(runner); - if runner == Runner::CargoMake { - parts.push("make".to_string()); - } - if runner == Runner::VitePlus { - parts.push("run".to_string()); - } - if runner == Runner::Mise { - parts.push("run".to_string()); - } + let mut parts = runner_prefix_args(runner); parts.push(task.to_string()); parts.extend(passthrough.iter().cloned()); format_program_args(program, &parts) } +fn runner_prefix_args(runner: Runner) -> Vec { + match runner { + Runner::CargoMake => vec!["make".to_string()], + Runner::VitePlus | Runner::Mise => vec!["run".to_string()], + _ => Vec::new(), + } +} + pub fn format_program_args(program: &str, args: &[String]) -> String { let mut parts = Vec::new(); parts.push(program.to_string()); @@ -146,6 +133,24 @@ mod tests { assert_eq!(args, vec!["make".to_string()]); } + #[test] + fn runner_prefix_args_include_vite_plus_run_subcommand() { + assert_eq!( + runner_prefix_args(Runner::VitePlus), + vec!["run".to_string()] + ); + } + + #[test] + fn runner_prefix_args_include_runner_specific_prefixes() { + assert_eq!( + runner_prefix_args(Runner::CargoMake), + vec!["make".to_string()] + ); + assert_eq!(runner_prefix_args(Runner::Mise), vec!["run".to_string()]); + assert!(runner_prefix_args(Runner::Justfile).is_empty()); + } + #[test] fn ensure_tool_returns_error_for_missing_binary() { let err = ensure_tool("__rt_missing_tool_for_test__").unwrap_err(); diff --git a/src/tasks.rs b/src/tasks.rs index ab57997..6d78e5a 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -191,4 +191,9 @@ mod tests { assert!(exact.is_some()); assert!(fuzzy.is_none()); } + + #[test] + fn list_command_variants_for_vite_plus_uses_run() { + assert_eq!(list_command_variants(Runner::VitePlus), vec![vec!["run"]]); + } } From 8f90d397ef1290693639c8199fe7b5ffc596fdc8 Mon Sep 17 00:00:00 2001 From: unvalley Date: Fri, 27 Mar 2026 18:01:10 +0900 Subject: [PATCH 4/4] fix: make Vite+ detection content-aware --- src/detect.rs | 116 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 6 deletions(-) diff --git a/src/detect.rs b/src/detect.rs index e23a5db..90219fb 100644 --- a/src/detect.rs +++ b/src/detect.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use crate::RtError; -const RUNNER_CANDIDATES: [(&str, Runner); 21] = [ +const RUNNER_CANDIDATES: [(&str, Runner); 22] = [ ("Justfile", Runner::Justfile), ("justfile", Runner::Justfile), ("Taskfile.yml", Runner::Taskfile), @@ -21,6 +21,7 @@ const RUNNER_CANDIDATES: [(&str, Runner); 21] = [ ("vite.config.js", Runner::VitePlus), ("vite.config.mjs", Runner::VitePlus), ("vite.config.cjs", Runner::VitePlus), + ("package.json", Runner::VitePlus), ("mise.toml", Runner::Mise), ("Makefile.toml", Runner::CargoMake), ("Makefile", Runner::Makefile), @@ -47,7 +48,7 @@ pub struct Detection { pub fn detect_runner(dir_path: &Path) -> Result { for (name, runner) in RUNNER_CANDIDATES { let path = dir_path.join(name); - if path.is_file() { + if candidate_matches(runner, &path) { return Ok(Detection { runner, runner_file: path, @@ -70,7 +71,7 @@ pub fn detect_runners(dir_path: &Path) -> Result, RtError> { continue; } let path = dir_path.join(name); - if path.is_file() { + if candidate_matches(runner, &path) { seen.insert(runner); detections.push(Detection { runner, @@ -88,6 +89,61 @@ pub fn detect_runners(dir_path: &Path) -> Result, RtError> { } } +fn candidate_matches(runner: Runner, path: &Path) -> bool { + if !path.is_file() { + return false; + } + + match runner { + Runner::VitePlus => vite_plus_marker_matches(path), + _ => true, + } +} + +fn vite_plus_marker_matches(path: &Path) -> bool { + match path.file_name().and_then(|name| name.to_str()) { + Some("package.json") => package_json_declares_vite_plus(path), + Some(name) if name.starts_with("vite.config.") => vite_config_declares_vite_plus(path), + _ => true, + } +} + +fn vite_config_declares_vite_plus(path: &Path) -> bool { + std::fs::read_to_string(path) + .map(|content| content.contains("vite-plus")) + .unwrap_or(false) +} + +fn package_json_declares_vite_plus(path: &Path) -> bool { + #[derive(serde::Deserialize)] + struct PackageJson { + #[serde(default)] + dependencies: std::collections::BTreeMap, + #[serde(default, rename = "devDependencies")] + dev_dependencies: std::collections::BTreeMap, + #[serde(default, rename = "peerDependencies")] + peer_dependencies: std::collections::BTreeMap, + #[serde(default, rename = "optionalDependencies")] + optional_dependencies: std::collections::BTreeMap, + } + + let Ok(content) = std::fs::read_to_string(path) else { + return false; + }; + let Ok(package_json) = serde_json::from_str::(&content) else { + return false; + }; + + [ + &package_json.dependencies, + &package_json.dev_dependencies, + &package_json.peer_dependencies, + &package_json.optional_dependencies, + ] + .into_iter() + .any(|deps| deps.contains_key("vite-plus")) +} + /// Returns the command name for the given runner. pub fn runner_command(runner: Runner) -> &'static str { match runner { @@ -113,6 +169,12 @@ mod tests { path } + fn write(dir: &Path, name: &str, contents: &str) -> PathBuf { + let path = dir.join(name); + std::fs::write(&path, contents).unwrap(); + path + } + #[test] fn detect_none_returns_error() { let dir = tempdir().unwrap(); @@ -160,6 +222,36 @@ mod tests { assert_eq!(detection.runner_file, yml); } + #[test] + fn detect_ignores_plain_vite_config_without_vite_plus_marker() { + let dir = tempdir().unwrap(); + write( + dir.path(), + "vite.config.ts", + "import { defineConfig } from 'vite'; export default defineConfig({});", + ); + + let err = detect_runner(dir.path()).unwrap_err(); + match err { + RtError::NoRunnerFound { .. } => {} + other => panic!("unexpected error: {other:?}"), + } + } + + #[test] + fn detect_package_json_with_vite_plus_dependency() { + let dir = tempdir().unwrap(); + let package_json = write( + dir.path(), + "package.json", + r#"{"devDependencies":{"vite-plus":"^1.0.0"}}"#, + ); + + let detection = detect_runner(dir.path()).unwrap(); + assert_eq!(detection.runner, Runner::VitePlus); + assert_eq!(detection.runner_file, package_json); + } + #[test] fn runner_command_mapping() { assert_eq!(runner_command(Runner::Justfile), "just"); @@ -177,7 +269,11 @@ mod tests { touch(dir.path(), "Makefile"); touch(dir.path(), "Makefile.toml"); touch(dir.path(), "mise.toml"); - touch(dir.path(), "vite.config.ts"); + write( + dir.path(), + "vite.config.ts", + "import { defineConfig } from 'vite-plus'; export default defineConfig({});", + ); touch(dir.path(), "maskfile.md"); touch(dir.path(), "Taskfile.yml"); touch(dir.path(), "justfile"); @@ -202,8 +298,16 @@ mod tests { #[test] fn detect_runners_deduplicates_vite_plus_config_variants() { let dir = tempdir().unwrap(); - touch(dir.path(), "vite.config.ts"); - touch(dir.path(), "vite.config.mjs"); + write( + dir.path(), + "vite.config.ts", + "import { defineConfig } from 'vite-plus'; export default defineConfig({});", + ); + write( + dir.path(), + "vite.config.mjs", + "import { defineConfig } from 'vite-plus'; export default defineConfig({});", + ); let detections = detect_runners(dir.path()).unwrap(); let runners: Vec = detections.into_iter().map(|d| d.runner).collect();