代码规范/Lint
ESLint + Prettier + Stylelint + Commitlint + husky 提交拦截
实践 demo:commit-lint-test - github
1. ESLint + Prettier
ESLint,可以用于提示/避免 JS 错误、使团队代码 格式保持一致。
Prettier,它不具备发现 js/ts/vue 语法、功能错误能力,仅用于格式化代码。当做 ESLint 插件使用,正好可以弥补 lint 在 vue template, css 代码格式化方面的弱项,提供统一的格式化功能。
1.1 ESLint JS/TS/Vue 校验
安装 ESLint
npm init @eslint/config
npm init @eslint/config
根据初始化引导选择适合自己项目的选项,我选的是:vue + ts + es模块 + 格式化/报告所有错误 + 配置使用单独文件(js格式)
,选择后,会自动安装下面的开发依赖 npm 包
点击查看安装的 npm 包详情
// package.json
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.59.9",
"eslint": "^8.42.0",
"eslint-config-prettier": "^8.8.0",
"eslint-config-standard-with-typescript": "^35.0.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-vue": "^9.14.1",
"typescript": "^5.1.3",
"prettier": "2.8.8",
}
// package.json
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.59.9",
"eslint": "^8.42.0",
"eslint-config-prettier": "^8.8.0",
"eslint-config-standard-with-typescript": "^35.0.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-vue": "^9.14.1",
"typescript": "^5.1.3",
"prettier": "2.8.8",
}
并自动写入配置文件(自动生成默认内容如下)
// .eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: ["standard-with-typescript", "plugin:vue/vue3-essential"],
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
plugins: ["vue"],
rules: {},
};
// .eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: ["standard-with-typescript", "plugin:vue/vue3-essential"],
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
plugins: ["vue"],
rules: {},
};
使用 npx eslint "**/*.js
即可校验出 js 错误。安装 VSCode ESLint 插件,配置自动 fix,可以在保存后自动修复 js/ts 代码格式。
1.2 Prettier css/template 格式化
ESLint 也拥有格式化代码能力,但对于 Vue template、css 相关格式化支持不好,一般会引入 Prettier 来做这方面的工作。
安装 Prettier
npm install --save-dev --save-exact prettier
echo {} > .prettierrc.js // 或手动创建 .prettierrc.js,写入 {} 内容
npm install --save-dev --save-exact prettier
echo {} > .prettierrc.js // 或手动创建 .prettierrc.js,写入 {} 内容
作为 ESLint 插件使用,一般 prettier 和 eslint/stylelint 会有一些格式化冲突,需要使用 eslint-config-prettier
, stylelint-config-prettier
(stylelint v15 废弃了所有与 prettier 有冲突的选项,v15 以下版本才需要使用) 来避免冲突
相关链接:eslint-plugin-prettier、stylelint-prettier
npm install --save-dev eslint-config-prettier eslint-plugin-prettier
npm install --save-dev eslint-config-prettier eslint-plugin-prettier
点击查看安装的 npm 包详情
// package.json
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.59.9",
"eslint": "^8.42.0",
"eslint-config-prettier": "^8.8.0",
"eslint-config-standard-with-typescript": "^35.0.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-vue": "^9.14.1",
"typescript": "^5.1.3",
"prettier": "2.8.8",
}
// package.json
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.59.9",
"eslint": "^8.42.0",
"eslint-config-prettier": "^8.8.0",
"eslint-config-standard-with-typescript": "^35.0.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-vue": "^9.14.1",
"typescript": "^5.1.3",
"prettier": "2.8.8",
}
修改配置
// .eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
"standard-with-typescript",
"plugin:vue/vue3-essential",
"plugin:prettier/recommended",
],
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
plugins: ["vue"],
rules: {},
};
// .eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
"standard-with-typescript",
"plugin:vue/vue3-essential",
"plugin:prettier/recommended",
],
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
plugins: ["vue"],
rules: {},
};
1.3 vscode 保存后自动修复配置
虽然 npx eslint "**/*.js
和 npx stylelint "**/*.{css,scss,html,vue}"
可以监测到 css、js 错误,但并不能在保存后自动格式化/修复,需要安装 ESLint、Prettier ESLint、Stylelint 3 个 vscode 插件,且进行如下配置
// settings.json
{
"[scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": true, // 基础 eslint, vue/js/ts 自动修复
"source.fixAll.stylelint": true, // 开启 stylelint 自动修复
},
// 关闭编辑器内置样式检查(避免与 stylelint 冲突)
"css.validate": true,
"less.validate": true,
"scss.validate": true,
"eslint.validate": ["javascript", "javascriptreact", "vue"],
// 配置 stylelint 检查文件类型
"stylelint.validate": [
"css",
"less",
"postcss",
"scss",
"sass",
"vue"
],
}
// settings.json
{
"[scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": true, // 基础 eslint, vue/js/ts 自动修复
"source.fixAll.stylelint": true, // 开启 stylelint 自动修复
},
// 关闭编辑器内置样式检查(避免与 stylelint 冲突)
"css.validate": true,
"less.validate": true,
"scss.validate": true,
"eslint.validate": ["javascript", "javascriptreact", "vue"],
// 配置 stylelint 检查文件类型
"stylelint.validate": [
"css",
"less",
"postcss",
"scss",
"sass",
"vue"
],
}
1.4 提交时校验
结合 husky,执行 npx eslint "src/**/*.{js,ts,vue}"
即可完成校验,参见:4. husky 提交拦截 - 4.4 ESLint 提交校验
2. Stylelint
Stylelint,一个强大的 CSS linter,可帮助避免样式错误、强制执行约定的规范。特点如下
- 拥有超过 100 条现代 CSS 语法和规则
- 支持插件,因此您可以创建自己的自定义规则
- 尽可能自动修复问题
- 有 15k 单元测试使其健壮
- 受到 Google 和 GitHub 等全球公司的信任
支持扩展
- 从 HTML、Markdown 和 CSS-in-JS template 中提取嵌入的样式
- 解析类似 CSS 的语言,如 SCSS、Sass、Less 和 SugarSS 等
2.1 安装运行
npm init stylelint
npm init stylelint
该命令会自动创建 .stylelintrc.json
配置文件写入默认内容,并自动安装两个 dev 依赖包:stylelint
, stylelint-config-standard
// .stylelintrc.json 默认内容
{ "extends": ["stylelint-config-standard"] }
// .stylelintrc.json 默认内容
{ "extends": ["stylelint-config-standard"] }
运行完成后,执行 npx stylelint "**/*.css"
即可校验项目内 css 内容
/* src\css\index.css */
a {
color: red;
}
a {
color: blue;
} /* 重复选择器样式问题 */
/* src\css\index.css */
a {
color: red;
}
a {
color: blue;
} /* 重复选择器样式问题 */
2.2 支持 scss 与 vue、html 内嵌
上面的默认配置,无法支持 scss、html、vue 内的样式校验,需要添加 scss 扩展配置、自定义语法。
scss 扩展配置
安装 scss 扩展
npm install -D stylelint-config-standard-scss
npm install -D stylelint-config-standard-scss
为了给配置添加注释,建议将 .stylelintrc.json 改为 .stylelintrc.js,然后增加 scss 配置
// .stylelintrc.js
module.exports = {
extends: [
"stylelint-config-standard", // 普通css,默认
"stylelint-config-standard-scss", // scss 支持
],
};
// .stylelintrc.js
module.exports = {
extends: [
"stylelint-config-standard", // 普通css,默认
"stylelint-config-standard-scss", // scss 支持
],
};
运行 npx stylelint "**/*.{css,scss}"
即可校验 css 与 scss 文件。
这些 extends 扩展配置,命名以 stylelint-config-
开头,更新扩展配置,参见:Awesome Stylelint Configs
自定义语法 html/vue
像 html、vue 等就需要使用自定义语法了(Custom syntaxes),命名一般以 postcss-
开头。
比如 postcss-html,用于支持 HTML 包括 Vue SFC. postcss-angular 用于支持 Angular Components. 更多 Custom syntaxes 参考 Awesome Stylelint Custom syntaxes
# 安装 postcss-html
npm install -D postcss-html
# 安装 postcss-html
npm install -D postcss-html
修改 .stylelintrc.js 配置
module.exports = {
extends: [
"stylelint-config-standard", // 普通css,默认
"stylelint-config-standard-scss", // scss 支持
],
rules: {
"max-nesting-depth": 2, // 样式最大嵌套层数,总共最多 3 层
},
overrides: [
{
files: ["*.vue", "*.html"],
customSyntax: "postcss-html", // 支持 HTML 包括 Vue SFC
},
],
};
module.exports = {
extends: [
"stylelint-config-standard", // 普通css,默认
"stylelint-config-standard-scss", // scss 支持
],
rules: {
"max-nesting-depth": 2, // 样式最大嵌套层数,总共最多 3 层
},
overrides: [
{
files: ["*.vue", "*.html"],
customSyntax: "postcss-html", // 支持 HTML 包括 Vue SFC
},
],
};
运行 npx stylelint "**/*.{css,scss,html,vue}"
即可校验对应格式文件。文件内容代码地址 stylint - commit-lint-test,运行效果:
2.3 跳过样式校验
- 部分代码跳过
/* stylelint-disable */
a {
}
/* stylelint-enable */
/* stylelint-disable */
a {
}
/* stylelint-enable */
- 整个文件跳过校验,
.stylelintignore
file to ignore specific files. For example:
/public/vendor
node_modules
/coverage
/dist
/public/vendor
node_modules
/coverage
/dist
2.4 不规范样式提交拦截
将 stylelint 执行命令,放在 .husky/pre-commit 中即可在 git 时校验。详情参见后面介绍的 4. husky 提交拦截 - 4.3 StyleLint 提交校验 部分内容
3. git commit msg 校验
为防止 git commit 提交消息不规范,导维后期维护越来越困难的问题
git commit -m 'xx'
git commit -m 'xx'
一般会使用 conventional commits 约定式提交规范来填写提交信息。像 vue、react 等开源项目源码的提交规范一般也是这种,下图是 vue 源码提交信息
规范化步骤
- 怎么写规范的提交信息、消息格式
- git 提交前校验,如果 commit msg 不符合规范,不能提交成功(使用 husky)
3.1 约定式提交信息格式
规范提交信息格式如下,一般 type/scope/description 用的频率最多
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
字段信息 | 必填 | 解释 |
---|---|---|
type | 是 | 提交类型,feat 特性、fix bug 修复、docs 文档、style 样式等 |
scope | 否 | 修改模块/范围,如果是业务版本迭代,这个字段可以填版本号,更有利于维护 |
description | 是 | 简短提交描述 |
body | 否 | longer description,用于补充简短提交描述 |
footers | 否 | 破环性更新(BREAKING CHANGE)描述、fix issue 关联信息等 |
提交消息示例
feat(types): map declared emits to onXXX props in inferred prop types
feat(lang): add Polish language
docs: correct spelling of CHANGELOG
chore: fix typo
feat(v1.0.6): 用户模块,新增登录逻辑
feat(types): map declared emits to onXXX props in inferred prop types
feat(lang): add Polish language
docs: correct spelling of CHANGELOG
chore: fix typo
feat(v1.0.6): 用户模块,新增登录逻辑
3.2 commitizen 提交工具
除了手动按照格式写 git commit message 外,还可以使用工具(commitizen)来创建规范的提交信息,并且是引导式的
npm install -g commitizen
npm install -g commitizen
全局安装 commitizen 后,会全局新增命令 cz,可以用来代替 git commit -m 'xx'
如下图,如果没有 git add 将代码改动添加到已暂存区(Staged Changes),cz 命令不会出现提交引导提示,而是提示 No files added to staging! Did you forget to run git add?
完成后,会根据步骤信息拼接信息,并自动调用 git commit -m '组装后的提交信息'
进行提交
type 1.选择提交类型(必填)
scope 2.选择 scope 模块名(选填)
description 3.填写精炼的提交信息(必填)
body 4.填写补充信息(选填)
footers 5.选择是否有破坏性更新(选填)
footers 6.是否关联是 open 状态的 issue(选填)
type 1.选择提交类型(必填)
scope 2.选择 scope 模块名(选填)
description 3.填写精炼的提交信息(必填)
body 4.填写补充信息(选填)
footers 5.选择是否有破坏性更新(选填)
footers 6.是否关联是 open 状态的 issue(选填)
3.3 不规范 git 提交信息拦截
git commit -m 'xxx'
这种不规范提交信息的拦截,需要使用 husky + commitlint,具体步骤参见后面介绍的 4. husky 提交拦截 - 4.2 添加 commit-msg lint 部分内容
4. husky 提交拦截
You can use it to lint your commit messages, run tests, lint code, etc... when you commit or push. Husky supports all Git hooks.
当在 git commit 或 git push 时,可以使用 husky 校验提交消息、运行测试、lint 代码等。 Husky 支持所有 Git 钩子。
4.1 安装运行
安装 husky,并自动完成初始化
npx husky-init
npx husky-init
WARNING
安装 husky 时,需要项目是一个 git 管理的项目(如果不是,请先运行 git init)
因为 husky 是基于 git hooks (比如 pre-commit)来做提交拦截、校验
它会自动完成以下操作
- Add prepare script to package.json(
"prepare": "husky install"
) - Create a sample pre-commit hook that you can edit (by default, npm test will run when you commit)
- Configure Git hooks path
会自动创建 .husky 目录,结构如下
├── .husky
│ ├── _
│ │ ├── .gitigore
│ │ └── husky.sh
└── pre-commit # 提交钩子,提交前默认执行 npm test
├── .husky
│ ├── _
│ │ ├── .gitigore
│ │ └── husky.sh
└── pre-commit # 提交钩子,提交前默认执行 npm test
执行安装 log
C:\Users\Administrator\Desktop\commit-lint-test> npx husky-init
# Need to install the following packages:
# husky-init@8.0.0
# Ok to proceed? (y) y(选择 y 安装)
# husky-init updating package.json
# setting prepare script to command "husky install"
# husky - Git hooks installed
# husky - created .husky/pre-commit
# please review changes in package.json
C:\Users\Administrator\Desktop\commit-lint-test> npx husky-init
# Need to install the following packages:
# husky-init@8.0.0
# Ok to proceed? (y) y(选择 y 安装)
# husky-init updating package.json
# setting prepare script to command "husky install"
# husky - Git hooks installed
# husky - created .husky/pre-commit
# please review changes in package.json
运行
安装完成后,运行 git commit -m 'xx'
时会执行默认创建的 .husky/pre-commit
shell 文件。
其中默认的脚本是 npm test
,如果项目中没有引入单元测试,该命令会不存在,提交会失败,我们可以暂时先注释。
4.2 添加 commit-msg lint
可以使用 husky add 添加添加其他的钩子,比如 commit-msg 钩子
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
上面的命令中,使用了 commitlint,用于校验 commit msg,需要先安装
# 安装 commitlint 依赖
npm install --save-dev @commitlint/cli @commitlint/config-conventional
# 创建 commitlint.config.js,并写入内容 module.exports = {extends: ['@commitlint/config-conventional']}
# 注意:如果是 windows,不建议使用命令,建议手动创建文件并复制内容到文件
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
# 安装 commitlint 依赖
npm install --save-dev @commitlint/cli @commitlint/config-conventional
# 创建 commitlint.config.js,并写入内容 module.exports = {extends: ['@commitlint/config-conventional']}
# 注意:如果是 windows,不建议使用命令,建议手动创建文件并复制内容到文件
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
配置完成后,执行 git commit -m 'xxx'
会被成功拦截,并给出提示。log 如下
C:\Users\x\commit-lint-test> git commit -m 'xxx'
# ⧗ input: xxx
# ✖ subject may not be empty [subject-empty]
# ✖ type may not be empty [type-empty]
#
# ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
#
# husky - commit-msg hook exited with code 1 (error)
C:\Users\x\commit-lint-test> git commit -m 'xxx'
# ⧗ input: xxx
# ✖ subject may not be empty [subject-empty]
# ✖ type may not be empty [type-empty]
#
# ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
#
# husky - commit-msg hook exited with code 1 (error)
自定义 commit msg 规则
默认的配置文件 commitlint.config.js 配置如下,假设需要修改规则,需要指定 rules
module.exports = {
extends: ["@commitlint/config-conventional"],
};
module.exports = {
extends: ["@commitlint/config-conventional"],
};
假设需要支持 upd
类型的提交信息,git commit -m 'upd: update func'
,配置如下
module.exports = {
extends: ["@commitlint/config-conventional"],
rules: {
"type-enum": [
2, // Level 0 disable、1 warning、2 error
"always", // always|never
// 默认 11 个类型,这里新增一个 upd 类型
[
"upd",
"feat",
"fix",
"docs",
"style",
"refactor",
"perf",
"test",
"build",
"ci",
"chore",
"revert",
],
],
},
};
module.exports = {
extends: ["@commitlint/config-conventional"],
rules: {
"type-enum": [
2, // Level 0 disable、1 warning、2 error
"always", // always|never
// 默认 11 个类型,这里新增一个 upd 类型
[
"upd",
"feat",
"fix",
"docs",
"style",
"refactor",
"perf",
"test",
"build",
"ci",
"chore",
"revert",
],
],
},
};
完整测试 demo,参见 commit-lint-test - github
4.3 StyleLint 提交校验
在当前页面前面介绍的 2. Stylelint 部分内容中,介绍了 stylelint 相关配置,完成对应配置后
在 .husky/pre-commit 中增加对应的 stylelint 执行命令即可
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# npm test
npx stylelint "**/*.{css,scss,html,vue}"
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# npm test
npx stylelint "**/*.{css,scss,html,vue}"
4.4 ESLint 提交校验
在 .husky/pre-commit 中增加对应的 eslint 执行命令即可
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# npm test
npx eslint "src/**/*.{js,ts,vue}
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# npm test
npx eslint "src/**/*.{js,ts,vue}
4.5 lint-staged 仅校验本次提交
当项目庞大时,每次提交都对所有文件进行 lint 会有不必要的耗时。借用 lint-staged
可以仅校验每次提交的内容
npm install -D lint-staged
npm install -D lint-staged
修改 husky pre-commit 钩子执行脚本 .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx pretty-quick --staged # prettier 格式化修复
npx lint-staged # 校验
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx pretty-quick --staged # prettier 格式化修复
npx lint-staged # 校验
修改 package.json
"lint-staged": {
"src/**/*.{js,ts,vue}": "npx eslint",
"src/**/*.{css,scss,html,vue}": "npx stylelint --allow-empty-input"
}
"lint-staged": {
"src/**/*.{js,ts,vue}": "npx eslint",
"src/**/*.{css,scss,html,vue}": "npx stylelint --allow-empty-input"
}
完整 demo:commit-lint-test
4.6 跳过 husky 校验 --no-verify
对于有些场景、需要跳过 husky 校验(比如:电脑 node 环境异常,lint 脚本运行不起来,但此时需要尽快提交代码,部署后给测试去验证),可以使用 git commit 自带的 --no-verify
参数,临时跳过 git 的 pre-commit hooks
WARNING
跳过校验这种方式不建议使用,仅供紧急情况临时使用
git commit -m '提交信息' --no-verify
git commit -m '提交信息' --no-verify
5. codecov 代码覆盖率
覆盖率不达标 PR 拦截
Status Checks,可以用于阻止不满足特定覆盖率值的 PR(pull request) 请求
coverage:
status:
project:
default: false # disable the default status that measures entire project
tests: # declare a new status context "tests"
target: 100% # we always want 100% coverage here
paths: "tests/" # only include coverage in "tests/" folder
coverage:
status:
project:
default: false # disable the default status that measures entire project
tests: # declare a new status context "tests"
target: 100% # we always want 100% coverage here
paths: "tests/" # only include coverage in "tests/" folder
如果成功,如下图 √