【青训营】基于 pnpm 的 monorepo 包管理实践

3,691 阅读4分钟

由于结课项目的工程需要 monorepo 因此记录一下基于 pnpm 的 monorepo 包管理实践

img

Monorepo 的优点

  • 工作流的一致性
  • 项目基建成本的降低
  • 降低依赖管理的复杂度
  • 团队协作也更加容易

限制只能使用pnpm

pnpm 的 monorepo 项目在 node_modules 以及开发中,项目依赖 pnpm workspace 使用 npm 或 yarn 运行时会出现问题。

因此需要在安装依赖之前对包管理器进行检查。

"scripts": {
  "preinstall": "node ./scripts/preinstall.js"
}

实现当运行 npm install 或 yarn,就会发生错误并且不会继续安装。

if (!/pnpm/.test(process.env.npm_execpath || '')) {
  console.warn(
    `\u001b[33mThis repository requires using pnpm as the package manager ` +
      ` for scripts to work properly.\u001b[39m\n`
  )
  process.exit(1)
}

安装初始化依赖

pnpm install

pnpm install 会同时为根目录和所有 workspace 中指定的 package 安装需要的依赖相应依赖

添加全局依赖

pnpm add rimraf -D -W

-D 把依赖作为 devDependencies 安装;-W 把依赖安装到根目录的 node_modules

虽然 packages 下的项目都没有安装 rimraf ,但是倘若在项目中使用到,就会逐级往上寻找

devDependencies 与 dependencies

dependencies运行时依赖,模块消费者会安装这里的依赖。

devDependencies开发时依赖,消费者不会安装,只为生产者服务。

peerDependencies宿主依赖,指定了当前模块包在使用前需要安装的依赖

添加局部依赖

pnpm install jquery -r --filter @monorepo/package-a

  • 对于某些依赖,可能仅存在于某几个 package 中,我们就可以单独为他们安装,当然,可以通过cd packges/xxx 后,执行 pnpm add xxx 但这样重复操作多次未免有些麻烦,pnpm 提供了一个快捷指令 ——filter
  • 比如我们只在 package-a 应用中用到 react,那就可以为它单独安装。首先要拿到它的package name,在本例中是@monorepo/package-a:
  • 在packages/package-a/package.json中,我们可以看到:
 "dependencies": {
   "react": "^17.0.0"
 }
  • packages/package-a的node_modules中看到react被添加了进来。

内部包的相互引用

pnpm add @monorepo/package-a -r --filter @monorepo/package-b

  • 在 monorepo 中,我们往往需要 package 间的引用,比如本例中的@monorepo/package-a就会被@monorepo/package-b引用。
  • 在packages/package-b/package.json中,我们可以看到:
"dependencies": {
  "@monorepo/package-a": "workspace:^1.0.0"
}
  • 这时你会有一个疑问:当这样的工具包被发布到平台后,如何识别其中的workspace呢?
  • 那就需要执行 pnpm publish,会把基于的workspace的依赖变成外部依赖
// before
"dependencies": {
  "@monorepo/package-a": "workspace:^1.0.0"
}
// after
"dependencies": {
  "@monorepo/package-a": "^1.0.0"
}

项目 git Lint 配置

在多人协同的项目中,需要代码提交时,约束 commit 信息

一个 monorepo 仓库可能被不同的开发者提交不同子项目的代码,如果没有规范化的 commit 信息,在故障排查或版本回滚时毫无意外会遭遇灾难。因此,千万不要小看 commit 信息格式化的重要性

为了我们能够一目了然的追踪每次代码变更的信息,我们使用 commitlint 工具作为格式化 commit 信息的不二之选。

顾名思义,commitlint 可以帮助我们检查提交的 commit 信息,它强制约束我们的 commit 信息必须在开头附加指定类型,用于标示本次提交的大致意图,支持的类型关键字有:

  • feat:表示添加一个新特性;
  • chore:表示做了一些与特性和修复无关的「家务事」;
  • fix:表示修复了一个 Bug;
  • refactor:表示本次提交是因为重构了代码;
  • style:表示代码美化或格式化;
  • ...

除了限定 commit 信息类型外,commitlint 还支持(虽然不是必须的)显示指定我们本次提交所对应的子项目名称。假如我们有一个名为 @monorepo/package-a 的子项目,我们针对该项目提交的 commit 信息可以写为:

git commit -m "feat(package-a): add a default page" 

我们可以通过下面的命令安装 commitlint 以及周边依赖:

pnpm add @commitlint/cli @commitlint/config-conventional commitlint husky -W -D

通过安装 husky ,它能够帮助我们在提交 commit 信息时自动运行 commitlint 进行检查

其他的如何配置项目的 Eslint prettier 就不多赘述了,网上文章很多可以进行参考。