从eslint自定义插件的auto fix看ast

avatar
前端

上文已经有介绍了如何根据业务需要自定义自己的规则,如果忘记了可以点击文末的链接。这一篇讲下如何实现auto fix,也就是自动修复的功能。

自定义的eslint规则,如果没有自动修复的功能,它就是不完美的规则。如何去设计auto fix需要根据各自的业务类型去分析,但是最终的原则是不变的,就是ast(抽象语法树),无论什么规则,最终都是要在这里去分析源码,再利用eslint提供的fix函数,进行修复。

定义auto fix

官网描述

如果你向 ESLint 库提交了一个 核心规则,你 必须遵循下面的约定。 具体的可以看连接:eslint.bootcss.com/docs/develo…

"use strict"

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
        };
    }
};

分析auto fix

如果需要插件自动auto fix,就需要了解几个概念,什么是context, 其上面有什么属性方法等。 fix和fixable的关联。

context

context上面暴露了很多方法,具体可以看下官网链接。

在上一个分享中使用context.report(descriptor)来报告问题的代码, 也就是eslint规则的检查部分,其参数descriptor如下

context.report({
    node: node,
    message: "Unexpected identifier"  // 代码不符合改规则时的提示
});

fix方法和元数据属性fixable

在插件中如果要支持auto fix的话,必须有这两个要素:第一:meta中的fixable属性,第二: context.report()传入对象的fix函数

fixable(元数据属性)

针对这个参数,在官网中提到,必须按照下面的规范去定义值。

fixable: "code",   // 这个参数后续会分析到

从eslint源码分析fixable的参数: 源码位置: linter.js runRules函数

    //在这段代码就可以看出fixable是干嘛用的,一个开关,true(支持修复) / false(不支持修复)
    if (problem.fix && rule.meta && !rule.meta.fixable) {
        throw new Error("Fixable rules should export a `meta.fixable` property.");
    }
    
    lintingProblems.push(problem);

其实从源码来看的话,fixable不一定是特定的值,只要有值,就会进行fix的修复

个人对于这个参数的看法: 可能是因为为了好理解,类似于语义化,文档规范了某些特定的值,能够表达具体的含义。如:‘code'是对code进行修复,'whiteSpace'是对空白字符进行修复等。

fix函数

fix函数是context.report()传入描述参数上的一个属性,用户如果需要fix,就提供这个函数。这个函数的关键是其传出来的参数。

context.report({
  message: "xxx",
  node,
  fix(fixer) {
    // TODO
  }
})

参数fixer上面挂载了很多的方法,替换节点信息,获取tonken等等。可以参考官网:https://eslint.bootcss.com/docs/developer-guide/working-with-rules#applying-fixes

在fix中分析ast,获取对应的信息

到这一步,auto fix已经完成了一大半,接下来就是看如何去分析ast,如果获取到你想要的东西。

聊到ast,科班的同学们应该不陌生,抽象语法树在底层编程是很常见的,比如编译器,webpack的compiler, bable内部都是用ast去分析代码,有感兴趣的人可以自行去了解他们的异同。

分析ast

  create(context) {
    const sourceCode = context.getSourceCode()

    return {
      CallExpression(node) {
        // 拦截函数调用的ast节点

        const code = sourceCode.getText(node) //获取原始diamante
      }
    }

经过上面的代码,是不是有人疑惑这是什么东西,这就涉及到ast的遍历了。CallExpression是一个ast节点中的type,也就是类型,当遍历该ast时,如果用户有传对应的节点类型函数,内部就会去执行对应的函数,为用户提供接口去访问对应的资源。

类似于CallExpression的节点类型还有很多,比如表达式是MemberExpression等等,如果感兴趣,可以自行去了解更多关于这方面的知识。

总结

通过上面的内容,大概已经了解了一个插件如何进行auto fix, 也待大家看了下eslint一小部分源码,针对于源码如果有兴趣可以再自己去拓展开,针对写插件或者了解整个eslint都有很大的帮助。

针对上面的分享,整理和码字不容易,如果喜欢的小伙伴可以点个👍 哈。

eslint rule定义