Skip to content

Commit 25741aa

Browse files
authored
fix: get vite_plus_home from node wrapper's path (#1185)
Close #1184
1 parent 273ba39 commit 25741aa

3 files changed

Lines changed: 75 additions & 1 deletion

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/vite_shared/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ supports-color = "3"
1717
tracing-subscriber = { workspace = true }
1818
vite_path = { workspace = true }
1919
vite_str = { workspace = true }
20+
which = { workspace = true }
2021

2122
[target.'cfg(not(target_os = "windows"))'.dependencies]
2223
rustls = { workspace = true }

crates/vite_shared/src/home.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use directories::BaseDirs;
22
use vite_path::{AbsolutePathBuf, current_dir};
3+
use which::which;
34

45
use crate::EnvConfig;
56

@@ -8,7 +9,9 @@ const VITE_PLUS_HOME_DIR: &str = ".vite-plus";
89

910
/// Get the vite-plus home directory.
1011
///
11-
/// Uses `EnvConfig::get().vite_plus_home` if set, otherwise defaults to `~/.vite-plus`.
12+
/// Uses `EnvConfig::get().vite_plus_home` if set,
13+
/// or the `node` executable's grandparent directory if it ends with `.vite-plus`,
14+
/// otherwise defaults to `~/.vite-plus`.
1215
/// Falls back to `$CWD/.vite-plus` if the home directory cannot be determined.
1316
pub fn get_vite_plus_home() -> std::io::Result<AbsolutePathBuf> {
1417
let config = EnvConfig::get();
@@ -18,6 +21,16 @@ pub fn get_vite_plus_home() -> std::io::Result<AbsolutePathBuf> {
1821
}
1922
}
2023

24+
// Get from `node` executable file's grandparent directory (~/.vite-plus/bin/node)
25+
// For the case where `$HOME` is overridden
26+
if let Ok(path) = which("node")
27+
&& let Some(parent) = path.parent()
28+
&& let Some(grandparent) = parent.parent()
29+
&& grandparent.ends_with(VITE_PLUS_HOME_DIR)
30+
{
31+
return Ok(AbsolutePathBuf::new(grandparent.to_path_buf()).unwrap());
32+
}
33+
2134
// Default to ~/.vite-plus
2235
match BaseDirs::new() {
2336
Some(dirs) => {
@@ -49,4 +62,63 @@ mod tests {
4962
assert_eq!(home.as_path(), temp_dir.as_path());
5063
});
5164
}
65+
66+
#[test]
67+
fn test_get_vite_plus_without_home() {
68+
use std::path::PathBuf;
69+
70+
// Create a temp directory structure: /tmp/xxx/.vite-plus/bin/node
71+
let temp_dir = PathBuf::from(
72+
std::env::temp_dir().join(format!("vp-test-node-path-{}", std::process::id())),
73+
);
74+
let vite_plus_home = temp_dir.join(".vite-plus");
75+
let bin_dir = vite_plus_home.join("bin");
76+
std::fs::create_dir_all(&bin_dir).unwrap();
77+
78+
// Create a fake node executable with platform-specific extension
79+
#[cfg(windows)]
80+
let node_path = bin_dir.join("node.exe");
81+
#[cfg(not(windows))]
82+
let node_path = bin_dir.join("node");
83+
84+
// Write minimal content - on Windows, the file just needs to exist with .exe extension
85+
// On Unix, we need a shebang and executable permissions
86+
#[cfg(windows)]
87+
std::fs::write(&node_path, b"MZ").unwrap(); // Minimal PE header for Windows
88+
#[cfg(not(windows))]
89+
{
90+
std::fs::write(&node_path, "#!/bin/sh\necho 'fake node'").unwrap();
91+
use std::os::unix::fs::PermissionsExt;
92+
let mut perms = std::fs::metadata(&node_path).unwrap().permissions();
93+
perms.set_mode(0o755);
94+
std::fs::set_permissions(&node_path, perms).unwrap();
95+
}
96+
97+
// Set PATH to include the fake node directory FIRST (prepended)
98+
let original_path = std::env::var("PATH").unwrap_or_default();
99+
#[cfg(windows)]
100+
let path_separator = ';';
101+
#[cfg(not(windows))]
102+
let path_separator = ':';
103+
let new_path = format!("{}{}{}", bin_dir.display(), path_separator, original_path);
104+
// SAFETY: restore PATH after test
105+
unsafe {
106+
std::env::set_var("PATH", &new_path);
107+
}
108+
109+
// Clear any existing VITE_PLUS_HOME env var by using a test config without it
110+
EnvConfig::test_scope(EnvConfig::for_test(), || {
111+
// Test: get_vite_plus_home should return /tmp/xxx/.vite-plus
112+
let home = get_vite_plus_home().unwrap();
113+
assert_eq!(home.as_path(), vite_plus_home.as_path());
114+
});
115+
116+
// SAFETY: restore PATH after test
117+
unsafe {
118+
std::env::set_var("PATH", original_path);
119+
}
120+
121+
// Cleanup
122+
let _ = std::fs::remove_dir_all(&temp_dir);
123+
}
52124
}

0 commit comments

Comments
 (0)