ESLint原理理解

为啥要用ESLint?

团队成员代码风格不同,水平各异。如果允许成员任意发挥,随意coding,不做任何约束的话,随着项目规模慢慢变大,项目代码将很有可能成为难以维护的屎山。所有对于代码的基本写法需要有约束,当代码编写不合约束时能给出提醒,同时能自动修复,这些正是ESLint 要做的事情。

  • 找出代码中不符合规范的地方,报出异常或错误,给出提示
  • 能自动修复 不合规的代码
  • 自定义规则

ESLint 如何工作?

ESLint 是如何读懂代码,甚至自动修复不合规代码呢?关键是AST(抽象语法树),其中 ESLint 是使用espree来生成 AST 的。

ESLint 会遍历 AST,然后在遍历到不同的节点特定的时机的时候,触发相应的处理函数,在处理函数中抛出错误、提示。

ESLint 流程大致分为 读取配置、加载配置、检验、修复

读取配置

ESLint 首先会从各种配置文件中读取配置,例如 eslintrcpackage.json中的eslintConfig字段中,或使用命令行执行 eslint 时指定任意一个配置文件

  • 先读取给定目录下最近的配置文件

  • 相同目录存在多个配置文件,仅有一个配置文件会被读取,.eslintrc 优先级的配置文件会高于package.json配置

  • 内层目录没有,会默认向外层文件夹逐层读取配置文件, 可通过在配置文件添加root:true来阻止逐层读取

  • 如果多个配置文件里都配置了重复字段的话,离给定目录最近的配置会生效

加载配置

ESLint会依次加载配置里的extends,parser,plugin等,其中

  • extends 是其他配置文件,可以复用插件中的配置或者第三方模块中的配置

ESLint 会递归地去读取配置文件中的extends,最终各个配置对象的顺序是[{内层配置},{内层配置的extends},{外层配置},{外层配置中的extends}],之后会进行合并操作,具体合并逻辑

  • 对于 parser、processor字段,后面的配置会覆盖前面的配置

  • 对应 env、globals、parserOptions,settings 字段会合并在一起,但只有当后面的配置存在,而前面的没有该字段时,这个字段才会被合并进来,如果前面有,那么后面的相同自读会被摒弃

  • 对应 rules,前面的配置优先级高,如果存在的rule里有参数,参数会被合并

  • parser 用于解析AST

  • plugin 是用户自定义的插件,可映入自定义规则,以及对非js文件的检查和处理等

检查

当获得所有需要的配置后,接下来就会进入检验流程,大致执行顺序

  • 处理 processor

    • processor 是在插件上定义的处理器,并针对特定后缀文件定义preprocess 和 postprocess 两个方法。

    • preprocess方法接受文件源码和文件名作为参数,返回需要被检测的代码或文件的数组。

    • postprocess 在文件被检验完后,对所有的lint problem 进行统一处理

  • 解析代码, 获取AST 和 节点 数组,没有指定parser时,默认使用 espress

  • 跑规则 runRules

    ESLint 的核心就是处理一条一条的规则,如何处理?

    • 收集AST所有的节点
    • 遍历所有配置中的rule,并通过rule的名称找到对应的rule对象。ESLint 会为rule对象里的AST节点添加相应的监听函数,以便在后面遍历AST节点时可以出发相应的处理函数
    • 再次遍历收集的AST节点,出发相应的节点监听函数,在监听函数中调用方法收集所有的eslint 问题
修复

ESLint 的 rule 对AST进行检查,并报错。fixer 则根据 AST节点中保留的 range 信息(源码的下标范围)来修改代码