工作空间
pnpm 内置支持 monorepositories(又称多包仓库、多项目仓库或单体仓库)。您可以创建一个工作空间来将多个项目统一到单个代码仓库中。
工作空间的根目录必须有一个 pnpm-workspace.yaml
文件。
如果你正在寻找 monorepo 管理方案,不妨也看看 Bit。
Bit 在底层使用 pnpm,但自动化了许多在传统的由 pnpm/npm/Yarn 管理的工作空间中需要手动完成的事情。这篇关于 bit install
的文章详细介绍了这一点:使用 Bit 实现无痛的 Monorepo 依赖管理。
工作空间协议 (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
将从注册表中安装。这种行为带来了一些不确定性。
幸运的是,pnpm 支持 workspace:
协议。当使用此协议时,pnpm 将只解析到本地工作空间包,不会使用其他来源。所以,如果你设置 "foo": "workspace:2.0.0"
,由于工作空间中不存在 "foo@2.0.0"
,安装将会失败。
当 linkWorkspacePackages 选项设置为 false
时,这个协议特别有用。在这种情况下,pnpm 只 会在使用 workspace:
协议时链接工作空间中的包。
通过别名引用工作空间包
假设你在工作空间中有一个名为 foo
的包。通常情况下,你会这样引用它:"foo": "workspace:*"
。
如果你想使用一个不同的别名,下面的语法也是可行的:"bar": "workspace:foo@*"
。
发布之前,别名会被转换为常规的别名依赖。上面的例子会变成:"bar": "npm:foo@1.0.0"
。
通过相对路径引用工作空间包
在一个包含 2 个包的工作空间中:
+ packages
+ foo
+ bar
bar
可以在其依赖中通过 "foo": "workspace:../foo"
来声明 foo
。在发布之前,这些规范会被转换为所有包管理器都支持的常规版本规范。
发布工作空间包
当工作空间包被打包成归档文件时(无论是通过 pnpm pack
还是诸如 pnpm publish
之类的发布命令),我们会动态地将任何 workspace:
依赖替换为:
- 目标工作空间中的对应版本(如果你使用
workspace:*
、workspace:~
或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"
}
}
此功能允许你依赖本地工作空间包,同时仍然能够将结果包发布到远程注册表,而无需中间发布步骤 —— 你的用户将能够像使用任何其他包一样使用你发布的工作空间,同时仍然享受语义化版本带来的保证。
发布工作流程
在工作空间内为包进行版本管理是一项复杂的任务,目前 pnpm 并没有提供内置的解决方案。不过,有 2 个经过充分测试且支持 pnpm 的工具:
关于如何使用 Rush 设置代码仓库,请阅读此页面。
关于如何在 pnpm 中使用 Changesets,请阅读此指南。
故障排除
如果工作空间依赖之间存在循环,pnpm 无法保证脚本会按拓扑顺序运行。如果 pnpm 在安装过程中检测到循环依赖,它会产生一个警告。如果 pnpm 能够找出哪些依赖导致了循环,它也会显示出来。
如果你看到 There are cyclic workspace dependencies
(存在循环工作空间依赖)这条消息,请检查在 dependencies
、optionalDependencies
和 devDependencies
中声明的工作空间依赖。
使用示例
以下是一些使用 pnpm 工作空间功能的最受欢迎的开源项目: