Monorepo
- 前言
- 开始
-
- 一、使用turborepo初始化项目
- 二、调整目录结构及文件
-
- [1. 调整`package.json`文件](#1. 调整
package.json文件) - [2. 调整app目录](#2. 调整app目录)
- [3. 调整`eslint`包](#3. 调整
eslint包) - [4. 调整`ui`包](#4. 调整
ui包) - [5. 调整eslint配置](#5. 调整eslint配置)
- [6. 调整.npmrc](#6. 调整.npmrc)
- [7. 使用commitizen规范代码提交](#7. 使用commitizen规范代码提交)
- [8. 使用commitlint+husky进行 commit提交信息校验](#8. 使用commitlint+husky进行 commit提交信息校验)
- [9. 使用husky进行commit前的规范校验](#9. 使用husky进行commit前的规范校验)
- [1. 调整`package.json`文件](#1. 调整
- [三、dev run](#三、dev run)
- 四、打包
- 五、发版
- 总结
前言
当你想要搭建一个Monorepo时,相信你已经调研过很多技术选型,之前我也使用过yarn+workspace+lerna+dumi开发过一套组件库,后续有时间把这套工具也抽一个模版。
因为pnpm本身支持workspace并且lerna已经停止维护,因此决定使用新的工具从新搭建一套Monorepo模版,那么这套工具就是:
pnpm---包管理器turborepo---外壳changesets---版本号管理、changelog管理dumi---文档预览其他的规范代码的工具如:---commitizen、eslint、lint-staged、commitlint、husky等
你可能对Turborepo不太了解,他的官方解释是高性能的JavaScript和TypeScript代码库构建系统,他在这只是作为一个壳子用来提升开发及打包效率,不用了解太多。
开始
一、使用turborepo初始化项目
pnpm dlx create-turbo@latest- 选择目录(项目名)、选择包管理器

- 项目初始化完成,具体的目录结构什么意思可以去Turborepo看一下,不看也没关系,因为后续我们要调整它。

我现在这里的turbo的version是
1.10.12,如果你很晚看见这篇文章,可能版本有了很大不同。
- 链接gitHub仓库,当然这一步不是必须的,链接它的目的只是为了测试规范代码的工具是否生效。
git remote add origin https://github.com/Atw-Lee/monorepo-template.git
不要划走 :Monorepo可以用来管理多应用工程(app目录下的各种工程) ,所有工程依赖相同的UI ,而Turborepo初始化的项目也是这么干的。但是 ,我需要的是搭建一个我的UI组件库 ,这是与它不同的地方,app下我只需要一个文档预览工程(dumi),因此,我需要调整它的目录结构
二、调整目录结构及文件
1. 调整package.json文件
- 安装eslint@latest(eslint提到全局,并更新到最新版本)
- 安装@changesets/cli(版本号、changelog管理)
- 安装commitizen cz-conventional-changelog(规范commit信息)
- 安装@commitlint/cli @commitlint/config-conventiona husky(检验commit信息)
- 安装lint-staged stylelint(校验规范格式)
- 安装father(打包)
- 增加
"preinstall": "npx only-allow pnpm"只允许使用pnpm包管理器
pnpm i -Dw eslint@latest @changesets/cli commitizen cz-conventional-changelog @commitlint/cli @commitlint/config-conventional husky lint-staged stylelint father
2. 调整app目录
- 删除web及docs目录的所有内容
- 新建docs目录并初始化
dumi项目

- 进入docs目录执行
git init - 删除
.fatherrc.ts文件(不需要在这打包组件) .gitignore文件增加docs-dist(该目录为dumi的build产物)- 修改
package.json文件:- 删除
docs:build - 修改
build:dumi build(打包直接是该文档工程打包,不打包组件) - 删除
module、types、files配置(用不着) - 依赖删除与外边重复的(commitlint、eslint、father、husky、lint-staged、stylelint等)
- 删除
3. 调整eslint包
我不准备使用它默认的eslint,因为我用不到nextjs相关的内容,我选择使用Tencent的eslint-config-alloy
- 进入packages/eslint-config-custom目录,手动删除不需要的依赖,
pnpm i eslint-config-alloy安装alloy

- 更新
eslint-plugin-react(后续补充,不是最新的出错了)
- 调整
tsconfig包
- 删除
package.json的private属性(为了后续可以正常发版) - 删除
nextjs.js文件(不需要next的ts配置)

4. 调整ui包
- 删除
turbo目录(用不着) package.json文件删除generate脚本(用不着)package.json文件删除eslint依赖(外边已经有了)package.json文件新增脚本"build": "father build"(打包组件,app里的docs不需要了,因为UI组件包的开发在这里)package.json文件修改main属性"main": "./src/index",package.json文件修改types属性"main": "./src/index",- 新建src目录,增加button组件

- 修改
tsconfig.json文件
javascript
{
"extends": "tsconfig/react-library.json",
"include": ["src"],
"exclude": ["lib", "dist", "es", "node_modules"]
}
- 新增
.fatherrc.ts文件
javascript
import { defineConfig } from "father";
export default defineConfig({
// more father config: https://github.com/umijs/father/blob/master/docs/config.md
esm: { output: "es" },
cjs: { output: "lib" },
});
.gitignore文件增加lib及es(该目录为ui通过father打包产物,具体名称还是根据上一步的配置来).gitignore文件删除next相关的(用不到)
5. 调整eslint配置
javascript
module.exports = {
root: true,
extends: ["custom"],
};
6. 调整.npmrc
- 增加
registry=你公司的私有源,开发的UI组件库发布到这(在这直接定义注册地址就比较方便了)
7. 使用commitizen规范代码提交
package.json文件增加"commit": "cz"脚本package.json文件增加config属性
javascript
"config": {
"commitizen": {
"path": "node_modules/cz-conventional-changelog"
}
}
- 执行
pnpm commit测试

8. 使用commitlint+husky进行 commit提交信息校验
- 根目录新增
commitlint.config.js文件
javascript
module.exports = { extends: ["@commitlint/config-conventional"] };
package.json文件增加"postinstall": "husky install"- 执行
pnpm i生成.husky文件,初始化husky。完成后可删除postinstall属性 - 执行
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'生成commit-msg的hook package.json文件增加commitlint及lint-staged属性
javascript
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
},
"lint-staged": {
"*.{md,json}": [
"prettier --write --no-error-on-unmatched-pattern"
],
"*.{css,less}": [
"stylelint --fix",
"prettier --write"
],
"*.{js,jsx}": [
"eslint --fix",
"prettier --write"
],
"*.{ts,tsx}": [
"eslint --fix",
"prettier --parser=typescript --write"
]
},
- 执行错误的commit信息测试

9. 使用husky进行commit前的规范校验
- 执行
npx husky add .husky/pre-commit "npx --no-install lint-staged" - 上一步已经添加完成了
lint-staged属性 - 执行commit进行校验

三、dev run
- 执行
pnpm dev:页面正常打开dumi运行的应用 - docs的
package.json增加ui的依赖"ui": "workspace:*" - 页面中使用
ui包中的button组件

到这我们的运行时的组件与文档预览已经ok了,接下来就是ui组件的打包
四、打包
ts与eslint
他俩不需要打包,都是些配置项
UI
上文我们已经设置好了father打包的配置文件,直接执行pnpm build

生成了对应的lib和es文件夹,docs也跟着打包了,这里是turborepo的操作逻辑,我们不用关心它
- ui的
package.json中新增files属性 - ui的
package.json的main、types属性也要做一个修改,之前是./src
javascript
"main": "./lib/index",
"types": "./lib/index",
"files": [
"es",
"lib",
"dist"
],
五、发版
接下来就需要把我们的包发布到我们的私有源上了,当然开源也没有问题
我们现在有三个包,分别是
eslint、typescript、ui。我们的包肯定不能叫这个名字,那我们给他们改下名字,加上组织的前缀 ,我这里以
@you-org为例
修改所有的包名

该变更中的文件均有需要修改
使用Changesets进行版本管理并发版
- 执行
pnpm changeset init生成.changeset文件 - 执行
pnpm changeset选择要发布的包

- 选择版本(一版fix是patch,feat是minor)

- 执行
changeset version增加版本号

- commit提交
- 执行
pnpm -r publish --tag alpha0.1.0发布版本

OK到这里就结束了~
总结
收获:从0开始搭建了一个多包(Monorepo)的UI组件模版,了解了一个简单开源UI工程应该具有的工具及搭建步骤。
不足之处在于:
- 组件库的打包使用
rollup可能会更好,但是因为额外的学习成本,还是使用了dumi上自带的father - 除了
ui包,应该在添加一个utils包,这样才更加具有Monorepo的特性 tsconfig和eslint包不确定是否有必要单独提成一个包,如果项目很复杂的话,eslint和tsconfig需要灵活配置的话,可能会更有用吧- 缺少了一些脚本,通过脚本来减少复杂的原生命令
- 需要额外的脚手架来协调模版工作,以便不需要更改额外的
package.json文件,后续也会做这方面的工作
参考文章