Skip to content
On this page

代码规范/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

bash
npm init @eslint/config
npm init @eslint/config

根据初始化引导选择适合自己项目的选项,我选的是:vue + ts + es模块 + 格式化/报告所有错误 + 配置使用单独文件(js格式),选择后,会自动安装下面的开发依赖 npm 包

点击查看安装的 npm 包详情
json
// 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",
}

并自动写入配置文件(自动生成默认内容如下)

js
// .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

js
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-prettierstylelint-prettier

js
npm install --save-dev eslint-config-prettier eslint-plugin-prettier
npm install --save-dev eslint-config-prettier eslint-plugin-prettier
点击查看安装的 npm 包详情
json
// 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",
}

修改配置

js
// .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 "**/*.jsnpx stylelint "**/*.{css,scss,html,vue}" 可以监测到 css、js 错误,但并不能在保存后自动格式化/修复,需要安装 ESLint、Prettier ESLint、Stylelint 3 个 vscode 插件,且进行如下配置

js
// 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"
    ],
}

参考: stylelint 实现 scss 样式格式化

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 安装运行

bash
npm init stylelint
npm init stylelint

该命令会自动创建 .stylelintrc.json 配置文件写入默认内容,并自动安装两个 dev 依赖包:stylelintstylelint-config-standard

json
// .stylelintrc.json 默认内容
{ "extends": ["stylelint-config-standard"] }
// .stylelintrc.json 默认内容
{ "extends": ["stylelint-config-standard"] }

运行完成后,执行 npx stylelint "**/*.css" 即可校验项目内 css 内容

css
/* src\css\index.css */
a {
  color: red;
}
a {
  color: blue;
} /* 重复选择器样式问题 */
/* src\css\index.css */
a {
  color: red;
}
a {
  color: blue;
} /* 重复选择器样式问题 */

stylelint-base.png

2.2 支持 scss 与 vue、html 内嵌

上面的默认配置,无法支持 scss、html、vue 内的样式校验,需要添加 scss 扩展配置、自定义语法。

scss 扩展配置

安装 scss 扩展

bash
npm install -D stylelint-config-standard-scss
npm install -D stylelint-config-standard-scss

为了给配置添加注释,建议将 .stylelintrc.json 改为 .stylelintrc.js,然后增加 scss 配置

js
// .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

bash
# 安装 postcss-html
npm install -D postcss-html
# 安装 postcss-html
npm install -D postcss-html

修改 .stylelintrc.js 配置

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,运行效果:

style-lint-final.png

2.3 跳过样式校验

  • 部分代码跳过
scss
/* stylelint-disable */
a {
}
/* stylelint-enable */
/* stylelint-disable */
a {
}
/* stylelint-enable */
  • 整个文件跳过校验,.stylelintignore file to ignore specific files. For example:
bash
/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 提交消息不规范,导维后期维护越来越困难的问题

bash
git commit -m 'xx'
git commit -m 'xx'

一般会使用 conventional commits 约定式提交规范来填写提交信息。像 vue、react 等开源项目源码的提交规范一般也是这种,下图是 vue 源码提交信息

vue-commit-msg.png

规范化步骤

  1. 怎么写规范的提交信息、消息格式
  2. git 提交前校验,如果 commit msg 不符合规范,不能提交成功(使用 husky)

3.1 约定式提交信息格式

规范提交信息格式如下,一般 type/scope/description 用的频率最多

bash
<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简短提交描述
bodylonger description,用于补充简短提交描述
footers破环性更新(BREAKING CHANGE)描述、fix issue 关联信息等

提交消息示例

bash
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)来创建规范的提交信息,并且是引导式的

bash
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?

commitizen-cz.png

完成后,会根据步骤信息拼接信息,并自动调用 git commit -m '组装后的提交信息' 进行提交

bash
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,并自动完成初始化

js
npx husky-init
npx husky-init

WARNING

安装 husky 时,需要项目是一个 git 管理的项目(如果不是,请先运行 git init)

因为 husky 是基于 git hooks (比如 pre-commit)来做提交拦截、校验

它会自动完成以下操作

  1. Add prepare script to package.json("prepare": "husky install"
  2. Create a sample pre-commit hook that you can edit (by default, npm test will run when you commit)
  3. Configure Git hooks path

会自动创建 .husky 目录,结构如下

bash
├── .husky
   ├── _
      ├── .gitigore
      └── husky.sh
└── pre-commit # 提交钩子,提交前默认执行 npm test
├── .husky
   ├── _
      ├── .gitigore
      └── husky.sh
└── pre-commit # 提交钩子,提交前默认执行 npm test

执行安装 log

bash
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 钩子

bash
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,需要先安装

bash
# 安装 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 如下

bash
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

js
module.exports = {
  extends: ["@commitlint/config-conventional"],
};
module.exports = {
  extends: ["@commitlint/config-conventional"],
};

假设需要支持 upd 类型的提交信息,git commit -m 'upd: update func',配置如下

js
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 执行命令即可

bash
#!/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 执行命令即可

bash
#!/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 可以仅校验每次提交的内容

bash
npm install -D lint-staged
npm install -D lint-staged

修改 husky pre-commit 钩子执行脚本 .husky/pre-commit

bash
#!/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

js
"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

跳过校验这种方式不建议使用,仅供紧急情况临时使用

bash
git commit -m '提交信息' --no-verify
git commit -m '提交信息' --no-verify

5. codecov 代码覆盖率

codecov

覆盖率不达标 PR 拦截

Status Checks,可以用于阻止不满足特定覆盖率值的 PR(pull request) 请求

yaml
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

codecov-fail.png

如果成功,如下图 √

codecov-success.png

站在前人的肩膀上