Linters 及其周边工具使用方式

发布于 2023-07-20 12:32:11 字数 14330 浏览 68 评论 0

本文并非旨在介绍 Linters 及其周边工具的具体使用方式,而是详细介绍它们之间的关系和基本原理,并探究更优雅的配合方式。这一切都先从 ESLint 开始:

ESLint

ESLint 是现在最流行 JS Linter,它的运行原理是通过解析器将源代码解析成 AST,ESLint 在遍历 AST 过程中会遍历到每个选择器,ESLint 每条规则定义时都会返回一个或者多个以选择器为 key 的回调函数,选择器被遍历到则会执行对应的回调以检测代码是否符合规则。

通常以配置文件的形式配置规则:

  • parser : 解析器,默认使用 Espree 。如果你使用了 Babel 或者 TypeScript,则需要配置对应的解析器完成代码解析,比如 babel-eslint@typescript-eslint/parser
  • extends : 开启一系列规则。比如设置 eslint:all 将开启 ESLint 所有规则, eslint:recommended 则仅开启 eslint 官方推荐的一些 规则 。也可以安装并使用更严格的社区规则,比如 airbnb
  • rules : 修改单条规则的错误级别或关闭规则。
// .eslintrc.json
{
  "parser": "babel-eslint",
  "extends": "eslint:recommended",
  "rules": {
    "no-console": "off", // 关闭 "禁用 console" 规则
    "no-unused-vars": "error", // 出现未使用过的变量将发出 error 级别的错误,错误会导致程序退出
    "max-len": ["warn", { "code": 80 }] // 强制一行的最大长度不得超过 80 个字符,否则将发出 warn 级别的错误,不会导致程序退出
  },
  // ...
}

配置解析器并开启一些检查规则后即可对 JavaScript 代码进行检查。

"scripts": {
  "lint:script": "eslint --ext '.js,.jsx' src", // 检查指定目录的特定扩展名文件
  "lint-fix:script": "npm run lint:script -- --fix", // `--fix` 选项开启自动修复
  "lint": "npm run lint-fix:script && lint-fix:style",
}

ESLint 中的两类规则

由于 ESLint 中存在两类规则:

  • 代码质量规则。如 no-unused-vars (禁止出现未使用过的变量)。
  • 代码格式规则 。如 max-len (每行代码最大长度)、 comma-style (逗号风格)、 keyword-spacing (关键字前后空格)等。

所以执行 eslint . 命令除了会检测出代码质量问题,还会报告代码格式问题。前者是它最核心的功能。
ESLint 在有 --fix 选项之前 ESLint 不能自动修复格式问题,因为代码格式并不存在错误或潜在的问题,但这类错误却往往非常多,为了解决这一个头疼的问题,引入了 Prettier ,它专门用于统一代码风格。当 ESLint 有了 --fix 之后依然不能完全替代 Prettier

Prettier 与 ESLint

ESLint 不能完全替代 Prettier 的原因在于 Prettier 不仅仅可以是 JavaScript 的代码格式化工具,它还支持格式化 JSXVueTypeScriptCSSLessSCSSHTMLMarkdown ...
如果你想统一项目中所有代码的编码风格,ESLint 能做到的并不全面,所以通常是 ESLint 捕获 JS 代码错误,Prettier 格式化代码(包括 JS 的部分)。下面是 Prettier 的配置文件:

// .prettierrc
{
    "arrowParens": "avoid", // 箭头函数参数只有一个时省略小括号
    "singleQuote": true, // 使用单引号代替双引号
    "semi": true // 句尾添加分号
}

eslint-config-prettier

由于 ESLint 存在 代码格式规则 ,而且其中部分规则与 Prettier 中的规则存在冲突。

当确定了 ESLint 捕获 JS 代码错误、Prettier 格式代码的分工原则之后,ESLint 中的代码格式规则已经不再必要, eslint-config-prettier 的作用就是关闭这些不必要且可能会与 Prettier 发生冲突的规则:

// .eslintrc    
{      
  "extends": ["eslint:recommended", "prettier"] // prettier 写在最后才能确保关闭规则
}

