Skip to content

Commit 5e0e08b

Browse files
committed
fix(command): look up PATH case-insensitively in resolve_program
On Windows the canonical key is `Path`, and the TS layer's `prependToPathToEnvs` preserves whatever casing the process started with when adding the downloaded package-manager bin dir. Hard-coding `envs.get("PATH")` missed a `Path`-keyed override and silently fell back to the process PATH — where the prepended shim is absent — making `vp create` (and any other flow routed through `runCommandAndDetectProjectDir` / `run_command_with_fspy`) fail with `Cannot find binary path...` for `npx`/`pnpm`. Match the case-insensitive lookup `fspy::Command::resolve_program` already does, so the previously-correct behavior of the fspy path holds after vp-side resolution moved up a layer in 1b77707 (refactor(command): extract resolve_program to dedupe ps1-rewrite plumbing). Adds a Windows-only regression test (`resolve_program_tests`) that writes a `.exe` into a tempdir and resolves via a `Path`-keyed env map; without the fix the test fails because resolution falls back to the process PATH.
1 parent 822b0c9 commit 5e0e08b

1 file changed

Lines changed: 54 additions & 1 deletion

File tree

crates/vite_command/src/lib.rs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,16 @@ fn resolve_program(
5353
envs: &HashMap<String, String>,
5454
cwd: &AbsolutePath,
5555
) -> Result<(AbsolutePathBuf, Vec<OsString>), Error> {
56-
let bin_path = resolve_bin(bin_name, envs.get("PATH").map(|p| OsStr::new(p.as_str())), cwd)?;
56+
// Look up PATH case-insensitively: on Windows the canonical key is `Path`
57+
// and the TS layer's `prependToPathToEnvs` preserves whatever casing the
58+
// process started with. Hard-coding `"PATH"` would miss a `Path`-keyed
59+
// override and silently fall back to the process PATH (where the
60+
// prepended package-manager bin dir is absent).
61+
let path_env = envs
62+
.iter()
63+
.find(|(k, _)| k.eq_ignore_ascii_case("path"))
64+
.map(|(_, v)| OsStr::new(v.as_str()));
65+
let bin_path = resolve_bin(bin_name, path_env, cwd)?;
5766
Ok(match ps1_shim::rewrite_cmd_to_powershell(&bin_path) {
5867
Some(rewritten) => rewritten,
5968
None => (bin_path, Vec::new()),
@@ -369,6 +378,50 @@ mod tests {
369378
}
370379
}
371380

381+
#[cfg(windows)]
382+
mod resolve_program_tests {
383+
use super::*;
384+
385+
// Regression for voidzero-dev/vite-plus#1489 follow-up: on Windows the
386+
// PATH environment variable is conventionally keyed `Path` (and the
387+
// TS layer's `prependToPathToEnvs` preserves that casing when adding
388+
// the downloaded package-manager bin dir). `resolve_program` must
389+
// look up PATH case-insensitively or it falls back to the process
390+
// environment and can't find the prepended shim.
391+
#[test]
392+
fn resolve_program_finds_binary_via_lowercase_path_key() {
393+
let temp_dir = create_temp_dir();
394+
let temp_dir_path =
395+
AbsolutePathBuf::new(temp_dir.path().canonicalize().unwrap().to_path_buf())
396+
.unwrap();
397+
398+
// Unique name so it can't accidentally resolve via the process PATH.
399+
let bin_name = "vp_test_resolve_program_path_casing";
400+
let bin_path = temp_dir.path().join(format!("{bin_name}.exe"));
401+
std::fs::write(&bin_path, b"").unwrap();
402+
403+
// `Path`, not `PATH` — the real-world Windows casing.
404+
let envs = HashMap::from([(
405+
"Path".to_string(),
406+
temp_dir_path.as_path().to_string_lossy().into_owned(),
407+
)]);
408+
409+
let result = resolve_program(bin_name, &envs, &temp_dir_path);
410+
assert!(
411+
result.is_ok(),
412+
"resolve_program must find the binary via the `Path` env key on Windows; got: {:?}",
413+
result.err()
414+
);
415+
let (program, prefix_args) = result.unwrap();
416+
assert!(prefix_args.is_empty(), "no .ps1 sibling, no PowerShell prefix expected");
417+
assert_eq!(
418+
program.as_path().file_stem().and_then(|s| s.to_str()),
419+
Some(bin_name),
420+
"resolved program should be the .exe in the tempdir"
421+
);
422+
}
423+
}
424+
372425
mod run_command_with_fspy_tests {
373426
use super::*;
374427

0 commit comments

Comments
 (0)