工作空间(Workspace)

pnpm 内置了对单一存储库(也称为多包存储库、多项目存储库或单体存储库)的支持, 你可以创建一个 workspace 以将多个项目合并到一个仓库中。

一个 workspace 的根目录下必须有 pnpm-workspace.yaml 文件, 也可能会有 .npmrc 文件。

Workspace 协议 (workspace:)

添加于:v3.7.0.

默认情况下,如果可用的 packages 与已声明的可用范围相匹配,pnpm 将从工作区链接这些 packages。 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 not in the workspace. 但是,如果 bar 的依赖项中有 "foo": "2.0.0",而 foo@2.0.0 在工作空间中并不存在,则将从 npm registry 安装 foo@2.0.0 。 这种行为带来了一些不确定性。

幸运的是,pnpm 支持 workspace 协议 workspace: 。 当使用此协议时,pnpm 将拒绝解析除本地 workspace 包含的 package 之外的任何内容。 因此,如果您设置为 "foo": "workspace:2.0.0" 时,安装将会失败,因为 "foo@2.0.0" 不存在于此 workspace 中。

link-workspace-packages 选项 设置为 false 时,这个协议尤其有用。 在这种情况下,仅当使用 workspace: 协议声明依赖,pnpm 才会从此 workspace 链接所需的包。

通过别名引用 workspace 包

添加于:v5.12.0

假设你在 workspace 中有一个名为 foo 的包, 通常你会像这样引用:"foo": "workspace:*"

如果要使用其他别名,那么以下语法也将起作用: "bar": "workspace:foo@*"

在发布之前,别名被转换为常规名称。 上面的示例将变为:"bar": "npm:foo@1.0.0"

通过相对路径引用 workspace 包

添加于:v5.12.0

假如 workspace 中有两个包:

  1. + packages
  2. + foo
  3. + bar

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

发布 workspace 包