eslint-config-prettier 是一个预设配置,是多个规则的集合,原理就是将这些规则全部设置为设置 0"off" ,添加到 extends 选项的尾部实现覆盖。由于这些规则并不影响代码质量的检测,所以覆盖后不会有问题。

module.exports = {
  rules: {
    // The following rules can be used in some cases. See the README for more information. 
    // (These are marked with `0` instead of `"off"` so that a script can distinguish them.)
    "curly": 0,
    "lines-around-comment": 0,
    "max-len": 0,
    "no-confusing-arrow": 0,
    "no-mixed-operators": 0,
    "no-tabs": 0,
    "no-unexpected-multiline": 0,
    "quotes": 0,
    "@typescript-eslint/quotes": 0,
    "babel/quotes": 0,
    "vue/html-self-closing": 0,
    "vue/max-len": 0,

    // The rest are rules that you never need to enable when using Prettier.
    "array-bracket-newline": "off",
    // ...
  }
}

eslint-plugin-prettier(不推荐)

关闭 ESLint 的代码格式规则之后,JS 代码的格式化就全部交由 Prettier。但会有些人期望 JS 代码质量和代码格式的错误都通过 ESLint 来报告, eslint-plugin-prettier 的作用就在于此。
插件增加了一条 prettier/prettier 规则,并使用 prettier 对代码风格进行检查,先使用 Prettier 对代码进行格式化,并与格式化前的代码进行对比,出现不一致的地方就会被 Prettier 标记。若 prettier/prettier 规则开启则 Prettier 并将错误信息通过 ESLint 报告。

eslint-plugin-prettier 同样需要预先关闭 ESLint 中的代码格式规则,所以需要配合 eslint-config-prettier 一起使用,因此它附带了一个 plugin:prettier/recommended 配置,通过此配置可以一次性完成与 eslint-config-prettier 的配置:

