From c28edc959642c251d778dfed7a125f1e278fc343 Mon Sep 17 00:00:00 2001 From: MK Date: Wed, 29 Apr 2026 19:52:43 +0900 Subject: [PATCH 1/5] refactor(ps1_shim): extract shared primitives into vite_powershell crate Pull `POWERSHELL_PREFIX`, `powershell_host()`, and `find_ps1_sibling()` out of `vite_task_plan::ps1_shim` and into a new `vite_powershell` crate so vite-plus can reuse them for its package-manager command spawn path. `vite_task_plan::ps1_shim` keeps its workspace + `node_modules/.bin` scope check and cwd-relative arg conversion; only the platform-shared primitives moved. Companion change in vite-plus: `vite_command::ps1_shim` (added in voidzero-dev/vite-plus#1489 to fix Ctrl+C terminal corruption for pm commands) will switch to `vite_powershell` once this lands. --- Cargo.lock | 11 ++ Cargo.toml | 1 + crates/vite_powershell/Cargo.toml | 22 ++++ crates/vite_powershell/src/lib.rs | 140 ++++++++++++++++++++++++++ crates/vite_task_plan/Cargo.toml | 1 + crates/vite_task_plan/src/ps1_shim.rs | 92 +++++++---------- 6 files changed, 210 insertions(+), 57 deletions(-) create mode 100644 crates/vite_powershell/Cargo.toml create mode 100644 crates/vite_powershell/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 3b897f53..185f3d77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3889,6 +3889,16 @@ dependencies = [ "wincode", ] +[[package]] +name = "vite_powershell" +version = "0.1.0" +dependencies = [ + "tempfile", + "tracing", + "vite_path", + "which", +] + [[package]] name = "vite_select" version = "0.0.0" @@ -4046,6 +4056,7 @@ dependencies = [ "vite_glob", "vite_graph_ser", "vite_path", + "vite_powershell", "vite_shell", "vite_str", "vite_task", diff --git a/Cargo.toml b/Cargo.toml index 56300f3e..e1e16cef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -145,6 +145,7 @@ vec1 = "1.12.1" vite_glob = { path = "crates/vite_glob" } vite_graph_ser = { path = "crates/vite_graph_ser" } vite_path = { path = "crates/vite_path" } +vite_powershell = { path = "crates/vite_powershell" } vite_select = { path = "crates/vite_select" } vite_shell = { path = "crates/vite_shell" } vite_str = { path = "crates/vite_str" } diff --git a/crates/vite_powershell/Cargo.toml b/crates/vite_powershell/Cargo.toml new file mode 100644 index 00000000..3cb9fd1e --- /dev/null +++ b/crates/vite_powershell/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "vite_powershell" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +publish = false +rust-version.workspace = true + +[dependencies] +tracing = { workspace = true } +vite_path = { workspace = true } +which = { workspace = true } + +[dev-dependencies] +tempfile = { workspace = true } + +[lints] +workspace = true + +[lib] +doctest = false diff --git a/crates/vite_powershell/src/lib.rs b/crates/vite_powershell/src/lib.rs new file mode 100644 index 00000000..aade0013 --- /dev/null +++ b/crates/vite_powershell/src/lib.rs @@ -0,0 +1,140 @@ +//! Windows-specific helpers for routing `.cmd` invocations through +//! `PowerShell` so spawning never goes through `cmd.exe`. +//! +//! Running a `.cmd` from any shell makes `cmd.exe` prompt "Terminate batch +//! job (Y/N)?" on Ctrl+C, which leaves the terminal corrupt. Routing +//! through `PowerShell` against the sibling `.ps1` shim sidesteps the prompt +//! and lets Ctrl+C propagate cleanly. +//! +//! This crate carries only the platform-shared primitives (the +//! `PowerShell` host lookup, the fixed argument prefix, and the +//! sibling-`.ps1` discovery). Higher-level wrappers in +//! `vite_task_plan::ps1_shim` (cwd-relative arg rewrite, scoped to +//! `node_modules/.bin`) and `vite_command::ps1_shim` (absolute-path +//! arg rewrite, applied to any `.cmd`) compose these primitives with +//! their own scope rules and return-type conventions. +//! +//! See and +//! . + +use vite_path::{AbsolutePath, AbsolutePathBuf}; + +/// Fixed arguments prepended before the `.ps1` path. `-NoProfile`/`-NoLogo` +/// skip user profile loading; `-ExecutionPolicy Bypass` allows running the +/// unsigned shims that npm/pnpm/yarn install. +pub const POWERSHELL_PREFIX: &[&str] = + &["-NoProfile", "-NoLogo", "-ExecutionPolicy", "Bypass", "-File"]; + +/// Cached location of the `PowerShell` host. Prefers cross-platform +/// `pwsh.exe` when present, falling back to the Windows built-in +/// `powershell.exe`. Returns `None` on non-Windows or when neither host +/// is on `PATH`. +#[cfg(windows)] +#[must_use] +pub fn powershell_host() -> Option<&'static AbsolutePathBuf> { + use std::sync::LazyLock; + + static POWERSHELL_HOST: LazyLock> = LazyLock::new(|| { + let resolved = which::which("pwsh.exe").or_else(|_| which::which("powershell.exe")).ok()?; + AbsolutePathBuf::new(resolved) + }); + POWERSHELL_HOST.as_ref() +} + +#[cfg(not(windows))] +#[must_use] +pub const fn powershell_host() -> Option<&'static AbsolutePathBuf> { + None +} + +/// Given a resolved `.cmd` path, return its sibling `.ps1` if one exists +/// on disk. The extension match is case-insensitive (matches `.cmd`, +/// `.CMD`, `.Cmd`). +/// +/// Returns `None` when the path is not a `.cmd` or no `.ps1` sibling +/// exists. Callers that need additional scope checks (e.g. "must live +/// inside the workspace's `node_modules/.bin`") should layer those on +/// top of this primitive. +#[must_use] +pub fn find_ps1_sibling(resolved: &AbsolutePath) -> Option { + let ext = resolved.as_path().extension().and_then(|e| e.to_str())?; + if !ext.eq_ignore_ascii_case("cmd") { + return None; + } + + let ps1 = resolved.with_extension("ps1"); + if !ps1.as_path().is_file() { + return None; + } + + Some(ps1) +} + +#[cfg(test)] +mod tests { + use std::fs; + + use tempfile::tempdir; + + use super::*; + + #[expect(clippy::disallowed_types, reason = "tempdir bridges std PathBuf into AbsolutePath")] + fn abs(buf: std::path::PathBuf) -> AbsolutePathBuf { + AbsolutePathBuf::new(buf).unwrap() + } + + #[test] + fn find_ps1_sibling_returns_path_when_both_present() { + let dir = tempdir().unwrap(); + let root = abs(dir.path().canonicalize().unwrap()); + fs::write(root.as_path().join("npm.cmd"), "").unwrap(); + fs::write(root.as_path().join("npm.ps1"), "").unwrap(); + + let resolved = abs(root.as_path().join("npm.cmd")); + let sibling = find_ps1_sibling(&resolved).expect("should find sibling"); + assert_eq!(sibling.as_path(), root.as_path().join("npm.ps1")); + } + + #[test] + fn find_ps1_sibling_is_case_insensitive_on_extension() { + let dir = tempdir().unwrap(); + let root = abs(dir.path().canonicalize().unwrap()); + fs::write(root.as_path().join("pnpm.CMD"), "").unwrap(); + fs::write(root.as_path().join("pnpm.ps1"), "").unwrap(); + + let resolved = abs(root.as_path().join("pnpm.CMD")); + assert!(find_ps1_sibling(&resolved).is_some()); + } + + #[test] + fn find_ps1_sibling_returns_none_when_sibling_missing() { + let dir = tempdir().unwrap(); + let root = abs(dir.path().canonicalize().unwrap()); + fs::write(root.as_path().join("npm.cmd"), "").unwrap(); + + let resolved = abs(root.as_path().join("npm.cmd")); + assert!(find_ps1_sibling(&resolved).is_none()); + } + + #[test] + fn find_ps1_sibling_returns_none_for_non_cmd() { + let dir = tempdir().unwrap(); + let root = abs(dir.path().canonicalize().unwrap()); + fs::write(root.as_path().join("bun.exe"), "").unwrap(); + fs::write(root.as_path().join("bun.ps1"), "").unwrap(); + + let resolved = abs(root.as_path().join("bun.exe")); + assert!(find_ps1_sibling(&resolved).is_none()); + } + + #[test] + fn find_ps1_sibling_returns_none_for_no_extension() { + let dir = tempdir().unwrap(); + let root = abs(dir.path().canonicalize().unwrap()); + fs::write(root.as_path().join("node"), "").unwrap(); + fs::write(root.as_path().join("node.ps1"), "").unwrap(); + + let resolved = abs(root.as_path().join("node")); + assert!(find_ps1_sibling(&resolved).is_none()); + } +} diff --git a/crates/vite_task_plan/Cargo.toml b/crates/vite_task_plan/Cargo.toml index 17abd81a..f63fec68 100644 --- a/crates/vite_task_plan/Cargo.toml +++ b/crates/vite_task_plan/Cargo.toml @@ -28,6 +28,7 @@ tracing = { workspace = true } vite_glob = { workspace = true } vite_graph_ser = { workspace = true } vite_path = { workspace = true } +vite_powershell = { workspace = true } vite_shell = { workspace = true } vite_str = { workspace = true } vite_task_graph = { workspace = true } diff --git a/crates/vite_task_plan/src/ps1_shim.rs b/crates/vite_task_plan/src/ps1_shim.rs index 60e380d8..9e857e16 100644 --- a/crates/vite_task_plan/src/ps1_shim.rs +++ b/crates/vite_task_plan/src/ps1_shim.rs @@ -21,6 +21,10 @@ //! globally installed tool's shim somewhere on the user's system PATH — is //! left alone even if it happens to live under some other `node_modules/.bin`. //! +//! Cross-platform primitives (`POWERSHELL_PREFIX`, `powershell_host`, +//! `find_ps1_sibling`) live in the `vite_powershell` crate so +//! `vite_command::ps1_shim` can share them. +//! //! See . use std::sync::Arc; @@ -32,13 +36,6 @@ use vite_path::AbsolutePath; use vite_path::AbsolutePathBuf; use vite_str::Str; -/// Fixed arguments prepended before the `.ps1` path. `-NoProfile`/`-NoLogo` -/// skip user profile loading; `-ExecutionPolicy Bypass` allows running the -/// unsigned shims that npm/pnpm install into `node_modules/.bin`. -#[cfg(any(windows, test))] -const POWERSHELL_PREFIX: &[&str] = - &["-NoProfile", "-NoLogo", "-ExecutionPolicy", "Bypass", "-File"]; - /// Rewrite a `node_modules/.bin/*.cmd` invocation to go through PowerShell. /// See the module docstring for the full contract; the short form: returns /// `(powershell.exe, [-NoProfile, …, -File, , ...args])` @@ -51,7 +48,7 @@ pub fn rewrite_cmd_shim_with_args( cwd: &AbsolutePath, workspace_root: &AbsolutePath, ) -> (Arc, Arc<[Str]>) { - if let Some(host) = powershell_host() + if let Some(host) = vite_powershell::powershell_host() && let Some(rewritten) = rewrite_with_host(&resolved, &args, cwd, workspace_root, host) { return rewritten; @@ -70,21 +67,6 @@ pub const fn rewrite_cmd_shim_with_args( (resolved, args) } -/// Cached location of the PowerShell host used to run `.ps1` shims. Prefers -/// cross-platform `pwsh.exe` when present, falling back to the Windows -/// built-in `powershell.exe`. `None` means no host was found in PATH (or we -/// aren't on Windows). -#[cfg(windows)] -fn powershell_host() -> Option<&'static Arc> { - use std::sync::LazyLock; - - static POWERSHELL_HOST: LazyLock>> = LazyLock::new(|| { - let resolved = which::which("pwsh.exe").or_else(|_| which::which("powershell.exe")).ok()?; - AbsolutePathBuf::new(resolved).map(Arc::::from) - }); - POWERSHELL_HOST.as_ref() -} - /// Pure rewrite logic, factored out so tests can exercise it on any platform /// without depending on a real `powershell.exe` being on PATH. #[cfg(any(windows, test))] @@ -93,9 +75,12 @@ fn rewrite_with_host( args: &Arc<[Str]>, cwd: &AbsolutePath, workspace_root: &AbsolutePath, - host: &Arc, + host: &AbsolutePathBuf, ) -> Option<(Arc, Arc<[Str]>)> { - let ps1 = find_ps1_sibling(resolved, workspace_root)?; + if !is_in_workspace_node_modules_bin(resolved, workspace_root) { + return None; + } + let ps1 = vite_powershell::find_ps1_sibling(resolved)?; let ps1_rel = pathdiff::diff_paths(ps1.as_path(), cwd.as_path())?; let ps1_rel_str = ps1_rel.to_str()?.cow_replace('\\', "/"); @@ -106,7 +91,7 @@ fn rewrite_with_host( ps1_rel_str, ); - let new_args: Arc<[Str]> = POWERSHELL_PREFIX + let new_args: Arc<[Str]> = vite_powershell::POWERSHELL_PREFIX .iter() .copied() .map(Str::from) @@ -114,41 +99,30 @@ fn rewrite_with_host( .chain(args.iter().cloned()) .collect(); - Some((Arc::clone(host), new_args)) + let host_arc = Arc::::from(host.clone()); + Some((host_arc, new_args)) } +/// True when `resolved` is a `/.../node_modules/.bin/` path +/// inside the workspace. Used to keep the rewrite hands-off for globally +/// installed shims (e.g. `%AppData%\npm\node_modules\.bin`). #[cfg(any(windows, test))] -fn find_ps1_sibling( +fn is_in_workspace_node_modules_bin( resolved: &AbsolutePath, workspace_root: &AbsolutePath, -) -> Option { +) -> bool { let path = resolved.as_path(); - let ext = path.extension().and_then(|e| e.to_str())?; - if !ext.eq_ignore_ascii_case("cmd") { - return None; - } - - // Must live inside the workspace so we don't retarget system-wide / - // globally installed shims (e.g. a user's `%AppData%\npm\node_modules\.bin`). if !path.starts_with(workspace_root.as_path()) { - return None; + return false; } - let mut parents = path.components().rev(); - parents.next()?; // shim filename - if !parents.next()?.as_os_str().eq_ignore_ascii_case(".bin") { - return None; + let Some(_) = parents.next() else { return false }; // shim filename + let Some(bin) = parents.next() else { return false }; + if !bin.as_os_str().eq_ignore_ascii_case(".bin") { + return false; } - if !parents.next()?.as_os_str().eq_ignore_ascii_case("node_modules") { - return None; - } - - let ps1 = path.with_extension("ps1"); - if !ps1.is_file() { - return None; - } - - AbsolutePathBuf::new(ps1) + let Some(node_modules) = parents.next() else { return false }; + node_modules.as_os_str().eq_ignore_ascii_case("node_modules") } #[cfg(test)] @@ -171,6 +145,10 @@ mod tests { bin } + fn host_buf(root: &AbsolutePath) -> AbsolutePathBuf { + AbsolutePathBuf::new(root.as_path().join("powershell.exe")).unwrap() + } + #[test] fn rewrites_cmd_to_cwd_relative_ps1_at_workspace_root() { let dir = tempdir().unwrap(); @@ -179,7 +157,7 @@ mod tests { fs::write(bin.join("vite.CMD"), "").unwrap(); fs::write(bin.join("vite.ps1"), "").unwrap(); - let host = abs(workspace.as_path().join("powershell.exe")); + let host = host_buf(&workspace); let resolved = abs(bin.join("vite.CMD")); let args: Arc<[Str]> = Arc::from(vec![Str::from("--port"), Str::from("3000")]); @@ -219,7 +197,7 @@ mod tests { fs::create_dir_all(&sub_pkg_path).unwrap(); let sub_pkg = abs(sub_pkg_path); - let host = abs(workspace.as_path().join("powershell.exe")); + let host = host_buf(&workspace); let resolved = abs(bin.join("vite.cmd")); let args: Arc<[Str]> = Arc::from(vec![]); @@ -240,7 +218,7 @@ mod tests { let bin = bin_dir(workspace.as_path()); fs::write(bin.join("vite.cmd"), "").unwrap(); - let host = abs(workspace.as_path().join("powershell.exe")); + let host = host_buf(&workspace); let resolved = abs(bin.join("vite.cmd")); let args: Arc<[Str]> = Arc::from(vec![Str::from("build")]); @@ -254,7 +232,7 @@ mod tests { fs::write(workspace.as_path().join("where.cmd"), "").unwrap(); fs::write(workspace.as_path().join("where.ps1"), "").unwrap(); - let host = abs(workspace.as_path().join("powershell.exe")); + let host = host_buf(&workspace); let resolved = abs(workspace.as_path().join("where.cmd")); let args: Arc<[Str]> = Arc::from(vec![]); @@ -269,7 +247,7 @@ mod tests { fs::write(bin.join("node.exe"), "").unwrap(); fs::write(bin.join("node.ps1"), "").unwrap(); - let host = abs(workspace.as_path().join("powershell.exe")); + let host = host_buf(&workspace); let resolved = abs(bin.join("node.exe")); let args: Arc<[Str]> = Arc::from(vec![Str::from("--version")]); @@ -294,7 +272,7 @@ mod tests { fs::write(global_bin.join("vite.cmd"), "").unwrap(); fs::write(global_bin.join("vite.ps1"), "").unwrap(); - let host = abs(root.as_path().join("powershell.exe")); + let host = host_buf(&workspace); let resolved = abs(global_bin.join("vite.cmd")); let args: Arc<[Str]> = Arc::from(vec![]); From 1d144f186f69fe307a0dcaf2e1790fce64fc076a Mon Sep 17 00:00:00 2001 From: MK Date: Wed, 29 Apr 2026 19:59:51 +0900 Subject: [PATCH 2/5] refactor(ps1_shim): cache PowerShell host as Arc Restore the pre-extraction zero-clone path for vite_task_plan: caching the host as Arc means rewrite_with_host can do Arc::clone(host) instead of allocating a fresh PathBuf+Arc on every .cmd shim rewrite. Callers that need an owned AbsolutePathBuf (e.g. vite-plus's vite_command::ps1_shim) call to_absolute_path_buf() themselves. Also drop the awkward `let Some(_) = parents.next() else { ... }` for the discarded shim-filename component in is_in_workspace_node_modules_bin in favor of a plain `parents.next();`. --- crates/vite_powershell/src/lib.rs | 14 ++++++++++---- crates/vite_task_plan/src/ps1_shim.rs | 25 +++++++++++++------------ 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/crates/vite_powershell/src/lib.rs b/crates/vite_powershell/src/lib.rs index aade0013..d59ab9c4 100644 --- a/crates/vite_powershell/src/lib.rs +++ b/crates/vite_powershell/src/lib.rs @@ -17,6 +17,8 @@ //! See and //! . +use std::sync::Arc; + use vite_path::{AbsolutePath, AbsolutePathBuf}; /// Fixed arguments prepended before the `.ps1` path. `-NoProfile`/`-NoLogo` @@ -29,21 +31,25 @@ pub const POWERSHELL_PREFIX: &[&str] = /// `pwsh.exe` when present, falling back to the Windows built-in /// `powershell.exe`. Returns `None` on non-Windows or when neither host /// is on `PATH`. +/// +/// Cached as `Arc` so callers that want shared ownership +/// (e.g. `vite_task_plan`'s plan-time rewrite) can do `Arc::clone(host)` +/// without copying the path. #[cfg(windows)] #[must_use] -pub fn powershell_host() -> Option<&'static AbsolutePathBuf> { +pub fn powershell_host() -> Option<&'static Arc> { use std::sync::LazyLock; - static POWERSHELL_HOST: LazyLock> = LazyLock::new(|| { + static POWERSHELL_HOST: LazyLock>> = LazyLock::new(|| { let resolved = which::which("pwsh.exe").or_else(|_| which::which("powershell.exe")).ok()?; - AbsolutePathBuf::new(resolved) + AbsolutePathBuf::new(resolved).map(Arc::::from) }); POWERSHELL_HOST.as_ref() } #[cfg(not(windows))] #[must_use] -pub const fn powershell_host() -> Option<&'static AbsolutePathBuf> { +pub const fn powershell_host() -> Option<&'static Arc> { None } diff --git a/crates/vite_task_plan/src/ps1_shim.rs b/crates/vite_task_plan/src/ps1_shim.rs index 9e857e16..623bbdd1 100644 --- a/crates/vite_task_plan/src/ps1_shim.rs +++ b/crates/vite_task_plan/src/ps1_shim.rs @@ -75,7 +75,7 @@ fn rewrite_with_host( args: &Arc<[Str]>, cwd: &AbsolutePath, workspace_root: &AbsolutePath, - host: &AbsolutePathBuf, + host: &Arc, ) -> Option<(Arc, Arc<[Str]>)> { if !is_in_workspace_node_modules_bin(resolved, workspace_root) { return None; @@ -99,8 +99,7 @@ fn rewrite_with_host( .chain(args.iter().cloned()) .collect(); - let host_arc = Arc::::from(host.clone()); - Some((host_arc, new_args)) + Some((Arc::clone(host), new_args)) } /// True when `resolved` is a `/.../node_modules/.bin/` path @@ -116,7 +115,7 @@ fn is_in_workspace_node_modules_bin( return false; } let mut parents = path.components().rev(); - let Some(_) = parents.next() else { return false }; // shim filename + parents.next(); // shim filename let Some(bin) = parents.next() else { return false }; if !bin.as_os_str().eq_ignore_ascii_case(".bin") { return false; @@ -145,8 +144,10 @@ mod tests { bin } - fn host_buf(root: &AbsolutePath) -> AbsolutePathBuf { - AbsolutePathBuf::new(root.as_path().join("powershell.exe")).unwrap() + fn host_arc(root: &AbsolutePath) -> Arc { + Arc::::from( + AbsolutePathBuf::new(root.as_path().join("powershell.exe")).unwrap(), + ) } #[test] @@ -157,7 +158,7 @@ mod tests { fs::write(bin.join("vite.CMD"), "").unwrap(); fs::write(bin.join("vite.ps1"), "").unwrap(); - let host = host_buf(&workspace); + let host = host_arc(&workspace); let resolved = abs(bin.join("vite.CMD")); let args: Arc<[Str]> = Arc::from(vec![Str::from("--port"), Str::from("3000")]); @@ -197,7 +198,7 @@ mod tests { fs::create_dir_all(&sub_pkg_path).unwrap(); let sub_pkg = abs(sub_pkg_path); - let host = host_buf(&workspace); + let host = host_arc(&workspace); let resolved = abs(bin.join("vite.cmd")); let args: Arc<[Str]> = Arc::from(vec![]); @@ -218,7 +219,7 @@ mod tests { let bin = bin_dir(workspace.as_path()); fs::write(bin.join("vite.cmd"), "").unwrap(); - let host = host_buf(&workspace); + let host = host_arc(&workspace); let resolved = abs(bin.join("vite.cmd")); let args: Arc<[Str]> = Arc::from(vec![Str::from("build")]); @@ -232,7 +233,7 @@ mod tests { fs::write(workspace.as_path().join("where.cmd"), "").unwrap(); fs::write(workspace.as_path().join("where.ps1"), "").unwrap(); - let host = host_buf(&workspace); + let host = host_arc(&workspace); let resolved = abs(workspace.as_path().join("where.cmd")); let args: Arc<[Str]> = Arc::from(vec![]); @@ -247,7 +248,7 @@ mod tests { fs::write(bin.join("node.exe"), "").unwrap(); fs::write(bin.join("node.ps1"), "").unwrap(); - let host = host_buf(&workspace); + let host = host_arc(&workspace); let resolved = abs(bin.join("node.exe")); let args: Arc<[Str]> = Arc::from(vec![Str::from("--version")]); @@ -272,7 +273,7 @@ mod tests { fs::write(global_bin.join("vite.cmd"), "").unwrap(); fs::write(global_bin.join("vite.ps1"), "").unwrap(); - let host = host_buf(&workspace); + let host = host_arc(&workspace); let resolved = abs(global_bin.join("vite.cmd")); let args: Arc<[Str]> = Arc::from(vec![]); From 1d8a96ced14f57827a938f044f1ff3250b0ca254 Mon Sep 17 00:00:00 2001 From: MK Date: Wed, 29 Apr 2026 20:04:42 +0900 Subject: [PATCH 3/5] fix(ps1_shim): drop unused tracing dep and tighten AbsolutePathBuf gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two CI failures from the previous commit: 1. `cargo shear` flagged `tracing` as unused in `vite_powershell` — the `tracing::debug!` call lives in callers (vite_task_plan and vite-plus's vite_command), not in the shared crate. Drop the dep. 2. Windows build flagged `use vite_path::AbsolutePathBuf` as unused under `#[cfg(any(windows, test))]`. Pre-extraction the production Windows path of `rewrite_with_host` and `find_ps1_sibling` named the type; after extraction only tests do. Tighten the gate to `#[cfg(test)]`. --- crates/vite_powershell/Cargo.toml | 1 - crates/vite_task_plan/src/ps1_shim.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/vite_powershell/Cargo.toml b/crates/vite_powershell/Cargo.toml index 3cb9fd1e..b656ee7d 100644 --- a/crates/vite_powershell/Cargo.toml +++ b/crates/vite_powershell/Cargo.toml @@ -8,7 +8,6 @@ publish = false rust-version.workspace = true [dependencies] -tracing = { workspace = true } vite_path = { workspace = true } which = { workspace = true } diff --git a/crates/vite_task_plan/src/ps1_shim.rs b/crates/vite_task_plan/src/ps1_shim.rs index 623bbdd1..13ee5877 100644 --- a/crates/vite_task_plan/src/ps1_shim.rs +++ b/crates/vite_task_plan/src/ps1_shim.rs @@ -32,7 +32,7 @@ use std::sync::Arc; #[cfg(any(windows, test))] use cow_utils::CowUtils as _; use vite_path::AbsolutePath; -#[cfg(any(windows, test))] +#[cfg(test)] use vite_path::AbsolutePathBuf; use vite_str::Str; From 1e2b7913840c15aa670d702f8bf4c7fdcd45c56f Mon Sep 17 00:00:00 2001 From: MK Date: Wed, 29 Apr 2026 20:06:04 +0900 Subject: [PATCH 4/5] chore: refresh Cargo.lock after dropping tracing dep --- Cargo.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 185f3d77..cd8ea0b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3894,7 +3894,6 @@ name = "vite_powershell" version = "0.1.0" dependencies = [ "tempfile", - "tracing", "vite_path", "which", ] From 957278dfba4eac6756a564372c0bb26df0870707 Mon Sep 17 00:00:00 2001 From: MK Date: Wed, 29 Apr 2026 20:29:49 +0900 Subject: [PATCH 5/5] refactor(ps1_shim): move test-only AbsolutePathBuf import into tests module Per review on #368: instead of `#[cfg(test)] use vite_path::AbsolutePathBuf;` at module level (and re-importing through `super::AbsolutePathBuf` in the test module), import directly inside `mod tests` where it's used. --- crates/vite_task_plan/src/ps1_shim.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/vite_task_plan/src/ps1_shim.rs b/crates/vite_task_plan/src/ps1_shim.rs index 13ee5877..0bcb381c 100644 --- a/crates/vite_task_plan/src/ps1_shim.rs +++ b/crates/vite_task_plan/src/ps1_shim.rs @@ -32,8 +32,6 @@ use std::sync::Arc; #[cfg(any(windows, test))] use cow_utils::CowUtils as _; use vite_path::AbsolutePath; -#[cfg(test)] -use vite_path::AbsolutePathBuf; use vite_str::Str; /// Rewrite a `node_modules/.bin/*.cmd` invocation to go through PowerShell. @@ -129,8 +127,9 @@ mod tests { use std::fs; use tempfile::tempdir; + use vite_path::AbsolutePathBuf; - use super::{AbsolutePath, AbsolutePathBuf, Arc, Str, rewrite_with_host}; + use super::{AbsolutePath, Arc, Str, rewrite_with_host}; #[expect(clippy::disallowed_types, reason = "tempdir bridges std PathBuf into AbsolutePath")] fn abs(buf: std::path::PathBuf) -> Arc {