跳到主内容
版本:11 & 12

全局软件包

全局软件包是使用 pnpm add -g 安装到系统范围内的 CLI 工具和实用程序。 在 pnpm v11 中,全局包管理被重新设计,以提升隔离性和可靠性。

安装全局软件包

pnpm add -g <pkg>

例如:

pnpm add -g typescript prettier eslint

独立安装

每个全局安装的软件包(或一起安装的一组软件包)都有其自己的独立安装目录,其中包含自己的 package.jsonnode_modules/ 和锁文件。 这样可以阻止全局包之间因对等依赖冲突、提升更改或版本错位而相互干扰。

隔离安装存储在 {pnpmHomeDir}/global/v11/{hash}/,其中哈希值是从一起安装的软件包集合中派生出来的。

例如,运行以下两个命令:

pnpm add -g typescript
pnpm add -g prettier

创建两个独立的隔离安装——typescriptprettier 各自拥有自己的 node_modules 树,并且不会影响彼此的依赖项解析。

在单个命令中安装多个空格分隔 软件包也会为每个软件包单独创建一个单独的安装:

pnpm add -g eslint prettier

eslintprettier都会得到它们自己的 node_modules 树和锁pnpm文件并可以被独立删除 — — pnpm remove -g eslint 保持 prettier 不变。

若要将多个包捆绑到同一个隔离安装环境中——从而使它们共享 node_modules 树和锁文件、相互解析对等依赖项,并可被一并移除——请将它们作为逗号分隔的列表进行传递:

pnpm add -g eslint,prettier

这里的 eslintprettier 形成了一个安装组。 使用 pnpm remove -g 删除任一项都会删除整个组。

这两种形式可以混合使用。 例如:

pnpm add -g eslint,prettier typescript

eslintprettier 打包为一次孤立安装,同时单独安装 typescript

目录布局

{pnpmHomeDir}/global/v11/ 的内容如下所示:

{pnpmHomeDir}/global/v11/
├── {hash-A} → 符号链接 → ./{hash-A-target}/
├── {hash-A-target}/ ← 隔离的安装目录
│ ├── package.json ← 列出一起安装的软件包
│ ├── pnpm-lock.yaml ← 此安装组的锁定文件
│ └── node_modules/
│ ├── <pkg>/ ← 顶级依赖项,符号链接到全局虚拟存储
│ └── .pnpm/
├── {hash-B} → 符号链接 → ./{hash-B-target}/
├── {hash-B-target}/ ← 另一个隔离的安装目录
└── store/ ← 共享的全局虚拟存储
└── ...
  • {hash} 条目是符号链接;pnpm 会扫描它们以枚举活动的安装。
  • 目标是真实的目录,它们就像普通的 pnpm 项目一样——每个项目都有自己的 package.json 和 lockfile。
  • 共享的 store/ 目录保存了 全局虚拟存储。 每个安装组的直接依赖项(即其 node_modules/ 根目录下的条目)都是指向该存储的符号链接,因此实际的包内容是共享的,而不是按组复制的。
  • 二进制 shims 位于 {pnpmHomeDir}/bin/ 中,并指向相应的安装组的 node_modules

当一个软件包被删除或其安装组被替换时,哈希符号链接会被更新,孤立的目标目录最终会被 pnpm store prune 清理掉。

列出全局软件包

pnpm list -g
pnpm list -g --json # 机器可读
pnpm list -g --parseable # 仅路径

由于每个安装组都有自己的锁文件,因此跨多个组列出只能可靠地聚合它们一起安装的顶级软件包——来自不同组的传递依赖关系树无法一致地合并。 因此:

  • pnpm list -g(默认 --depth=0)始终有效,并显示所有全局安装的软件包。
  • pnpm list -g --depth=<n>(其中 n > 0)仅在以下情况下显示完整的依赖关系树:
    • 只有一个全局安装组,或者
    • 位置参数将请求范围缩小到单个安装组,例如 pnpm list -g eslint --depth=1

如果请求 --depth>0 但无法将请求缩小到单个安装组,pnpm 将报错 ERR_PNPM_GLOBAL_LS_DEPTH_NOT_SUPPORTED

管理全局软件包

命令描述
pnpm add -g <pkg>全局安装软件包
pnpm remove -g <pkg>移除一个全局安装的软件包(如果该软件包被打包在某个安装组中,则整个组将被移除)
pnpm update -g [pkg]更新全局软件包(重新安装到新的独立目录中)
pnpm list -g列出所有全局安装的软件包
注意

不再支持 pnpm install -g (无参数)。 使用 pnpm add -g <pkg> 安装特定软件包。

二进制文件位置

全局安装的二进制文件存储在 PNPM_HOMEbin 子目录中(即 $PNPM_HOME/bin/)。 这样可以保持 PNPM_HOME 目录的清洁——当 PNPM_HOME 在 PATH 中时,像 global/store/ 这样的内部目录不会污染 shell 自动补全。

升级到 pnpm v11 后,运行 pnpm setup 来更新你的 shell 配置,以便 $PNPM_HOME/bin 在你的 PATH 中。

