Skip to main content
Version: 11.x

工作区

pnpm 内置支持多包仓库(也称为多项目仓库或单体仓库)。你可以创建一个工作区,将多个项目整合在单个仓库中。

🌐 pnpm has built-in support for monorepositories (AKA multi-package repositories, multi-project repositories, or monolithic repositories). You can create a workspace to unite multiple projects inside a single repository.

一个工作区的根目录必须包含一个 pnpm-workspace.yaml 文件。

🌐 A workspace must have a pnpm-workspace.yaml file in its root.

tip

如果你正在研究 monorepo 管理,你可能也想了解一下 Bit。 Bit 在底层使用 pnpm,但它自动化了许多目前在由 pnpm/npm/Yarn 管理的传统工作区中手动完成的操作。关于 bit install 有一篇文章讲到了它:[使用 Bit 轻松管理 Monorepo 依赖]。

🌐 If you are looking into monorepo management, you might also want to look into Bit. Bit uses pnpm under the hood but automates a lot of the things that are currently done manually in a traditional workspace managed by pnpm/npm/Yarn. There's an article about bit install that talks about it: Painless Monorepo Dependency Management with Bit.

工作区协议(工作区:)

🌐 Workspace protocol (workspace:)

如果 linkWorkspacePackages 设置为 true,pnpm 将会链接工作区中的包,如果可用的包符合声明的版本范围。例如,如果 bar 的依赖中有 "foo": "^1.0.0" 并且 foo@1.0.0 在工作区中,foo@1.0.0 就会被链接到 bar。然而,如果 bar 的依赖中有 "foo": "2.0.0" 并且 foo@2.0.0 不在工作区中,foo@2.0.0 将会从注册表中安装。这个行为会引入一些不确定性。

🌐 If linkWorkspacePackages is set to true, pnpm will link packages from the workspace if the available packages match the declared ranges. For instance, foo@1.0.0 is linked into bar if bar has "foo": "^1.0.0" in its dependencies and foo@1.0.0 is in the workspace. However, if bar has "foo": "2.0.0" in dependencies and foo@2.0.0 is not in the workspace, foo@2.0.0 will be installed from the registry. This behavior introduces some uncertainty.

幸运的是,pnpm 支持 workspace: 协议。当使用此协议时,pnpm 将拒绝解析为除本地工作区包以外的任何内容。因此,如果你设置了 "foo": "workspace:2.0.0",这次安装将失败,因为工作区中不存在 "foo@2.0.0"

🌐 Luckily, pnpm supports the workspace: protocol. When this protocol is used, pnpm will refuse to resolve to anything other than a local workspace package. So, if you set "foo": "workspace:2.0.0", this time installation will fail because "foo@2.0.0" isn't present in the workspace.

linkWorkspacePackages 选项设置为 false 时,这个协议特别有用。在这种情况下,pnpm 只有在使用 workspace: 协议时,才会从工作区链接包。

🌐 This protocol is especially useful when the linkWorkspacePackages option is set to false. In that case, pnpm will only link packages from the workspace if the workspace: protocol is used.

通过别名引用工作区包

🌐 Referencing workspace packages through aliases

假设你的工作区中有一个名为 foo 的包。通常,你会将其引用为 "foo": "workspace:*"

🌐 Let's say you have a package in the workspace named foo. Usually, you would reference it as "foo": "workspace:*".

如果你想使用不同的别名,以下语法也可行: "bar": "workspace:foo@*"

🌐 If you want to use a different alias, the following syntax will work too: "bar": "workspace:foo@*".

在发布之前,别名会被转换为常规别名依赖。上述示例将变为:"bar": "npm:foo@1.0.0"

🌐 Before publish, aliases are converted to regular aliased dependencies. The above example will become: "bar": "npm:foo@1.0.0".

通过相对路径引用工作区包

🌐 Referencing workspace packages through their relative path

在有 2 个包的工作区中:

🌐 In a workspace with 2 packages:

+ packages
+ foo
+ bar

bar 的依赖中可能声明了 foo"foo": "workspace:../foo"。在发布之前,这些规范会被转换为所有包管理器支持的常规版本规范。

发布工作区包

🌐 Publishing workspace packages

当工作区包被打包成归档文件时(无论是通过 pnpm pack 还是类似 pnpm publish 的发布命令),我们会动态地将任何 workspace: 依赖替换为:

