上文说了eslint配置之plugins(插件),今天说下ESLint之自定义规则,前文传送门,
eslint从入门到放弃(二)esLint配置之globals
eslint从入门到放弃(三)ESLint配置之env(三)
eslint从入门到放弃(五)eslint配置之extends(共享配置)
eslint从入门到放弃(六)eslint 配置之plugins(插件)
大家好我是【小枫学幽默】,这是我eslint从入门到放弃系列教程的第七篇,欢迎关注后续更新。接下来步入正题:
ESLint配置之自定义规则
讲在前面
在开发过程中,我们可能会遇到一些代码规范,函数命名不能使用驼峰命名,函数命名不能使用下划线命名等等,这些规范我们都可以通过eslint来约束,但是eslint提供的规则可能并不能满足我们的需求,所以我们需要自定义规则。
今天做什么?
今天,我们创建一个叫no-forbidden-var的自定义规则,该规则用于禁止使用特定的变量名(例如forbiddenVar)
准备工作
让我们先新建一个
demo目录,文件结构如下
demo
├── .eslintrc.js # eslint配置文件
├── package.json # 项目配置文件
└── index.js # 入口文件
安装
eslint
npm install eslint --save-dev
# 当前demo 使用的 eslint 版本为 3.14.1
index.js中编写测试代码
// 既然今天是要禁止使用forbiddenVar作为变量名,
// 那我们就故意使用forbiddenVar作为变量名 【坏笑】
var forbiddenVar = 'forbiddenVar';
创建自定义规则
按照今天的计划,我们应该要创建一个叫no-forbidden-var的自定义规则,该规则用于禁止使用特定的变量名(例如forbiddenVar)。
Let's go...
在demo根目录下创建一个名为rules的文件夹,用于存放我们自定义的no-forbidden-var规则。
- 1、 在
rules文件夹中创建一个名为no-forbidden-var.js的文件,
no-forbidden-var.js文件创建后目录结构如下
demo
├── .eslintrc.js # eslint配置文件
├── package.json # 项目配置文件
└── index.js # 测试文件
└── rules # 规则目录
└──-- no-forbidden-var.js # 规则
- 2、 在
no-forbidden-var.js编写以下代码:
module.exports = {
meta: {
// 规则类型
/*
"problem" 意味着该规则正在识别将导致错误或可能导致混乱行为的代码。开发人员应该把它作为一个高度优先事项来解决。
"suggestion" 意味着该规则确定了一些可以用更好的方式完成的事情,但如果不改变代码,就不会发生错误。
"layout" 意味着该规则主要关心的是空白、分号、逗号和括号,所有决定代码外观的部分,而不是代码的执行方式。这些规则对代码中没有在 AST 中指定的部分起作用。
*/
type: "suggestion",
// 规则文档
docs: {
description: "禁止使用特定的变量名",
},
},
// 规则逻辑 实际编写规则逻辑的地方
create(context) {
// context 对象有哪些方法和属性
// 点这个链接去看 => https://zh-hans.eslint.org/docs/latest/extend/custom-rules
// 检查变量声明
return {
VariableDeclaration(node) {
// 遍历这个节点的是所有变量声明
node.declarations.forEach((declaration) => {
// 判断变量名是否为forbiddenVar
if (declaration.id.name === 'forbiddenVar') {
// 如果为forbiddenVar则报告错误
// 使用context.report方法上报发现的错误,用户就可以感知到这里有错误了
context.report({
node,
message: '禁止使用forbiddenVar作为变量名',
});
}
});
},
};
},
};
嗯?create 函数是干啥的?
【熟悉 AST 语法树(Abstract Syntax Tree 即抽象语法树)同学可跳过这段, 不要问我为什么,问就是你们技术太牛X了】
create是插件的核心处理函数,它接收一个 context 对象作为参数,并返回一个对象,该对象包含一个或多个钩子函数。这些钩子函数会在特定类型的节点被访问时被调用。
create函数的返回值是一个对象,该对象的属性名是AST的节点类型,属性值是一个函数,该函数会在ESLint遍历到该类型的节点时被调用。
例如,如果create函数返回以下对象:
module.exports = {
create(context) {
return {
VariableDeclaration(node) {
// 当遍历到VariableDeclaration节点时调用此函数
node.declarations.forEach((declaration) => {
console.log(declaration.id.name);
});
}
}
}
}
那么,当ESLint遍历到VariableDeclaration节点时,会调用VariableDeclaration函数。这个函数可以用来检查节点的属性或报告错误。上面的代码会在遍历到VariableDeclaration节点时,在控制台中输出当前节点所有变量定义的变量名。
啥?这都是啥?AST ? 节点 ?节点类型...脑袋嗡嗡的吧...
别着急,你看AST语法树长这个样子,像不像 DOM 树,节点和节点类型都可以类比 DOM中的节点和节点类型
<看不懂看看下面这个例子>
module.exports = {
create(context) {
return {
div(node) {
// 当遍历到div节点时调用此函数
node.childNodes.forEach((child) => {
console.log(child.textContent);
});
}
}
}
}
module.exports = {
create(context) {
return {
VariableDeclaration(node) {
// 当遍历到VariableDeclaration节点时调用此函数
node.declarations.forEach((declaration) => {
console.log(declaration.id.name);
});
}
}
}
}
Are we clear ?
我怎么知道create函数的返回值应该包含哪些钩子函数?
看我们今天开发的规则要规范的代码是什么样子,从代码的 AST 语法树中判断哪些节点类型是需要处理的,然后去ESLint的官方文档中查找对应的钩子函数。
例如,如果我们要处理的代码是:
// 既然今天是要禁止使用forbiddenVar作为变量名,
// 那我们就故意使用forbiddenVar作为变量名 【坏笑】
var forbiddenVar = 'forbiddenVar';
它的AST语法树如下:(是不是很像 DOM 树?)
可以看到,forbiddenVar是一个VariableDeclaration节点(即节点类型为VariableDeclaration),它的id属性是一个Identifier节点,name属性就是forbiddenVar。所以,我们可以通过遍历VariableDeclaration节点,然后检查它的id.name属性是否为forbiddenVar,如果是,则报告一个错误。
所以 create函数的返回值应该包含一个名为 VariableDeclaration 的钩子函数,代码如下:
create(context) {
// 检查变量声明
return {
VariableDeclaration(node) {
// 遍历这个节点的是所有变量声明
node.declarations.forEach((declaration) => {
// 判断变量名是否为forbiddenVar
if (declaration.id.name === 'forbiddenVar') {
// 如果为forbiddenVar则报告错误
// 使用context.report方法上报发现的错误,用户就可以感知到这里有错误了
context.report({
node,
message: '禁止使用forbiddenVar作为变量名',
});
}
});
},
};
}
如何查看AST语法树?
你可以使用ESLint提供的SourceCode对象的getAST方法来获取AST语法树。例如:
create(context) {
const sourceCode = context.getSourceCode();
const ast = sourceCode.ast;
console.log(ast);
return {
VariableDeclaration(node) {
// 遍历这个节点的是所有变量声明
},
};
}
这样就可以在控制台中看到AST语法树了。 你也可以使用AST Explorer工具来查看AST语法树。 AST Explorer工具的网址是:点我去look look
ESLint提供了许多内置的钩子函数,例如:
Program:在解析整个程序时调用。VariableDeclaration:在解析变量声明时调用。FunctionDeclaration:在解析函数声明时调用。CallExpression:在解析函数调用时调用。- ...
你可以查看ESLint的官方文档,了解所有可用的钩子函数。你也可以查看其他ESLint插件的源代码,了解如何使用这些钩子函数。
在.eslintrc.js中引用no-forbidden-var规则
module.exports = {
"rules": {
// 禁止使用未定义的变量
"no-forbidden-var": ['error'],
},
}
使用 eslint 检查代码
cd demo
# --rulesdir ./rules 指定本地自己开发的规则所在的目录
npx eslint index.js -c .eslintrc.js --rulesdir ./rules
检查结果
# demo
1:1 error 禁止使用forbiddenVar作为变量名 no-forbidden-var
等等,这个规则只能禁用forbiddenVar,不通用啊!!
规则支持使用时传入禁止使用的变量名数组
module.exports = {
meta: {
// 规则类型
/*
"problem" 意味着该规则正在识别将导致错误或可能导致混乱行为的代码。开发人员应该把它作为一个高度优先事项来解决。
"suggestion" 意味着该规则确定了一些可以用更好的方式完成的事情,但如果不改变代码,就不会发生错误。
"layout" 意味着该规则主要关心的是空白、分号、逗号和括号,所有决定代码外观的部分,而不是代码的执行方式。这些规则对代码中没有在 AST 中指定的部分起作用。
*/
type: "suggestion",
// 规则文档
docs: {
description: "禁止使用特定的变量名",
},
// 规则配置项
schema: [
// 定义参数为一个数组(array),没一个数组条目类型为 字符串(string)
{
type: "array",
items: {
type: "string",
},
},
],
},
// 规则逻辑 实际编写规则逻辑的地方
create(context) {
// context 对象有哪些方法和属性
// 点这个链接去看 => https://zh-hans.eslint.org/docs/latest/extend/custom-rules
// 获取用户提供的禁止使用的变量名数组
const forbiddenVars = context.options[0] || [];
// 检查变量名是否被禁止
function isForbiddenVar(name) {
return forbiddenVars.includes(name);
}
// 检查变量声明
return {
VariableDeclaration(node) {
node.declarations.forEach((declaration) => {
if (isForbiddenVar(declaration.id.name)) {
// 如果为forbiddenVar则报告错误
// 使用context.report方法上报发现的错误,用户就可以感知到这里有错误了
context.report({
node,
message: `禁止使用${declaration.id.name}作为变量名`,
});
}
});
},
};
},
};
在.eslintrc.js中配置no-forbidden-var规则,并传入禁止使用的变量名列表
module.exports = {
"rules": {
// 禁止使用未定义的变量
"no-forbidden-var": ['error', ['aaa', 'bbb', 'ccc', 'a']],
},
}
index.js中编写测试代码
var a = 'I am a';
var aaa = 'I am aaa';
var bbb = 'I am bbb';
var ccc = 'I am ccc';
使用 eslint 检查代码
cd demo
# --rulesdir ./rules 指定自定义规则目录
npx eslint index.js -c .eslintrc.js --rulesdir ./rules
检查结果
# demo
1:1 error 禁止使用a作为变量名 no-forbidden-var
2:1 error 禁止使用aaa作为变量名 no-forbidden-var
3:1 error 禁止使用bbb作为变量名 no-forbidden-var
4:1 error 禁止使用ccc作为变量名 no-forbidden-var
官方文档
欢迎关注我的个人公众号「「小枫学幽默」」一起成长,一起分享生活!!