在pnpm的monorepo工作区中,一个子包怎么把其他的子包作为依赖

2,867 阅读4分钟

这篇笔记📒主要记录: 在pnpm的monorepo工作区中,一个子包怎么把其他的子包作为依赖, 顺便延伸到monorepo的概念,pnpm的workspace实现monorepo.

  1. monorepo的概念
  2. React、 Vue、 Angular实现monorepo的实现方式(实现workspace的方式)
  3. 用pnpm的workspace实现monorepo (Vue.js实现monorepo的方式)
  4. 在pnpm的monorepo工作空间中,一个子包怎么把其他的子包作为依赖
monorepo的概念

先对齐下概念,monorepo, mono为前缀的时候表示单个,repo是repository(仓库)的简写,所以表示单个代码仓库的意思。就是把所有的项目代码都放在一个代码仓库里。

image.png

一个包里面有多个子包,每个子包都可以有自己的依赖,构建命令,package.json. 前端React、Vue、Angular都是monorepo这种形式。

React.js packages里面有多个子包

image.png

Vue.js packages里面有多个子包

image.png

Angular.js packages里面有多个子包

image.png

React、Vue、Angular实现monorepo的实现方式
React实现monorepo的实现方式

在2017年的时候,Dan Abramov就用了Yarn的workspaces实现monorepo.

{
  "workspaces": [
    "packages/*"
  ]
}

image.png

看下yarn文档

Workspaces are the name of individual packages that are part of the same project and that Yarn will install and link together to simplify cross-references.

This pattern is often called monorepo when used in conjunction with a repository. Workspaces were initially popularized by projects like Lerna, but Yarn was the first package manager to provide native support for them - support which never stopped improving over years as we build more features around them.

大概翻译下就是: Workspaces(工作区)是同一项目中各个独立包的名称,Yarn 会安装并链接这些包,以简化它们之间的交叉引用。

这种模式在与代码库一起使用时通常称为 monorepo。Workspaces(工作区)最初由像 Lerna 这样的项目推广,但 Yarn 是第一个为它们提供原生支持的包管理器,而且随着我们围绕它们构建更多功能,这种支持在多年来不断改进。

Vue实现monorepo的实现方式

Vue是通过Pnpm的workspaces,看下工作区文档描述

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.

A workspace must have a pnpm-workspace.yaml file in its root. A workspace also may have an .npmrc in its root.

大概翻译下: pnpm 内置了对 monorepositories(即多包仓库、多项目仓库或单一仓库)的支持。你可以创建一个工作区来将多个项目统一在一个仓库中。

一个工作区必须在其根目录中包含一个 pnpm-workspace.yaml 文件。工作区的根目录中也可以包含一个 .npmrc 文件。

看下Vue的配置

packages:
  - 'packages/*'

image.png

Angular实现monorepo的实现方式

应该是Google自己的工具Bazel, 如果有知道的,可以评论区回复下。

image.png

在pnpm的monorepo工作空间中,一个子包怎么把其他的子包作为依赖

回到写这篇笔记的最初,因为我用pnpm的workspace, 子包安装其它子包的时候,安装不成功, 项目结构是这样子的,

compact-vue/
├── packages/
│   ├── reactivity/
│   │   ├── package.json
│   │   └── src/
│   |
│   └── shared/
│       ├── package.json
│       └── src/
├── pnpm-workspace.yaml
├── package.json
└── .npmrc

执行的这个命令安装,

pnpm add @compact-vue/shared --filter reactivity

就是子包reactivity要依赖子包@compact-vue/shared, 提示This error happened while installing a direct dependency of /Users/gongzemin/Documents/playground/compact-vue/packages/reactivity

image.png

看下了pnpm workspace文档的用法, 有这么一句话

If link-workspace-packages 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.

翻译下: 如果 link-workspace-packages 设置为 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 将从注册表中安装。这种行为会引入一些不确定性。

就是说我们要设置link-workspace-packages为true, 才可以把这些包link起来

image.png

增加.npmrc

link-workspace-packages=true

再次执行pnpm add @compact-vue/shared --filter reactivity, 就安装成功了

image.png

安装成功是这样子,通过workspace协议

image.png

Tip 一些命令

pnpm add -D vitest -w // -w表示把vitest安装到workspace,而不是workspace下面的包。
pnpm add packageName // 在子目录路径下可以安装想要的包名
pnpm add packageName --filter subpackage // 在外面主目录下给subpackge安装包