Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
5c2cae7
feat(env)!: replace Corepack with package manager shims
liangmiQwQ Aug 10, 2026
47c1aac
refactor(pm): centralize managed binary resolution
liangmiQwQ Aug 10, 2026
e127601
refactor(pm): simplify latest version cache
liangmiQwQ Aug 10, 2026
9ffe5bf
feat(env)!: manage package manager environments
liangmiQwQ Aug 9, 2026
04db805
docs(env): document unified environment management
liangmiQwQ Aug 9, 2026
8f1acde
test(env): update unified environment snapshots
liangmiQwQ Aug 9, 2026
146264b
fix(env): simplify help examples
liangmiQwQ Aug 9, 2026
be3d0c8
feat(env): warn on package manager mismatch
liangmiQwQ Aug 9, 2026
9f9be9b
fix(env): align package manager environment state
liangmiQwQ Aug 9, 2026
8d631f2
test(env): cover floating Bun default fallback
liangmiQwQ Aug 10, 2026
f4425dd
refactor(env): remove unused resolution source kind
liangmiQwQ Aug 10, 2026
9e22fa6
refactor(env): share package manager spec resolution
liangmiQwQ Aug 10, 2026
5defd0a
refactor(pm): execute package managers from PATH
liangmiQwQ Aug 9, 2026
c1cde27
refactor(cli): remove package manager download plumbing
liangmiQwQ Aug 9, 2026
9a769ea
test(pm): cover direct binary dispatch
liangmiQwQ Aug 9, 2026
5ada545
docs(pm): describe PATH-owned package managers
liangmiQwQ Aug 9, 2026
343c0cc
style(pm): satisfy dispatcher lints
liangmiQwQ Aug 9, 2026
238052b
refactor(cli): remove duplicate package manager state
liangmiQwQ Aug 10, 2026
26f38f8
refactor(pm): remove obsolete package manager plumbing
liangmiQwQ Aug 10, 2026
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
5 changes: 0 additions & 5 deletions .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -382,11 +382,6 @@ jobs:
- name: varlet
node-version: 22
command: |
# scripts/bootstrap.mjs spawns `pnpm build` via tinyexec and needs
# pnpm on PATH (not exposed by the vp install itself). corepack
# enable creates a pnpm launcher in the vp bin dir that resolves
# the project's pinned packageManager version (pnpm@9.15.9).
corepack enable
node scripts/bootstrap.mjs
# Report-only: oxlint 1.68.0 surfaces ts1038 ("A 'declare' modifier
# cannot be used in an already ambient context.") on varlet's
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ _runtime and package management, create, dev, check, test, build, pack, and mono

