Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ steps = [
{ argv = ["node", "list-shims.cjs", "$VP_HOME/bin", "$VP_HOME/fallback-bin"], envs = [["PATH", "${VP_HOME}/bin${PATH_SEPARATOR}${VP_HOME}/fallback-bin${PATH_SEPARATOR}${PATH}"]], comment = "Only Node moves to fallback." },
{ argv = ["node", "-p", "process.execPath.includes('system-bin')"], envs = [["PATH", "${VP_HOME}/bin${PATH_SEPARATOR}${workspace}/system-bin${PATH_SEPARATOR}${PATH}"]], comment = "The shell selects system Node before fallback." },
{ argv = ["node", "assert-node-resolution.cjs"], envs = [["PATH", "${VP_HOME}/bin${PATH_SEPARATOR}${workspace}/system-bin${PATH_SEPARATOR}${PATH}"]], comment = "Internal Node resolution agrees with PATH." },
{ argv = ["vp", "node", "-p", "process.execPath.includes('system-bin')"], envs = [["PATH", "${VP_HOME}/bin${PATH_SEPARATOR}${workspace}/system-bin${PATH_SEPARATOR}${PATH}"]], comment = "vp node (env exec node) selects system Node like the shell." },

# Move the pnpm family together; refresh must preserve both preferences.
["vp", "env", "off", "pnpm"],
Expand All @@ -23,6 +24,7 @@ steps = [
# Reverse PATH: reaching the Vite+ shim first must select managed Node.
{ argv = ["node", "-p", "process.execPath.includes('system-bin')"], envs = [["PATH", "${VP_HOME}/fallback-bin${PATH_SEPARATOR}${workspace}/system-bin${PATH_SEPARATOR}${PATH}"]], comment = "A fallback shim before system Node selects managed Node." },
{ argv = ["node", "assert-node-resolution.cjs"], envs = [["PATH", "${VP_HOME}/fallback-bin${PATH_SEPARATOR}${workspace}/system-bin${PATH_SEPARATOR}${PATH}"]], comment = "Internal resolution must also stop at the first Vite+ shim." },
{ argv = ["vp", "node", "-p", "process.execPath.includes('system-bin')"], envs = [["PATH", "${VP_HOME}/fallback-bin${PATH_SEPARATOR}${workspace}/system-bin${PATH_SEPARATOR}${PATH}"]], comment = "vp node also stops at the first Vite+ shim." },

# Restore each family independently; fallback should end up empty.
["vp", "env", "on", "node"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ Internal Node resolution agrees with PATH.
env which selects the same Node as PATH
```

## `PATH=${VP_HOME}/bin${PATH_SEPARATOR}${workspace}/system-bin${PATH_SEPARATOR}${PATH} vp node -p process.execPath.includes('system-bin')`

vp node (env exec node) selects system Node like the shell.

```
true
```

## `vp env off pnpm`

```
Expand Down Expand Up @@ -101,6 +109,14 @@ Internal resolution must also stop at the first Vite+ shim.
env which selects the same Node as PATH
```

## `PATH=${VP_HOME}/fallback-bin${PATH_SEPARATOR}${workspace}/system-bin${PATH_SEPARATOR}${PATH} vp node -p process.execPath.includes('system-bin')`

vp node also stops at the first Vite+ shim.

```
false
```

## `vp env on node`

```
Expand Down
12 changes: 12 additions & 0 deletions crates/vp_global_cli/src/commands/env/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,18 @@ impl Config {
self.configured_package_manager_shim_mode_for(package_manager).unwrap_or_default()
}

/// Node.js and each package-manager family have their own mode; package binaries are always managed.
#[must_use]
pub fn shim_mode_for_tool(&self, tool: &str) -> ShimMode {
if tool == "node" {
self.node_shim_mode
} else if let Some(kind) = PackageManagerType::from_tool(tool) {
self.package_manager_shim_mode_for(kind)
} else {
ShimMode::Managed
}
}

pub fn set_shim_modes(&mut self, node: bool, package_manager: bool, mode: ShimMode) {
if node {
self.node_shim_mode = mode;
Expand Down
14 changes: 11 additions & 3 deletions crates/vp_global_cli/src/commands/env/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! 1. Explicit version: `vp env exec --node <version> [--npm <version>] <command>`
//! 2. Shim mode: `vp env exec <tool> [args...]` where tool is node/npm/npx or a global package binary
//!
//! Direct invocations resolve a fresh tool selection through shim dispatch.
//! Direct invocations prefer system-first tools from PATH, then resolve a fresh selection through shim dispatch.
//! Windows .cmd wrappers and Git Bash shell scripts inherit tools like Unix shims.

use std::process::ExitStatus;
Expand All @@ -21,7 +21,7 @@ use super::{
use crate::{
cli::exit_status,
error::Error,
shim::{dispatch as shim_dispatch, is_shim_tool},
shim::{dispatch as shim_dispatch, is_core_shim_tool, is_shim_tool},
};

/// Execute the exec command.
Expand Down Expand Up @@ -67,7 +67,6 @@ pub async fn execute(
// - Core tools: Version resolved from .node-version/package.json/default
// - Package binaries: Uses Node.js version from package metadata
// - Automatic Node.js download if needed
// - Shim mode checking (managed vs system-first)
// Explicit env exec starts a new selection; wrappers inherit like shims.
let env = if from_wrapper {
ToolPathEnv::from_env()
Expand All @@ -77,6 +76,15 @@ pub async fn execute(
let args: Vec<String> = command[1..].to_vec();
// stdout belongs to the dispatched tool; route vp's own output to stderr.
vp_shared::output::route_user_output_to_stderr();
// Dispatch assumes the shell's PATH lookup already preferred system-first tools, so repeat it here.
if !from_wrapper
&& is_core_shim_tool(tool)
&& config::load_config().await?.shim_mode_for_tool(tool)
== config::ShimMode::SystemFirst
&& let Some(path) = crate::shim::dispatch::find_system_tool(tool)
{
return Ok(exit_status(crate::shim::exec::exec_tool(&path, &args, env)));
}
let exit_code = shim_dispatch(tool, &args, env).await;
return Ok(exit_status(exit_code));
}
Expand Down
9 changes: 1 addition & 8 deletions crates/vp_global_cli/src/commands/env/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,7 @@ fn is_owned_shim(path: &vt_path::AbsolutePath, current_exe: &std::path::Path) ->
/// Resolve placement independently for Node.js and each package-manager family.
pub(super) fn shim_dir(settings: &super::config::Config, tool: &str) -> vt_path::AbsolutePathBuf {
let dirs = &vp_shared::EnvConfig::get().dirs;
let mode = if tool == "node" {
settings.node_shim_mode
} else if let Some(kind) = vp_pm_cli::PackageManagerType::from_tool(tool) {
settings.package_manager_shim_mode_for(kind)
} else {
super::config::ShimMode::Managed
};
match mode {
match settings.shim_mode_for_tool(tool) {
super::config::ShimMode::Managed => dirs.bin.clone(),
super::config::ShimMode::SystemFirst => dirs.fallback_bin(),
}
Expand Down
Loading