Summary
I'd like an option to use system-installed Node.js for all parts of vp — not just shims, but also vp create, vp install, vp build, and every other command that delegates to JS. Currently there's no way to do this.
Problem
vp commands that delegate to JS always download a managed Node.js binary, even when a compatible node is already installed on the system. The existing system-first shim mode (vp env off) only applies to shimmed tools (node/npm/npx wrappers via dispatch.rs:679-703), not to vp commands themselves.
Who's affected
- NixOS and GNU Guix — downloaded binaries are dynamically linked against
/lib64/ld-linux-x86-64.so.2 which doesn't exist on these distros. vp create fails immediately:
Could not start dynamically linked executable: ~/.vite-plus/js_runtime/node/24.14.0/bin/node
NixOS cannot run dynamically linked executables intended for generic linux environments out of the box.
- Distro packages (Nix, Homebrew, AUR, Fedora COPR, etc.) — packagers want to use the distro-managed Node.js
- Air-gapped environments — no network access to download from nodejs.org
- Container images — Node.js is already installed; downloading a second copy wastes time and space
Code path showing the gap
vp create
→ commands/delegate.rs:execute()
→ JsExecutor::delegate_to_local_cli()
→ js_executor.rs:ensure_project_runtime()
→ download_runtime_for_project() ← always downloads
→ download_runtime_with_provider() ← no system check here
The shim dispatch has find_system_tool() which locates system binaries — but JsExecutor never calls it. It goes directly to download_runtime() which only checks its own cache at ~/.vite-plus/js_runtime/node/{version}/.
After resolution, the downloaded Node.js is only used for two things:
get_binary_path() → passed to Command::new() to execute JS scripts
get_bin_prefix() → prepended to PATH so child processes find npm/npx
A system Node.js satisfies both of these identically.
What I'd like
A way to tell vp "use my system Node.js everywhere" — for shims and for all vp commands. Ideally this would be:
- An environment variable like
VITE_PLUS_USE_SYSTEM_NODE=1 that makes all Node.js resolution prefer the system node in PATH before downloading
- Or extending
system-first mode so that vp env off applies to JsExecutor too — if a user opts into system-first, they expect all node usage to prefer system node, not just shims
Suggested implementation
The simplest approach: add a system check in download_runtime_with_provider() (the single bottleneck all download paths funnel through), before the cache check at line 121:
use std::process::Command as StdCommand;
if runtime_type == JsRuntimeType::Node {
if std::env::var_os("VITE_PLUS_USE_SYSTEM_NODE").is_some() {
if let Ok(output) = StdCommand::new("node").arg("--version").output() {
if output.status.success() {
if let Ok(version_str) = String::from_utf8(output.stdout) {
let version_str = version_str.trim().trim_start_matches('v');
if let Some(path_var) = std::env::var_os("PATH") {
for dir in std::env::split_paths(&path_var) {
let candidate = dir.join("node");
if candidate.exists() {
if let Some(install_dir) = dir.parent() {
if let Some(install_dir) =
AbsolutePathBuf::new(install_dir.to_path_buf())
{
tracing::info!(
"Using system Node.js v{version_str} at {:?}",
candidate
);
return Ok(JsRuntime {
runtime_type,
version: version_str.into(),
install_dir,
binary_relative_path,
bin_dir_relative_path,
});
}
}
break;
}
}
}
}
}
}
}
}
This covers all call paths at once:
JsExecutor::ensure_project_runtime() → download_runtime() / download_runtime_for_project()
JsExecutor::ensure_cli_runtime() → download_runtime_for_project()
- Shim dispatch runtime resolution
vp env install / vp env use
Distro packager workaround (current)
Nix currently patches download_runtime_with_provider() unconditionally (no env var gate) and wraps the binary with nodejs in PATH. This works but diverges from upstream. An official option would let us drop the patch.
Summary
I'd like an option to use system-installed Node.js for all parts of
vp— not just shims, but alsovp create,vp install,vp build, and every other command that delegates to JS. Currently there's no way to do this.Problem
vpcommands that delegate to JS always download a managed Node.js binary, even when a compatiblenodeis already installed on the system. The existingsystem-firstshim mode (vp env off) only applies to shimmed tools (node/npm/npxwrappers viadispatch.rs:679-703), not tovpcommands themselves.Who's affected
/lib64/ld-linux-x86-64.so.2which doesn't exist on these distros.vp createfails immediately:Code path showing the gap
The shim dispatch has
find_system_tool()which locates system binaries — butJsExecutornever calls it. It goes directly todownload_runtime()which only checks its own cache at~/.vite-plus/js_runtime/node/{version}/.After resolution, the downloaded Node.js is only used for two things:
get_binary_path()→ passed toCommand::new()to execute JS scriptsget_bin_prefix()→ prepended toPATHso child processes findnpm/npxA system Node.js satisfies both of these identically.
What I'd like
A way to tell
vp"use my system Node.js everywhere" — for shims and for allvpcommands. Ideally this would be:VITE_PLUS_USE_SYSTEM_NODE=1that makes all Node.js resolution prefer the systemnodein PATH before downloadingsystem-firstmode so thatvp env offapplies toJsExecutortoo — if a user opts into system-first, they expect all node usage to prefer system node, not just shimsSuggested implementation
The simplest approach: add a system check in
download_runtime_with_provider()(the single bottleneck all download paths funnel through), before the cache check at line 121:This covers all call paths at once:
JsExecutor::ensure_project_runtime()→download_runtime()/download_runtime_for_project()JsExecutor::ensure_cli_runtime()→download_runtime_for_project()vp env install/vp env useDistro packager workaround (current)
Nix currently patches
download_runtime_with_provider()unconditionally (no env var gate) and wraps the binary withnodejsin PATH. This works but diverges from upstream. An official option would let us drop the patch.