ESLint 插件规则编写的正确打开方式
1. ESLint 安装和配置
ESLint
是⼀个开源的代码静态分析修复⼯具 cli
,解析代码为 AST
使用的是 espree
解析器,该解析器最初是从经典的 esprima
解析器中 fork 出来的,但是现在基于另一个媲美 esprima
的新轮子 acorn
,同时,@babel/parser
也是基于 acorn
解析器的。
首先安装 eslint:
代码语言:shell复制$ npm init
$ npm i eslint -D # 安装eslint
$ npm create @eslint/config # 初始化eslint配置⽂件
⽣成的配置⽂件如下:
代码语言:javascript复制module.exports = {
// 当前可以使用哪个环境的全局变量
env: {
browser: true, // 浏览器环境 document
es2021: true, // ECMAScript语法 Promise
node: true, // node环境 require
},
// extends 和 plugins 的区别:extends = plugin rule
extends: [
'eslint:recommended',
],
parserOptions: {
ecmaVersion: 'latest', // ⽀持最新语法
sourceType: 'module', // ⽀持模块化
ecmaFeatures: {
'jsx': true, // 支持 jsx
},
},
// plugins: ['@typescript-eslint'],
rules: { // eslint规则覆盖
'semi': ['error', 'always'], // 0 off 1 warn 2 error
'quotes': ['error', 'double'],
},
globals: { // 配置全局变量
custom: 'readonly', // readonly 、 writable
},
};
可以配置 parser
字段来指定使⽤的解析器,如配置 @typescript-eslint/parser
解析器解析 ts 语法。
$ npm install @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest
typescript -D
修改配置文件:
代码语言:javascript复制"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
],
"parser": "@typescript-eslint/parser", // 内部用的是 espress
2. ESLint 核心 api
eslint 会将源码解析为 AST 语法树,遍历 AST 语法树,将节点拍平
代码语言:txt复制ESLint 核⼼API
- lintFiles 检验⽂件
- lintText 校验⽂本
- loadFormatter 加载formatter
- calculateConfigForFile 通过⽂件获取配置
- isPathIgnored 此路径是否是被忽略的
- static outputFixes 输出修复的⽂件
- static getErrorResults 获得错误结果
CLIEngine 脚⼿架核⼼
- getRules 获取规则
- resolveFileGlobPatterns 解析⽂件成glob模式
- executeOnFiles 根据⽂件执⾏逻辑
- executeOnText 根据⽂本执⾏逻辑
- getConfigForFile 获取⽂件的配置
- isPathIgnored 此路径是否是被忽略的
- getFormatter 获取输出的格式
- static getErrorResults 获取错误结果
- static outputFixes 输出修复的结果
Linter 校验js⽂本
- verifyAndFix 校验和修复
- verify 校验⽅法
- _verifyWithConfigArray 通过配置⽂件校验
- _verifyWithoutProcessors 校验没有processors
- _verifyWithProcessor 校验有processors
3. 从模板生成 cslint 插件和 no-var 规则
在 eslint 配置中 extends = plugin rule
,插件开发分为插件和规则,eslint 官⽅提供了 Yeoman 脚⼿架来⽣成插件和规则模板
$ npm install yo generator-eslint -g
$ yo eslint:plugin # 插件模板初始化,插件ID填写 cslint
# create package.json
# create .eslintrc.js
# create lib/index.js
# create README.md
$ yo eslint:rule # 规则模版初始化
# create docs/rules/no-var.md
# create lib/rules/no-var.js
# create tests/lib/rules/no-var.js
生成的模板文件如下:
代码语言:javascript复制// /lib/index.js
const requireIndex = require('requireindex');
module.exports.rules = requireIndex(__dirname '/rules');
// /lib/rules/no-var.js
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: null, // `problem`, `suggestion`, or `layout`
docs: {
description: 'no var expression',
recommended: false,
url: null, // URL to the documentation page for this rule
},
fixable: null, // Or `code` or `whitespace`
schema: [], // Add a schema if the rule has options
},
create(context) {
return {};
},
};
对插件入口文件 /lib/index.js
添加插件配置
const requireIndex = require('requireindex');
module.exports = {
rules: requireIndex(__dirname '/rules'),
configs: {
recommended: {
plugins: ['cslint'],
rules: {
'cslint/no-var': ['error'],
},
},
},
processors: {
'.vue': {
preprocess(code) { // 提取.vue 文件中的js
return [code];
},
postprocess(messages) {
console.log(messages);
return [];
},
},
},
};
4. no-var 规则开发
开发 no-var
规则
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem', // `problem`, `suggestion`, or `layout`
docs: {
description: '代码中不能包含var关键字',
},
fixable: 'code', // Or `code` or `whitespace`
schema: [], // Add a schema if the rule has options
messages: {
unexpectedVar: '不能用 {{type}} ',
},
},
create(context) {
// 用于修复代码
const sourceCode = context.getSourceCode();
return {
VariableDeclaration(node) { // 拦截var声明
if (node.kind == 'var') {
context.report({ // 警告
node,
data: { type: 'var' },
messageId: 'unexpectedVar', // 和上面的配置对应
fix(fixer) {
const varToken = sourceCode.getFirstToken(node, { filter: t => t.value === 'var' });
// var 替换为 let
return fixer.replaceText(varToken, 'let');
}
});
}
}
};
},
};
5. 编写自动测试用例
代码语言:javascript复制const rule = require('./lib/rules/no-var'),
RuleTester = require('eslint').RuleTester;
const ruleTester = new RuleTester({
parserOptions: {
ecmaVersion: 'latest',
}
});
ruleTester.run('no-var', rule, {
valid: [
{ code: 'let a = 1' },
],
invalid: [
{
code: 'var a = 1',
errors: [{ message: '禁用 var' }],
output: 'let a = 1',
},
],
});
6. 插件及规则应用
在新项目中应用插件和规则
代码语言:javascript复制module.exports = {
'env': {
'browser': true,
'es2021': true,
'node': true,
},
'plugins': ['cslint'],
'rules': {
'cslint/no-var': ['error', 'always'],
},
};
或使用 extends 直接应用全部规则
代码语言:javascript复制module.exports = {
'env': {
'browser': true,
'es2021': true,
'node': true,
},
extends: ['plugin:zlint/recommended'],
};