你可以通过以下方式检查当前的全局 bin 目录:

pnpm bin -g

Project-aware global bins

Added in: v12.0.0-rc.2 (pnpm v12 only)

Global commands can follow the project you are standing in. When you run a global command from inside a project that asks for a different version of the same tool, pnpm runs the version the project asks for.

The most useful case is Node.js itself. Given a project that pins its runtime:

package.json
{
"devEngines": {
"runtime": {
"name": "node",
"version": "^22.0.0",
"onFail": "download"
}
}
}

a bare node inside that directory runs Node.js 22, even if your global Node.js is a different major:

cd ~/projects/legacy-app
node --version # v22.x.x — the version the project pins
cd ~
node --version # your globally installed version

No shell hooks, .bashrc edits, or use-style commands are involved — the shims pnpm writes into the global bin directory do the dispatch themselves.

How a version is chosen

pnpm walks up from the current working directory to the nearest project that provides the command, then:

  • For the runtimes (node, deno, bun), only the manifest pin counts — devEngines.runtime, then engines.runtime. The pinned version is downloaded into the global virtual store on demand and executed directly. The project's node_modules/.bin is never consulted for a runtime, so a dependency cannot supply the node you run.
  • For the package managers (npm, yarn, bun), added in v12.0.0-rc.6, the project's packageManager or devEngines.packageManager pin counts, and pnpm provisions that version on demand. The pin outranks a copy of that package manager installed globally, because it is the project's own statement of what installs it.
  • For any other package, the project's node_modules/.bin/<name> is used.

Directories inside the pnpm home are skipped, since global installs are not projects.

If the project provides nothing, or the lookup is declined, the globally installed version runs — commands never fail merely because dispatch did not apply.

Trust

A project you cd into is not automatically allowed to run its own binaries in place of your global ones.

This section describes the default auto policy. Under auto:

  • A stable Node.js release is verified against the Node.js release team's signatures before it runs, so it switches without asking. (On musl-based systems the matching builds are unsigned, so they fall under the prompt instead.)

  • Everything else — Deno, Bun, Node.js prereleases, and any ordinary package you enable — asks once, per project and per candidate:

    The project at "/home/user/projects/app" provides its own "tsc", which will be used
    instead of the globally installed one.
    Do you trust this project? [y/N]

Answers are remembered in a machine-local registry, keyed to the project directory and to a fingerprint of the exact binary. If the binary or the providing package changes, pnpm asks again rather than reusing the old approval.

In CI and any non-interactive session, the question cannot be asked, so the global version runs and nothing is recorded.

The other two policies change which candidates reach that question. prompt sends every candidate through it, including a signature-verified stable Node.js release; answers are still remembered, so it asks once per project and candidate rather than on every run. always skips the question entirely and switches straight away — which is also what makes it usable in CI, where a prompt would otherwise fall back to the global version.

There is one more guard that applies regardless of policy: the project's command must come from the same package as the global one. A project that ships a lookalike tsc from some other package does not match, and your global tsc runs.

Which packages participate

Only the runtimes participate by default. Since v12.0.0-rc.6, installing a package manager globally (pnpm add -g yarn) also adds an entry for it, so it follows a project's pin the way a globally installed Node.js already follows devEngines.runtime. An entry you set yourself, including false, is left as you set it.

Enable others with globalShims, keyed by the providing package's name:

~/.config/pnpm/config.yaml
globalShims:
typescript: true

Because pnpm decides at install time which bins to give dispatching shims, a package that is already installed globally needs to be reinstalled after you enable it:

pnpm add -g typescript

Turning a package off needs no reinstall — the setting is re-read on every dispatch. The same setting disables dispatch per package, or entirely with globalShims: false. For a single command, set PNPM_SHIM_BYPASS=1:

PNPM_SHIM_BYPASS=1 node --version

A package that is not installed globally at all can still get a dispatching command, with pnpm shim add — that is what makes yarn work inside a Yarn project on a machine that has only pnpm.

注意

globalShims is deliberately not read from a project's pnpm-workspace.yaml — only from the global configuration file, the pnpm home directory's own pnpm-workspace.yaml, and the environment. See the setting's documentation for details.

全局虚拟存储

全局安装使用全局虚拟存储。 软件包存储在 {storeDir}/links 中,并在全局安装之间共享。 这样可以避免多个全局包依赖于相同库时进行冗余获取。

全局注册本地软件包

要使本地软件包的二进制文件在系统范围内可用,请在软件包目录中使用 pnpm add -g . 命令:

cd ~/projects/my-tool
pnpm add -g .

这样就注册了软件包的 bin 条目,以便可以从任何地方调用它们。 请参阅 pnpm link 了解更多详情。

构建脚本批准

包含构建脚本(例如 postinstall)的全局软件包需要获得批准。 当你安装需要运行构建脚本的全局软件包时,pnpm 会提示你以交互方式批准或拒绝构建。

你还可以使用 --allow-build 标志预先批准构建:

pnpm add -g --allow-build=esbuild esbuild