ESLint 是一个开源的 JavaScript 代码检查工具,由 Nicholas C. Zakas 于2013年6月建立。代码检查是一种静态的分析,经常使用于寻找有问题的模式或者代码,而且不依赖于具体的编码风格。对大多数编程语言来讲都会有代码检查,通常来讲编译程序会内置检查工具。html
JavaScript 是一个动态的弱类型语言,在开发中比较容易出错。由于没有编译程序,为了寻找 JavaScript 代码错误一般须要在执行过程当中不断调试。像 ESLint 这样的可让程序员在编码的过程当中发现问题而不是在执行的过程当中。vue
ESLint 的初衷是为了让程序员能够建立本身的检测规则。ESLint 的全部规则都被设计成可插拔的。为了便于人们使用,ESLint 内置了一些规则,固然,你能够在使用过程当中自定义规则。全部的规则默认都是禁用的。node
ESLint 使用 Node.js 编写。git
以使用项目为例,简单介绍一下eslint的具体配置及做用:程序员
module.exports = {
parser: 'babel-eslint', // parser指定解析器,默认的为espree。babel-eslint是一个Babel parser的包装器,这个包装器使得 Babel parser 能够和 ESLint 协调工做
parserOptions: {
sourceType: 'module', // 设置为 "script" (默认) 或 "module"(ES6)。
ecmaFeatures: { // 这是个对象,表示你想使用的额外的语言特性:
jsx: true // 启用 JSX
}
},
extends: ['eslint:recommended'], // 使用eslint推荐的规则做为基础配置,能够在rules中覆盖
plugins: ['html', 'vue', 'prettier', 'import'], // vue是eslint-plugin-vue的简写,此插件的做用是可让eslint识别.vue中的script代码
rules: { // 0或者off表示规则关闭,出错也被忽略;1或者warn表示若是出错会给出警告(不会致使程序退出);2或者error表示若是出错会报出错误(会致使程序退出,退出码是1)
'no-console': 'off',
'prefer-const': 'error',
'prettier/prettier': 'warn',
'prefer-arrow-callback': 'warn',
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
},
globals: { // 容许在代码中使用全局变量
location: true,
setTimeout: true
}
};
复制代码
具体的配置文档:eslint.cn/docs/user-g… 具体的eslint:recommended支持的规则:cn.eslint.org/docs/rules/es6
“extends”除了能够引入推荐规则,还能够以文件形式引入其它的自定义规则,而后在这些自定义规则的基础上用rules去定义个别规则,从而覆盖掉”extends”中引入的规则。github
{
"extends": [
"./node_modules/coding-standard/eslintDefaults.js",
// Override eslintDefaults.js
"./node_modules/coding-standard/.eslintrc-es6",
// Override .eslintrc-es6
"./node_modules/coding-standard/.eslintrc-jsx",
],
"rules": {
// Override any settings from the "parent" configuration
"eqeqeq": "warn"
}
}
复制代码
除了在配置文件中指定规则外,还能够在代码中指定规则,代码文件内以注释配置的规则会覆盖配置文件里的规则,即优先级要更高。平时咱们经常使用的就是 eslint-disable-next-line
express
能够经过在项目目录下创建.eslintignore文件,并在其中配置忽略掉对哪些文件的检查。须要注意的是,无论你有没有在.eslintignore中进行配置,eslint都会默认忽略掉对/node_modules/** 的检查。也能够在package.json文件的 eslintIgnore 字段进行配置。npm
要实现静态分析则须要自建一个预编译阶段对代码进行解析。编程
首先咱们看看大部分编译器工做时的三个阶段:
解析:将未经处理的代码解析成更为抽象的表达式,一般为抽象语法树,即 AST。 转换:经过修改解析后的代码表达式,将其转换为符合预期的新格式。 代码生成:将转换后的表达式生成为新的目标代码。
对于eslint来讲,规则校验发生在将JavaScript 代码解析为 AST 以后,遍历 AST 的过程当中。eslint采用 Espree 来生成AST。具体的生成方法在这里。 咱们能够使用AST explorer来查看代码被解析后生成的AST。
首先来看看eslint源码中关于rules的编写。eslint中的rules源码存在于lib/rules下。每个rules都是一个node模块,用module.exports导出一个meta对象及一个create函数。
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "disallow unnecessary semicolons",
category: "Possible Errors",
recommended: true,
url: "https://eslint.org/docs/rules/no-extra-semi"
},
fixable: "code",
schema: [] // no options
},
create: function(context) {
return {
// callback functions
};
}
};
复制代码
meta 表明了这条规则的元数据,如这条规则的类别,文档,可接收的参数 schema 等等。
create 返回一个对象,其中定义了一些在 AST 遍历访问到对应节点须要执行的方法等等。函数接受一个context对象做为参数,里面包含了例如能够报告错误或者警告的context.report()、能够获取源代码的context.getSourceCode()等方法,能够简化规则的编写。
function checkLastSegment (node) {
// report problem for function if last code path segment is reachable
}
module.exports = {
meta: { ... },
create: function(context) {
// declare the state of the rule
return {
ReturnStatement: function(node) {
// 在AST从上向下遍历到ReturnStatement node 时执行
},
// 在AST 从下向上遍历到 function expression node 时执行:
"FunctionExpression:exit": checkLastSegment,
"ArrowFunctionExpression:exit": checkLastSegment,
onCodePathStart: function (codePath, node) {
// 在分析代码路径开始时执行
},
onCodePathEnd: function(codePath, node) {
// 在分析代码路径结束时执行
}
};
}
};
复制代码
遍历 AST 的过程当中会以“从上至下”再“从下至上”的顺序通过节点两次,selector 默认会在下行的过程当中执行对应的访问函数,若是须要再上行的过程当中执行,则须要添加:exit。
详细的原理在官方文档中有说明,点这里。 详细的代码路径分析在这里。
知道了rules的原理,接下来能够自定义一个rules。每个rules须要有三个以该规则名命名的文件,分别是:
在 lib/rules 目录下: 一个源文件(例如,no-extra-semi.js)
在 tests/lib/rules 目录下: 一个测试文件 (例如, no-extra-semi.js)
在 docs/rules 目录: 一个 markdown 文档文件 (例如, no-extra-semi)
接下来咱们来编写一个简单的rules,例如禁止块级注释,当代码中使用了块级注释,eslint将报错。
rules文件:
// lib/rules/no-block-comments.js
module.exports = {
meta: {
docs: {
description: '禁止块级注释',
category: 'Stylistic Issues',
recommended: true
}
},
create (context) {
// 获取源代码
const sourceCode = context.getSourceCode()
return {
Program () {
// 获取源代码中全部的注释
const comments = sourceCode.getAllComments()
const blockComments = comments.filter(({ type }) => type === 'Block')
blockComments.length && context.report({
node: node,
message: 'No block comments'
})
}
}
}
}
复制代码
rules的测试文件:
// tests/lib/rules/no-block-comments.js
const RuleTester = require("eslint").RuleTester;
const rule = require("../../../lib/rules/no-block-comments");
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2018 } }); // You do have to tell eslint what js you're using ruleTester.run("no-block-comments", rule, { valid: ["var a = 1; console.log(a)"], invalid: [ { code: "var a = 1; /* block comments */ console.log(a)", errors: [ { messageId: "blockComments", line: 1, nodeType: "Block" } ] } ] }); 复制代码
官网的working with rules文档中有关于如何编写一个rules的详细介绍。
编写好的rules须要发布到npm上,做为一个eslint-plugin,在项目中下载下来才可以使用。例子中代码的npm在这里。
在项目中的配置:
// .eslintrc.js
module.exports = {
...
"plugins": [
"eslint-plugin-no-block-comments"
// 你 publish 的 npm 包名称,能够省略 eslint-plugin
],
"rules": { // 启用的规则及其各自的错误级别
'no-console': 'off',
"no-block-comments/no-block-comments": 2 // 引用no-block-comments插件中的no-block-comments规则
}
};
复制代码
以后就能够对代码进行检查了。好比我要检查的代码以下:
// src/index.js
const a = 1;
/*
这里是块级注释
*/
console.log(a);
复制代码
在命令行中执行eslint src
,就能够看到报错结果。 evernotecid://43E55629-1A51-495A-878E-7C9D5C55CC19/appyinxiangcom/15304103/ENResource/p297