自己动手写符合自己业务需求的 eslint 规则

使用eslint和stylelint之类的工具扫描前端代码现在已经基本成为前端同学的标配。但是,业务这么复杂,指望eslint等提供的工具完全解决业务中遇到的代码问题还是不太现实的。我们一线业务同学也要有自己的写规则的能力。

eslint是构建在AST Parser基础上的规则扫描器,缺省情况下使用espree作为AST解析器。rules写好对于AST事件的回调,linter处理源代码之后会根据相应的事件来回调rules中的处理函数。

另外,在进入细节之前,请思考一下:eslint的边界在哪里?哪些功能是通过eslint写规则可以做到的,哪些是用eslint无法做到的?

一 先学会如何写规则测试

兵马未动,测试先行。规则写出来,如何用实际代码进行测试呢?

所幸非常简单,直接写个json串把代码写进来就好了。

我们来看个no-console的例子,就是不允许代码中出现console.*语句的规则。

首先把规则和测试运行对象ruleTester引进来:

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const rule = require("../../../lib/rules/no-console"),
    { RuleTester } = require("../../../lib/rule-tester");

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const ruleTester = new RuleTester();

然后我们就直接调用ruleTester的run函数就好了。有效的样例放在valid下面,无效的样例放在invalid下面,是不是很简单。

我们先看下有效的:

ruleTester.run("no-console", rule, {
    valid: [
        "Console.info(foo)",

        // single array item
        { code: "console.info(foo)", options: [{ allow: ["info"] }] },
        { code: "console.warn(foo)", options: [{ allow: ["warn"] }] },
        { code: "console.error(foo)", options: [{ allow: ["error"] }] },
        { code: "console.log(foo)", options: [{ allow: ["log"] }] },

        // multiple array items
        { code: "console.info(foo)", options: [{ allow: ["warn", "info"] }] },
        { code: "console.warn(foo)", options: [{ allow: ["error", "warn"] }] },
        { code: "console.error(foo)", options: [{ allow: ["log", "error"] }] },
        { code: "console.log(foo)", options: [{ allow: ["info", "log", "warn"] }] },

        // https://github.com/eslint/eslint/issues/7010
        "var console = require('myconsole'); console.log(foo)"
    ],

能通过的情况比较容易,我们就直接给代码和选项就好。

然后是无效的:

invalid: [

        // no options
        { code: "console.log(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression" }] },
        { code: "console.error(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression" }] },
        { code: "console.info(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression" }] },
        { code: "console.warn(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression" }] },

        //  one option
        { code: "console.log(foo)", options: [{ allow: ["error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] },
        { code: "console.error(foo)", options: [{ allow: ["warn"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] },
        { code: "console.info(foo)", options: [{ allow: ["log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] },
        { code: "console.warn(foo)", options: [{ allow: ["error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] },

        // multiple options
        { code: "console.log(foo)", options: [{ allow: ["warn", "info"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] },
        { code: "console.error(foo)", options: [{ allow: ["warn", "info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] },
        { code: "console.info(foo)", options: [{ allow: ["warn", "error", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] },
        { code: "console.warn(foo)", options: [{ allow: ["info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] },

        // In case that implicit global variable of 'console' exists
        { code: "console.log(foo)", env: { node: true }, errors: [{ messageId: "unexpected", type: "MemberExpression" }] }
    ]
});

无效的要判断下出错信息是不是符合预期。

我们使用mocha运行下上面的测试脚本:

./node_modules/.bin/mocha tests/lib/rules/no-console.js

运行结果如下:

no-console
    valid
      ✓ Console.info(foo)
      ✓ console.info(foo)
      ✓ console.warn(foo)
      ✓ console.error(foo)
      ✓ console.log(foo)
      ✓ console.info(foo)
      ✓ console.warn(foo)
      ✓ console.error(foo)
      ✓ console.log(foo)
      ✓ var console = require('myconsole'); console.log(foo)
    invalid
      ✓ console.log(foo)
      ✓ console.error(foo)
      ✓ console.info(foo)
      ✓ console.warn(foo)
      ✓ console.log(foo)
      ✓ console.error(foo)
      ✓ console.info(foo)
      ✓ console.warn(foo)
      ✓ console.log(foo)
      ✓ console.error(foo)
      ✓ console.info(foo)
      ✓ console.warn(foo)
      ✓ console.log(foo)


  23 passing (83ms)

如果在valid里面放一个不能通过的,则会报错,比如我们加一个:

ruleTester.run("no-console", rule, {
    valid: [
        "Console.info(foo)",

        // single array item
        { code: "console.log('Hello,World')", options: [] },

就会报下面的错:

1 failing

  1) no-console
       valid
         console.log('Hello,World'):

      AssertionError [ERR_ASSERTION]: Should have no errors but had 1: [
  {
    ruleId: 'no-console',
    severity: 1,
    message: 'Unexpected console statement.',
    line: 1,
    column: 1,
    nodeType: 'MemberExpression',
    messageId: 'unexpected',
    endLine: 1,
    endColumn: 12
  }
]
      + expected - actual

      -1
      +0
      
      at testValidTemplate (lib/rule-tester/rule-tester.js:697:20)
      at Context.< anonymous> (lib/rule-tester/rule-tester.js:972:29)
      at processImmediate (node:internal/timers:464:21)

说明我们刚加的console是会报一个messageId为unexpected,而nodeType为MemberExpression的错误。

我们应将其放入到invalid里面:

invalid: [

    // no options
    { code: "console.log('Hello,World')", errors: [{ messa
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值