diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_injected_tool_contracts/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_injected_tool_contracts/snapshots.toml index ba5b0bf731..e27fd6c4a8 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_injected_tool_contracts/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_injected_tool_contracts/snapshots.toml @@ -152,9 +152,11 @@ steps = [ ] [[case]] -name = "explicit_npm_after_partial_path_reset" +name = "pinned_npm_after_partial_path_reset" vp = "global" skip-platforms = ["windows"] steps = [ - { argv = ["vp", "env", "exec", "--node", "22.18.0", "--npm", "10.5.0", "node", "assert-unix-shim-path.cjs", "partial", "10.5.0"], comment = "An explicit npm selection still takes priority over bundled npm after a PATH reset" }, + { argv = ["vpt", "json-edit", "package.json", "packageManager", "npm@10.5.0"], snapshot = false }, + { argv = ["vp", "env", "on", "npm"], snapshot = false }, + { argv = ["vp", "env", "exec", "--node", "22.18.0", "node", "assert-unix-shim-path.cjs", "partial", "10.5.0"], comment = "A project npm pin still takes priority over bundled npm after a PATH reset" }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_injected_tool_contracts/snapshots/explicit_npm_after_partial_path_reset.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_injected_tool_contracts/snapshots/explicit_npm_after_partial_path_reset.md deleted file mode 100644 index 294e5368fc..0000000000 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_injected_tool_contracts/snapshots/explicit_npm_after_partial_path_reset.md +++ /dev/null @@ -1,9 +0,0 @@ -# explicit_npm_after_partial_path_reset - -## `vp env exec --node 22.18.0 --npm 10.5.0 node assert-unix-shim-path.cjs partial 10.5.0` - -An explicit npm selection still takes priority over bundled npm after a PATH reset - -``` -Node and its tools survive partial PATH -``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_injected_tool_contracts/snapshots/pinned_npm_after_partial_path_reset.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_injected_tool_contracts/snapshots/pinned_npm_after_partial_path_reset.md new file mode 100644 index 0000000000..5833c84221 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_injected_tool_contracts/snapshots/pinned_npm_after_partial_path_reset.md @@ -0,0 +1,15 @@ +# pinned_npm_after_partial_path_reset + +## `vpt json-edit package.json packageManager npm@10.5.0` + + +## `vp env on npm` + + +## `vp env exec --node 22.18.0 node assert-unix-shim-path.cjs partial 10.5.0` + +A project npm pin still takes priority over bundled npm after a PATH reset + +``` +Node and its tools survive partial PATH +``` diff --git a/crates/vp_global_cli/src/commands/env/package_manager.rs b/crates/vp_global_cli/src/commands/env/package_manager.rs index 0559ded0bf..dfd823ef1a 100644 --- a/crates/vp_global_cli/src/commands/env/package_manager.rs +++ b/crates/vp_global_cli/src/commands/env/package_manager.rs @@ -13,6 +13,24 @@ pub(crate) async fn resolve_current( resolve_current_for(cwd, None).await } +/// Selecting a manager for vp commands must not change direct shim versions. +pub(crate) async fn resolve_shim_for( + cwd: &AbsolutePath, + expected: PackageManagerType, +) -> Result, Error> { + let session = config::read_session_package_manager().await; + let session = session.as_deref().map(parse_package_manager_spec_with_hash).transpose()?; + let default = configured_default_for(&config::load_config().await?, expected)?; + resolve_environment_package_manager( + cwd, + session.as_ref().map(|(kind, version, hash)| (*kind, version.as_str(), hash.as_deref())), + default.as_ref().map(|(kind, version, hash)| (*kind, version.as_str(), hash.as_deref())), + Some(expected), + ) + .await + .map_err(Error::from) +} + pub(crate) async fn resolve_current_for( cwd: &AbsolutePath, expected: Option, diff --git a/crates/vp_global_cli/src/shim/dispatch.rs b/crates/vp_global_cli/src/shim/dispatch.rs index 2fbd388e18..40ca462c49 100644 --- a/crates/vp_global_cli/src/shim/dispatch.rs +++ b/crates/vp_global_cli/src/shim/dispatch.rs @@ -661,7 +661,7 @@ async fn resolve_package_manager_tool( return Ok(None); }; - let resolution = package_manager::resolve_current_for(cwd, Some(expected_type)).await?; + let resolution = package_manager::resolve_shim_for(cwd, expected_type).await?; let (version, hash) = match resolution { Some(resolution) => (resolution.version, resolution.hash), None if expected_type == PackageManagerType::Npm => return Ok(None), diff --git a/docs/guide/env.md b/docs/guide/env.md index b20f41b688..f027b30204 100644 --- a/docs/guide/env.md +++ b/docs/guide/env.md @@ -54,7 +54,9 @@ Package-manager selection uses this priority: 6. The named package manager's global default version 7. The named shim's latest release -A selected manager controls only its named shims. For example, pnpm controls `pnpm` and `pnpx`; invoking `npm` still resolves npm independently. Alias pairs are `npm`/`npx`, `pnpm`/`pnpx`, `yarn`/`yarnpkg`, and `bun`/`bunx`. Without a matching project selection, a named shim uses its configured default version and otherwise uses the latest release without prompting. The resolved version is cached for one hour and an expired cache remains available when the registry cannot be reached. The directly invoked npm shim keeps its Node-bundled fallback, while an explicit `vp env ... npm` family scope uses standalone npm's latest release. +`VP_PACKAGE_MANAGER` selects the manager and version for commands such as `vp install`. Direct package-manager shims ignore this variable and continue to resolve their versions from the session file, project configuration, and family default. + +A project selection controls only its named shims. For example, pnpm controls `pnpm` and `pnpx`; invoking `npm` still resolves npm independently. Alias pairs are `npm`/`npx`, `pnpm`/`pnpx`, `yarn`/`yarnpkg`, and `bun`/`bunx`. Without a matching project selection, a named shim uses its configured default version and otherwise uses the latest release without prompting. The resolved version is cached for one hour and an expired cache remains available when the registry cannot be reached. The directly invoked npm shim keeps its Node-bundled fallback, while an explicit `vp env ... npm` family scope uses standalone npm's latest release. A fresh install uses the split platform layout by default. On Unix, Vite+ stores managed runtimes and related files in `~/.local/share/vite-plus`. It