为什么要创建自定义规则?
如果 ESLint 内置规则 和社区发布的自定义规则不能满足你的需求,请创建自定义规则。你可以创建自定义规则来为你的公司或项目实现最佳实践,防止特定错误再次出现,或确保符合风格指南。
在创建不特定于你的公司或项目的自定义规则之前,值得在网上搜索一下,看看是否有人发布了一个带有自定义规则的插件来解决你的用例。该规则很可能已经存在。
插件
创建 ESLint 插件,ESLint 推荐使用 Yeoman generator。首先需要安装 Yeoman,安装命令如下:
npm i -g yo
Yeoman 是一款通用的初始化工具,想要初始化 ESLint 插件,需要安装 ESLint 模板,安装命令如下:
npm i -g generator-eslint
接下来,新建一个目录,eslint-plugin-utils (通常与发布npm的包名一致)
切换到上面新建的目录,执行“yo eslint:plugin”命令会进入交互界面,询问作者、插件名字等,输入如图所示的内容即可。
稍等片刻即可完成自动初始化
编写插件
举个例子,写一个限制可选链操作符数据不能超过3个的规则
const maxOptionalChainConfig = {
meta: {
type: "suggestion",
docs: {
description: "限制可选链操作符的数量"
},
schema: [
{
type: "object",
properties: {
maxCount: {
type: "integer",
minimum: 1,
default: 2 // 默认值为2
},
message: {
type: "string",
default: '可选链数量不能超过{maxCount}个,请声明常量' // 默认消息
}
},
additionalProperties: false
}
],
messages: {
optional_chain: '{{message}}',
}
},
create(context) {
const options = context.options[0] || {};
const maxCount = options.maxCount || 2; // 获取最大可选链数量
const message = options.message || '可选链数量不能超过' + maxCount + '个'; // 获取自定义消息
const reportedLines = new Set(); // 用于跟踪已报告的行
return {
MemberExpression(node) {
const sourceCode = context.sourceCode;
const lines = sourceCode?.lines || [];
const { start } = node.loc || {};
const lineNumber = start.line; // 获取当前节点的行号
// 只在当前行进行检查
if (lines[lineNumber - 1]) {
const lineContent = lines[lineNumber - 1];
const questionMarkCount = (lineContent.match(/\?/g) || []).length;
// 检查是否超过最大可选链操作符数量
if (questionMarkCount > maxCount && !reportedLines.has(lineNumber)) {
reportedLines.add(lineNumber); // 标记该行已报告
context.report({
node,
messageId: 'optional_chain',
data: {
message: message.replace('{maxCount}', maxCount) // 替换消息中的占位符
},
loc: {
start: { line: lineNumber, column: 0 }, // 报告位置为行的开始
end: { line: lineNumber, column: lineContent.length } // 报告位置为行的结束
},
});
}
}
}
};
}
};
入口文件直接导出
const plugin = {
rules: {
"max-optional-chain": maxOptionalChainConfig
}
};
module.exports = plugin;
本地调试是否生效
新建eslint.config.js文件
const eslintPluginExample = require("./src/index");
module.exports = [ { files: ["**/*.js","**/*.jsx"],
languageOptions: {
sourceType: "commonjs",
ecmaVersion: "latest",
},
plugins: {"eslint-plugin-utils": eslintPluginExample},
rules: {
"eslint-plugin-utils/max-optional-chain": ["error",
{
maxCount: 3,
message: '傻逼,不要写那么多可选链,声明常量'
}
],
},
}
]
写个example.js的测试文件
function incorrectFoo() {
const obj = {};
if (obj.name === '') {
}
if (obj?.name?.a?.c?.e === '') {
}
if (typeof obj === 'object') {
}
if (typeof obj === 'function') {
}
}
然后执行 npx eslint example.js 规则生效的话终端会输出eslint的错误提示
接着正常发布npm即可
附package.josn
{
"name": "eslint-plugin-utils",
"version": "0.0.1",
"description": "自定义eslint-plugin",
"keywords": [
"eslint",
"eslintplugin",
"eslint-plugin"
],
"author": "crp",
"main": "./index.js",
"exports": {
".": {
"import": "./index.mjs",
"require": "./index.js"
}
},
"module": "./index.mjs",
"files": [
"lib"
],
"scripts": {
"lint": "npm-run-all \"lint:*\"",
"lint:eslint-docs": "npm-run-all \"update:eslint-docs -- --check\"",
"lint:js": "eslint .",
"test": "mocha tests --recursive",
"update:eslint-docs": "eslint-doc-generator"
},
"dependencies": {
"react": "^19.0.0",
"requireindex": "^1.2.0"
},
"devDependencies": {
"@eslint/js": "^9.0.0",
"eslint": "^9.17.0",
"eslint-doc-generator": "^1.0.0",
"eslint-plugin-eslint-plugin": "^6.0.0",
"eslint-plugin-n": "^17.0.0",
"mocha": "^10.0.0",
"npm-run-all2": "^6.1.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"peerDependencies": {
"eslint": ">=8.57.0"
},
"license": "MIT",
"private": false
}