Skip to content

我对 monorepo 的一些思考

前言

最近在自己的新项目中,我创建并使用了 monorepo-template 模板,并不断地做了不少改进。

在此,我想借此机会分享一下这个模板的演变过程,以及我对 monorepo 的一些思考。

它的由来

为了应对越来越多的发包场景,我创建了 npm-lib-template 模板

这是一个 git 单仓单 npm 包模板,使用 rollup 进行打包,然后再发布到 npmgithub

然而,在后续开发中,我发现单仓库的模式在某些场景下难以应对。

例如,当需要引用其他自己的包,进行单元测试并再次发包时,这时候往往要在多个项目之间进行来回的切换更改,管理复杂度会迅速上升。

git submodule 也是另外一条路子,但是我对每次都要同步 hash 感到深恶痛绝,遂放弃 (不过某些极其特殊场景的实现,还是只能利用这个功能)。

因此,我决定要创建一个 monorepo 项目模板 ,以应对这些需求。

技术选型

管理工具

monorepo 的管理上,我选择了 pnpmturborepo 这对组合,原因很简单:它们都非常快。

  • pnpm 又可以节省磁盘空间,又能够链接自特定的内容寻址存储库,是为快也
  • turborepo 有构建缓存,是为快也 too.

语言与打包

我选择使用纯 TypeScript 来编写所有类库项目,并统一使用 tsdown 进行打包,默认输出格式为 cjsesm,并利用 package.json 中的 exports 字段进行分发。

直接调试

在直接调试时,我抛弃了 dist + sourcemap 的调试方式,使用了 tsx,它非常适合调试 TypeScript 编写的 CLI 项目。

测试框架

测试方面,我选择了 vitest

它不仅速度快,还很好地支持 cjsesmTypeScript,同时也适合 monorepo 项目。你可以利用 turbo 来执行单个的 vitest 任务,也可以利用 vitest.workspace 来进行多任务测试。

最初我使用的是 jest + ts-jest,但它对多格式的混合模块的支持不够理想,最终我选择了 vitest

代码规范与质量控制

为了保持代码质量,我使用了 eslintstylelint,并基于自己的配置包 @icebreakers/eslint-config@icebreakers/stylelint-config 来进行代码格式化和规范化。

我还为 .vscode 配置了一些推荐插件和编辑器选项。

此外,通过 husky 添加了 git hook,配合 lint-staged 对提交的代码进行校验。与 commitlint 结合使用,以确保 git 提交信息符合规范。

本地引用与发包替换

利用 publishConfig 会在发包的时候替换 package.json 字段的方式,在本地包相互引用的时候,都使用 Typescript 源文件的方式导出,在 publishConfig 里定义的导出为,真正在不同环境中,指向 dist 中不同格式的产物地址。

通过这种方式,可以大大加速整个 monorepo 的开发测试速度,避免反复通过 watch 来构建 distsourcemap,也避免一构建出产物,对应的 Typescript 文件,就报错的问题。

发包流程

现在使用 pnpm 原生 versioning:用 pnpm change 记录意图,用 pnpm version -r 生成版本和 changelog,再用 pnpm publish -r 发布。版本计划保留在 .changeset/ledger.yaml,预发布则由 pnpm lanes 管理。

Github 相关配置

.github 目录下,我提供了默认的 CI/CD 流程配置,以及用户提交 issue 时的模板。经过少量配置后,就可以实现自动发布 npm 包、创建 git tag 以及生成 GitHub release

此外,模板里还有许多为 GitHub 显示优化的 md 文档。

文档部署

文档是一个 VitePress 静态站点,以名为 repoctl-docs 的 Cloudflare Worker 发布。Workers Static Assets 直接托管 .vitepress/dist,同时处理自定义 404 页面和 _redirects 兼容规则。站点没有动态请求逻辑,因此不额外编写 Worker handler,也不声明 ASSETS binding。

Cloudflare Workers Builds 从仓库根目录执行:

bash
pnpm --filter @icebreakers/website build
pnpm --filter @icebreakers/website run deploy

非生产分支使用 pnpm --filter @icebreakers/website run deploy:preview 上传预览版本。生产发布前运行 pnpm --filter @icebreakers/website run deploy:dry-run,校验静态资产清单和 Wrangler 配置。需要回滚时,先用 wrangler versions list 确认版本,再执行 wrangler rollback <VERSION_ID>

唯一主域名是 https://repoctl.icebreaker.toprepo.icebreaker.topmonorepo.icebreaker.top 只作为兼容入口,由 Cloudflare 301 跳转到主域名,并保留原路径与查询参数。

总结

从单仓到 monorepo 的转变不仅仅是对工具的选择,更是对项目管理模式的优化。通过采用合适的工具链,能够更高效地管理多包项目的同时,利用 CI/CD 确保代码的质量和发布的顺畅。

希望这些思考对你有所帮助,也欢迎大家提出各种建议和意见。