使用Eslint进行代码的静态代码检查
前言
在团队协作中,为避免低级 Bug、产出风格统一的代码,会预先制定编码规范。使用 Lint 工具和代码风格检测工具,则可以辅助编码规范执行,有效控制代码质量。EsLint帮助我们检查Javascript编程时的语法错误。
Eslint简介
EsLint是建立在Esprima(ECMAScript解析架构)的基础上的。Esprima支持ES5.1,本身也是用ECMAScript编写的,用于多用途分析。EsLint不但提供一些默认的规则(可扩展),也提供用户自定义规则来约束我们写的Javascript代码。
ESLint 是在 ECMAScript/JavaScript
代码中识别和报告模式匹配的工具,它的目标是保证代码的一致性和避免错误。在许多方面,它和 JSLint
、JSHint
相似,除了少数的例外:
- ESLint 使用 Espree 解析 JavaScript。
- ESLint 使用 AST(Abstract Syntax Tree抽象语法树) 去分析代码中的模式
- ESLint 是完全插件化的。每一个规则都是一个插件并且你可以在运行时添加更多的规则。
安装
先决条件:Node.js (>=4.x
), npm version 2+。
-
本地安装
如果你想让 ESLint 成为你项目构建系统的一部分,我们建议在本地安装。你可以使用 npm:
npm install eslint --save-dev
./node_modules/.bin/eslint --init //初始化配置文件
然后会出现一个选择,使用箭头键选择即可:
? How would you like to configure ESLint? (Use arrow keys)
> Answer questions about your style
Use a popular style guide
Inspect your JavaScript file(s)
成功后会在你的项目根目录出现一个配置文件.eslintrc.js
或者.eslintrc.json
等文件(取决于你的选择)。
./node_modules/.bin/eslint yourfile.js // 代码检查
使用本地安装的 ESLint 时,你使用的任何插件或可分享的配置也都必须在本地安装。
-
全局安装
npm install -g eslint
eslint --init
eslint yourfile.js
使用全局安装的 ESLint 时,你使用的任何插件或可分享的配置也都必须在全局安装。
配置
Eslint的配置有4种方式可以进行设置:
- 使用 JavaScript 注释把配置信息直接嵌入到一个代码源文件中
- 在 package.json 文件里的
eslintConfig
字段指定配置 - 配置一个独立的
.eslintrc.*
文件,可以是 JavaScript、JSON 或者 YAML 文件 - 在命令行运行时使用
-c
指定一个任意的配置文件
如果同一个目录下有多个配置文件,ESLint 只会使用一个。优先级顺序如下:
.eslintrc.js
.eslintrc.yaml
.eslintrc.yml
.eslintrc.json
.eslintrc
package.json
如下是一个Javascript格式的配置文件简单例子.eslintrc.js
:
module.exports = {
"env": {
"browser": true,
"node": true
},
"plugins": ["example"],
"parser": "esprima",
"globals": {
"var1": true,
"var2": false
},
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"extends": "eslint:recommended",
"rules": {
// enable additional rules
"indent": ["error", 4]
},
"settings": {
"sharedData": "Hello"
},
"overrides": [
{
"files": [ "bin/*.js", "lib/*.js" ],
"excludedFiles": "*.test.js",
"rules": {
"quotes": [ 2, "single" ]
}
}
]
}
解析器配置
ESLint 默认使用Espree作为其解析器,你可以在配置文件中使用"parser": "esprima"
指定一个不同的解析器,以下解析器与 ESLint 兼容:
Esprima
Babel-ESLint
- 一个对Babel解析器的包装,使其能够与 ESLint 兼容。typescript-eslint-parser
(实验) - 一个把 TypeScript 转换为ESTree
兼容格式的解析器
你也可以使用自定义的解析器,不过要注意,在使用自定义解析器时,为了让 ESLint 在处理非 ECMAScript 5 特性时正常工作,配置属性 parserOptions
仍然是必须的。解析器会被传入 parserOptions
,但是不一定会使用它们来决定功能特性的开关。
解析器选项 parserOptions
可用的选项有:
ecmaVersion
- 默认设置为3,5(默认), 你可以使用 6、7、8 或 9 来指定你想要使用的 ECMAScript 版本。你也可以用使用年份命名的版本号指定为 2015(同 6),2016(同 7),或 2017(同 8)或 2018(同 9)sourceType
- 设置为 "script" (默认) 或 "module"(如果你的代码是 ECMAScript 模块)。ecmaFeatures
- 这是个对象,表示你想使用的额外的语言特性:globalReturn
- 允许在全局作用域下使用 return 语句impliedStrict
- 启用全局 strict mode (如果 ecmaVersion 是 5 或更高)jsx
- 启用 JSXexperimentalObjectRestSpread
- 启用实验性的object rest/spread properties
支持。
请注意,对 JSX 语法的支持不用于对 React 的支持。React 使用了一些特定的 ESLint 无法识别的 JSX 语法。如果你正在使用 React 并且想要 React 语义支持,我们推荐你使用 eslint-plugin-react。
同样的,支持 ES6 语法并不意味着同时支持新的 ES6 全局变量或类型(比如 Set 等新类型)。使用 { "parserOptions": { "ecmaVersion": 6 } }
来启用 ES6 语法支持;要额外支持新的 ES6 全局变量,使用 { "env":{ "es6": true } }
(这个设置会同时自动启用 ES6 语法支持)。
环境设置
一个环境定义了一组预定义的全局变量。可用的环境包括:
- browser - 浏览器环境中的全局变量。
- node - Node.js 全局变量和 Node.js 作用域。
- commonjs - CommonJS 全局变量和 CommonJS 作用域 (用于 Browserify/WebPack 打包的只在浏览器中运行的代码)。
- es6 - 启用除了 modules 以外的所有 ECMAScript 6 特性(该选项会自动设置 ecmaVersion 解析器选项为 6)。
- worker - Web Workers 全局变量。
- amd - 将
require()
和define()
定义为像 amd 一样的全局变量。 - jquery - jQuery 全局变量。
- ...
要在配置文件里指定环境,使用 env 关键字指定你想启用的环境,并设置它们为 true。例如,以下示例启用了 browser 和 Node.js 的环境:
{
"env": {
"browser": true,
"node": true
}
}
如果你想在一个特定的插件中使用一种环境,确保提前在 plugins 数组里指定了插件名,然后在 env 配置中不带前缀的插件名后跟一个 / ,紧随着环境名。例如:
{
"plugins": ["example"],
"env": {
"example/custom": true
}
}
全局变量
当访问当前源文件内未定义的变量时,no-undef
规则将发出警告。如果你想在一个源文件里使用全局变量,推荐你在 ESLint 中定义这些全局变量,这样 ESLint 就不会发出警告了。
在配置文件里配置全局变量时,使用 globals 指出你要使用的全局变量。将变量设置为 true 将允许变量被重写,或 false 将不允许被重写。比如:
{
"globals": {
"var1": true,
"var2": false
}
}
插件
ESLint 支持使用第三方插件。在使用插件之前,你必须使用 npm 安装它。
在配置文件里配置插件时,可以使用 plugins 关键字来存放插件名字的列表。插件名称可以省略 eslint-plugin-
前缀。
{
"plugins": [
"plugin1",
"eslint-plugin-plugin2"
]
}
还可以使用extends
使用插件提供的规则:
extends
属性值可以由以下组成:
plugin
:- 包名 (省略了前缀,比如,react)
/
- 配置名称 (比如 recommended)
JSON 格式的一个配置文件的例子:
{
"plugins": [
"react"
],
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
"rules": {
"no-set-state": "off"
}
}
规则配置
ESLint 附带有大量的规则。你可以使用注释或配置文件修改你项目中要使用的规则。要改变一个规则设置,你必须将规则 ID 设置为下列值之一:
- "off" 或 0 - 关闭规则
- "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
- "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
例如:
{
"rules": {
"eqeqeq": "off",
"curly": "error",
"quotes": ["error", "double"]
}
}
使用注释临时禁止规则
可以在你的文件中使用以下格式的块注释来临时禁止规则出现警告:
/* eslint-disable */
alert('foo');
/* eslint-enable */
你也可以对指定的规则启用或禁用警告:
/* eslint-disable no-alert, no-console */
alert('foo');
console.log('bar');
/* eslint-enable no-alert, no-console */
如果在整个文件范围内禁止规则出现警告,将 /* eslint-disable */ 块注释放在文件顶部:
/* eslint-disable */
alert('foo');
可以在你的文件中使用以下格式的行注释或块注释在某一特定的行上禁用所有规则:
alert('foo'); // eslint-disable-line
// eslint-disable-next-line
alert('foo');
/* eslint-disable-next-line */
alert('foo');
alert('foo'); /* eslint-disable-line */
在某一特定的行上禁用某个指定的规则或多个规则:
alert('foo'); // eslint-disable-line no-alert, quotes, semi
// eslint-disable-next-line no-alert, quotes, semi
alert('foo');
alert('foo'); /* eslint-disable-line no-alert */
/* eslint-disable-next-line no-alert */
alert('foo');
上面的所有方法同样适用于插件规则。例如,禁止 eslint-plugin-example
的 rule-name
规则,把插件名(example)和规则名(rule-name)结合为 example/rule-name
:
foo(); // eslint-disable-line example/rule-name
foo(); /* eslint-disable-line example/rule-name */
共享设置
ESLint 支持在配置文件添加共享设置。你可以添加 settings 对象到配置文件,它将提供给每一个将被执行的规则。如果你想添加的自定义规则而且使它们可以访问到相同的信息,这将会很有用,并且很容易配置。
{
"settings": {
"sharedData": "Hello"
}
}
层叠配置
例如,假如你有以下结构:
your-project
├── .eslintrc
├── lib
│ └── source.js
└─┬ tests
├── .eslintrc
└── test.js
层叠配置使用离要检测的文件最近的 .eslintrc
文件作为最高优先级,然后才是父目录里的配置文件,等等。
当你在这个项目中允许 ESLint 时,lib/
下面的所有文件将使用项目根目录里的 .eslintrc
文件作为它的配置文件。当 ESLint 遍历到 test/ 目录,your-project/.eslintrc 之外,它还会用到 your-project/tests/.eslintrc
。所以 your-project/tests/test.js 是基于它的目录层次结构中的两个.eslintrc
文件的组合,并且离的最近的一个优先。通过这种方式,你可以有项目级 ESLint 设置,也有覆盖特定目录的 ESLint 设置。
默认情况下,ESLint 会在所有父级目录里寻找配置文件,一直到根目录。如果你想要你所有项目都遵循一个特定的约定时,这将会很有用,但有时候会导致意想不到的结果。为了将 ESLint 限制到一个特定的项目,在你项目根目录下的 package.json 文件或者 .eslintrc.* 文件里的 eslintConfig 字段下设置 "root": true。ESLint 一旦发现配置文件中有 "root": true,它就会停止在父级目录中寻找。
{
"root": true
}
完整的配置层次结构,从最高优先级最低的优先级,如下:
- 行内配置
/*eslint-disable*/
和/*eslint-enable*/
/*global*/
/*eslint*/
/*eslint-env*/
- 命令行选项(或 CLIEngine 等价物):
--global
--rule
--env
-c
、--config
- 项目级配置:
- 与要检测的文件在同一目录下的
.eslintrc.*
或package.json
文件 - 继续在父级目录寻找
.eslintrc
或package.json
文件,直到根目录(包括根目录)或直到发现一个有"root": true
的配置。
- 如果不是(1)到(3)中的任何一种情况,退回到
~/.eslintrc
中自定义的默认配置。
规则继承
一个配置文件可以被基础配置中的已启用的规则继承。
extends 属性值可以是:
- 在配置中指定的一个字符串
- 字符串数组:每个配置继承它前面的配置
也可以使用extends
继承插件的配置或者一个或多个配置文件:
{
"plugins": [
"react"
],
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
"rules": {
"no-set-state": "off"
}
}
{
"extends": [
"./node_modules/coding-standard/eslintDefaults.js",
"./node_modules/coding-standard/.eslintrc-es6",
"./node_modules/coding-standard/.eslintrc-jsx"
],
"rules": {
"eqeqeq": "warn"
}
}
eslint:recommended
值为 "eslint:recommended" 的 extends 属性启用一系列核心规则,这些规则报告一些常见问题,在 规则页面 中被标记为√
。这个推荐的子集只能在 ESLint 主要版本进行更新。
如果你的配置集成了推荐的规则:在你升级到 ESLint 新的主版本之后,在你使用命令行的 --fix
选项之前,检查一下报告的问题,这样你就知道一个新的可修复的推荐的规则将更改代码。
module.exports = {
"extends": "eslint:recommended",
"rules": {
// enable additional rules
"indent": ["error", 4],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double"],
"semi": ["error", "always"],
// override default options for rules from base configurations
"comma-dangle": ["error", "always"],
"no-cond-assign": ["error", "always"],
// disable rules from base configurations
"no-console": "off",
}
}
使用overrides
更精细的配置
有时,你可能需要更精细的配置,比如,如果同一个目录下的文件需要有不同的配置。因此,你可以在配置中使用 overrides 键,它只适用于匹配特定的 glob 模式的文件,使用你在命令行上传递的格式 (e.g., app/**/*.test.js)。
- Glob 模式覆盖只能在配置文件 (.eslintrc.* 或 package.json) 中进行配置。
- 模式应用于相对于配置文件的目录的文件路径。 比如,如果你的配置文件的路径为
/Users/john/workspace/any-project/.eslintrc.js
而你要检测的路径为/Users/john/workspace/any-project/lib/util.js
,那么你在.eslintrc.js
中提供的模式是相对于lib/util.js
来执行的. - 在相同的配置文件中,Glob 模式覆盖比其他常规配置具有更高的优先级。 同一个配置中的多个覆盖将按顺序被应用。也就是说,配置文件中的最后一个覆盖会有最高优先级。
- 一个 glob 特定的配置几乎与 ESLint 的其他配置相同。覆盖块可以包含常规配置中的除了 extends、overrides 和 root 之外的其他任何有效配置选项,
- 可以在单个覆盖块中提供多个 glob 模式。一个文件必须匹配至少一个配置中提供的模式。
- 覆盖块也可以指定从匹配中排除的模式。如果一个文件匹配了任何一个排除模式,该配置将不再被应用。
{
"rules": {
"quotes": [ 2, "double" ]
},
"overrides": [
{
"files": [ "bin/*.js", "lib/*.js" ],
"excludedFiles": "*.test.js",
"rules": {
"quotes": [ 2, "single" ]
}
}
]
}
忽略不要检查的文件
你可以通过在项目根目录创建一个 .eslintignore 文件告诉 ESLint 去忽略特定的文件和目录。.eslintignore
文件是一个纯文本文件,其中的每一行都是一个 glob 模式表明哪些路径应该忽略检测。例如,以下将忽略所有的 JavaScript 文件:
**/*.js
当 ESLint 运行时,在确定哪些文件要检测之前,它会在当前工作目录中查找一个 .eslintignore 文件。如果发现了这个文件,当遍历目录时,将会应用这些偏好设置。一次只有一个 .eslintignore 文件会被使用,所以,不是当前工作目录下的 .eslintignore 文件将不会被用到。
Globs 匹配使用 node-ignore,所以大量可用的特性有:
- 以
#
开头的行被当作注释,不影响忽略模式。 - 路径是相对于
.eslintignore
的位置或当前工作目录。这也会影响通过--ignore-pattern
传递的路径。 - 忽略模式同
.gitignore
规范 - 以
!
开头的行是否定模式,它将会重新包含一个之前被忽略的模式。
除了 .eslintignore
文件中的模式,ESLint总是忽略 /node_modules/*
和 /bower_components/*
中的文件。
例如:把下面 .eslintignore 文件放到当前工作目录里,将忽略项目根目录下的 node_modules
,bower_components
以及 build/
目录下除了 build/index.js
的所有文件。
# /node_modules/* and /bower_components/* in the project root are ignored by default
# Ignore built files except build/index.js
build/*
!build/index.js
如果相比于当前工作目录下 .eslintignore
文件,你更想使用一个不同的文件,你可以在命令行使用 --ignore-path 选项指定它。例如,你可以使用 .jshintignore
文件,因为它有相同的格式:
eslint --ignore-path .jshintignore file.js
任何文件只要满足标准忽略文件格式都可以用。记住,指定 --ignore-path 意味着任何现有的 .eslintignore 文件将不被使用。请注意,.eslintignore
中的匹配规则比 .gitignore 中的更严格。
如果没有发现 .eslintignore
文件,也没有指定替代文件,ESLint 将在 package.json
文件中查找 eslintIgnore
键,来检查要忽略的文件。
{
"name": "mypackage",
"version": "0.0.1",
"eslintConfig": {
"env": {
"browser": true,
"node": true
}
},
"eslintIgnore": ["hello.js", "world.js"]
}
常见规则
规则 | 含义 |
---|---|
no-compare-neg-zero |
禁止与 -0 进行比较 |
no-cond-assign |
禁止条件表达式中出现赋值操作符 |
no-console |
禁用 console |
no-constant-condition |
禁止在条件中使用常量表达式 |
no-control-regex |
禁止在正则表达式中使用控制字符 |
no-debugger |
禁用 debugger |
no-dupe-args |
禁止 function 定义中出现重名参数 |
no-dupe-keys |
禁止对象字面量中出现重复的 key |
no-duplicate-case |
禁止出现重复的 case 标签 |
no-empty |
禁止出现空语句块 |
no-empty-character-class |
禁止在正则表达式中使用空字符集 |
no-ex-assign |
禁止对 catch 子句的参数重新赋值 |
no-extra-boolean-cast |
禁止不必要的布尔转换 |
no-extra-semi |
禁止不必要的分号 |
no-func-assign |
禁止对 function 声明重新赋值 |
no-inner-declarations |
禁止在嵌套的块中出现变量声明或 function 声明 |
no-invalid-regexp |
禁止 RegExp 构造函数中存在无效的正则表达式字符串 |
no-irregular-whitespace |
禁止在字符串和注释之外不规则的空白 |
no-obj-calls |
禁止把全局对象作为函数调用 |
no-regex-spaces |
禁止正则表达式字面量中出现多个空格 |
no-sparse-arrays |
禁用稀疏数组 |
no-unexpected-multiline |
禁止出现令人困惑的多行表达式 |
no-unreachable |
禁止在return、throw、continue 和 break 语句之后出现不可达代码 |
no-unsafe-finally |
禁止在 finally 语句块中出现控制流语句 |
no-unsafe-negation |
禁止对关系运算符的左操作数使用否定操作符 |
use-isnan |
要求使用 isNaN() 检查 NaN |
valid-typeof |
强制 typeof 表达式与有效的字符串进行比较 |
no-case-declarations |
不允许在 case 子句中使用词法声明 |
no-empty-pattern |
禁止使用空解构模式 |
no-fallthrough |
禁止 case 语句落空 |
no-global-assign |
禁止对原生对象或只读的全局对象进行赋值 |
no-octal |
禁用八进制字面量 |
no-redeclare |
禁止多次声明同一变量 |
no-self-assign |
禁止自我赋值 |
no-unused-labels |
禁用出现未使用过的标 |
no-useless-escape |
禁用不必要的转义字符 |
no-delete-var |
禁止删除变量 |
no-undef |
禁用未声明的变量,除非它们在 /*global */ 注释中被提到 |
no-unused-vars |
禁止出现未使用过的变量 |
no-mixed-spaces-and-tabs |
禁止空格和 tab 的混合缩进 |
constructor-super |
要求在构造函数中有 super() 的调用 |
no-class-assign |
禁止修改类声明的变量 |
no-const-assign |
禁止修改 const 声明的变量 |
no-dupe-class-members |
禁止类成员中出现重复的名称 |
no-new-symbol |
禁止 Symbolnew 操作符和 new 一起使用 |
no-this-before-super |
禁止在构造函数中,在调用 super() 之前使用 this 或 super |
require-yield |
要求 generator 函数内有 yield |
命令行
安装:
npm i -g eslint
这句命令从 npm 仓库安装了 ESLint CLI。使用以下格式运行 ESLint:
eslint [options] [file|dir|glob]*
命令行工具有几个选项,你可以通过运行 eslint -h 查看所有选项。
Basic configuration:
--no-eslintrc 禁用 .eslintrc.* 和 package.json 文件中的配置
-c, --config path::String 该选项允许你为 ESLint 指定一个额外的配置文件。
--env [String] 指定环境
--ext [String] 查找 JavaScript 文件时要使用的文件扩展名(只有在参数为目录时,才生效。如果你使用 glob 模式或文件名,--ext 将被忽略)
--global [String] 定义了全局变量,这样它们就不会被 no-undef 规则标记为未定义了
--parser String 指定一个解析器
--parser-options Object 解析器选项
Specifying rules and plugins:
--rulesdir [path::String] 指定另一个加载规则文件的目录
--plugin [String] 插件
--rule Object 使用的规则
Fixing problems:
--fix 指示 ESLint 试图修复尽可能多的问题。修复只针对实际文件本身,而且剩下的未修复的问题才会输出
--fix-dry-run 该选项与 --fix 有相同的效果,唯一一点不同是,修复不会保存到文件系统中。这也是从 stdin(当使用 --stdin 标记时)修复代码成为可能。
Ignoring files:
--ignore-path path::String 指定一个文件作为 .eslintignore
--no-ignore 禁止排除 .eslintignore、--ignore-path 和 --ignore-pattern 文件中指定的文件。
--ignore-pattern [String] 要忽略的文件模式(除了那些在 .eslintignore 的)
Using stdin:
--stdin 告诉 ESLint 从 STDIN 而不是从文件中读取和检测源码
--stdin-filename String 这个选项允许你指定一个文件名去处理 STDIN
Handling warnings:
--quiet 禁止报告警告
--max-warnings Int 指定一个警告的阈值,当你的项目中有太多违反规则的警告时,这个阈值被用来强制 ESLint 以错误状态退出
Output:
-o, --output-file path::String 将报告写到一个文件
-f, --format String 指定了控制台的输出格式
--color, --no-color 强制启用/禁用彩色输出
Inline configuration comments:
--no-inline-config 这个选项会阻止像 /*eslint-disable*/ 或者 /*global foo*/ 这样的内联注释起作用
--report-unused-disable-directives 当像 // eslint-disable-line 这样的指令注释所在行没有任何错误报告时,该选项会导致 ESLint 报告这样的指令注释。通过清理旧的不再适用 eslint-disable 注释,对防止将来意外的被抑制的错误很有用
Caching:
--cache 存储处理过的文件的信息以便只对有改变的文件进行操作。缓存默认被存储在 .eslintcache。启用这个选项可以显著改善 ESLint 的运行时间,确保只对有改变的文件进行检测。
--cache-file path::String 缓存文件的路径
--cache-location path::String 缓存文件的路径
Miscellaneous:
--init 配置初始化向导
--debug 这个选项将调试信息输出到控制台
-h, --help 这个选项会输出帮助菜单,显示所有可用的选项
-v, --version 这个选项在控制台输出当前 ESlint 的版本
--print-config path::String 这个选项输出传递的文件使用的配置