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
13 changes: 7 additions & 6 deletions crates/vite_global_cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -650,10 +650,10 @@ Examples:
vp env use lts # Use latest LTS for this shell session
vp env use # Use project version for this shell session
vp env use --unset # Remove session override
vp env run --node 20 node -v # Run 'node -v' with Node.js 20
vp env run --node lts npm i # Run 'npm i' with latest LTS
vp env run node -v # Shim mode (version auto-resolved)
vp env run npm install # Shim mode (version auto-resolved)
vp env exec --node 20 node -v # Execute 'node -v' with Node.js 20
vp env exec --node lts npm i # Execute 'npm i' with latest LTS
vp env exec node -v # Shim mode (version auto-resolved)
vp env exec npm install # Shim mode (version auto-resolved)

Global Packages:
vp install -g <package> # Install a global package
Expand Down Expand Up @@ -766,8 +766,9 @@ pub enum EnvSubcommands {
sort: SortingMethod,
},

/// Run a command with a specific Node.js version
Run {
/// Execute a command with a specific Node.js version
#[command(visible_alias = "run")]
Exec {
/// Node.js version to use (e.g., "20.18.0", "lts", "^20.0.0")
/// If not provided and command is node/npm/npx or a global package binary,
/// version is resolved automatically (same as shim behavior)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Run command for executing commands with a specific Node.js version.
//! Exec command for executing commands with a specific Node.js version.
//!
//! Handles two modes:
//! 1. Explicit version: `vp env run --node <version> [--npm <version>] <command>`
//! 2. Shim mode: `vp env run <tool> [args...]` where tool is node/npm/npx or a global package binary
//! 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
//!
//! The shim mode uses the same dispatch logic as Unix symlinks, ensuring identical behavior
//! across platforms (used by Windows .cmd wrappers and Git Bash shell scripts).
Expand All @@ -17,7 +17,7 @@ use crate::{
shim::{dispatch as shim_dispatch, is_shim_tool},
};

/// Execute the run command.
/// Execute the exec command.
///
/// When `--node` is provided, runs a command with the specified Node.js version.
/// When `--node` is not provided and the command is a shim tool (node/npm/npx or global package),
Expand All @@ -28,8 +28,8 @@ pub async fn execute(
command: &[String],
) -> Result<ExitStatus, Error> {
if command.is_empty() {
eprintln!("vp env run: missing command to execute");
eprintln!("Usage: vp env run [--node <version>] <command> [args...]");
eprintln!("vp env exec: missing command to execute");
eprintln!("Usage: vp env exec [--node <version>] <command> [args...]");
return Ok(exit_status(1));
}

Expand All @@ -45,7 +45,7 @@ pub async fn execute(
let tool = &command[0];
if is_shim_tool(tool) {
// Clear recursion env var to force fresh version resolution.
// This is needed because `vp env run` may be invoked from within a context
// This is needed because `vp env exec` may be invoked from within a context
// where VITE_PLUS_TOOL_RECURSION is already set (e.g., when pnpm runs through
// the vite-plus shim). Without clearing it, shim_dispatch would passthrough
// to the system node instead of resolving the version.
Expand All @@ -67,13 +67,13 @@ pub async fn execute(
}

// Not a shim tool and no --node - error
eprintln!("vp env run: --node is required when running non-shim commands");
eprintln!("Usage: vp env run --node <version> <command> [args...]");
eprintln!("vp env exec: --node is required when running non-shim commands");
eprintln!("Usage: vp env exec --node <version> <command> [args...]");
eprintln!();
eprintln!("For shim tools, --node is optional (version resolved automatically):");
eprintln!(" vp env run node script.js # Core tool");
eprintln!(" vp env run npm install # Core tool");
eprintln!(" vp env run tsc --version # Global package");
eprintln!(" vp env exec node script.js # Core tool");
eprintln!(" vp env exec npm install # Core tool");
eprintln!(" vp env exec tsc --version # Global package");
Ok(exit_status(1))
}

Expand Down
10 changes: 5 additions & 5 deletions crates/vite_global_cli/src/commands/env/global_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ const CORE_SHIMS: &[&str] = &["node", "npm", "npx", "vp"];
/// Create a shim for a package binary.
///
/// On Unix: Creates a symlink to ../current/bin/vp
/// On Windows: Creates a .cmd wrapper that calls `vp env run <bin_name>`
/// On Windows: Creates a .cmd wrapper that calls `vp env exec <bin_name>`
async fn create_package_shim(
bin_dir: &vite_path::AbsolutePath,
bin_name: &str,
Expand Down Expand Up @@ -405,24 +405,24 @@ async fn create_package_shim(
return Ok(());
}

// Create .cmd wrapper that calls vp env run <bin_name>
// Create .cmd wrapper that calls vp env exec <bin_name>
// Set VITE_PLUS_HOME using %~dp0.. which resolves to the parent of bin/
// This ensures the vp binary knows its home directory
let wrapper_content = format!(
"@echo off\r\nset VITE_PLUS_HOME=%~dp0..\r\n\"%VITE_PLUS_HOME%\\current\\bin\\vp.exe\" env run {} %*\r\nexit /b %ERRORLEVEL%\r\n",
"@echo off\r\nset VITE_PLUS_HOME=%~dp0..\r\n\"%VITE_PLUS_HOME%\\current\\bin\\vp.exe\" env exec {} %*\r\nexit /b %ERRORLEVEL%\r\n",
bin_name
);
tokio::fs::write(&cmd_path, wrapper_content).await?;

// Also create shell script for Git Bash (bin_name without extension)
// Uses explicit "vp env run <bin_name>" instead of symlink+argv[0] because
// Uses explicit "vp env exec <bin_name>" instead of symlink+argv[0] because
// Windows symlinks require admin privileges
let sh_path = bin_dir.join(bin_name);
let sh_content = format!(
r#"#!/bin/sh
VITE_PLUS_HOME="$(dirname "$(dirname "$(readlink -f "$0" 2>/dev/null || echo "$0")")")"
export VITE_PLUS_HOME
exec "$VITE_PLUS_HOME/current/bin/vp.exe" env run {} "$@"
exec "$VITE_PLUS_HOME/current/bin/vp.exe" env exec {} "$@"
"#,
bin_name
);
Expand Down
6 changes: 3 additions & 3 deletions crates/vite_global_cli/src/commands/env/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod config;
mod current;
mod default;
mod doctor;
mod exec;
pub mod global_install;
mod list;
mod list_remote;
Expand All @@ -16,7 +17,6 @@ mod on;
pub mod package_metadata;
pub mod packages;
mod pin;
mod run;
mod setup;
mod unpin;
mod r#use;
Expand Down Expand Up @@ -49,8 +49,8 @@ pub async fn execute(cwd: AbsolutePathBuf, args: EnvArgs) -> Result<ExitStatus,
crate::cli::EnvSubcommands::ListRemote { pattern, lts, all, json, sort } => {
list_remote::execute(pattern, lts, all, json, sort).await
}
crate::cli::EnvSubcommands::Run { node, npm, command } => {
run::execute(node.as_deref(), npm.as_deref(), &command).await
crate::cli::EnvSubcommands::Exec { node, npm, command } => {
exec::execute(node.as_deref(), npm.as_deref(), &command).await
}
crate::cli::EnvSubcommands::Uninstall { version } => {
let provider = vite_js_runtime::NodeProvider::new();
Expand Down
14 changes: 7 additions & 7 deletions crates/vite_global_cli/src/commands/env/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//!
//! On Windows:
//! - bin/vp.cmd is a wrapper script that calls ..\current\bin\vp.exe
//! - bin/node.cmd, bin/npm.cmd, bin/npx.cmd are wrappers calling `vp env run <tool>`
//! - bin/node.cmd, bin/npm.cmd, bin/npx.cmd are wrappers calling `vp env exec <tool>`

use std::process::ExitStatus;

Expand Down Expand Up @@ -220,9 +220,9 @@ async fn create_unix_shim(
Ok(())
}

/// Create Windows shims using .cmd wrappers that call `vp env run <tool>`.
/// Create Windows shims using .cmd wrappers that call `vp env exec <tool>`.
///
/// All tools (node, npm, npx) get .cmd wrappers that invoke `vp env run`.
/// All tools (node, npm, npx) get .cmd wrappers that invoke `vp env exec`.
/// Also creates shell scripts (without extension) for Git Bash compatibility.
/// This is consistent with Volta's Windows approach.
#[cfg(windows)]
Expand All @@ -233,26 +233,26 @@ async fn create_windows_shim(
) -> Result<(), Error> {
let cmd_path = bin_dir.join(format!("{tool}.cmd"));

// Create .cmd wrapper that calls vp env run <tool>
// Create .cmd wrapper that calls vp env exec <tool>
// Use a for loop to canonicalize VITE_PLUS_HOME path.
// %~dp0.. would produce paths like C:\Users\x\.vite-plus\bin\..
// The for loop resolves this to a clean C:\Users\x\.vite-plus
let cmd_content = format!(
"@echo off\r\nfor %%I in (\"%~dp0..\") do set VITE_PLUS_HOME=%%~fI\r\n\"%VITE_PLUS_HOME%\\current\\bin\\vp.exe\" env run {} %*\r\nexit /b %ERRORLEVEL%\r\n",
"@echo off\r\nfor %%I in (\"%~dp0..\") do set VITE_PLUS_HOME=%%~fI\r\n\"%VITE_PLUS_HOME%\\current\\bin\\vp.exe\" env exec {} %*\r\nexit /b %ERRORLEVEL%\r\n",
tool
);

tokio::fs::write(&cmd_path, cmd_content).await?;

// Also create shell script for Git Bash (tool without extension)
// Uses explicit "vp env run <tool>" instead of symlink+argv[0] because
// Uses explicit "vp env exec <tool>" instead of symlink+argv[0] because
// Windows symlinks require admin privileges
let sh_path = bin_dir.join(tool);
let sh_content = format!(
r#"#!/bin/sh
VITE_PLUS_HOME="$(dirname "$(dirname "$(readlink -f "$0" 2>/dev/null || echo "$0")")")"
export VITE_PLUS_HOME
exec "$VITE_PLUS_HOME/current/bin/vp.exe" env run {} "$@"
exec "$VITE_PLUS_HOME/current/bin/vp.exe" env exec {} "$@"
"#,
tool
);
Expand Down
4 changes: 2 additions & 2 deletions crates/vite_global_cli/src/shim/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//!
//! Detection methods:
//! - Unix: Symlinks to vp binary preserve argv[0], allowing tool detection
//! - Windows: .cmd wrappers call `vp env run <tool>` directly
//! - Windows: .cmd wrappers call `vp env exec <tool>` directly
//! - Legacy: VITE_PLUS_SHIM_TOOL env var (kept for backward compatibility)

mod cache;
Expand Down Expand Up @@ -92,7 +92,7 @@ const SHIM_TOOL_ENV_VAR: &str = "VITE_PLUS_SHIM_TOOL";
/// 2. Check `VITE_PLUS_SHIM_TOOL` env var (for shell wrapper scripts)
/// 3. Fall back to argv[0] detection (primary method on Unix with symlinks)
///
/// Note: Modern Windows wrappers use `vp env run <tool>` instead of env vars.
/// Note: Modern Windows wrappers use `vp env exec <tool>` instead of env vars.
///
/// IMPORTANT: This function clears `VITE_PLUS_SHIM_TOOL` after reading it to
/// prevent the env var from leaking to child processes.
Expand Down
10 changes: 5 additions & 5 deletions packages/global/snap-tests/cli-helper-message/snap.txt
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ Commands:
unpin Remove the .node-version file from current directory (alias for `pin --unpin`)
list List locally installed Node.js versions [aliases: ls]
list-remote List available Node.js versions from the registry [aliases: ls-remote]
run Run a command with a specific Node.js version
exec Execute a command with a specific Node.js version [aliases: run]
uninstall Uninstall a Node.js version [aliases: uni]
install Install a Node.js version [aliases: i]
use Use a specific Node.js version for this shell session
Expand Down Expand Up @@ -329,10 +329,10 @@ Examples:
vp env use lts # Use latest LTS for this shell session
vp env use # Use project version for this shell session
vp env use --unset # Remove session override
vp env run --node 20 node -v # Run 'node -v' with Node.js 20
vp env run --node lts npm i # Run 'npm i' with latest LTS
vp env run node -v # Shim mode (version auto-resolved)
vp env run npm install # Shim mode (version auto-resolved)
vp env exec --node 20 node -v # Execute 'node -v' with Node.js 20
vp env exec --node lts npm i # Execute 'npm i' with latest LTS
vp env exec node -v # Shim mode (version auto-resolved)
vp env exec npm install # Shim mode (version auto-resolved)

Global Packages:
vp install -g <package> # Install a global package
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "command-env-run-shim-mode",
"name": "command-env-exec-shim-mode",
"version": "1.0.0",
"private": true,
"engines": {
Expand Down
18 changes: 18 additions & 0 deletions packages/global/snap-tests/command-env-exec-shim-mode/snap.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
> vp env exec node -v # Shim mode: version resolved from package.json engines.node
v20.18.0

> vp env exec npm -v # Shim mode: npm uses same version
10.8.2

> vp env exec node -e "console.log('Hello from shim mode')" # Shim mode: run inline script
Hello from shim mode

> vp env exec nonexistent-tool --version || echo 'Expected error: non-shim command requires --node' # Error: non-shim tool
vp env exec: --node is required when running non-shim commands
Usage: vp env exec --node <version> <command> [args...]

For shim tools, --node is optional (version resolved automatically):
vp env exec node script.js # Core tool
vp env exec npm install # Core tool
vp env exec tsc --version # Global package
Expected error: non-shim command requires --node
10 changes: 10 additions & 0 deletions packages/global/snap-tests/command-env-exec-shim-mode/steps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"env": {},
"ignoredPlatforms": [],
"commands": [
"vp env exec node -v # Shim mode: version resolved from package.json engines.node",
"vp env exec npm -v # Shim mode: npm uses same version",
"vp env exec node -e \"console.log('Hello from shim mode')\" # Shim mode: run inline script",
"vp env exec nonexistent-tool --version || echo 'Expected error: non-shim command requires --node' # Error: non-shim tool"
]
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "command-env-run",
"name": "command-env-exec",
"version": "1.0.0",
"private": true
}
5 changes: 5 additions & 0 deletions packages/global/snap-tests/command-env-exec/snap.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
> vp env exec --node 20.19 node -v # Run node with specific major version
v20.19.6

> vp env exec --node 20.19 node -e "console.log('Hello from Node ' + process.version)" # Run inline script
Hello from Node v<semver>
8 changes: 8 additions & 0 deletions packages/global/snap-tests/command-env-exec/steps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"env": {},
"ignoredPlatforms": [],
"commands": [
"vp env exec --node 20.19 node -v # Run node with specific major version",
"vp env exec --node 20.19 node -e \"console.log('Hello from Node ' + process.version)\" # Run inline script"
]
}
18 changes: 0 additions & 18 deletions packages/global/snap-tests/command-env-run-shim-mode/snap.txt

This file was deleted.

10 changes: 0 additions & 10 deletions packages/global/snap-tests/command-env-run-shim-mode/steps.json

This file was deleted.

5 changes: 0 additions & 5 deletions packages/global/snap-tests/command-env-run/snap.txt

This file was deleted.

8 changes: 0 additions & 8 deletions packages/global/snap-tests/command-env-run/steps.json

This file was deleted.

2 changes: 1 addition & 1 deletion packages/global/snap-tests/command-env-which/snap.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
> vp env run node --version # Ensure Node.js is installed first
> vp env exec node --version # Ensure Node.js is installed first
v20.18.0

> vp env which node # Core tool - shows resolved Node.js binary path
Expand Down
2 changes: 1 addition & 1 deletion packages/global/snap-tests/command-env-which/steps.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"env": {},
"ignoredPlatforms": ["win32"],
"commands": [
"vp env run node --version # Ensure Node.js is installed first",
"vp env exec node --version # Ensure Node.js is installed first",
"vp env which node # Core tool - shows resolved Node.js binary path",
"vp env which npm # Core tool - shows resolved npm binary path",
"vp env which npx # Core tool - shows resolved npx binary path",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"env": {
"VITE_DISABLE_AUTO_INSTALL": "1"
},
"ignoredPlatforms": ["win32"],
"commands": [
"vp run hello # should fall back to pnpm run when no vite-plus dependency",
"vp run greet --arg1 value1 # should pass through args to pnpm run",
Expand Down
Loading
Loading