toolfree

npm → pnpm

你熟的 npm 指令與對應的 pnpm 寫法並排,並說明 node_modules 不再攤平之後,實際上會有什麼東西壞掉。

在你的瀏覽器中執行

要做的事npmpnpm
安裝 lockfile 裡的全部相依
CI 安裝,lockfile 若會改動就失敗
新增相依套件
新增開發相依
鎖定版本,不用範圍
全域安裝
移除相依套件
在既有版本範圍內更新
列出落後最新版的套件
執行 package.json 裡的 script
不安裝就執行套件的執行檔
建立新套件
查出某個套件為什麼被裝進來
檢查相依套件的安全通報
把相依裝到某一個 workspace

簡短版

npmpnpm
npm installpnpm install,或 pnpm i
npm install <pkg>pnpm add <pkg>
npm install -D <pkg>pnpm add -D <pkg>
npm uninstall <pkg>pnpm remove <pkg>
npm run buildpnpm build
npx <pkg>pnpm dlx <pkg>
npm install <pkg> -w <name>pnpm --filter <name> add <pkg>

pnpm 的指令刻意做得跟 npm 很接近,所以這一層翻譯幾乎是機械式的。完整的表格在上面,可以填入你 自己的套件名稱。

真正改變的是什麼

指令是簡單的部分。安裝出來的結構才是你換過去的理由,也是東西壞掉的理由。

node_modules 不是攤平的。 npm 與 Yarn 1 會把所有間接相依提升到最上層,於是程式碼可以 require('某個套件')——即使 package.json 裡從來沒宣告過它——而且真的會動。pnpm 只把你宣告過的 相依放在最上層,其餘全部從一個 content-addressed 的 store 用 symlink 連進去。

這確實抓得出 bug,而且抓出來的多半在別人寫的套件裡。依賴提升的套件在 pnpm 下會直接 Cannot find module。解法通常是在 .npmrcpublic-hoist-pattern,有時候是去上游開 issue。

硬碟用量會直接垮下來。 每個套件的每個版本在一台機器上只存一份,再 hard link 進各個專案。 十個專案用同一版 React,只佔一份。

pnpm dlx 不是換名字的 npx npx 會優先執行本地 node_modules 裡已有的執行檔; pnpm dlx 一律抓到暫存區再執行。要跑本地的執行檔請用 pnpm exec

遷移一個專案

  1. 刪掉 package-lock.jsonnode_modules
  2. 執行 pnpm install
  3. 跑一次 build 和測試。提升假設是在這裡爆的,不是在安裝的時候。
  4. Commit pnpm-lock.yaml,而且只 commit 這一個。

monorepo 還要加一個 pnpm-workspace.yaml。pnpm 不讀 package.json 裡的 workspaces 欄位——這是最多人卡住的一點,因為安裝會成功,只是什麼都沒連起來。

相關

npm 轉 Yarn,或含 Bun 的 四欄完整對照表