From 05173938701c14e18a257866c3d6f09704955cfa Mon Sep 17 00:00:00 2001 From: Liang Mi Date: Sat, 28 Mar 2026 22:20:30 +0800 Subject: [PATCH 1/7] fix: get vite_plus_home from node wrapper's path --- Cargo.lock | 1 + crates/vite_shared/Cargo.toml | 1 + crates/vite_shared/src/home.rs | 9 +++++++++ 3 files changed, 11 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 6cb3e00cd8..81eae06573 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7422,6 +7422,7 @@ dependencies = [ "tracing-subscriber", "vite_path", "vite_str", + "which", ] [[package]] diff --git a/crates/vite_shared/Cargo.toml b/crates/vite_shared/Cargo.toml index 96cd080500..055aa6cb81 100644 --- a/crates/vite_shared/Cargo.toml +++ b/crates/vite_shared/Cargo.toml @@ -17,6 +17,7 @@ supports-color = "3" tracing-subscriber = { workspace = true } vite_path = { workspace = true } vite_str = { workspace = true } +which = { workspace = true} [target.'cfg(not(target_os = "windows"))'.dependencies] rustls = { workspace = true } diff --git a/crates/vite_shared/src/home.rs b/crates/vite_shared/src/home.rs index 3a4fbf03eb..13f7ae8903 100644 --- a/crates/vite_shared/src/home.rs +++ b/crates/vite_shared/src/home.rs @@ -1,5 +1,6 @@ use directories::BaseDirs; use vite_path::{AbsolutePathBuf, current_dir}; +use which::which; use crate::EnvConfig; @@ -18,6 +19,14 @@ pub fn get_vite_plus_home() -> std::io::Result { } } + // Get from `node`'s path (~/.vite-plus/bin/node) + if let Ok(path) = which("node") + && let Some(parent) = path.parent() + && let Some(grandparent) = parent.parent() + { + return Ok(AbsolutePathBuf::new(grandparent.to_path_buf()).unwrap()); + } + // Default to ~/.vite-plus match BaseDirs::new() { Some(dirs) => { From 64b4a4d48d9c5ffb730c3c9223a34474c6a82c67 Mon Sep 17 00:00:00 2001 From: Liang Mi Date: Sat, 28 Mar 2026 22:24:38 +0800 Subject: [PATCH 2/7] update --- crates/vite_shared/src/home.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/vite_shared/src/home.rs b/crates/vite_shared/src/home.rs index 13f7ae8903..188fab1cf1 100644 --- a/crates/vite_shared/src/home.rs +++ b/crates/vite_shared/src/home.rs @@ -23,6 +23,7 @@ pub fn get_vite_plus_home() -> std::io::Result { if let Ok(path) = which("node") && let Some(parent) = path.parent() && let Some(grandparent) = parent.parent() + && grandparent.ends_with(VITE_PLUS_HOME_DIR) { return Ok(AbsolutePathBuf::new(grandparent.to_path_buf()).unwrap()); } From b18823c4307e12cacf701b10c0606e5a461b2ccf Mon Sep 17 00:00:00 2001 From: Liang Mi Date: Sat, 28 Mar 2026 22:26:48 +0800 Subject: [PATCH 3/7] format --- crates/vite_shared/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/vite_shared/Cargo.toml b/crates/vite_shared/Cargo.toml index 055aa6cb81..4614724285 100644 --- a/crates/vite_shared/Cargo.toml +++ b/crates/vite_shared/Cargo.toml @@ -17,7 +17,7 @@ supports-color = "3" tracing-subscriber = { workspace = true } vite_path = { workspace = true } vite_str = { workspace = true } -which = { workspace = true} +which = { workspace = true } [target.'cfg(not(target_os = "windows"))'.dependencies] rustls = { workspace = true } From 21903845e88e578b900a970646a3be79b688433f Mon Sep 17 00:00:00 2001 From: Liang Mi Date: Sun, 29 Mar 2026 06:39:30 +0800 Subject: [PATCH 4/7] tests --- crates/vite_shared/src/home.rs | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/crates/vite_shared/src/home.rs b/crates/vite_shared/src/home.rs index 188fab1cf1..e239e69e45 100644 --- a/crates/vite_shared/src/home.rs +++ b/crates/vite_shared/src/home.rs @@ -59,4 +59,52 @@ mod tests { assert_eq!(home.as_path(), temp_dir.as_path()); }); } + + #[test] + fn test_get_vite_plus_without_home() { + use std::path::PathBuf; + + // Create a temp directory structure: /tmp/xxx/.vite-plus/bin/node + let temp_dir = PathBuf::from( + std::env::temp_dir().join(format!("vp-test-node-path-{}", std::process::id())), + ); + let vite_plus_home = temp_dir.join(".vite-plus"); + let bin_dir = vite_plus_home.join("bin"); + std::fs::create_dir_all(&bin_dir).unwrap(); + + // Create a fake node executable + let node_path = bin_dir.join("node"); + std::fs::write(&node_path, "#!/bin/sh\necho 'fake node'").unwrap(); + #[cfg(unix)] + { + // Make it executable on Unix + use std::os::unix::fs::PermissionsExt; + let mut perms = std::fs::metadata(&node_path).unwrap().permissions(); + perms.set_mode(0o755); + std::fs::set_permissions(&node_path, perms).unwrap(); + } + + // Set PATH to include the fake node directory + let original_path = std::env::var("PATH").unwrap_or_default(); + let new_path = format!("{}", bin_dir.display()); + // SAFETY: restore PATH after test + unsafe { + std::env::set_var("PATH", &new_path); + } + + // Clear any existing VITE_PLUS_HOME env var by using a test config without it + EnvConfig::test_scope(EnvConfig::for_test(), || { + // Test: get_vite_plus_home should return /tmp/xxx/.vite-plus + let home = get_vite_plus_home().unwrap(); + assert_eq!(home.as_path(), vite_plus_home.as_path()); + }); + + // SAFETY: restore PATH after test + unsafe { + std::env::set_var("PATH", original_path); + } + + // Cleanup + let _ = std::fs::remove_dir_all(&temp_dir); + } } From 4fb5028dbd10da2c00c78b6cfcc6db4ffbfb31c3 Mon Sep 17 00:00:00 2001 From: Liang Mi Date: Sun, 29 Mar 2026 06:42:25 +0800 Subject: [PATCH 5/7] comments --- crates/vite_shared/src/home.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/vite_shared/src/home.rs b/crates/vite_shared/src/home.rs index e239e69e45..7c195bb899 100644 --- a/crates/vite_shared/src/home.rs +++ b/crates/vite_shared/src/home.rs @@ -9,7 +9,9 @@ const VITE_PLUS_HOME_DIR: &str = ".vite-plus"; /// Get the vite-plus home directory. /// -/// Uses `EnvConfig::get().vite_plus_home` if set, otherwise defaults to `~/.vite-plus`. +/// Uses `EnvConfig::get().vite_plus_home` if set, +/// or the `node` executable's grandparent directory if it ends with `.vite-plus`, +/// otherwise defaults to `~/.vite-plus`. /// Falls back to `$CWD/.vite-plus` if the home directory cannot be determined. pub fn get_vite_plus_home() -> std::io::Result { let config = EnvConfig::get(); @@ -19,7 +21,7 @@ pub fn get_vite_plus_home() -> std::io::Result { } } - // Get from `node`'s path (~/.vite-plus/bin/node) + // Get from `node` executable file's grandparent directory (~/.vite-plus/bin/node) if let Ok(path) = which("node") && let Some(parent) = path.parent() && let Some(grandparent) = parent.parent() From a9396dc6e4fa70bdc9ff6f24b05edbcd14f05af3 Mon Sep 17 00:00:00 2001 From: Liang Mi Date: Sun, 29 Mar 2026 08:02:05 +0800 Subject: [PATCH 6/7] fix tests on windows --- crates/vite_shared/src/home.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/crates/vite_shared/src/home.rs b/crates/vite_shared/src/home.rs index 7c195bb899..ee79cd1a7a 100644 --- a/crates/vite_shared/src/home.rs +++ b/crates/vite_shared/src/home.rs @@ -74,21 +74,32 @@ mod tests { let bin_dir = vite_plus_home.join("bin"); std::fs::create_dir_all(&bin_dir).unwrap(); - // Create a fake node executable + // Create a fake node executable with platform-specific extension + #[cfg(windows)] + let node_path = bin_dir.join("node.exe"); + #[cfg(not(windows))] let node_path = bin_dir.join("node"); - std::fs::write(&node_path, "#!/bin/sh\necho 'fake node'").unwrap(); - #[cfg(unix)] + + // Write minimal content - on Windows, the file just needs to exist with .exe extension + // On Unix, we need a shebang and executable permissions + #[cfg(windows)] + std::fs::write(&node_path, b"MZ").unwrap(); // Minimal PE header for Windows + #[cfg(not(windows))] { - // Make it executable on Unix + std::fs::write(&node_path, "#!/bin/sh\necho 'fake node'").unwrap(); use std::os::unix::fs::PermissionsExt; let mut perms = std::fs::metadata(&node_path).unwrap().permissions(); perms.set_mode(0o755); std::fs::set_permissions(&node_path, perms).unwrap(); } - // Set PATH to include the fake node directory + // Set PATH to include the fake node directory FIRST (prepended) let original_path = std::env::var("PATH").unwrap_or_default(); - let new_path = format!("{}", bin_dir.display()); + #[cfg(windows)] + let path_separator = ';'; + #[cfg(not(windows))] + let path_separator = ':'; + let new_path = format!("{}{}{}", bin_dir.display(), path_separator, original_path); // SAFETY: restore PATH after test unsafe { std::env::set_var("PATH", &new_path); From 0ba979ce1109a1c4ec75632639969b540d330baa Mon Sep 17 00:00:00 2001 From: Liang Mi Date: Sun, 29 Mar 2026 08:16:12 +0800 Subject: [PATCH 7/7] comments --- crates/vite_shared/src/home.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/vite_shared/src/home.rs b/crates/vite_shared/src/home.rs index ee79cd1a7a..52010bf3f9 100644 --- a/crates/vite_shared/src/home.rs +++ b/crates/vite_shared/src/home.rs @@ -22,6 +22,7 @@ pub fn get_vite_plus_home() -> std::io::Result { } // Get from `node` executable file's grandparent directory (~/.vite-plus/bin/node) + // For the case where `$HOME` is overridden if let Ok(path) = which("node") && let Some(parent) = path.parent() && let Some(grandparent) = parent.parent()