ToC
ESLint 的规则
ESLint
的规则配置方法主要分两种,一种是直接设置规则的 error
warn
off
, 一种就是以数组的形式来设置规则的开启条件,以及需要附加的配置项,比如:
rules
中的每一个 key
即表示一条风格规则,我们可以思考一下如何去实现这些规则约束。
ESLint 的核心规则
先看一下简单的规则: no-with
:
规则由两部分组成: meta
create
;
meta
meta
对象是描述规则的元数据, 包括了规则的 类型
文档
是否可修复
等信息.
create
create
函数返回一个对象包含了 ESLint 在遍历 JavaScript 代码的抽象语法树(AST)
(ESTree 定义的 AST) 时, 用来访问节点的方法, 入参为该节点.
- 如果一个 key 是个节点类型或 selector,在 向下 遍历树时,ESLint 调用 visitor 函数
- 如果一个 key 是个节点类型或 selector,并带有
:exit
,在 向上 遍历树时,ESLint 调用 visitor 函数 - 如果一个 key 是个事件名字,ESLint 为 代码路径分析 调用 handler 函数
ESLint 命令的执行
在 package.json 里配置 bin
ESLint 执行的调用栈
eslint
的主要代码执行逻辑流程如下:
- 解析命令行参数,校验参数正确与否及打印相关信息;
- 根据配置实例一个 engine 对象 CLIEngine 实例;
engine.executeOnFiles
读取源代码进行检查,返回报错信息和修复结果。
可以看到 eslint 实际上是在执行 engine.lintFiles(files)
方法:
engine.lintFiles(files)
方法内部则是在执行 executeOnFiles(patterns)
方法来进行文件内容的校验.
executeOnFiles(patterns) 函数
executeOnFiles(patterns)
函数主要作用是对一组文件和目录名称执行当前配置. 看一下它做了什么:
verifyText() 函数
verifyText()
则是调用了 linter.verifyAndFix()
函数.
verifyAndFix() 函数主要作用是对一组文件和目录名称执行当前配置
这个函数是核心函数,顾名思义 verify & fix 代码核心处理逻辑是通过一个 do while 循环控制;以下两个条件会打断循环
- 没有更多可以被 fix 的代码了
- 循环超过十次
- 其中 verify 函数对源代码文件进行代码检查,从规则维度返回检查结果数组
- applyFixes 函数拿到上一步的返回,去 fix 代码
- 如果设置了可以 fix,那么使用 fix 之后的结果 代替原本的 text
在 verify 过程中,会调用 parse 函数,把代码转换成 AST
parse 函数会返回两种结果
{success: false, error: Problem}
解析 AST 成功{success: true, sourceCode: SourceCode}
解析 AST 失败
最终会调用 runRules() 函数
这个函数是代码检查和修复的核心方法,会对代码进行规则校验。
- 创建一个 eventEmitter 实例。是 eslint 自己实现的很简单的一个事件触发类 on 监听 emit 触发;
- 递归遍历 AST,深度优先搜索,把节点添加到 nodeQueue。一个 node 放入两次,类似于 A->B->C->…->C->B->A;
- 遍历 rules,调用 rule.create()(rules 中提到的 meta 和 create 函数) 拿到事件(selector)映射表,添加事件监听。
- 包装一个 ruleContext 对象,会通过参数,传给 rule.create(),其中包含 report() 函数,每个 rule 的 handler 都会执行这个函数,抛出问题;
- 调用 rule.create(ruleContext), 遍历其返回的对象,添加事件监听;(如果需要 lint 计时,则调用 process.hrtime()计时);
- 遍历 nodeQueue,触发当前节点事件的回调,调用 NodeEventGenerator 实例里面的函数,触发 emitter.emit()。
执行节点匹配 NodeEventGenerator
在该类里面,会根据前面 nodeQueque 分别调用 进入节点和离开节点,来区分不同的调用时机。
总体运行机制
概括来说就是,ESLint 会遍历前面说到的 AST,然后在遍历到「不同的节点」或者「特定的时机」的时候,触发相应的处理函数,然后在函数中,可以抛出错误,给出提示。