61DH61DH
Home
Diary
English
Tools
Photography
Health
Tech
Home
Diary
English
Tools
Photography
Health
Tech
  • 日记

    • 前言
    • pnpm 10 也可以管理项目指定的 pnpm 版本
    • Node.js、nvm、Corepack 与 pnpm 环境说明
    • 装修故事 3|厨房
    • 装修的故事二 · 卫生间
    • 装修的故事一 · 选柜子
    • 讨好 Google,不是一件容易的事
    • VS Code 中 Markdown 粘贴图片工作流(VuePress)
    • Best Buy 与我的电器店记忆:从 2001 到今天的一次“退货又下单”
    • 中国流量异常日记
    • 我的 iPhone 17 Pro 首日开箱记
    • Perplexity 是流氓软件吗?– 一个技术站长的实录与更正
    • 给 VuePress 添加评论系统:Waline 实战指南
    • 从“骨瘤?”到“脂肪瘤”:我的第一次手术日记
    • Coffee Sleeve 是什麼?
    • 一次 Maker 升级到 SKY 的实操:从 Coinbase 到 MetaMask,再谈钱包安全
    • Skyoo「脑力素」广告很火,但靠谱吗?
    • 回国礼物推荐:清单与避坑指南
    • 厨房装修小记:岛台移除后的地板方案
    • 苹果 2025 发布会小记
    • VuePress v2 index.scss 覆盖问题笔记
    • 康师傅酸梅汤:异乡小记
    • 升级笔记:VuePress 从 2.0.0-beta.51 到 ^2.0.0-rc.24
    • 旧日记
2026/08/24

补充:pnpm 10 自己也可以管理项目指定的 pnpm 版本

这是一个很重要的补充:即使 Corepack 没有启用,现代 pnpm 也可以根据项目 package.json 中的 packageManager 自动选择并运行指定版本的 pnpm。

1. 我的实际环境

Mac 上全局安装的 pnpm 是:

which pnpm
# /opt/homebrew/bin/pnpm

pnpm --version
# 在普通目录下:10.28.0

这个 executable 实际指向:

/opt/homebrew/bin/pnpm
    ↓
/opt/homebrew/lib/node_modules/pnpm/bin/pnpm.cjs

也就是说:

Global pnpm = 10.28.0
Corepack    = disabled

但是进入一个项目以后,如果 package.json 有:

{
  "packageManager": "pnpm@9.12.0"
}

执行:

pnpm --version

得到:

9.12.0

而离开项目:

cd ~
pnpm --version

又得到:

10.28.0

所以这是 pnpm 自己的版本管理功能,不是 Corepack。


2. 实际工作流程

可以理解成:

/opt/homebrew/bin/pnpm
Global pnpm 10.28.0
        ↓
启动
        ↓
检查当前 project
        ↓
读取 package.json
        ↓
发现:
"packageManager": "pnpm@9.12.0"
        ↓
查找 pnpm 自己管理的 9.12.0
        ↓
运行 pnpm 9.12.0

因此:

which pnpm

仍然可能显示:

/opt/homebrew/bin/pnpm

但是:

pnpm --version

却显示:

9.12.0

这两个结果并不矛盾。

which pnpm 告诉我:

Shell 最开始启动的是哪个 executable。

pnpm --version 告诉我:

针对当前项目,最终实际运行的是哪个 pnpm 版本。


3. 项目指定版本到底下载到哪里?

已经在我的 Mac 上确认,pnpm 自己管理的版本放在:

~/Library/pnpm/.tools/pnpm/

实际看到:

~/Library/pnpm/.tools/pnpm/

10.4.1 -> 10.4.1_tmp_86136
10.4.1_tmp_86136/

9.12.0 -> 9.12.0_tmp_67505
9.12.0_tmp_67505/

所以当前机器上至少已经有:

pnpm-managed tools
├── pnpm 9.12.0
└── pnpm 10.4.1

这些版本:

  • 不是安装在项目的 node_modules
  • 不是 /opt/homebrew/bin/pnpm 那个 global installation
  • 不是 Corepack cache
  • 是 pnpm 自己管理的 user-level package-manager tools

同一个版本可以被多个项目共用,不需要每个 project 下载一份。


4. Global pnpm 和 project pnpm 的关系

例如:

Global installation

/opt/homebrew/bin/pnpm
        ↓
pnpm 10.28.0
        │
        │ reads package.json
        ↓
"packageManager": "pnpm@9.12.0"
        │
        ↓
~/Library/pnpm/.tools/pnpm/9.12.0
        │
        ↓
实际执行 pnpm 9.12.0

另一个项目如果有:

{
  "packageManager": "pnpm@10.4.1"
}

则:

/opt/homebrew/bin/pnpm
        ↓
pnpm 10.28.0
        ↓