🌐 When a workspace package is packed into an archive (whether it's through pnpm pack or one of the publish commands like pnpm publish), we dynamically replace any workspace: dependency by:

  • 目标工作区中的对应版本(如果你使用 workspace:workspace:*workspace:~workspace:^
  • 关联的 semver 范围(对于任何其他范围类型)

没有版本范围的裸 workspace: 会被视为 workspace:*

🌐 A bare workspace: without a version range is treated as workspace:*.

例如,如果我们的工作区中有 foobarqarzoo,并且它们的版本都是 1.5.0,则以下情况:

🌐 So for example, if we have foo, bar, qar, zoo in the workspace and they all are at version 1.5.0, the following:

{
"dependencies": {
"foo": "workspace:*",
"bar": "workspace:~",
"qar": "workspace:^",
"zoo": "workspace:^1.5.0"
}
}

将转化为:

🌐 Will be transformed into:

{
"dependencies": {
"foo": "1.5.0",
"bar": "~1.5.0",
"qar": "^1.5.0",
"zoo": "^1.5.0"
}
}

此功能允许你依赖本地工作区包,同时仍然能够将生成的包发布到远程注册表,而无需中间发布步骤——你的使用者将能够像使用其他包一样使用你发布的工作区包,并且仍能受益于 semver 提供的保证。

🌐 This feature allows you to depend on your local workspace packages while still being able to publish the resulting packages to the remote registry without needing intermediary publish steps - your consumers will be able to use your published workspaces as any other package, still benefitting from the guarantees semver offers.

发布工作流程

🌐 Release workflow

在工作区内对包进行版本控制是一项复杂的任务,而 pnpm 目前并未提供内置的解决方案。然而,有两个经过充分测试的工具可以处理版本控制并支持 pnpm:

🌐 Versioning packages inside a workspace is a complex task and pnpm currently does not provide a built-in solution for it. However, there are 2 well tested tools that handle versioning and support pnpm:

有关如何使用 Rush 设置存储库,请阅读 此页面

🌐 For how to set up a repository using Rush, read this page.

有关在 pnpm 中使用 Changesets,请阅读 本指南

🌐 For using Changesets with pnpm, read this guide.

故障排除

🌐 Troubleshooting

如果工作区依赖之间存在循环,pnpm 无法保证脚本会按拓扑顺序运行。如果 pnpm 在安装过程中检测到循环依赖,它会发出警告。如果 pnpm 能够找出导致循环的依赖,它也会显示这些依赖。

🌐 pnpm cannot guarantee that scripts will be run in topological order if there are cycles between workspace dependencies. If pnpm detects cyclic dependencies during installation, it will produce a warning. If pnpm is able to find out which dependencies are causing the cycles, it will display them too.

如果你看到消息 There are cyclic workspace dependencies,请检查在 dependenciesoptionalDependenciesdevDependencies 中声明的工作区依赖。

🌐 If you see the message There are cyclic workspace dependencies, please inspect workspace dependencies declared in dependencies, optionalDependencies and devDependencies.

使用示例

🌐 Usage examples

以下是一些使用 pnpm 工作区功能的最流行的开源项目:

🌐 Here are a few of the most popular open source projects that use the workspace feature of pnpm:

ProjectStarsMigration dateMigration commit
Next.js2022-05-29f7b81316aea4fc9962e5e54981a6d559004231aa
n8n2022-11-09736777385c54d5b20174c9c1fda38bb31fbf14b4
Material UI2024-01-03a1263e3e5ef8d840252b4857f85b33caa99f471d
Vite2021-09-263e1cce01d01493d33e50966d0d0fd39a86d229f9
Nuxt2022-10-1774a90c566c936164018c086030c7de65b26a5cb6
Vue2021-10-0961c5fbd3e35152f5f32e95bf04d3ee083414cecb
Astro2022-03-08240d88aefe66c7d73b9c713c5da42ae789c011ce
Prisma2021-09-21c4c83e788aa16d61bae7a6d00adc8a58b3789a06
Novu2021-12-23f2ea61f7d7ac7e12db4c9e70767082841ed98b2b
Slidev2021-04-12d6783323eb1ab1fc612577eb63579c8f7bc99c3a
Turborepo2022-03-02fd171519ec02a69c9afafc1bc5d9d1b481fba721
Quasar Framework2024-03-137f8e550bb7b6ab639ce423d02008e7f5e61cbf55
Element Plus2021-09-23f9e192535ff74d1443f1d9e0c5394fad10428629
NextAuth.js2022-05-034f29d39521451e859dbdb83179756b372e3dd7aa
Ember.js2023-10-18b6b05da662497183434136fb0148e1dec544db04
Qwik2022-11-14021b12f58cca657e0a008119bc711405513e1ee9
VueUse2021-09-25826351ba1d9c514e34426c85f3d69fb9875c7dd9
SvelteKit2021-09-26b164420ab26fa04fd0fbe0ac05431f36a89ef193
Verdaccio2021-09-219dbf73e955fcb70b0a623c5ab89649b95146c744
Vercel2023-01-129c768b98b71cfc72e8638bf5172be88c39e8fa69
Vitest2021-12-13d6ff0ccb819716713f5eab5c046861f4d8e4f988
Cycle.js2021-09-21f2187ab6688368edb904b649bd371a658f6a8637
Milkdown2021-09-264b2e1dd6125bc2198fd1b851c4f00eda70e9b913
Nhost2022-02-0710a1799a1fef2f558f737de3bb6cadda2b50e58f
Logto2021-07-290b002e07850c8e6d09b35d22fab56d3e99d77043
Rollup plugins2021-09-2153fb18c0c2852598200c547a0b1d745d15b5b487
icestark2021-12-164862326a8de53d02f617e7b1986774fd7540fccd
ByteMD2021-02-1836ef25f1ea1cd0b08752df5f8c832302017bb7fb
Stimulus Components2024-10-268e100d5b2c02ad5bf0b965822880a60f543f5ec3
Serenity/JS2025-01-0143dbe6f440d8dd81811da303e542381a17d06b4d
kysely2025-07-295ac19105ddb17af310c67e004c11fa3345454b66

配置

🌐 Configuration

linkWorkspacePackages

  • 默认:
  • 类型:truefalsedeep

如果启用此功能,本地可用的包将会链接到 node_modules,而不是从注册表下载。这在单一代码仓库中非常方便。如果你需要本地包也链接到子依赖,可以使用 deep 设置。

🌐 If this is enabled, locally available packages are linked to node_modules instead of being downloaded from the registry. This is very convenient in a monorepo. If you need local packages to also be linked to subdependencies, you can use the deep setting.

否则,软件包将从注册表下载并安装。然而,工作区软件包仍然可以通过使用 workspace: 范围协议进行链接。

🌐 Else, packages are downloaded and installed from the registry. However, workspace packages can still be linked by using the workspace: range protocol.

只有当软件包的版本满足依赖范围时,才会链接软件包。

🌐 Packages are only linked if their versions satisfy the dependency ranges.

injectWorkspacePackages

  • 默认:
  • 类型:布尔

启用将所有本地工作区依赖硬链接,而不是创建符号链接。或者,也可以使用 dependenciesMeta[].injected 实现,这允许为特定依赖选择性地启用硬链接。

🌐 Enables hard-linking of all local workspace dependencies instead of symlinking them. Alternatively, this can be achieved using dependenciesMeta[].injected, which allows to selectively enable hard-linking for specific dependencies.

note

即使启用了此设置,pnpm 仍会优先使用符号链接来去重注入的依赖——除非由于不匹配的 peer 依赖需要多个依赖图。此行为由 dedupeInjectedDeps 设置控制。

🌐 Even if this setting is enabled, pnpm will prefer to deduplicate injected dependencies using symlinks—unless multiple dependency graphs are required due to mismatched peer dependencies. This behaviour is controlled by the dedupeInjectedDeps setting.

dedupeInjectedDeps

  • 默认:
  • 类型:布尔

当启用此设置时,被注入的依赖将在可能的情况下从工作区创建符号链接。如果依赖目和被注入的依赖引用相同的对等依赖,则无需将被注入的依赖物理复制到依赖目的 node_modules 中;创建符号链接即可。

🌐 When this setting is enabled, dependencies that are injected will be symlinked from the workspace whenever possible. If the dependent project and the injected dependency reference the same peer dependencies, then it is not necessary to physically copy the injected dependency into the dependent's node_modules; a symlink is sufficient.

syncInjectedDepsAfterScripts

新增于:v10.5.0

🌐 Added in: v10.5.0

  • 默认:未定义
  • 类型:String[]

注入的工作区依赖是硬链接的集合,当它们的来源发生变化时不会增加或删除文件。这会在需要构建的包(例如 TypeScript 项目)中引发问题。

🌐 Injected workspace dependencies are collections of hardlinks, which don't add or remove the files when their sources change. This causes problems in packages that need to be built (such as in TypeScript projects).

此设置是一个脚本名称列表。当这些脚本中的任何一个在工作区包中执行时,node_modules 内注入的依赖也将被同步。

🌐 This setting is a list of script names. When any of these scripts are executed in a workspace package, the injected dependencies inside node_modules will also be synchronized.

preferWorkspacePackages

  • 默认:
  • 类型:布尔

如果启用此功能,本地工作区中的包会优先于注册表中的包,即使注册表中有该包的更新版本。

🌐 If this is enabled, local packages from the workspace are preferred over packages from the registry, even if there is a newer version of the package in the registry.

只有在工作区未使用 saveWorkspaceProtocol 时,此设置才有用。

🌐 This setting is only useful if the workspace doesn't use saveWorkspaceProtocol.

sharedWorkspaceLockfile

  • 默认:
  • 类型:布尔

如果启用此功能,pnpm 会在工作区的根目录创建一个单独的 pnpm-lock.yaml 文件。这也意味着工作区包的所有依赖将集中在一个 node_modules 中(并且会通过符号链接连接到它们的包 node_modules 文件夹,以便 Node 进行模块解析)。

🌐 If this is enabled, pnpm creates a single pnpm-lock.yaml file in the root of the workspace. This also means that all dependencies of workspace packages will be in a single node_modules (and get symlinked to their package node_modules folder for Node's module resolution).

此选项的优点:

🌐 Advantages of this option:

  • 每个依赖都是一个单例
  • monorepo 中的安装速度更快
  • 代码审查中的更改较少,因为它们都在一个文件中
note

即使所有依赖都会被硬链接到根目录 node_modules,包仍然只能访问其 package.json 中声明的依赖,因此 pnpm 的严格性得以保持。这是上述符号链接的结果。

🌐 Even though all the dependencies will be hard linked into the root node_modules, packages will have access only to those dependencies that are declared in their package.json, so pnpm's strictness is preserved. This is a result of the aforementioned symbolic linking.

saveWorkspaceProtocol

  • 默认:滚动
  • 类型:truefalserolling

此设置控制如何将从工作区链接的依赖添加到 package.json

🌐 This setting controls how dependencies that are linked from the workspace are added to package.json.

如果 foo@1.0.0 在工作区中,而你在工作区的另一个项目中运行 pnpm add foo,下面是 foo 将如何被添加到依赖字段中的方式。savePrefix 设置也会影响规范的创建方式。

🌐 If foo@1.0.0 is in the workspace and you run pnpm add foo in another project of the workspace, below is how foo will be added to the dependencies field. The savePrefix setting also influences how the spec is created.

saveWorkspaceProtocolsavePrefixspec
false''1.0.0
false'~'~1.0.0
false'^'^1.0.0
true''workspace:1.0.0
true'~'workspace:~1.0.0
true'^'workspace:^1.0.0
rolling''workspace:*
rolling'~'workspace:~
rolling'^'workspace:^

includeWorkspaceRoot

  • 默认:
  • 类型:布尔

在工作区中递归执行命令时,也在根工作区项目上执行它们。

🌐 When executing commands recursively in a workspace, execute them on the root workspace project as well.

ignoreWorkspaceCycles

  • 默认:
  • 类型:布尔

当设置为 true 时,将不会打印工作区循环警告。

🌐 When set to true, no workspace cycle warnings will be printed.

disallowWorkspaceCycles

  • 默认:
  • 类型:布尔

当设置为 true 时,如果工作区存在循环,安装将失败。

🌐 When set to true, installation will fail if the workspace has cycles.

failIfNoMatch

  • 默认:
  • 类型:布尔

当设置为 true 时,如果没有软件包符合提供的筛选条件,CLI 将以非零代码退出。

🌐 When set to true, the CLI will exit with a non-zero code if no packages match the provided filters.

例如,以下命令将以非零代码退出,因为工作区中不存在 bad-pkg-name

🌐 For example, the following command will exit with a non-zero code because bad-pkg-name is not present in the workspace:

pnpm --filter=bad-pkg-name test