@@ -23,7 +23,7 @@ This RFC proposes adding a `vp env` command that provides system-wide, IDE-safe
2323A shim-based approach where:
2424
2525- ` VITE_PLUS_HOME/bin/ ` directory is added to PATH (system-level for IDE reliability)
26- - Shims (` node ` , ` npm ` , ` npx ` ) are hardlinks/copies of the ` vp ` binary
26+ - Shims (` node ` , ` npm ` , ` npx ` ) are symlinks to the ` vp ` binary (Unix) or ` .cmd ` wrappers (Windows)
2727- The ` vp ` CLI itself is also in ` VITE_PLUS_HOME/bin/ ` , so users only need one PATH entry
2828- The binary detects invocation via ` argv[0] ` and dispatches accordingly
2929- Version resolution and installation leverage existing ` vite_js_runtime ` infrastructure
@@ -174,7 +174,7 @@ argv[0] = "npx" → Shim mode: resolve version, exec npx
174174│ │ │
175175│ ▼ │
176176│ ┌──────────────────────────────┐ │
177- │ │ ~/.vite-plus/bin/node │ ◄── Hardlink to vp binary (via PATH) │
177+ │ │ ~/.vite-plus/bin/node │ ◄── Symlink to vp binary (via PATH) │
178178│ │ (shim intercepts command) │ │
179179│ └──────────────┬───────────────┘ │
180180│ │ │
@@ -212,11 +212,11 @@ argv[0] = "npx" → Shim mode: resolve version, exec npx
212212│ │
213213│ ~/.vite-plus/ (VITE_PLUS_HOME) │
214214│ ├── bin/ │
215- │ │ ├── vp ────────────────────── Symlink to ../current/vp │
215+ │ │ ├── vp ────────────────────── Symlink to ../current/bin/vp │
216216│ │ ├── node ──────────────────────┐ │
217- │ │ ├── npm ──────────────────────┼──▶ Hardlinks to vp binary │
217+ │ │ ├── npm ──────────────────────┼──▶ Symlinks to ../current/bin/vp │
218218│ │ └── npx ──────────────────────┘ │
219- │ ├── current/vp The actual vp CLI binary │
219+ │ ├── current/bin/vp The actual vp CLI binary │
220220│ ├── js_runtime/node/ Node.js installations │
221221│ │ ├── 20.18.0/bin/node Installed Node.js versions │
222222│ │ ├── 22.13.0/bin/node │
@@ -257,18 +257,19 @@ argv[0] = "npx" → Shim mode: resolve version, exec npx
257257```
258258VITE_PLUS_HOME/ # Default: ~/.vite-plus
259259├── bin/
260- │ ├── vp -> ../current/vp # Symlink to current vp binary (Unix)
261- │ ├── node # Hardlink to vp binary (Unix)
262- │ ├── npm # Hardlink to vp binary (Unix)
263- │ ├── npx # Hardlink to vp binary (Unix)
264- │ ├── tsc # Hardlink for global package binary (Unix)
265- │ ├── vp.cmd # Wrapper script calling ..\current\vp.exe (Windows)
266- │ ├── node.exe # Copy of current\vp.exe (Windows)
267- │ ├── npm.cmd # Wrapper script (Windows)
268- │ └── npx.cmd # Wrapper script (Windows)
260+ │ ├── vp -> ../current/bin/vp # Symlink to current vp binary (Unix)
261+ │ ├── node -> ../current/bin/vp # Symlink to vp binary (Unix)
262+ │ ├── npm -> ../current/bin/vp # Symlink to vp binary (Unix)
263+ │ ├── npx -> ../current/bin/vp # Symlink to vp binary (Unix)
264+ │ ├── tsc -> ../current/bin/vp # Symlink for global package (Unix)
265+ │ ├── vp.cmd # Wrapper calling ..\current\bin \vp.exe (Windows)
266+ │ ├── node.cmd # Wrapper calling vp env run node (Windows)
267+ │ ├── npm.cmd # Wrapper calling vp env run npm (Windows)
268+ │ └── npx.cmd # Wrapper calling vp env run npx (Windows)
269269├── current/
270- │ ├── vp # The actual vp CLI binary (Unix)
271- │ └── vp.exe # The actual vp CLI binary (Windows)
270+ │ └── bin/
271+ │ ├── vp # The actual vp CLI binary (Unix)
272+ │ └── vp.exe # The actual vp CLI binary (Windows)
272273├── js_runtime/
273274│ └── node/
274275│ ├── 20.18.0/ # Installed Node versions
@@ -301,7 +302,7 @@ VITE_PLUS_HOME/ # Default: ~/.vite-plus
301302| Directory | Purpose |
302303| ------------------ | ------------------------------------------------------------------ |
303304| ` bin/ ` | vp symlink and all shims (node, npm, npx, global package binaries) |
304- | ` current/ ` | The actual vp CLI binary (bin/vp symlinks here) |
305+ | ` current/bin/ ` | The actual vp CLI binary (bin/ shims point here) |
305306| ` js_runtime/node/ ` | Installed Node.js versions |
306307| ` packages/ ` | Installed global packages with metadata |
307308| ` shared/ ` | NODE_PATH symlinks for package require() resolution |
@@ -605,27 +606,29 @@ fn execute_run_command() {
605606- Consistent behavior across all tools
606607- Already proven pattern (used by fnm, volta)
607608
608- ### 2. Hardlinks over Symlinks (Unix)
609+ ### 2. Symlinks for Shims (Unix)
609610
610- ** Decision** : Use hardlinks for shims on Unix, with fallback to copy .
611+ ** Decision** : Use symlinks for all shims on Unix, pointing to the vp binary .
611612
612613** Rationale** :
613614
614- - Hardlinks work across more filesystem types than symlinks
615- - Symlinks can cause argv[ 0] to resolve to the target name
616- - Hardlinks preserve the intended argv[ 0] value
617- - Copy fallback for cross-filesystem scenarios
615+ - Symlinks preserve argv[ 0] - executing a symlink sets argv[ 0] to the symlink path, not the target
616+ - Proven pattern used by Volta successfully
617+ - Single binary to maintain - update ` current/bin/vp ` and all shims work
618+ - No binary accumulation issues (symlinks are just filesystem pointers)
619+ - Relative symlinks (e.g., ` ../current/bin/vp ` ) work within the same directory tree
618620
619- ### 3. Wrapper Scripts for Windows npm/npx
621+ ### 3. Wrapper Scripts for Windows
620622
621- ** Decision** : Use ` .cmd ` wrapper scripts for npm/npx on Windows with ` VITE_PLUS_SHIM_TOOL ` environment variable .
623+ ** Decision** : Use ` .cmd ` wrapper scripts on Windows that call ` vp env run <tool> ` .
622624
623625** Rationale** :
624626
625627- Windows PATH resolution prefers ` .cmd ` over ` .exe ` for extensionless commands
626- - npm is typically invoked as ` npm ` not ` npm.exe `
627- - ` .cmd ` wrappers set ` VITE_PLUS_SHIM_TOOL ` env var and forward to ` vp.exe `
628- - More maintainable than multiple .exe copies - only one binary to update
628+ - Simple wrapper format: ` vp env run npm %* ` - no binary copies needed
629+ - Same pattern as Volta (` volta run <tool> ` )
630+ - Single ` vp.exe ` binary to maintain in ` current/bin/ `
631+ - No ` VITE_PLUS_SHIM_TOOL ` env var complexity - dispatch via ` vp env run ` command
629632
630633### 4. execve on Unix, spawn on Windows
631634
@@ -733,8 +736,8 @@ Recommended Fix:
733736
734737The global CLI installation script (` packages/global/install.sh ` ) will be updated to:
735738
736- 1 . Install the ` vp ` binary to ` ~/.vite-plus/current/vp `
737- 2 . Create symlink ` ~/.vite-plus/bin/vp ` → ` ../current/vp `
739+ 1 . Install the ` vp ` binary to ` ~/.vite-plus/current/bin/ vp `
740+ 2 . Create symlink ` ~/.vite-plus/bin/vp ` → ` ../current/bin/ vp `
7387413 . Configure shell PATH to include ` ~/.vite-plus/bin `
7397424 . Setup Node.js version manager based on environment:
740743 - ** CI environment** : Auto-enable (no prompt)
@@ -1499,61 +1502,112 @@ $ vp env --current --json
14991502| ` VITE_PLUS_TOOL_RECURSION` | ** Internal** : Prevents shim recursion | unset |
15001503| ` VITE_PLUS_UNSAFE_GLOBAL` | Bypass global package interception | unset |
15011504
1505+ # # Unix-Specific Considerations
1506+
1507+ # ## Shim Structure
1508+
1509+ ` ` `
1510+ VITE_PLUS_HOME/
1511+ ├── bin/
1512+ │ ├── vp -> ../current/bin/vp # Symlink to actual binary
1513+ │ ├── node -> ../current/bin/vp # Symlink to same binary
1514+ │ ├── npm -> ../current/bin/vp # Symlink to same binary
1515+ │ ├── npx -> ../current/bin/vp # Symlink to same binary
1516+ │ └── tsc -> ../current/bin/vp # Symlink for global package
1517+ └── current/
1518+ └── bin/
1519+ └── vp # The actual vp CLI binary
1520+ ` ` `
1521+
1522+ # ## How argv[0] Detection Works
1523+
1524+ When a user runs ` node` :
1525+ 1. Shell finds ` ~/.vite-plus/bin/node` in PATH
1526+ 2. This is a symlink to ` ../current/bin/vp`
1527+ 3. Kernel resolves symlink and executes ` vp` binary
1528+ 4. ` argv[0]` is set to the invoking path: ` node` (or full path)
1529+ 5. ` vp` binary extracts tool name from ` argv[0]` (gets " node" )
1530+ 6. Dispatches to shim logic for node
1531+
1532+ ** Key Insight** : Symlinks preserve argv[0]. This is the same pattern Volta uses successfully.
1533+
1534+ # ## Symlink Creation
1535+
1536+ All shims use relative symlinks:
1537+
1538+ ` ` ` bash
1539+ # Core tools
1540+ ln -sf ../current/bin/vp ~ /.vite-plus/bin/node
1541+ ln -sf ../current/bin/vp ~ /.vite-plus/bin/npm
1542+ ln -sf ../current/bin/vp ~ /.vite-plus/bin/npx
1543+
1544+ # Global package binaries
1545+ ln -sf ../current/bin/vp ~ /.vite-plus/bin/tsc
1546+ ` ` `
1547+
15021548# # Windows-Specific Considerations
15031549
15041550# ## Shim Structure
15051551
15061552` ` `
15071553VITE_PLUS_HOME\
15081554├── bin\
1509- │ ├── vp.cmd # Wrapper script calling ..\current\vp.exe
1510- │ ├── node.exe # Copy of current\vp.exe
1511- │ ├── npm.cmd # Wrapper script
1512- │ └── npx.cmd # Wrapper script
1555+ │ ├── vp.cmd # Wrapper calling ..\current\bin \vp.exe
1556+ │ ├── node.cmd # Wrapper calling vp env run node
1557+ │ ├── npm.cmd # Wrapper calling vp env run npm
1558+ │ └── npx.cmd # Wrapper calling vp env run npx
15131559└── current\
1514- └── vp.exe # The actual vp CLI binary
1560+ └── bin\
1561+ └── vp.exe # The actual vp CLI binary
15151562` ` `
15161563
15171564# ## Wrapper Script Template (vp.cmd)
15181565
15191566` ` ` batch
15201567@echo off
1521- " %~dp0..\current\vp.exe" %*
1568+ " %~dp0..\current\bin\ vp.exe" %*
15221569exit /b %ERRORLEVEL%
15231570` ` `
15241571
1525- The ` vp.cmd` wrapper simply forwards all arguments to the actual ` vp.exe` binary in the ` current ` directory .
1572+ The ` vp.cmd` wrapper forwards all arguments to the actual ` vp.exe` binary.
15261573
1527- # ## Wrapper Script Template (npm.cmd)
1574+ # ## Wrapper Script Template (node.cmd, npm.cmd, npx.cmd)
1575+
1576+ ` ` ` batch
1577+ @echo off
1578+ " %~dp0..\current\bin\vp.exe" env run node %*
1579+ exit /b %ERRORLEVEL%
1580+ ` ` `
15281581
1582+ For npm:
15291583` ` ` batch
15301584@echo off
1531- setlocal
1532- set " VITE_PLUS_SHIM_TOOL=npm"
1533- " %~dp0node.exe" %*
1585+ " %~dp0..\current\bin\vp.exe" env run npm %*
15341586exit /b %ERRORLEVEL%
15351587` ` `
15361588
1537- The ` .cmd` wrapper sets ` VITE_PLUS_SHIM_TOOL` environment variable before calling ` node.exe` (which is a copy of ` vp.exe` ). The Rust binary checks this env var first before falling back to argv[0] detection.
1589+ ** How it works** :
1590+
1591+ 1. User runs ` npm install`
1592+ 2. Windows finds ` ~/.vite-plus/bin/npm.cmd` in PATH
1593+ 3. Wrapper calls ` vp.exe env run npm install`
1594+ 4. ` vp env run` command handles version resolution and execution
15381595
15391596** Benefits of this approach** :
15401597
1541- - Single ` vp.exe` binary to update in ` current\` directory
1542- - ` node.exe ` in ` bin \` is a copy for shim detection via argv[0]
1543- - ` .cmd ` wrappers are trivial text files
1544- - Clear separation of concerns: ` .cmd ` sets context, binary does the work
1598+ - Single ` vp.exe` binary to update in ` current\b in \`
1599+ - All shims are trivial ` .cmd ` text files (no binary copies)
1600+ - Consistent with Volta ' s Windows approach
1601+ - Clear, readable wrapper scripts
15451602
15461603### Windows Installation (install.ps1)
15471604
1548- The Windows installer (` install.ps1` ) follows the same flow:
1605+ The Windows installer (`install.ps1`) follows this flow:
15491606
1550- 1. Download and install ` vp.exe` to ` ~/.vite-plus/current/`
1607+ 1. Download and install `vp.exe` to `~/.vite-plus/current/bin/ `
155116082. Create `~/.vite-plus/bin/vp.cmd` wrapper script
1552- 3. Configure User PATH to include ` ~/.vite-plus/bin`
1553- 4. Setup Node.js version manager based on environment:
1554- - ** CI environment** : Auto-enable (no prompt)
1555- - ** No system Node.js** : Auto-enable (no prompt)
1556- - ** Interactive with system Node.js** : Prompt user
1609+ 3. Create shim wrappers: `node.cmd`, `npm.cmd`, `npx.cmd`
1610+ 4. Configure User PATH to include `~/.vite-plus/bin`
15571611
15581612## Testing Strategy
15591613
@@ -1605,9 +1659,9 @@ env-doctor/
16051659# ## Phase 1: Core Infrastructure (P0)
16061660
160716611. Add ` vp env` command structure to CLI
1608- 2. Implement argv[0] detection in main.rs (also check `VITE_PLUS_SHIM_TOOL` env var for Windows)
1662+ 2. Implement argv[0] detection in main.rs
160916633. Implement shim dispatch logic for ` node`
1610- 4. Implement `vp env setup` (Unix hardlinks , Windows .exe copy + .cmd wrappers)
1664+ 4. Implement ` vp env setup` (Unix symlinks , Windows .cmd wrappers)
161116655. Implement ` vp env doctor` basic diagnostics
161216666. Add resolution cache (persists across upgrades with version field)
161316677. Implement ` vp env default [version]` to set/show global default Node.js version
@@ -1661,7 +1715,7 @@ The following decisions have been made:
16611715
166217161. ** VITE_PLUS_HOME Default Location** : ` ~/.vite-plus` - Simple, memorable path that' s easy for users to find and configure.
16631717
1664- 2. ** Windows Wrapper Strategy** : ` .cmd` wrappers with ` VITE_PLUS_SHIM_TOOL ` environment variable - More maintainable, only one binary to update .
1718+ 2. **Windows Wrapper Strategy**: `.cmd` wrappers that call `vp env run <tool>` - Consistent with Volta, no binary copies needed .
16651719
166617203. **Corepack Handling**: Not included - vite-plus has integrated package manager functionality, making corepack shims unnecessary.
16671721
0 commit comments