最近在自己的新项目中,使用了 monorepo-template 模板,并不断地做了不少改进。
在此,我想借此机会分享一下这个模板的演变过程,以及我对 monorepo
的一些思考。
为了应对越来越多的发包场景,我创建了 npm-lib-template 模板
这是一个 git
单仓单 npm
包模板,使用 rollup
进行打包,然后再发布到 npm
和 github
然而,在后续开发中,我发现单仓库的模式在某些场景下难以应对。
例如,当需要引用其他自己的包,进行单元测试并再次发包时,这时候往往要在多个项目之间进行来回的切换更改,管理复杂度会迅速上升。
git submodule
也是另外一条路子,但是我对每次都要同步 hash
感到深恶痛绝,遂放弃 (不过某些极其特殊场景的实现,还是只能利用这个功能)。
因此,我决定要创建一个 monorepo 项目模板 ,以应对这些需求。
在 monorepo
的管理上,我选择了 pnpm
和 turborepo
这对组合,原因很简单:它们都非常快。
pnpm
又可以节省磁盘空间,又能够链接自特定的内容寻址存储库,是为快也turborepo
有构建缓存,是为快也too
.我选择使用纯 TypeScript
来编写所有类库项目,并使用 tsup
/ unbuild
进行打包,默认输出格式为 cjs
和 esm
,并利用 package.json
中的 exports
字段进行分发。
在直接调试时,我抛弃了 dist
+ sourcemap
的调试方式,使用了 tsx
,它非常适合调试 TypeScript
编写的 CLI
项目。
测试方面,我选择了 vitest
。
它不仅速度快,还很好地支持 cjs
、esm
和 TypeScript
,同时也适合 monorepo
项目。你可以利用 turbo
来执行单个的 vitest
任务,也可以利用 vitest.workspace
来进行多任务测试。
最初我使用的是 jest
+ ts-jest
,但它对多格式的混合模块的支持不够理想,最终我选择了 vitest
。
为了保持代码质量,我使用了 eslint
和 stylelint
,并基于自己的配置包 @icebreakers/eslint-config
和 @icebreakers/stylelint-config
来进行代码格式化和规范化。
我还为 .vscode
配置了一些推荐插件和编辑器选项。
此外,通过 husky
添加了 git hook
,配合 lint-staged
对提交的代码进行校验。与 commitlint
结合使用,以确保 git
提交信息符合规范。
利用 publishConfig
会在发包的时候替换 package.json
字段的方式,在本地包相互引用的时候,都使用 Typescript
源文件的方式导出,在 publishConfig
里定义的导出为,真正在不同环境中,指向 dist
中不同格式的产物地址。
通过这种方式,可以大大加速整个 monorepo
的开发测试速度,避免反复通过 watch
来构建 dist
和 sourcemap
,也避免一构建出产物,对应的 Typescript
文件,就报错的问题。
我采用了 changesets
,它在 monorepo
环境下发布非常方便。功能非常的多,具体可以查看官方文档。
在 .github
目录下,我提供了默认的 CI/CD
流程配置,以及用户提交 issue
时的模板。经过少量配置后,就可以实现自动发布 npm
包、创建 git tag
以及生成 GitHub release
。
此外,模板里还有许多为 GitHub
显示优化的 md
文档。
文档网站通过 netlify.toml
配置部署在 Netlify
上。最初我使用的是 Vercel
,但由于国内访问速度的原因,最终迁移到了 Netlify
。
monorepo
管理 (pnpm
+ turborepo
)vitest
)cli bin
全部都是 typescript
eslint
+ @icebreakers/eslint-config
+ @icebreakers/stylelint-config
)git
提交规范 (husky
+ commitlint
+ lint-staged
)pnpm
部署 Docker
模板Github Action
自动发布 npm
, github release
包 (changeset
)npx @icebreakers/monorepo@latest
首先,访问本模板的 Github 地址,然后按照一下条件:
有 Github
账号的,可以登录后,点击右上角的 Use this template
按钮
没有 Github
账号的,可以点击 Code
按钮,把这个仓库的源码,或 clone
或下载到本地
然后在根目录 (pnpm-workspace.yaml
所在的位置) 执行 pnpm i
去安装依赖
没有
pnpm
的,可以使用npm i -g pnpm
来进行安装。什么! 你不会连
nodejs
还没安装吧?
执行 pnpm script:clean
命令,可以删去大部分的初始 repo
,只保留一个 @icebreakers/bar
项目作为发包打包模板。
执行完成之后再去执行 pnpm i
来更新 pnpm-lock.yaml
, 并提交来锁定版本
默认把 repo
放在 packages
和 apps
这 2
个目录里面
@icebreakers/bar
- tsup
打包的库模板@icebreakers/foo
- unbuild
打包的库模板(不推荐,unbuild
很久没有更新了)@icebreakers/monorepo
- 本仓库的更新配置服务,可直接使用 npx @icebreakers/monorepo
执行远端 cli
命令其中 tsup
是使用 esbuild
打包库的,unbuild
是使用老版本的 rollup
进行打包的
本来笔者是使用 rollup
来进行打包的 (weapp-tailwindcss
就是 rollup
打出来的) ,但是不够傻瓜无脑,所以用了 tsup
@icebreakers/cli
- 使用 typescript
编写的 cli
模板@icebreakers/website
- 文档网站模板,也是 monorepo.icebreaker.top 的源代码在根目录中执行 pnpm up -rLi
来进行包的交互式更新,下面是解释:
-r
: recursive 递归选中所有 repo
-L
: latest 更新到最新-i
: interactive 交互式本项目使用 changesets 进行包的发布和 changelog
的生成
在使用的时候,首先你需要做一些配置:
首先你需要安装 Github App
: changeset-bot
然后,来到你复制这个模板仓库(repo
), 上方里的 Settings
Tab 页面,进行 2 个操作:
选择 Code and automation
> Actions
> General
然后在右侧 Workflow permissions
下方选择: Read and write permissions
然后选中 Allow GitHub Actions to create and approve pull requests
然后保存即可。
这样 changeset
就有权限对你进行 PR
和代码版本更新了!
选择 Security
> Secrets and variables
> Actions
然后在右侧的 Repository secrets
设置你的 NPM_TOKEN
这个可以在你的 npmjs.com
账号中生成获取
(假如你需要单元测试代码覆盖率,你需要设置 CODECOV_TOKEN
)
pnpm script:clean
删去大部分的初始repo
,只保留一个 @icebreakers/bar
项目作为发包打包模板pnpm script:init
初始化一些 package.json
里的字段pnpm script:sync
使用 cnpm sync
功能,把本地所有的包,同步到 npmmirror
上,需要安装 cnpm
在根目录下执行: npx @icebreakers/monorepo@latest
这个命令会把所有的文件从最新版本,对你本地进行覆盖,你可以从 git
暂存区把你不想要的文件剔除
npx @icebreakers/monorepo@latest --raw
这个命令会从全部文件中去除 Github
相关的文件
npx @icebreakers/monorepo@latest -i
这个命令会进行命令行选择模式,你可以在这里对想要复制的文件进行筛选
当然你可以同时使用这 2
个命令
npx @icebreakers/monorepo@latest -i --raw
从单仓到 monorepo
的转变不仅仅是对工具的选择,更是对项目管理模式的优化。
通过采用合适的工具链,能够更高效地管理多包项目的同时,利用 CI/CD
确保代码的质量和发布的顺畅。
希望我的这些思考对你有所帮助,也欢迎大家提出各种建议和意见。
相关文档见: monorepo.icebreaker.top 反正我以后创建项目的模板就用它了。
假如你都看到这了,说明我们是有缘之人,给我 monorepo-template 点个 Star
呗嘿嘿。