lint 工具用来检查编程错误,最初是从 C 语言中发展起来的。在 C 语言最初时期,编译器没法捕获一些常见的编程错误,所以开发出了一个叫作 lint
的辅助程序,经过扫描源文件来查找问题。javascript
当咱们在 linting 的时候咱们到底在干什么?实际上,最终目标是但愿代码更加健壮,而且不论团队有多少成员,代码就像同一我的写出来的同样,加强可读性强。css
能够将众多 linters 的检查目标大体分为三类:html
下图展现了 JavaScript linters 的进化史:前端
2002 年由 Douglas Crockford 建立,用来进行 JavaScript 语法检查和校验。JSLint 定义了一个比 ECMAScript 编程语言标准更为严格的子集,是一种更高的标准。JSLint 彻底是用 JavaScript 编写的。vue
JSLint 接收 JavaScript 源代码并对其进行扫描。若是发现问题,它将返回一条消息来描述问题以及源代码中的大概位置。这些问题多数时候是语法错误,但不全是语法错误,也多是代码风格和结构的问题。它不能证实程序是正确的,只是提供了一个方式来帮助发现问题。JSLint 更加关心代码质量,所以即便浏览器能够正常运行的代码,JSLint 也可能不会经过。使用 JSLint 就意味着要欣然接受它全部的建议。java
JSLint 能够对 JavaScript 源代码或 JSON 文本进行操做。 JSLint 将会承认 ES6 的一部分优秀的特性,例如 let
、 const
等等。react
2010年基于 JSLint 诞生了 JSHint ,它主要解决了 JSLint 过于独断的问题,提供了一些配置以及添加一些 rules 。相较之下更友好,也更容易配置,因此很快就发展了起来,也获得了众多 IDE 和编辑器的支持。git
JSHint 扫描用 JavaScript 编写的程序,并报告常见的错误和潜在的错误。 潜在的问题多是语法错误、因为隐式类型转换致使的错误、变量泄漏等。能够经过指定任意数量的 linting 选项或在源代码中声明指令来控制 JSHint 的行为。es6
JSHint 附带了一组默认的警告,但这些也是可配置的。能够在配置文件中指定要打开或关闭的 JSHint 选项。 例如,如下文件将启用有关未定义和未使用的变量的警告,并告知 JSHint 一个名为 MY_GLOBAL 的全局变量。github
{
"undef": true,
"unused": true,
"globals": {
"MY_GLOBAL": true
}
}
复制代码
可是,因为它是基于 JSLint 开发的,天然原有的一些问题它也继承下来了,好比不易扩展,不容易直接根据报错定位到具体的规则配置等。
2013年,Nicholas C. Zakas 建立了 ESLint ,提供了更好的 es6 支持,以及更多的 rules ,尤为是一些代码风格方面的,以及一个灵活的插件系统,可让开发者建立本身的 rules ,同时能够方便的根据报错定位到具体的规则配置。
规则的错误等级分为三级,能够更加细粒度地控制如何应用规则:
"off"
或 0
- 关闭此条规则检查"warn"
或 1
- 警告,不会影响 exit code"error"
或 2
- 错误,exit code 为 1默认状况下全部规则都是关闭的,"extends": "eslint:recommended"
会打开全部有“√”标记的规则,这些规则只跟着主版本更新,也能够在 npm 中查找以 eslint-config
开头的共享配置,经过 extends
配置项来添加。
ESLint 默认使用 Espree 做为 JavaScript 解析器,能够在 parser
配置项中更改解析器。解析器会将源代码解析成抽象语法树 AST (Abstract Syntax Tree),而后插件会根据这个 AST 来建立一些称为 lint rules 的断言,来描述代码应该是怎样的。
用来检查 TypeScript 的,可是2019年已经废弃了,如今使用的是ESLint,配合 typescript-eslint
。TypeScript 团队也宣布将 TypeScript 代码库从 TSLint 迁移到 typescript-eslint
。
ESLint 使用一个 parser 将 source code 转成抽象语法树 Abstract Syntax Tree (AST) 的数据格式,而后插件根据这个 AST 来进行 lint rules 的检查。
TypeScript 是 JavaScript 的静态代码分析器,在基础的 JavaScript 上添加了一些额外的语法。TypeScript 使用一个 parser 将 source code 转成 AST ,而后 TypeScript Compiler 的其余部分使用这个 AST 来执行其余操做,例如给出类型检查后的问题反馈等等。
然而,ESLint 和 TypeScript 使用的是不一样格式的 AST ,这就是 typescript-eslint
这个项目存在的主要缘由。typescript-eslint
就是为了可以一块儿使用 ESLint 和 TypeScript 。 TSLint 使用的就是 TypeScript AST 格式,其优势是不须要一个调和 AST 格式之间差别的工具,可是主要缺点是 TSLint 没法重用 JavaScript 生态中围绕 linting 已经作好的工做,而是从头开始从新实现全部的功能,从规则到自动修复功能等等。所以,TypeScript AST 不兼容 ESLint 用户写成并使用的 1000 多条规则。
因为TypeScript 是 JavaScript 的超集,它包含了全部 JavaScript 语法之外,还额外添加了一些语法,例如:
var x: number = 1;
复制代码
当 TypeScript Compiler 解析这段代码生成 TypeScript AST 时,: number
语法也会出如今语法树中,ESLint 不借助其余工具是没法理解的。但 ESLint 在设计时就考虑到了这些用例。ESLint 不只仅是一个库,而是由许多重要的移动部件组成。其中一个就是 parser 。ESLint 有一个内置的 parser 叫作 espree
,若是想支持非标准的 JavaScript 语法,只须要提供另一个 parser 给 ESLint ,它须要将 TypeScript source code 解析为 ESLint 能够兼容的 AST 。 @typescript-eslint/parser
就是这样一个自定义的 ESLint parser 的实现。流程以下:
@typescript-eslint/parser
)。@typescript-eslint/parser
处理全部特定于 ESLint 的配置,而后调用 @typescript-eslint/typescript-estree
。这个包只用来将 TypeScript source code 转为一个适当的 AST 。@typescript-eslint/typescript-estree
经过调用 TypeScript Compiler 将源代码生成一个 TypeScript AST ,而后将这个 AST 转换为 ESLint 须要的格式。这种 AST 格式不只仅用于 ESLint,还有更普遍的用途。它有本身的规范,也就是 ESTree 。@typescript-eslint/typescript-estree
还被其余工具重用,例如 Prettier 的 TypeScript 使用。TypeScript 和 ESLint 有相似的目标,所以可能出现 TypeScript 解决的一些问题本来是依赖 ESLint 解决的,二者可能会不兼容,最佳的解决方式是禁用相关的 ESLint 规则,转而交由 TypeScript Compiler 。
因为 TypeScript 是 JavaScript 的超集,即便 AST 进行了转换,最终的 AST 可能还会包含一部分让 ESLint 没法理解的部分,因此有些 ESLint rules 可能没法正常工做。有几种解决方案:要么解决 ESLint rules 的兼容性,要么使用另外的规则,即 @typescript-eslint/eslint-plugin
。
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
plugins: [
'@typescript-eslint',
],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
],
};
复制代码
Babel 如今支持解析 TypeScript source code 可是不进行类型检查。这是使用 TypeScript Compiler 的一个替代方法。经过插件,它一样也能够支持许多其余 TypeScript Compiler 不支持的语法。
typescript-eslint
是由 TypeScript Compiler 提供支持的。babel-eslint
支持了一些 TypeScript 自己不支持的额外的语法,可是 typescript-eslint 利用类型信息能够支持建立 rules ,而这是 babel 作不到的,由于 babel 没有类型检查。由于它们是由不一样的底层工具驱动的独立项目,因此目前不打算将它们一块儿使用。
用来检查样式,帮助避免错误和强制代码风格。能够理解最新的 CSS 语法,从 HTML 、 markdown 及 CSS-in-JS 对象和模板中提取内联的样式,能够解析类 CSS 语法,如 SCSS 、 Sass 、 Less 和 SugarSS 。支持插件,支持自定义规则。能够自动修复大多数违反代码风格的问题。而且是彻底可配置的,经过在根目录添加配置文件 .stylelintrc.json
来按需配置。
commitlint 用来检查 commit message ,帮助团队遵照 commit 约定,同一代码提交风格。支持经过 npm 安装已有的配置,或经过配置文件定义配置。使用 husky 来添加 git hooks :
// package.json
{
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}
复制代码
commit-msg
钩子会在一个新的 commit 建立时执行,经过 -E|--env
传递 husky 的 HUSKY_GIT_PARAMS
到 commitlint
,并将其定向到相关的编辑文件。
目前主流的代码风格主要有 Airbnb JavaScript Style Guide 、 Google JavaScript Style Guide 以及 JavaScript Standard 。
standard --fix
便可自动格式化代码,还能够进行代码检查。对应的 ESLint 配置:eslint-config-standard目前最主流的是 Prettier ,它是一个独断的代码格式化工具,专一于 style issues 。经过解析代码生成 AST ,而后再按照本身的规则从新输出格式化后的代码。Prettier 执行的时机能够是在编辑器保存时、在 pre-commit hook 中或使用 CLI 工具在命令行中执行,以确保代码风格的一致。
因为历史缘由,Prettier 仍然有一小部分选项,但官方更加推崇更少的配置项,由于其初衷就是终止团队关于代码风格的争论。这些选项一部分是初期添加的,一部分是需求增大致使的,还有一部分则与兼容性有关。选项越多,越有可能在团队内部引起争论。
Prettier 仅仅对 style issues 起做用。Prettier fork 了 recast 项目,并在内部使用了 Philip Wadler 提出的算法,该算法考虑了最大行宽(line width),最大行宽影响了代码的布局和包装代码,因此决定了最终输出的代码格式,而这是其余现有的代码格式化工具所欠缺的。例如,即使 eslint 会给出超出设定的最大行宽的警告,也没法自动修复。例如以下代码:
foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne());
复制代码
最终想要的效果多是这样:
foo(
reallyLongArg(),
omgSoManyParameters(),
IShouldRefactorThis(),
isThereSeriouslyAnotherOne()
);
复制代码
Wadler 算法是一个简单的基于约束的代码布局系统。它“测量”代码,若是代码跨越了最大的行宽,就会中断它。Prettier 禁止全部自定义样式,方法是将源代码解析成 AST 后,使用本身的规则同时考虑最大行宽,并在必要时包装代码,从新输出格式化的代码。
linters 有两类 rules :
Prettier 承担的是 Formatting rules 的工做,是一个“全自动”的风格指南。 所以,可使用 Prettier 来进行代码风格的格式化,使用 linters 来检查 bugs!
因为 linters 一般会包含样式相关的规则,使用Prettier时,大多数样式规则都是没必要要的,并且更糟糕的是,它们可能与 Prettier 冲突!所以能够将Prettier用于代码格式问题,将linter用于代码质量问题。
对于 ESLint ,能够安装 eslint-config-prettier ,来关闭全部不须要的或者可能会跟 Prettier 冲突的 ESLint rules。 stylelint 同理可使用 stylelint-config-prettier 。
linter + code style + code formatter 的组合:ESLint + Airbnb + Prettier 。
具体作法:
yarn add eslint --dev
复制代码
yarn run eslint --init
复制代码
配置文件:
// .eslintrc.js
module.exports = {
"env": {
"browser": true,
"es2021": true
},
"extends": [
"plugin:react/recommended",
"airbnb",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"react",
"@typescript-eslint"
],
"rules": {
}
};
复制代码
yarn add --dev --exact prettier
# 若是须要配置文件(若是须要忽略文件,则建立一个 .prettierignore 文件)
echo {}> .prettierrc.json
复制代码
最好能在保存时就自动格式化代码,让开发者无需关注代码格式,强迫症必备。
以 WebStorm 为例:对于 2020.1 及以上的版本:Preferences | Languages & Frameworks | JavaScript | Prettier 而后启用选项 Run on save for files
。
如今保存时就能够自动格式化代码了。
须要安装 eslint-config-prettier ,这个包会关闭全部不须要的或可能和 Prettier 冲突的ESLint rules。
yarn add eslint-config-prettier --dev
复制代码
而后将其添加到 ESLint 配置文件中的 extends
配置项的最后,好让它可以覆盖其余配置。这里的 prettier
除了关闭一些核心的 ESLint rules,还会关闭如下插件的部分 rules:
因为这个配置只会关闭 rules,因此跟其余配置一块儿使用才有意义,注意要放到最后:
{
"extends": [
"plugin:react/recommended",
"airbnb",
"plugin:@typescript-eslint/recommended",
"prettier"
]
}
复制代码
注意,还可使用 eslint-plugin-prettier 插件把 Prettier 当作一个 linter rule 来运行,但这不是官方推荐的作法,主要有如下几个劣势:
保险起见,最好在代码提交前也格式化一下。也就是能够在 pre-commit hook 里运行 Prettier 。
执行如下命令,会依赖 package.json 的 devDependencies
中的代码检查工具来自动安装并配置 husky 和 lint-staged ,所以须要确保事先已经安装了 Prettier 和 ESlint 。
npx mrm lint-staged
复制代码
package.json
会自动添加相关配置(默认生成的配置针对 .js
文件的,能够按需改成 .ts
或 .tsx
等),这样在每次提交前,都会经过 lint-staged 和 husky 运行 ESLint 和 Prettier 。
{
"devDependencies": {
"husky": ">=4",
"lint-staged": ">=10",
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.js": "eslint --cache --fix",
"*.{js,css,md}": "prettier --write"
}
}
复制代码
关于 lint-staged 的做用:在代码提交以前 linting 才有意义,这样能够确保不会将糟糕的代码上传到仓库中,以及强制统一风格。可是对整个项目运行一个 lint 过程很慢,而 linting 结果多是可有可无的。因此,只须要 lint 即将要提交的文件。 lint-staged 包含一个脚本,该脚本会运行任何shell任务,将staged files 做为参数,经过一个特定的 glob pattern 进行过滤。
husky(哈士奇)主要做用是添加 git hook(git 在特定的重要动做发生时触发自定义脚本),用来阻止很差的 git commit
、git push
等等。