工作区
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.
如果你正在研究 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:*.
例如,如果我们的工作区中有 foo、bar、qar、zoo,并且它们的版本都是 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,请检查在 dependencies、optionalDependencies 和 devDependencies 中声明的工作区依赖。
🌐 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:
配置
🌐 Configuration
linkWorkspacePackages
- 默认:假
- 类型:true,false,deep
如果启用此功能,本地可用的包将会链接到 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.
即使启用了此设置,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 中的安装速度更快
- 代码审查中的更改较少,因为它们都在一个文件中
即使所有依赖都会被硬链接到根目录 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
- 默认:滚动
- 类型:true,false,rolling
此设置控制如何将从工作区链接的依赖添加到 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.
| saveWorkspaceProtocol | savePrefix | spec |
|---|---|---|
| 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