packageManager says 10.4.1
        ↓
~/Library/pnpm/.tools/pnpm/10.4.1
        ↓
实际执行 pnpm 10.4.1

所以可以同时存在:

Global launcher:
pnpm 10.28.0

Project A:
pnpm 9.12.0

Project B:
pnpm 10.4.1

而不需要不断执行:

npm install -g pnpm@...

5. 这和 Corepack 非常像,但不是一回事

Corepack enabled

pnpm command
    ↓
Corepack shim
    ↓
读取 packageManager
    ↓
选择对应 pnpm

这时候:

which pnpm

可能看到:

~/.nvm/versions/node/v22.11.0/bin/pnpm
    ↓
corepack/dist/pnpm.js

Corepack disabled + pnpm 10

我的当前情况:

pnpm command
    ↓
/opt/homebrew/bin/pnpm
    ↓
global pnpm 10.28.0
    ↓
读取 packageManager
    ↓
pnpm自己的 version management
    ↓
~/Library/pnpm/.tools/pnpm/<version>

所以最终效果很相似:

每个 project 可以使用自己指定的 pnpm version。

但是负责这件事的工具不同。


6. 怎么确认 Corepack 到底有没有参与?

不能只看:

pnpm --version

因为看到:

9.12.0

并不代表 Corepack 一定参与。

应该看:

which pnpm
ls -l "$(which pnpm)"

我的 Corepack disabled 状态是:

/opt/homebrew/bin/pnpm
→ ../lib/node_modules/pnpm/bin/pnpm.cjs

这说明:

Corepack 没有参与

如果看到:

~/.nvm/versions/node/v22.11.0/bin/pnpm
→ ../lib/node_modules/corepack/dist/pnpm.js

才说明:

Corepack 正在接管 pnpm

7. 怎么查看 pnpm 自己下载了哪些版本?

我的实际目录:

cd ~/Library/pnpm/.tools/pnpm
ls -la

例如:

10.4.1 -> 10.4.1_tmp_86136
10.4.1_tmp_86136

9.12.0 -> 9.12.0_tmp_67505
9.12.0_tmp_67505

也可以通过 PNPM home 找:

pnpm bin -g

然后检查:

ls -la "$(pnpm bin -g)/.tools/pnpm"

如果要确认某个 managed version:

cat ~/Library/pnpm/.tools/pnpm/9.12.0/package.json | grep '"version"'

8. 对我现在环境的实际意义

目前没有必要为了 per-project pnpm version 而专门启用 Corepack。

当前环境已经可以做到:

nvm
└── Node 22.11.0

Global pnpm
└── 10.28.0

Corepack
└── disabled

Project A
packageManager: pnpm@9.12.0
→ 实际 pnpm 9.12.0

Project B
packageManager: pnpm@10.4.1
→ 实际 pnpm 10.4.1

普通目录 / 没有指定版本的项目
→ global pnpm 10.28.0

这已经实现了最重要的目标:

不同项目可以固定并使用不同 pnpm version。


9. 一个容易误判的地方

曾经在指定:

"packageManager": "pnpm@9.12.0"

的项目中,似乎看到过:

pnpm --version
# 10.28.0

目前无法重现。

现在稳定、可重复的结果是:

项目内:   pnpm 9.12.0
项目外:   pnpm 10.28.0

因此不应该根据之前一次无法重现的结果推断原因。

如果以后再次出现,不要关闭 Terminal,立即记录:

pwd

which node
node -v

which pnpm
type -a pnpm
pnpm --version

grep packageManager package.json

pnpm config get manage-package-manager-versions

这样可以确定当时到底是:

  • PATH 不同
  • Node/nvm 环境不同
  • pnpm executable 不同
  • project root 不同
  • pnpm version management 被关闭
  • 或其他 shell environment 差异

在没有重新出现之前,把那次 10.28.0 视为 unconfirmed transient state,而不是当前配置的正常行为。


10. 最终记住这三条

which pnpm

回答:

Shell 首先启动哪个 pnpm executable?

pnpm --version

回答:

在当前 project context 下,最终使用哪个 pnpm version?

packageManager

例如:

"packageManager": "pnpm@9.12.0"

对于现代 pnpm,可以告诉 pnpm:

这个项目应该使用 pnpm 9.12.0。

因此完全可能出现:

which pnpm
# /opt/homebrew/bin/pnpm

pnpm --version
# 9.12.0

这是正常的。

它表示:

global pnpm 10.28.0
        ↓
作为入口启动
        ↓
读取 project packageManager
        ↓
从 ~/Library/pnpm/.tools/pnpm/
选择项目指定版本
        ↓
实际运行 pnpm 9.12.0

整个过程不需要 Corepack。

Prev
前言
Next
Node.js、nvm、Corepack 与 pnpm 环境说明