Vite+ is the unified entry point for local web development. It combines [Vite](https://vite.dev/), [Vitest](https://vitest.dev/), [Oxlint](https://oxc.rs/docs/guide/usage/linter.html), [Oxfmt](https://oxc.rs/docs/guide/usage/formatter.html), [Rolldown](https://rolldown.rs/), [tsdown](https://tsdown.dev/), and [Vite Task](https://github.com/voidzero-dev/vite-task) into one zero-config toolchain that also manages runtime and package manager workflows:

- **`vp env`:** Manage Node.js globally and per project
- **`vp env`:** Manage Node.js and package managers globally and per project
- **`vp install`:** Install dependencies with automatic package manager detection
- **`vp dev`:** Run Vite's fast native ESM dev server with instant HMR
- **`vp check`:** Run formatting, linting, and type checks in one command
Expand Down Expand Up @@ -103,7 +103,7 @@ Use `vp migrate` to migrate to Vite+. It merges tool-specific config files such
- **config** - Configure hooks and agent integration
- **staged** - Run linters on staged files
- **install** (`i`) - Install dependencies
- **env** - Manage Node.js versions
- **env** - Manage Node.js and package managers

#### Develop

Expand Down
4 changes: 2 additions & 2 deletions crates/vp_cli_snapshots/tests/cli_snapshots/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ A step is a bare argv array or a table:

`argv[0]` may be `vpt`, a runner-provisioned tool such as `nu`, or any
executable exposed by the case's Vite+ installation, including default shims
such as `vp`, `node`, and `corepack` and globally installed package binaries.
There is no shell: no `&&`, no
such as `vp`, `node`, `npm`, and `pnpm` and globally installed package
binaries. There is no shell: no `&&`, no
redirects, no globs. File setup and assertions go through `vpt` so behavior
is identical on every platform:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Start:
hooks Manage the Git hook dispatcher
staged Run linters on staged files
install, i Install all dependencies, or add packages if package names are provided
env Manage Node.js versions
env Manage Node.js and package managers

Develop:
dev Run the development server
Expand Down Expand Up @@ -459,54 +459,57 @@ VITE+ - The Unified Toolchain for the Web

Usage: vp env [COMMAND]

Manage Node.js versions
Manage Node.js and package-manager environments

Setup:
setup Create or update shims in VP_HOME/bin
on Enable managed mode - shims always use vite-plus managed Node.js
off Enable system-first mode - shims prefer system Node.js, fallback to managed
print Print shell snippet to set environment for current session
on Enable managed mode for Node.js and package managers
off Enable system-first mode for Node.js and package managers
print Print PATH setup for the resolved environment

Manage:
default Set or show the global default Node.js version
pin Pin a Node.js version in the current directory
unpin Remove the Node.js pin from the current directory (alias for `pin --unpin`)
use Use a specific Node.js version for this shell session
install, i Install a Node.js version
uninstall, uni Uninstall a Node.js version
clean Remove unused managed runtimes and package manager caches
exec, run Execute a command with a specific Node.js version
default Set or show global environment defaults
pin Pin Node.js and package-manager versions in the project
unpin Remove project environment pins (alias for `pin --unpin`)
use Activate an environment for this shell session
install, i Install a resolved or explicit environment
uninstall, uni Uninstall explicit component versions
clean Remove unused runtimes and package managers
exec, run Execute a command in a resolved or explicit environment

Inspect:
current Show current environment information
doctor Run diagnostics and show environment status
which Show path to the tool that would be executed
list, ls List locally installed Node.js versions
list-remote, ls-remote List available Node.js versions from the registry
list, ls List locally installed environment components
list-remote, ls-remote List available versions from component registries

Examples:
Setup:
vp env setup # Create shims for node, npm, npx, corepack
vp env on # Use vite-plus managed Node.js
vp env print # Print shell snippet for this session
vp env setup # Create Node.js and package-manager shims
vp env on # Manage Node.js and package managers
vp env off pm # Prefer system package managers only
vp env print # Print PATH setup for both components

Manage:
vp env pin lts # Pin to latest LTS version
vp env install # Install version from .node-version / package.json / .nvmrc
vp env use 20 # Use Node.js 20 for this shell session
vp env use --unset # Remove session override
vp env clean # Remove unused managed caches
vp env default 22.19.0 # Set the Node.js default
vp env default pnpm@12 # Set the package-manager default
vp env pin 22.19.0 # Pin Node.js for this project
vp env use 22.19.0 # Use Node.js in this shell
vp env clean # Clean all unused managed versions

Inspect:
vp env current # Show current resolved environment
vp env current --json # JSON output for automation
vp env doctor # Check environment configuration
vp env which node # Show which node binary will be used
vp env list-remote --lts # List only LTS versions
vp env list node # List only Node.js installations
vp env list-remote --lts # List only Node.js LTS versions

Execute:
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 --node lts node -v # Override Node.js
vp env exec --package-manager pnpm@12 pnpm i # Override the package manager
vp env exec node -v # Resolve both components

Related Commands:
vp install -g <package> # Install a package globally
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ vp = "global"
seed-runtime = false
steps = [
{ argv = ["vp", "env", "clean"], comment = "Clean isolated Vite+ caches" },
{ argv = ["vp", "env", "clean", "node"], comment = "Clean only Node.js runtimes" },
{ argv = ["vp", "env", "clean", "pm"], comment = "Clean all package-manager families" },
{ argv = ["vp", "env", "clean", "pnpm"], comment = "Clean one package-manager family" },
]
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,33 @@ VITE+ - The Unified Toolchain for the Web
✓ Removed 0 Node.js runtimes
✓ Removed 0 package manager installs
```

## `vp env clean node`

Clean only Node.js runtimes

```
VITE+ - The Unified Toolchain for the Web

✓ Removed 0 Node.js runtimes
```

## `vp env clean pm`

Clean all package-manager families

```
VITE+ - The Unified Toolchain for the Web

✓ Removed 0 package manager installs
```

## `vp env clean pnpm`

Clean one package-manager family

```
VITE+ - The Unified Toolchain for the Web

✓ Removed 0 package manager installs
```
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ steps = [
{ argv = ["vp", "env", "exec", "node", "-v"], comment = "Shim mode: version resolved from package.json engines.node", continue-on-failure = true },
{ argv = ["vp", "env", "exec", "npm", "-v"], comment = "Shim mode: npm uses same version", continue-on-failure = true },
{ argv = ["vp", "env", "exec", "node", "-e", "console.log('Hello from shim mode')"], comment = "Shim mode: run inline script", continue-on-failure = true },
{ argv = ["vp", "env", "exec", "nonexistent-tool", "--version"], comment = "expected error: non-shim command requires --node", continue-on-failure = true },
{ argv = ["vp", "env", "exec", "nonexistent-tool", "--version"], comment = "automatic mode resolves the environment before reporting a missing command", continue-on-failure = true },
]
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,10 @@ Hello from shim mode

## `vp env exec nonexistent-tool --version`

expected error: non-shim command requires --node
automatic mode resolves the environment before reporting a missing command

**Exit code:** 1

```
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
error: Command execution failed: No such file or directory (os error 2)
```
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ vp = "global"
skip-platforms = ["windows"]
seed-runtime = false
steps = [
{ argv = ["vp", "env", "install"], comment = "No version config - should error", continue-on-failure = true },
{ argv = ["vp", "env", "install"], comment = "No project declaration - install the resolved fallback environment", continue-on-failure = true },
]
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@

## `vp env install`

No version config - should error

**Exit code:** 1
No project declaration - install the resolved fallback environment

```
VITE+ - The Unified Toolchain for the Web

No Node.js version found in current project.
Specify a version: vp env install <VERSION>
Or pin one: vp env pin <VERSION>
Installing Node.js <version>...
Installed Node.js <version>
```
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ seed-runtime = false
steps = [
{ argv = ["vp", "env", "install", "lts"], comment = "Install an LTS Node.js version locally", continue-on-failure = true },
{ argv = ["vp", "env", "default", "lts"], comment = "Set it as the global default (stored as the `lts` alias)", continue-on-failure = true },
{ argv = ["node", "-e", "const {execFileSync}=require('node:child_process'); const {versions}=JSON.parse(execFileSync('vp',['env','list-remote','--lts','--json'],{encoding:'utf8'})); console.log('installed marked:', versions.some(v=>v.installed)); console.log('current marked:', versions.some(v=>v.current)); console.log('default marked:', versions.some(v=>v.default));"], comment = "installed/current/default flags should all resolve, including the `lts` default alias", continue-on-failure = true },
{ argv = ["node", "-e", "const {execFileSync}=require('node:child_process'); const {node}=JSON.parse(execFileSync('vp',['env','list-remote','--lts','--json'],{encoding:'utf8'})); console.log('installed marked:', node.some(v=>v.installed)); console.log('current marked:', node.some(v=>v.current)); console.log('default marked:', node.some(v=>v.default));"], comment = "the unified JSON node entries resolve installed/current/default flags, including the `lts` default alias", continue-on-failure = true },
]
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ Set it as the global default (stored as the `lts` alias)
```
VITE+ - The Unified Toolchain for the Web

Default Node.js version set to lts (currently <version>)
Environment defaults updated.
```

## `node -e 'const {execFileSync}=require('\''node:child_process'\''); const {versions}=JSON.parse(execFileSync('\''vp'\'',['\''env'\'','\''list-remote'\'','\''--lts'\'','\''--json'\''],{encoding:'\''utf8'\''})); console.log('\''installed marked:'\'', versions.some(v=>v.installed)); console.log('\''current marked:'\'', versions.some(v=>v.current)); console.log('\''default marked:'\'', versions.some(v=>v.default));'`
## `node -e 'const {execFileSync}=require('\''node:child_process'\''); const {node}=JSON.parse(execFileSync('\''vp'\'',['\''env'\'','\''list-remote'\'','\''--lts'\'','\''--json'\''],{encoding:'\''utf8'\''})); console.log('\''installed marked:'\'', node.some(v=>v.installed)); console.log('\''current marked:'\'', node.some(v=>v.current)); console.log('\''default marked:'\'', node.some(v=>v.default));'`

