一文吃透 pnpm 如何使用 workspace 构建 monorepo,与 npm、yarn 的用法对比(pnpm 9.x 内部安装依赖问题 link-workspace-packages)
环境与版本
platform
=>mac os
node
=>v22.2.0
npm
=>10.8.0
pnpm
=>9.1.3
yarn
=>1.22.22
名词解释
monorepo
workspace
在 pnpm
中使用 workspace
项目结构
my-monorepo
├── docs
├── apps
│ └── web
├── packages
│ ├── ui
│ ├── eslint-config
│ └── shared-utils
├── pnpm-workspace.yaml
├── .npmrc => optional
└── sdk
根目录 package.json
{
"name": "my-monorepo",
"private": true,
"script": {
"dev": "pnpm -r dev"
}
}
pnpm-workspace.yaml
packages:
- "docs"
- "apps/*"
- "packages/*"
具体语法 - glob 通配符
packages:
# 选择 packages 目录下的所有首层子目录的包
- 'packages/*'
# 选择 components 目录下所有层级的包
- 'components/**'
# 排除所有包含 test 的包
- '!**/test/**'
安装依赖
pnpm install
在根目录中安装依赖
pnpm add <package-name> -w
# or
pnpm add <package-name> --workspace-root
给指定 workspace
(工作空间) 安装依赖
pnpm add <package-name> --filter <workspace-name>
# or
pnpm add lodash --filter docs
更新依赖
更新根目录依赖,看执行路径
pnpm update <package-name> [-w]
更新指定 workspace
依赖
pnpm update <package-name> --filter <workspace-name>
# or
pnpm update lodash --filter docs
卸载依赖
pnpm uninstall <package-name> [-w]
# or
pnpm uninstall <package-name> --filter <workspace-name>
# or
pnpm uninstall lodash --filter docs
执行脚本
执行 workspace 中的脚本
pnpm dev --filter docs
执行所有 workspace 中脚本
pnpm -r dev
# or
$ pnpm --recursive dev
或者在根目录的 package.json 中配置
{
"name": "my-monorepo",
"script": {
"docs:dev": "pnpm dev --filter docs"
}
}
直接执行
pnpm docs:dev
安装内部 workspace
依赖
pnpm add <package-name> --filter <workspace-name>
# or
pnpm add web --filter docs
- 请注意你当前的
pnpm
版本,在9.0
后pnpm
修改link-workspace-packages
的默认值为false
。该属性开启后,你在安装依赖时优先在本地链接,而不是从registry
(远程) 中下载。 - 所以在这个版本你若需要使用命令安装一个
新的
workspace
中的依赖需要在.npmrc
中启用link-workspace-packages
- 当然主动在
package.json
中声明的依赖不受影响,如web: "workspace:*"
,pnpm
还是会自动处理,这种不确定性的执行结果可能是导致pnpm
在该版本中禁用了该值
https://github.com/pnpm/pnpm/issues/7954#issuecomment-2062830615
9.x pnpm link-workspace-packages
8.x pnpm link-workspace-packages
在 .npmrc
中
link-workspace-packages = true
或临时启用
pnpm add <package-name> --filter <workspace-name> --link-workspace-packages=true
# or
pnpm add web --filter docs --link-workspace-packages=true
执行结果
{
"name": "docs",
"dependencies": {
"web": "workspace:^"
}
}
什么是 workspace:^
- semver 版本
当你将依赖项添加到 package.json
中时,pnpm
根据 .npmrc
或命令行中的 save-workspace-protocol
字段来决定是否使用 workspace:
协议,并根据 save-prefix
字段来决定版本的前缀(semver)
例如,save-prefix
为 "~"
,save-workspace-protocol
为 true
时
{
"name": "docs",
"dependencies": {
"web": "workspace:~1.0.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 install
# 给指定 workspace 安装依赖
$ pnpm add <package-name> --filter <workspace-name>
# 卸载依赖
$ pnpm uninstall <package-name> --filter <workspace-name>
# 更新依赖
$ pnpm update <package-name> --filter <workspace-name>
# 给根目录安装依赖 - -w 为安装 -workspace-root
$ pnpm add <package-name> -<D>w
# 内部包的互相引用 - 前提 .npmrc 中配置 link-workspace-packages = true
# 若未配置需手动
$ pnpm add <package-name> --filter <workspace-name>
# 执行 workspace 中的脚本, 或者在根目录的 package.json 中配置
$ pnpm dev --filter docs
# 执行所有 workspace 中的脚本
$ pnpm -r dev
# or
$ pnpm --recursive dev
在 npm
中使用 workspace
项目结构
my-monorepo
├── docs
├── apps
│ └── web
├── packages
│ ├── ui
│ ├── eslint-config
│ └── shared-utils
├── package.json
└── sdk
在 package.json
中配置 workspace
{
"name": "my-monorepo",
"workspaces": [
"apps/*",
"packages/*"
]
}
命令初始化子包
npm init -w ./newFolder/hooks -y
# or
npm init -w ./packages/hooks -y
为 workspace
添加、更新、移除依赖
npm install lodash -w docs
npm uninstall lodash -w docs
npm update lodash -w docs
# or
npm install lodash --workspace=docs
依赖内部的 workspace
npm install ui -w docs
{
"name": "docs",
"dependencies": {
"ui": "^1.0.0"
}
}
执行脚本
npm run dev -w docs
# run many
npm run dev -w docs -w ui
# or
npm run dev --workspace=docs
npm run test --workspace=docs --workspace=ui
批量执行 workspace
内的脚本
npm run dev
# or
npm run dev --if-present
在根目录 package.json 中运行 workspace 中的脚本
{
"name": "my-monorepo",
"script": {
"dev": "npm run dev",
"docs:dev": "npm run dev -w docs"
}
}
总结
# 新增子包
npm init -w ./packages/docs -y
# 为子包添加依赖
npm install lodash -w docs
# 运行子包的dev脚本
npm run dev -w docs
# 运行所有 workspace dev脚本
npm run dev
在 yarn
中使用 workspace
命令
yarn workspaces info
- 查询 workspace 之间依赖关系
yarn workspaces info
执行结果:
{
"docs": {
"location": "docs",
"workspaceDependencies": [
"eslint-config",
"typescript-config",
"ui"
],
"mismatchedWorkspaceDependencies": []
},
"web": {
"location": "web",
"workspaceDependencies": [
"eslint-config",
"typescript-config"
],
"mismatchedWorkspaceDependencies": []
}
}
yarn workspace <package-name> <command>
- 给 workspace 安装依赖
# 添加依赖
yarn workspace docs add lodash
# 移除依赖
yarn workspace docs remove lodash
yarn add <package-name> -W
- 根目录安装依赖
yarn add lodash -w
yarn workspace <package-name> add <package-name@version>
- 安装内部依赖
yarn workspace docs add ui@1.0.0
# or
yarn workspace docs add ui@^1.0.0
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 kshao-blog-前端知识记录!
评论