Vite+ 的 vp env:pin 了版本卻沒生效

vp env 能接手 Node.js 與套件管理器的版本,專案裡寫了 .node-version 也 pin 好了,跑出來的卻是別的版本。 原因是 vp env use 寫下的覆寫檔不只對當下的 shell 有效,還會蓋過所有專案的 pin。 附上隔離環境的重現步驟,以及這個站實際用 Vite+ 的版本與升級紀錄。

過去切換 Node.js 版本的標準答案是 nvm。Vite+(指令是 vp)把這件事收進同一個工具:vp env 管 Node.js 與套件管理器的版本,同一個 vp 還接手檢查、格式化、測試。

這個站用的是 vite-plus 1.0.0-rc.0。這篇先講它收了哪些事,再講一個我們實際遇到、而且工具自己沒有報錯的坑:.node-version 寫好、也 pin 成功了,實際跑的卻是另一個版本。

一、vp 一個指令收了哪些事

vp --help 列出的指令裡,這個站實際在用的是下面幾組:

以前要裝的東西現在的指令這個站有沒有在用
nvm 之類的 Node.js 版本管理vp env有,見下一節
ESLintvp lint有,--deny-warnings
Prettiervp fmt有
格式、檢查、型別三件事分開跑vp check有,一次跑完
Vitestvp test有
安裝依賴vp install沒有,仍用 pnpm

vp staged(對暫存檔跑檢查)與 vp hooks(管理 Git hook)在指令列表裡也有,我們這篇不評價,因為還沒有拿它們取代過別的東西。

底層是哪些工具,vp toolchain 會列出來。這個站目前的結果:

[email protected]
├── depends on @voidzero-dev/[email protected]
│   ├── bundles [email protected]
│   │   └── uses [email protected]
│   │       ├── compiles [email protected]
├── depends on [email protected]
├── depends on [email protected]
├── depends on [email protected]

也就是說 vp lint 背後是 oxlint、vp fmt 背後是 oxfmt。

二、用 .node-version 釘住 Node.js 版本

在專案根目錄跑:

vp env pin 22.19.0

實測會做兩件事:寫出內容為 22.19.0 的 .node-version,並且把這個版本預先下載好(不想下載可加 --no-install)。之後在這個目錄裡跑 vp node -v,預期得到 v22.19.0。

在一個乾淨的環境裡,這確實成立。問題出在不乾淨的環境。

三、坑:vp env use 的覆寫會蓋過所有專案的 pin

症狀

專案裡 .node-version 是 22.19.0,vp env pin 也顯示成功,但:

vp node -v
# v24.21.0

vp env doctor 的最後一行是 All checks passed。

為什麼直覺會猜錯

直覺有兩個:懷疑 .node-version 沒被讀到,或懷疑 Vite+ 當下是「優先用系統 Node.js」的模式。我們兩個都先查了,都不是主因:

  • 在 vp env off node(優先用系統)的模式下,vp node -v 仍然用了 pin 的版本。
  • 換成全新的 VP_HOME(Vite+ 存放自己資料的目錄),同一個 .node-version 立刻生效。

差別在於,原本的環境裡曾經跑過 vp env use。

根因

vp env use 24.21.0 的說明寫的是「Activate ... for this shell session」,容易讓人以為只影響當下這個終端機。以一般執行檔呼叫 vp 時,實測並不是:它在 Vite+ 的資料目錄寫了一個 .session-node-version 檔,內容是 24.21.0(設了 VP_HOME 就在 VP_HOME 底下;沒設的話,在我們的機器上是 ~/.local/state/vite-plus/)。檔案是全域的,所以:

  • 開另一個終端機、另一個 shell,一樣被覆寫。
  • 切到完全不同的專案(那裡 pin 的是 20.19.0),也一樣被覆寫。
  • 優先序高過專案的 .node-version。

vp env doctor 會把它標成警告(⚠ Session override (file)),但整體仍然顯示通過,不會讓指令失敗。