installed/current/default flags should all resolve, including the `lts` default alias
the unified JSON node entries resolve installed/current/default flags, including the `lts` default alias

```
installed marked: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ Switch to system-first mode
```
VITE+ - The Unified Toolchain for the Web

✓ Node.js management set to system-first.
✓ Node.js and package-manager management set to system-first.

All vp commands and shims will now prefer system Node.js, falling back to managed if not found.
Selected commands and shims will now prefer system tools, falling back to managed tools.

Run `vp env on` to always use Vite+ managed Node.js.
Run `vp env on` to always use Vite+ managed tools.
```

## `vp run assert-not-managed`
Expand All @@ -45,11 +45,11 @@ Switch back to managed mode
```
VITE+ - The Unified Toolchain for the Web

✓ Node.js management set to managed.
✓ Node.js and package-manager management set to managed.

All vp commands and shims will now always use Vite+ managed Node.js.
Selected commands and shims will now use Vite+ managed tools.

Run `vp env off` to prefer system Node.js instead.
Run `vp env off` to prefer system tools instead.
```

## `vp run assert-managed`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,15 @@ steps = [
{ argv = ["node", "-e", "const {execFileSync}=require('node:child_process');const text=execFileSync('vp',['env','which','npm'],{encoding:'utf8'});if(!text.includes('Package:')||!text.includes('npm@10.9.4')||!text.includes('package.json'))process.exit(1);console.log('which reports npm packageManager')"], comment = "which reports the npm packageManager pin" },
{ argv = ["node", "-e", "const {execFileSync}=require('node:child_process');const text=execFileSync('vp',['env','which','npx'],{encoding:'utf8'});if(!text.includes('Package:')||!text.includes('npm@10.9.4')||!text.includes('package.json'))process.exit(1);console.log('which reports npx packageManager')"], comment = "which reports the npx packageManager pin" },
]

[[case]]
name = "command_env_package_manager_session_provenance"
vp = "global"
skip-platforms = ["windows"]
steps = [
{ argv = ["vp", "env", "use", "npm@10.9.4", "--no-install"], snapshot = false },
{ argv = ["vpt", "write-file", "$VP_HOME/package_manager/npm/10.9.4/npm/bin/npm", "#!/bin/sh\n"], snapshot = false },
{ argv = ["vpt", "chmod", "+x", "$VP_HOME/package_manager/npm/10.9.4/npm/bin/npm"], snapshot = false },
{ argv = ["vp", "env", "current", "pm", "--json"], comment = "current reports the package-manager session file path" },
{ argv = ["vp", "env", "which", "npm"], comment = "which reports the package-manager session file as its source" },
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# command_env_package_manager_session_provenance

## `vp env use npm@10.9.4 --no-install`


## `vpt write-file $VP_HOME/package_manager/npm/10.9.4/npm/bin/npm '#'\!'/bin/sh
'`