// .eslintrc    
{
  "extends": ["plugin:prettier/recommended"]
}
// 相当于
{ "extends": ["prettier"], "plugins": ["prettier"], "rules": { "prettier/prettier": "error", // 开启 Prettier 中规则的错误报告 // 使用 eslint-plugin-prettier 若开启下面两条规则可能导致的问题: https://github.com/prettier/eslint-plugin-prettier/issues/65 "arrow-body-style": "off", "prefer-arrow-callback": "off" } }

不推荐的原因:

个人认为不推荐的最主要原因是第一点,实际开发中通常希望能及时发现代码中的质量问题,而不是等到代码提交时再发现并需要进行修改甚至改造,因为这会导致需要重新测试代码逻辑。所以通常的做法是使用 IDE 插件或者构建工具插件实时报告代码质量规则问题,如 VSCode ESLint 插件 )和 eslint-webpack-plugin 。而代码格式问题并不应该在开发过程中看到的,如果集成 eslint-plugin-prettier 将导致编辑器中或控制台中出现很多无关紧要的代码风格提示,这十分影响开发体验。所以对于代码格式问题最好的办法不是需要实时面对并修复它们,而是能忘记代码的格式化。

VSCode ESLint 插件 :读取目录下的 ESLint 配置文件,对代码进行检查后提示错误,可通过配置开启文件保存时自动修复(但没必要)。

tslint-config-prettier、tslint-plugin-prettier

stylelint 场景下也存在一个预设+插件,同样是解决冲突和在 lint 中调用 Prettier,后面会提到。

prettier-eslint、prettier-tslint、prettier-stylelint

有人想到在 ESLint 调用 Prettier( eslint-plugin-prettier ),就有人想用 Prettier 调用 ESLint,于是就有了 prettier-eslint

prettier-eslint 解决的问题是:如果没有禁用 ESLint 中的代码格式规则,Prettier 格式化后的代码很可能无法通过 ESLint。所以通常的做法是先使用 prettier --write 格式化后再经过 eslint --fix 处理。 prettier-eslint 的作用就是将这两个步骤合二为一,原理就是将被 Prettier 格式化后的代码传递给 ESLint 执行 eslint --fix 。对于 ESlint 无法处理的扩展名文件则只运行 Prettier,如 .css.less.scss.json

同样的思路诞生了 prettier-tslintprettier-stylelint

Git Hooks

上面提到处理代码格式问题的最好的办法是忘记代码的格式化。通常的做法是代码提交时,利用 Git 钩子在提交前自动修复代码格式问题。

Git Hooks 保证 Git 在特定的重要动作发生时触发自定义脚本。项目 git init 之后将初始化一个 .git 目录,在 .git/hooks 目录下默认会填充很多后缀为 .sample 的示例 shell 脚本,这些就是对应钩子执行的 shell 样本文件,将文件名中的后缀 .sample 去掉后即可执行,你可以可以自定义脚本内容并正确命名放入 .git/hooks 目录下。这些脚本除了本身可以被调用外,还透出了被触发时所传入的参数。

常用的 Git Hooks:

  • pre-commit : git commit 执行前触发。通常用于检查代码风格是否一致,如 lint 程序。
  • commit-msg : git commit 执行前触发。通常用于校验提交信息是否规范。
  • 更多参考

husky

.git/hooks 目录下添加 shell 脚本并非特别方便,所以 husky 的作用就是让我们更简单的在项目中添加 Git Hooks,它只需要像添加 npm 脚本一样使用 Git Hooks。

husky@4

husky@4 及其之前的版本,你只需要在 package.json 中加入类似如下配置即可在代码提交时自动完成代码的格式化:

// package.json
"husky": {
  "hooks": {
    "pre-commit": "lint-staged",
    "commit-msg": "commitlint -e $GIT_PARAMS", // 校验 commit message 是否规范
  }
},
// lint-staged 允许我们仅对 git 暂存区的文件上执行特定脚本
"lint-staged": {
  "*.{js,json,yml,yaml,css,scss,ts,tsx,md}": [
    "prettier --write"
  ]
}

husky@4 的工作原理是在 .git/hooks 下创建所有类型的 Git Hooks 和一个 husky.sh 脚本,所有 Git Hooks 执行脚本的内容都是执行 husky.sh (内容如下)。 husky.sh 的程序内容就是去检查用户在 package.json#husky.hooks 是否配置了对应的钩子,有则执行。这也导致 Git 每个特定动作都将执行 husky 设置的脚本,即使用户没有设置任何 Git Hooks。

# .git/hooks/pre-commit

#!/bin/sh
# husky
. "$(dirname "$0")/husky.sh"

这个问题并不好解决,如果不事先创建所有类型的 Git Hooks,用户在后续添加或者删除 package.json#husky.hooks 的配置时,husky 没有好的办法做到同步删除或者添加 .git/hooks 目录下的 hook。
只有用户 hooks 配置和 Git Hooks 在同一个地方才能解决同步问题。

新版本 husky

Git 2.9 引入了 core.hooksPath 的新功能,它允许指定 Git Hooks 执行脚本的存放的目录,而不再必须使用 .git/hooks 目录。这就可以解决了 husky@4 存在的问题。新版的 husky 使用 husky install 命令将 Git Hooks 目录指定为 .husky/ ,使用 husky add 命令向 .husky/ 中添加 hook。用户后续直接操作 .husky/ 目录的 hook 即完成增删改操作,也就不再有同步问题。

添加钩子:

# 创建 pre-commit 钩子
npx husky add .husky/pre-commit "npx lint-staged"
# 创建 commit-msg 钩子
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"' 

package.json 配置:

"scripts": {
  "prepare": "husky install", // prepare 会在 `npm install` 后自动执行
  // ...
},
"lint-staged": {
  "*.{js,json,yml,yaml,css,scss,ts,tsx,md}": [
    "prettier --write",
    "git add ."
  ]
}

pretty-quick 和 lint-staged

通常情况下我们并不希望对项目所有代码进行格式化,这会导致当前提交存在大量的修改难以 Code Review;格式化工具还可能存在未知 bug 导致无效代码,这类问题除非大范围回归否则难以发现。所以我们更希望仅对当前修改的代码文件进行格式化。 pretty-quicklint-staged 的作用就是允许我们仅对 Git 暂存区的文件上执行特定脚本,他们通常都与 husky 配合使用。

pretty-quick

pretty-quick 是对更改的文件运行 Prettier,本身仅格式化暂存区的文件。

# 使用 husky@7 添加 pre-commit hook
npx husky add .husky/pre-commit "npx --no-install pretty-quick --staged"

--staged 的作用是格式化后的代码会重新暂存,即相当于执行了 git add . 。执行上述命令后

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no-install pretty-quick --staged

lint-staged

lint-staged 并不与 Prettier 存在绑定关系,所以需要在 package.json#lint-staged 字段添加格式化命令。

# 使用 husky@7 添加 pre-commit hook
npx husky add .husky/pre-commit "npx --no-install lint-staged"
"lint-staged": {
  "*.{js,json,yml,yaml,css,scss,ts,tsx,md}": [
    "prettier --write",
    "git add ."
  ]
}

Stylelint

Stylelint 顾名思义是样式代码规范校验工具。类似 ESLint 它同样包含代码质量和代码格式两类规则,使用 --fix 选项自动修复检测出来的问题。下面是一简单的配置文件:

// .stylelintrc.js
module.exports = {
  extends: [
    'stylelint-config-standard',
    'stylelint-config-rational-order',
    'stylelint-config-prettier',
  ],
  ignoreFiles: ['{es,dist,browser}/**/*'],
};
  • stylelint-config-standard : 官网推荐的 CSS 标准。
  • stylelint-config-recess-order : 属性排列顺序,保证代码更好地可读性。
  • stylelint-config-prettier : 为了解决 Prettier 与 Stylelint 的规则冲突,关闭 Stylelint 中所有代码格式规则。放在 extends 选项最后以确保覆盖其他配置。

使用:

"scripts": {
  "lint:style": "stylelint src/**/*.{css,sass,scss,less} --fix"
}

VSCode Stylelint 插件 :读取 Stylelint 配置文件,对代码进行检查后提示错误,可通过配置开启文件保存时自动修复(但没必要)。

stylelint-prettier(不推荐)

类似 eslint-plugin-prettierstylelint-prettier 的作用也是将 prettier 代码风格规则应用于 stylelint。同样的,它最好与 stylelint-config-prettier 配合使用。

EditorConfig 与 Prettier

EditorConfig 有助于同一项目下使用不同 IDE 的多个开发人员维护一致的编码风格。

# .editorconfig
root = true # 不再往上搜索 .editorconfig

[*]
charset = utf-8
indent_size = 2
indent_style = space
insert_final_newline = true # 尾部是否插入一行
trim_trailing_whitespace = true # 是否移除行末的空白字符

EditorConfig 还存在一些与 Prettier 冲突的配置项,应避免出现重复配置。 Prettier 会解析 .editorconfig 文件,并将仅限以下配置项转换为 Prettier 中对应的默认配置,覆盖的前提是 Prettier 配置文件中配置对应选项:

  • end_of_line : 对应 Prettier 中的 endOfLine ,指定行结束符。
  • indent_style : 对应 useTabs ,表示是否使用 tab 缩进。
  • indent_size / tab_width : 对应 tabWidth ,表示 tab 缩进大小。
  • max_line_length : 对应 printWidth ,表示每行最大字符数。

commitlint

commitlint 也是 Linters 家族中的一员,用于校验 commit message 是否符合提交格式(通常为 type(scope?): subject )。

# 安装 commitlint 相关包
npm install --save-dev commitlint @commitlint/config-conventional @commitlint/cli

# 创建 commitlint 配置文件,并添加基本配置内容
echo "module.exports = {extends: ['@commitlint/config-conventional']};" > commitlint.config.js

# 测试是否生效
echo "foo: some message" | commitlint # 失败
echo "fix: some message" | commitlint # 通过

# 添加 commit-msg 钩子
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

参考

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

别念他

暂无简介

0 文章
0 评论
22 人气
更多

推荐作者

13886483628

文章 0 评论 0

流年已逝

文章 0 评论 0

℡寂寞咖啡

文章 0 评论 0

笑看君怀她人

文章 0 评论 0

wkeithbarry

文章 0 评论 0

素手挽清风

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文