以上是 vp 以一般執行檔呼叫時的行為。如果 shell 載入了 Vite+ 的整合(type vp 顯示 vp is a function),vp env use 改設當前 shell 的環境變數 VP_NODE_VERSION,不寫檔,影響範圍只到這個 shell 和它開出的子行程;vp env doctor 顯示的是 Session override VP_NODE_VERSION=24.21.0。兩種情況都會蓋過切過去的每一個專案的 pin,差別只在新開的終端機會不會一起被蓋。

最小重現

在隔離的 VP_HOME 裡做,不碰既有環境:

# vp 若是 shell 函式(type vp 顯示 function),以下的 vp 改成 command vp
export VP_HOME=$(mktemp -d)
mkdir demo other
(cd other && vp env pin 20.19.0)
cd demo
vp env pin 22.19.0
vp node -v                  # v22.19.0
vp env use 24.21.0
vp node -v                  # v24.21.0,.node-version 還是 22.19.0
cd ../other && vp node -v   # v24.21.0,這個目錄 pin 的是 20.19.0
# 另開終端機,export 同一個 VP_HOME 後到 other 跑 vp node -v,仍是 v24.21.0
vp env use --unset
vp node -v                  # v20.19.0,回到這個目錄自己 pin 的版本

判準

  • pin 好了卻沒生效,先看覆寫,不要先改 pin。 vp env doctor 裡有 Session override 就是它,vp env use --unset 拿掉。
  • 不要把 vp env use 當成「只試一下」的指令。 它改的是全域狀態。要在單一專案固定版本,用 vp env pin。
  • vp env current 與 vp node -v 兩個指令的答案不一定一致。 在「優先用系統」模式下,我們測到前者回報系統的 24.21.0,後者卻用了 pin 的 22.19.0。判斷實際跑的版本,以 vp node -v 為準。

四、管理模式是分開開關的

vp env 不是全收或全不收。vp env doctor 會分開列出 Node.js、npm、pnpm、Yarn、Bun 各自是「託管」還是「優先用系統」。這個站的開發機就是混合的:Node.js 仍由系統端的版本管理工具提供,pnpm 則交給 Vite+ 託管,版本取自 package.json 的 packageManager 欄位。

所以「Vite+ 能不能取代 nvm」要拆開看:版本管理它做得到,但你得決定哪些元件交給它。把 Node.js 留給原本的工具、只讓它管 pnpm,是可行的做法。

五、升級紀錄與版本綁定

這個站的 vite-plus 在一個多月內升了兩次:

日期變化
2026-09-100.1.22 → 0.3.1
2026-10-030.3.1 → 1.0.0-rc.0,vitest 同時從 4.1.11 升到 5.0.1

兩點值得記:

  • vitest 版本跟著 vite-plus 走。 1.0.0-rc.0 搭 vitest 5.0.1,兩者是同一個 commit 一起換的,沒有單獨升其中一個。
  • vite 是被別名取代的。 pnpm-workspace.yaml 用 catalog 把 vite 指向 @voidzero-dev/vite-plus-core,再用 overrides 讓所有依賴它的套件拿到同一份,並且在 peerDependencyRules 放行 vite 與 vitest 的任意版本。

另一個會踩到的是 pnpm 的 release-age 冷卻:剛發布的版本在冷卻期內裝不起來。升 1.0.0-rc.0 時,pnpm 把例外寫進 minimumReleaseAgeExclude,而且是一個一個列:vite-plus 本身、@voidzero-dev/vite-plus-core,加上 8 個平台的二進位套件(macOS 與 Windows 各 arm64、x64 兩個,Linux 的 arm64、x64 再各分 glibc 與 musl 兩種)。例外名單要跟著版本號寫,下一個版本就得重來一次。

升完之後的驗證,這個站是跑完整的 generate、build、型別檢查、vp test、lint 與 format 檢查,全部退出碼為 0 才算數。

這篇沒有驗證的

  • 只在 Linux 上測過 vp env,Windows 與 macOS 沒測。
  • vp staged 與 vp hooks 沒有拿來取代過別的東西,所以沒有評價。
  • 1.0.0-rc.0 仍是候選版,指令與行為在正式版前可能再變。這篇的重現步驟以此版本為準。