现在写 JavaScript 项目,ESlint 可以说是标配。静态检查虽不执行代码,主要检查 AST 和文件路径,但也可以检查出很多东西。比如说 import 的文件是否真是存在,React Hooks 用法有没有问题等等。有时候一些项目写法上有特殊的限制,也可以通过 eslint 来自动化代码检查。这时就需要自定义 eslint rules。

自定义 rules

rules 的写法详细的可以看ESLint 文档,不再赘述。
最重要的地方就是 export 的对象的 create 对象。在 create 对象中,key 是一个 selector,value 则是对应的 handler, 在 handler 就可以通过context.report 来对有问题的代码报错。语法树可以用 AST Explorer 来看。

例子:小程序自定义组件中防止额外触发内置事件

在小程序自定义组件可以通过 this.triggerEvent('eventName', ...) 来触发 eventName 事件。但是有一些内置事件是不需要额外触发的, 比如tap, longpress 之类的。我们就而可以写一个 rule 来禁止手动触发这些事件。
根据AST,可以写出下面这个 rule。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 内置事件
const bulidinEvents = [
'tap',
'longpress',
'longtap',
// ...
];

/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem',

docs: {
description: 'disallow trigger builtin events manually in miniprogram',
category: 'Possible Errors',
recommended: true,
},
schema: [
{
enum: ['always', 'never'],
},
],
},
create: function (context) {
return {
CallExpression: function (node) {
const { callee, arguments: args } = node;
if (callee.type === 'MemberExpression') {
const { object, property } = callee;
if (object.type === 'ThisExpression') {
if (
property.type === 'Identifier' &&
property.name === 'triggerEvent'
) {
if (args.length > 0) {
const firstArgrament = args[0];
const eventName = firstArgrament.value;
if (bulidinEvents.includes(eventName)) {
context.report({
node,
message: `trigger builtin events manually in miniprogram is forbidden`,
});
}
}
}
}
}
},
};
},
};

rulesdir

要让 rule 生效最简单的方式就是使用runtime rule 的机制,但是不是任何时候我们都有机会去加--rules。例如在一些集成度比较高的框架中比如 CRA 之类,只提供了修改 config 的方式,不支持修改命令行参数。

如果为了项目特异的规则专门发一个 plugin 的 npm 包,不仅显得有些小题大做,还不好维护。最好还是能把 rules 放在主项目内。

这里我们可以用 eslint-plugin-rulesdir, 它利用了 eslint plugin 的机制,在 rules getter 中获取某个目录内的所有 JS 文件作为 rules, 这样们就能在 rules 配置中通过rulesdir/<fileanme> 来使用自定义的 rules 了。

比如将上面我们写的 rule 命名为no-trigger-builtin-events-manually.js,放到对应目录中,就而可以配置

1
'rulesdir/no-trigger-builtin-events-manually': ['error', 'always']

来禁用多余的手动事件触发。