工作空间(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 中有两个包:
+ packages
+ foo
+ bar
bar
中可能有 foo
的依赖: "foo": "workspace:../foo"
, 在发布之前,这些将转换为所有包管理器支持的常规版本规范。
发布 workspace 包
当 workspace 包打包到归档(无论它是通过 pnpm pack
,还是 pnpm publish
之类的发布命令)时,我们将动态替换这些 workspace:
依赖:
- 目标 workspace 中的对应版本(如果使用
workspace:*
,workspace:~
, orworkspace:^
) - 相关的 semver 范围(对于任何其他范围类型)
看一个例子,假设我们的 workspace 中有 foo
、 bar
、 qar
、 zoo
并且它们的版本都是 1.5.0
,如下:
{
"dependencies": {
"foo": "workspace:*",
"bar": "workspace:~",
"qar": "workspace:^",
"zoo": "workspace:^1.5.0"
}
}
将会被转化为:
{
"dependencies": {
"foo": "1.5.0",
"bar": "~1.5.0",
"qar": "^1.5.0",
"zoo": "^1.5.0"
}
}
这个功能允许你发布转化之后的包到远端,并且可以正常使用本地 workspace 中的 packages,而不需要其它中间步骤。包的使用者也可以像常规的包那样正常使用,且仍然可以受益于语义化版本。
发布工作流
workspace 中的包版本管理是一个复杂的任务,pnpm 目前也并未提供内置的解决方案。 不过,有两个不错且支持 pnpm 的版本控制工具可以使用:
有关如何用 Rush 设置仓库,可以阅读 这篇文章。
有关在 pnpm 中使用 Changesets,可以阅读这篇指南。
配置项
link-workspace-packages
添加于: 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
中。(同时软链接到它们packages
的 node_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
, optionalDependencies
和 devDependencies
中声明的工作空间依赖。
使用示例
以下是几个使用了 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
)