## `vpt chmod +x $VP_HOME/package_manager/npm/10.9.4/npm/bin/npm`


## `vp env current pm --json`

current reports the package-manager session file path

```
{
"package_manager": {
"name": "npm",
"version": "<version>",
"source": ".session-package-manager",
"source_path": "<home>/.vite-plus/.session-package-manager",
"bin_paths": {
"npm": "<home>/.vite-plus/package_manager/npm/<version>/npm/bin/npm",
"npx": "<home>/.vite-plus/package_manager/npm/<version>/npm/bin/npx"
},
"installed": false,
"mode": "managed"
}
}
```

## `vp env which npm`

which reports the package-manager session file as its source

```
VITE+ - The Unified Toolchain for the Web

<home>/.vite-plus/package_manager/npm/<version>/npm/bin/npm
Package: npm@10.9.4
Source: <home>/.vite-plus/.session-package-manager
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "command-env-package-manager-mismatch",
"private": true,
"packageManager": "pnpm@10.18.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
[[case]]
name = "env_pin_warns_when_package_manager_differs"
vp = "global"
seed-runtime = false
steps = [
{ argv = ["vp", "env", "pin", "yarn@4.12.0", "--no-install"], comment = "an explicit project manager warns before a different manager is pinned" },
]

[[case]]
name = "env_use_warns_when_package_manager_differs"
vp = "global"
seed-runtime = false
skip-platforms = ["windows"]
env = { VP_ENV_USE_EVAL_ENABLE = "1" }
steps = [
{ argv = ["vp", "env", "use", "yarn@4.12.0", "--no-install"], comment = "an explicit project manager warns before a different session manager is used" },
]

[[case]]
name = "env_use_does_not_warn_for_different_default"
vp = "global"
seed-runtime = false
skip-platforms = ["windows"]
env = { VP_ENV_USE_EVAL_ENABLE = "1" }
steps = [
{ argv = ["vp", "env", "default", "pnpm@10.18.0"], snapshot = false },
{ argv = ["vpt", "write-file", "package.json", "{\"name\":\"command-env-package-manager-mismatch\",\"private\":true}\n"], snapshot = false },
{ argv = ["vp", "env", "use", "yarn@4.12.0", "--no-install"], comment = "a different fallback manager does not warn" },
]

[[case]]
name = "env_pin_warns_for_lockfile_selection_offline"
vp = "global"
seed-runtime = false
steps = [
{ argv = ["vpt", "write-file", "package.json", "{\"name\":\"command-env-package-manager-mismatch\",\"private\":true}\n"], snapshot = false },
{ argv = ["vpt", "touch-file", "pnpm-lock.yaml"], snapshot = false },
{ argv = ["vp", "env", "pin", "yarn@4.12.0", "--no-install"], comment = "lockfile mismatch warning does not depend on registry resolution", envs = [["NPM_CONFIG_REGISTRY", "http://127.0.0.1:9"]] },
]

[[case]]
name = "env_node_list_ignores_package_manager_resolution"
vp = "global"
seed-runtime = false
steps = [
{ argv = ["vpt", "write-file", "package.json", "{\"name\":\"command-env-package-manager-mismatch\",\"private\":true,\"devEngines\":{\"packageManager\":{\"name\":\"pnpm\",\"version\":\"^10.0.0\"}}}\n"], snapshot = false },
{ argv = ["vp", "env", "list", "node", "--json"], comment = "the node selector does not resolve an excluded package manager", envs = [["NPM_CONFIG_REGISTRY", "http://127.0.0.1:9"]] },
{ argv = ["vp", "env", "list-remote", "20.18.0", "--lts", "--json"], comment = "the implicit node selector does not resolve an excluded package manager", envs = [["NPM_CONFIG_REGISTRY", "http://127.0.0.1:9"]] },
]
Loading