在 pnpm workspace monorepo 这篇文章中讲述了一个单仓多包项目应该如何创建管理。但该项目可能会有多人共同开发,而每个人的开发习惯都不尽相同,所以在规范协商后还需要借助工具对项目进行约束,以确保该项目的开发不会有过多的个人风格。
代码格式化与 Git 仓库工具
接下来,我们在项目中将使用以下工具约束管理项目代码:
- eslint
- eslint-config-prettier
- eslint-plugin-prettier
- prettier
- husky
- lint-staged
- commitzen
- @commitlint/cli
- @commitlint/config-conventional
- cz-conventional-changelog
eslint
和 prettier
大家都知道是用来规范代码和格式化代码的,在 vscode
中很常用,但如果团队中有人不用 vscode
,而是用记事本这种软件,你又该如何应对呢?这时候就需要用到这两个工具了。
eslint-config-prettier
和 eslint-plugin-prettier
两个是解决 eslint
与 prettier
配置冲突的,想要详细解释请查看官网。
husky
是用来管理 git hooks
的,比如说在代码上传前对代码进行格式化,又或者对 commit-msg
进行校验等。
lint-staged
是用来过滤文件的。通常我们在项目中只改了一个文件,但运行代码格式化 prettier
时会对整个项目进行格式化,这会造成时间/资源的浪费,因此我们需要用这个工具对文件过滤,只格式化改动的的文件。
commitzen
、@commitlint/cli
、@commitlint/config-conventional
、cz-conventional-changelog
几个工具都是规范 git commit
内容的。
实操
接着上一篇文章新建的项目,对其进行约束管理。
安装代码格式化工具
仅安装 eslint
和 prettier
并不能让格式化符合我们的预期,因此我们还要安装 eslint
的插件来更好的对代码开发。
pnpm add -w -D eslint prettier eslint-config-prettier eslint-plugin-prettier @typescript-eslint/parser @typescript-eslint/eslint-plugin
新建这几个文件并填入相关配置
- .eslintrc
- .prettierrc
- .eslintignore
- .prettierignore
.eslintignore
和 .pretterignore
都是用来过滤文件不执行检验和格式化的,因此填入的内容是一样的。
node_modules/
dist/
pnpm-lock.yaml
README.md
.eslintrc
{
"root": true,
"parser": "@typescript-eslint/parser",
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/eslint-recommended",
"prettier"
],
"plugins": ["@typescript-eslint", "prettier"],
"rules": {
"prettier/prettier": "error",
"arrow-body-style": "off",
"prefer-arrow-callback": "off"
}
}
.prettierrc
.prettierrc
的配置需要团队之间去协商,这里仅代表我个人,具体的参数配置可以查看 prettier
文档1
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"printWidth": 120
}
此时代码格式化工具已配置完毕,我们在根目录的 package.json
添加指令尝试一下
{
...,
"scripts": {
...,
"lint": "eslint ./ --ext .ts,.js,.jsx,.tsx,.json --max-warnings=0",
"format": "prettier --config .prettierrc . --write"
}
}
指令已经添加,解释一下这两个指令:
lint
指令,使用eslint
对当前目录及所有子目录中包含ts,js,jsx,tsx,json
后缀的文件都进行代码检测,具体参数请查看eslint
官方文档2format
指令,使用prettier
的配置文件.prettierrc
对当前目录及所有子目录中的文件都进行文件格式化,具体参数请查看prettier
官方文档1
目前我们的代码是这样的
这里我们单独运行指令看看
pnpm run lint
通过截图可以看出,代码极其不规范,Eslint
报了一大堆错误,该指令目前符合我们的预期,接下来运行 format
指令
pnpm run format
很好,文件都进行了格式化,该指令也符合我们的预期,此时再次运行 pnpm run lint
指令进行代码检查。
发现位于 packages/space-play/index.ts
文件有个变量没有用到,先删除掉再运行 pnpm run lint
指令。
此时的代码检查已经完成。
但是有个问题,修改了文件后每次都要单独运行 pnpm run lint
和 pnpm run format
指令,这对我们的开发很不方便,因此我们要引入一个工具 onchange
帮我们监听文件的存储变动,而后自动格式化文件。
pnpm run -w -D onchange
安装好后在根目录的 package.json
文件新增一条指令
{
...,
"scripts": {
"format-auto": "onchange **/* -- prettier --config .prettierrc --write {{changed}}"
}
}
运行指令看看会有什么变化
pnpm run format-auto
可以看到 onchange
工具已经在监听,并且手动修改了 apps/play1/index.ts
文件后,执行了 prettier
格式化。
至此,代码检查和格式化已经完成。
Git 仓库相关
项目中采用 husky
工具对 git hooks
进行管理,这里用到的钩子为 pre-commit
和 commit-msg
。
pnpm add -w -D husky
添加 husky
钩子有两种方式,一种是通过命令添加,一种是通过 .huskyrc
文件添加。
文件添加的方式是在根目录新建 .huskyrc
文件,并在里面写入钩子,钩子的值时指令运行,这里不做叙述。
{
"hooks": {
"pre-commit: "",
"commit-msg": ""
}
}
Git Hook: Pre-Commit
我们采用的方式是通过命令添加,这里先添加 pre-commit
钩子用于在 git commit
前执行 eslint
代码检查,如果不通过则取消 git commit
:
npx husky add .husky/pre-commit "pnpm run lint"
指令已经添加,试一试添加一条 git commit
:
好的,通过截图可以看到,在添加 git commit
时已经运行了 pnpm run lint
指令,下面我们将通过新增一个工具 lint-staged
在进行 git-commit
时对文件进行过滤,仅对本次修改的文件进行代码检查。
pnpm add -w -D lint-staged
新建一个文件 .lintstagedrc.js
,为什么这个文件要添加一个 .js
的后缀?因为在这个文件里需要写一点代码对文件进行过滤:
const { ESLint } = require('eslint');
const removeIgnoredFiles = async (files) => {
const eslint = new ESLint();
const ignoredFiles = await Promise.all(files.map((file) => eslint.isPathIgnored(file)));
const filteredFiles = files.filter((_, i) => !ignoredFiles[i]);
return filteredFiles.join(' ');
};
module.exports = {
'*': async (files) => {
const filesToLint = await removeIgnoredFiles(files);
return [`eslint ${filesToLint} --max-warnings=0`];
},
};
添加了代码后,打开 .husky/pre-commit
文件,将里面的内容替换:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm run lint # 这条指令删掉
npx lint-staged # 新增该指令
在进行完上面的操作后,我们再次添加一条 git commit
:
可以看到,lint-staged
已成功过滤文件并执行了 prettier
文件格式化和 eslint
代码检查。
Git Hook: Commit-Msg
为什么要添加 commit-msg
钩子?设想一下,某天你团队中的某个人在提交代码的时候仅写了个 ...
,你应该如何知道他修改了什么文件?时间长了之后,你再问他,他还记得这次的提交修改了什么东西吗?
所以,为了避免上述类似问题和规范化,我们需要对 commit-msg
进行限制,并借助 cz
工具帮我们问答式提交。
对 commit-msg
进行限制,我们需要安装这些包 @commitlint/cli
, @commitlint/config-conventional
pnpm add -w -D @commitlint/cli @commitlint/config-conventional
安装后新建 .commitlintrc.json
文件并写入相关规则,具体规则请自行搜索
{
"extends": ["@commitlint/config-conventional"],
"rules": {
"scope-empty": [2, "never"]
}
}
然后添加 husky
钩子在进行 git commit
时对提交内容校验。
npx husky .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
此时,限制 git commit -m
的钩子已经准备完毕,我们来添加一条 git commit
试试:
git add .
git commit -m "feat: 添加 commitlint 对 git commit 提交内容限制"
可以看到这条 git commit
已经被阻止了,原因就是在内容中我没有指定范围。
现在 commit-msg
已经被加了限制,那么我们安装 cz
工具对提交内容规范化,看看能不能通过。
需要安装的工具有 commitizen
, cz-conventional-changelog
:
pnpm add -w -D commitizen cz-conventional-changelog
安装完后新建 .czrc
文件并添加内容:
{
"path": "cz-conventional-changelog"
}
然后再到根目录 package.json
添加一条新的指令:
{
...,
"scripts": {
...,
"cz": "cz"
}
}
每次执行完 git add .
之后,我们可以通过运行 pnpm run cz
命令进行问答式的 commit-msg
:
从截图中可以看到,commit-msg
的内容已经规范化并通过了 commitlint
拦截。
至此,代码检查、文件格式化、版本控制提交消息规范已完成。
项目代码 - Monorepo Example
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。