当 workspace 包打包到归档(无论它是通过 pnpm pack ,还是 pnpm publish 之类的发布命令)时,我们将动态替换这些 workspace: 依赖:

  • 目标 workspace 中的对应版本(如果使用 workspace:*, workspace:~, or workspace:^
  • 相关的 semver 范围(对于任何其他范围类型)

看一个例子,假设我们的 workspace 中有 foobarqarzoo 并且它们的版本都是 1.5.0,如下:

  1. {
  2. "dependencies": {
  3. "foo": "workspace:*",
  4. "bar": "workspace:~",
  5. "qar": "workspace:^",
  6. "zoo": "workspace:^1.5.0"
  7. }
  8. }

将会被转化为:

  1. {
  2. "dependencies": {
  3. "foo": "1.5.0",
  4. "bar": "~1.5.0",
  5. "qar": "^1.5.0",
  6. "zoo": "^1.5.0"
  7. }
  8. }

这个功能允许你发布转化之后的包到远端,并且可以正常使用本地 workspace 中的 packages,而不需要其它中间步骤。包的使用者也可以像常规的包那样正常使用,且仍然可以受益于语义化版本。

发布工作流

workspace 中的包版本管理是一个复杂的任务,pnpm 目前也并未提供内置的解决方案。 不过,有两个不错且支持 pnpm 的版本控制工具可以使用:

有关如何用 Rush 设置仓库,可以阅读 这篇文章

有关在 pnpm 中使用 Changesets,可以阅读这篇指南

配置项

添加于: v2.14.0

  • 默认值: true
  • 类型:true, false, deep

启用该选项,本地可用的 packages 将被链接到 <0>node_modules</0> 中而不是从配置源下载。 这在 monorepo 项目中使用起来将十分方便。 如果您需要将本地 packages 也链接到子依赖项,则可以设置为 deep (自 v5 版本起)。

否则, packages 将全部从注册表下载并安装。 不过,工作空间的 packages 仍然可以通过 workspace: 范围协议被链接到。

prefer-workspace-packages

添加于: v5.13.0

  • 默认值: false
  • 类型:Boolean

如果启用了此选项,工作空间中的本地 package 将优先于注册表,即使注册表中有了该 package 的新版本。

该设置只在工作空间未开启 save-workspace-protocol 时有效。

shared-workspace-lockfile

添加于:v2.17.0 为 shared-workspace-shrinkwrap减

  • 默认值: true
  • 类型:Boolean

如果启用此选项,pnpm 会在工作空间的根目录中创建一个唯一的 pnpm-lock.yaml 文件。 这也意味着工作空间的packages的所有依赖项都将位于单个 node_modules 中。(同时软链接到它们packagesnode_modules 文件夹中用于 Node 的模块解析)。

此选项的好处:

  • 每个依赖都是一个单例
  • 在 monorepo 中的安装更快
  • 代码更改都在一个文件中、代码审查(Cr )减少

::: 注意

尽管所有依赖项都将硬链接到根 node_modules 中,但packages 只能访问 package.json 中声明的 ,因此 pnpm 的严格性得以保留。 这是上述软链接的效果。

:::

save-workspace-protocol

  • 默认值: true
  • 类型:Boolean

如果启用此选项,新的依赖将会被工作空间协议 workspace:添加,当且仅当依赖存在于工作空间时。

如果您项目中的工具不支持工作空间协议,您可能希望将此设置更改为 false(最好提交一个 PR 让其在后续可以支持)。

故障排查

如果工作空间依赖项之间存在循环,则 pnpm 无法保证脚本将按拓扑顺序运行。 如果 pnpm 在安装过程中检测到循环依赖,则会提供一个 warning 警告。 如果 pnpm 能够找出导致循环的依赖项,也会将其展示出来。

如果您看到此消息 There are cyclic workspace dependencies ,请检查在dependencies, optionalDependenciesdevDependencies 中声明的工作空间依赖。

使用示例

以下是几个使用了 pnpm 工作空间功能的开源项目:

  • icestark (截至 12/16/2021, commit 4862326a8de53d02f617e7b1986774fd7540fccd)
  • Vue 3.0 (截至 10/9/2021, commit 61c5fbd3e35152f5f32e95bf04d3ee083414cecb)
  • Vite (截至9/26/2021, commit 3e1cce01d01493d33e50966d0d0fd39a86d229f9)
  • Cycle.js (截至 9/21/2021, commit f2187ab6688368edb904b649bd371a658f6a8637)
  • Prisma (截至 9/21/2021, commit c4c83e788aa16d61bae7a6d00adc8a58b3789a06)
  • Verdaccio (截至 9/21/2021, commit 9dbf73e955fcb70b0a623c5ab89649b95146c744)
  • Rollup plugins (截至 9/21/2021, commit 53fb18c0c2852598200c547a0b1d745d15b5b487)
  • Milkdown (截至 9/26/2021, commit 4b2e1dd6125bc2198fd1b851c4f00eda70e9b913)
  • ByteMD (截至 2/18/2021, commit 36ef25f1ea1cd0b08752df5f8c832302017bb7fb)
  • VueUse (截至 9/25/2021, commit 826351ba1d9c514e34426c85f3d69fb9875c7dd9)
  • Slidev (截至 4/12/2021, commit d6783323eb1ab1fc612577eb63579c8f7bc99c3a)
  • SvelteKit (截至 9/26/2021, commit b164420ab26fa04fd0fbe0ac05431f36a89ef193)
  • Telecraft (截至 9/26/2021, commit 73a9c48c9d4f160d758b8881f404cc52c20a7454)
  • GiraphQL (截至 8/4/2021, commit 3dd3ff148da382d6f406f20626a9a5c25707c0c8)
  • Tailchat (截至2021年12月27日,commit 298af71aa0619e0a8fa8717777afe2fb32739db4)
  • Vitest (截至 12/13/2021, commit d6ff0ccb819716713f5eab5c046861f4d8e4f988)
  • Element Plus (截至 2021 年 9 月 23 日, commit f9e192535ff74d1443f1d9e0c5394fad10428629)