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 的 四栏完整对照表