ESLint 核心规则贡献完全指南:从文件结构、单元测试到性能验证与冻结规则约定
2026/9/11 18:52:09 网站建设 项目流程

ESLint 核心规则贡献完全指南:从文件结构、单元测试到性能验证与冻结规则约定

【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint

本篇技术指南以 ESLint 官方贡献文档 docs/src/contribute/core-rules.md 为核心骨架,完整讲解核心规则(Core Rules)的定义、三文件结构、源码编写格式、RuleTester单元测试、npm run perf性能验证、命名约定以及「冻结规则」(Frozen Rules)机制,并结合本仓库中的真实源码(如 lib/rules/no-extra-semi.js、tests/lib/rules/no-extra-semi.js)逐层印证。读完本文,你将掌握向 ESLint 仓库提交一条合格核心规则所需的全部规范与可落地步骤。

什么是 ESLint 核心规则

ESLint 的核心规则(Core Rules)是指随eslint包一起发布的、内置于 ESLint 中的规则集合,分布在仓库的 lib/rules 目录下(共 300 余条,例如no-extra-semisemino-eval等)。与之相对的是由插件提供的自定义规则(Custom Rules)。

核心规则与自定义规则使用完全相同的 API——两者的规则模块都导出meta元数据对象与create访客函数,底层由Linter统一调度。核心规则与自定义规则的主要区别只有两点:

  1. 核心规则随eslint包分发,无需额外安装插件即可使用;
  2. 核心规则必须遵守本文档(docs/src/contribute/core-rules.md)所记录的仓库级约定。

规则编写的完整参考请见 Custom Rules 文档,其中详细规定了meta(含typedocsfixablehasSuggestionsschemadefaultOptionslanguagesdeprecated等)、create()访问器、context对象、context.report()问题上报、fix自动修复与suggest建议等全部细节,核心规则与自定义规则遵循同一套格式。

核心规则的三文件结构

每个核心规则都对应三个同名文件,以规则标识符(如no-extra-semi)命名:

位置用途no-extra-semi为例
lib/rules/下的源码文件规则实现lib/rules/no-extra-semi.js
tests/lib/rules/下的测试文件规则单元测试tests/lib/rules/no-extra-semi.js
docs/src/rules/下的文档文件规则使用文档docs/src/rules/no-extra-semi.md

重要:若向 ESLint 仓库提交一条核心规则,必须同时提供上述三个文件并遵守下列全部约定。

规则文档文件的格式要求

docs/src/rules/下的文档使用 Markdown 编写,文件头以 YAML front matter 声明元信息,例如 docs/src/rules/no-extra-semi.md 的开头:

--- title: no-extra-semi rule_type: suggestion related_rules: - semi - semi-spacing ---

其中rule_type必须与源码meta.type一致(problem/suggestion/layout),related_rules列出相关规则便于文档交叉引用。正文使用::: incorrect/::: correct等容器分别展示该规则的错误示例与正确示例。

核心规则源码的基本格式

官方文档给出了核心规则源码文件的基本骨架,本节将其与仓库真实实现对照解读。

官方模板

/** * @fileoverview Rule to disallow unnecessary semicolons * @author Nicholas C. Zakas */ "use strict"; //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ /** @type {import('../types').Rule.RuleModule} */ module.exports = { meta: { type: "suggestion", docs: { description: "disallow unnecessary semicolons", recommended: true, url: "https://eslint.org/docs/rules/no-extra-semi", }, fixable: "code", schema: [], // no options }, create: function (context) { return { // callback functions }; }, };

模板要点:

  • 文件头使用@fileoverview描述规则用途、@author标注作者;
  • 通过/** @type {import('../types').Rule.RuleModule} */进行 JSDoc 类型标注,该类型定义在 lib/types/index.d.ts 中;
  • meta.docs.description是核心规则的必填项,用于生成 rules index 规则索引;
  • meta.fixable只能取"code""whitespace",且可修复规则必须声明,否则 ESLint 会在规则尝试产生修复时抛错;
  • meta.schema用于校验规则配置项,无选项时写schema: [],此时用户传入任何选项都会被判定为非法。

真实实现:no-extra-semi的结构解读

查看仓库中 lib/rules/no-extra-semi.js,可以看到一条成熟核心规则的完整形态。其meta部分(lib/rules/no-extra-semi.js#L22-L58)包含:

meta: { deprecated: { message: "Formatting rules are being moved out of ESLint core.", url: "https://eslint.org/blog/2023/10/deprecating-formatting-rules/", deprecatedSince: "8.53.0", availableUntil: "11.0.0", replacedBy: [ { message: "ESLint Stylistic now maintains deprecated stylistic core rules.", url: "https://eslint.style/guide/migration", plugin: { name: "@stylistic/eslint-plugin", url: "https://eslint.style" }, rule: { name: "no-extra-semi", url: "https://eslint.style/rules/no-extra-semi" }, }, ], }, type: "suggestion", docs: { description: "Disallow unnecessary semicolons", recommended: false, url: "https://eslint.org/docs/latest/rules/no-extra-semi", }, fixable: "code", schema: [], messages: { unexpected: "Unnecessary semicolon.", }, },

对比模板可以发现几个核心规则进阶要点:

  • messages对象集中管理违规消息,通过messageIdcontext.report()中引用(此例为unexpected),避免消息文本在规则文件与测试文件中重复;
  • deprecated结构声明规则的废弃状态、废弃起始版本、可用截止版本以及替代方案(replacedBy);
  • docs.recommended标识该规则是否被@eslint/jsrecommended配置默认启用。

create部分(lib/rules/no-extra-semi.js#L60-L166)通过注册EmptyStatementClassBodyMethodDefinition, PropertyDefinition, StaticBlock等 AST 访客来识别多余分号,并使用context.report()配合fix函数上报可自动修复的问题,其中还通过FixTracker.retainSurroundingTokens扩大替换范围以避免与semi规则的修复冲突——这正是「核心规则修复必须小而安全」的典型实践。

核心规则的单元测试

每一条随包发布的核心规则都必须附带单元测试才能被接收。测试文件与源码文件同名,放在tests/lib/rules/目录下:若规则源码为lib/rules/foo.js,则测试文件应为tests/lib/rules/foo.js

ESLint 提供了RuleTester工具,让规则测试的编写变得非常简单。该工具由 lib/rule-tester/rule-tester.js 实现,内部基于 Mocha/Jest 的describe/it框架封装(见 lib/rule-tester/rule-tester.js#L1-L37)。

以 tests/lib/rules/no-extra-semi.js 为例,真实测试骨架如下:

const rule = require("../../../lib/rules/no-extra-semi"), RuleTester = require("../../../lib/rule-tester/rule-tester"); const ruleTester = new RuleTester({ languageOptions: { ecmaVersion: 5, sourceType: "script", }, }); ruleTester.run("no-extra-semi", rule, { valid: [ "var x = 5;", "for(;;);", { code: "for(a of b);", languageOptions: { ecmaVersion: 6 } }, { code: "class A { }", languageOptions: { ecmaVersion: 6 } }, ], invalid: [ { code: "var x = 5;;", output: "var x = 5;", errors: [{ messageId: "unexpected" }], }, { code: "class A { static { ; } }", output: "class A { static { } }", languageOptions: { ecmaVersion: 2022 }, errors: [{ messageId: "unexpected", column: 20 }], }, // 断言 output: null —— 期望规则报告问题但不提供自动修复 { code: "; 'use strict'", output: null, errors: [{ messageId: "unexpected" }] }, ], });

RuleTester的关键使用规则

根据 RuleTester 文档:

  • ruleTester.run(name, rule, tests)接收三个参数:规则名称、规则对象、以及包含validinvalid两个数组的测试对象;可选传assertionOptions(如requireMessage: true)对invalid用例的断言一致性做强制约束;
  • valid数组中的字符串表示该代码不应触发任何报告;对象形式可附加optionslanguageOptionssettingsfilenamebefore/after等属性;
  • invalid数组中的每个用例必须声明errors(可指定message字符串、正则表达式或messageId,并可用linecolumn精确断言位置),output表示自动修复后的期望代码;当outputnull时表示断言「该问题没有自动修复」,这正是no-extra-semi; 'use strict'场景下的行为——移除分号会使后续字符串语句变成指令(directive),因此不能自动修复;
  • RuleTester构造函数不传参时使用 ESLint 默认值(languageOptions: { ecmaVersion: "latest", sourceType: "module" });也可通过静态方法RuleTester.setDefaultConfig(config)RuleTester.getDefaultConfig()RuleTester.resetDefaultConfig()批量管理默认配置。

运行测试可使用仓库根目录 package.json 中定义的脚本npm test(实际执行node Makefile.js test),它会在 CI 与本地对全部核心规则测试文件进行跑批。

性能测试:用npm run perf验证规则开销

为了保持 lint 过程的效率与低侵入性,新规则或对现有规则的改动都应验证其性能影响。如何对单条规则进行剖析,可参考 Profile Rule Performance 章节——通过设置TIMING环境变量,在 lint 完成后展示运行时间最长的十条规则及其占比,例如:

$ TIMING=1 eslint lib Rule | Time (ms) | Relative :-----------------------|----------:|--------: no-multi-spaces | 52.472 | 6.1% camelcase | 48.684 | 5.7%

要单独测试某条规则,可组合--no-config-lookup--rule选项;将TIMING设为更大的数值(如TIMING=50)或TIMING=all可查看更长列表。

而在核心仓库内部开发时,npm run perf命令会给出开启全部核心规则后 ESLint 总运行时间的高层概览(该目标定义在仓库根目录 Makefile.js 中,其实现会用到hyperfine等基准工具做回归对比)。官方文档建议的对比流程如下:

$ git checkout main Switched to branch 'main' $ npm run perf CPU Speed is 2200 with multiplier 7500000 Performance Run #1: 1394.689313ms Performance Run #2: 1423.295351ms Performance Run #3: 1385.09515ms Performance Run #4: 1382.406982ms Performance Run #5: 1409.68566ms Performance budget ok: 1394.689313ms (limit: 3409.090909090909ms) $ git checkout my-rule-branch Switched to branch 'my-rule-branch' $ npm run perf CPU Speed is 2200 with multiplier 7500000 Performance Run #1: 1443.736547ms Performance Run #2: 1419.193291ms Performance Run #3: 1436.018228ms Performance Run #4: 1473.605485ms Performance Run #5: 1457.455283ms Performance budget ok: 1443.736547ms (limit: 3409.090909090909ms)

使用要点:

  • 先在main分支跑一次基线,再切换到自己的规则分支跑一次,对比多次运行(官方示例为 5 次)的平均耗时;
  • 输出中的Performance budget ok表示耗时未超出基于 CPU 速度折算的预算上限(示例中 limit 为3409.09ms);
  • 仓库会计算 CPU 速度并乘以 multiplier 折算成预算,因此不同机器上的数字不可直接横向比较,应以同机同环境的基线为准。

核心规则命名约定

ESLint 核心规则命名遵循以下约定:

  • 单词之间使用**连字符(dash)**分隔,如no-extra-semino-eval
  • 若规则仅用于禁止某类写法,必须以no-前缀命名,例如用no-eval禁止eval()、用no-debugger禁止debugger
  • 若规则用于强制要求某类写法,则使用不带特殊前缀的简短名称,例如semiquotescurly

这套命名约定同样适用于自定义规则,建议自定义规则也遵循它,便于使用者理解规则意图。

冻结规则(Frozen Rules)

当规则达到功能完备(feature complete)状态时,会被标记为冻结,在文档中用 ❄️ 表情指示(可在规则源码的meta.docs中找到对应标记,例如 lib/rules/arrow-body-style.js 声明了frozen: true)。

冻结的判定标准

规则被视为功能完备的标准是:规则的目标用途已被完整实现,能捕获 80% 及以上预期违规,并覆盖绝大多数常见例外场景。在此之后,若遇到未被覆盖的边缘情况,官方期望用户改用禁用注释(disable comments)来处理,而不是要求规则继续扩张。

冻结意味着什么

当一条规则被冻结后,维护策略为:

  • Bug 修复:仍会修复被确认的 bug;
  • 新 ECMAScript 特性:保证与新语法兼容,即规则不会在新语法上崩溃;
  • TypeScript 支持:保证与 TypeScript 语法兼容,不会在 TS 语法上出错,且对 TS 的违规判定保持恰当;
  • 新选项不再新增任何选项,除非新增选项是修复 bug 或支持新增 ECMAScript 特性的唯一途径。

冻结规则的替代方案

如果你认为某条冻结规则在你的场景下稍作改动会更好用,官方推荐的做法是:复制该规则源码到自己的项目中,按需修改后使用。这正好呼应了 Custom Rules 文档中的警告——eslint包中内置的核心规则不属于公共 API,不适合被直接继承扩展;基于核心规则二次开发非常脆弱,未来很可能因内部变化而彻底失效,因此应优先复制源码再改造。

提交核心规则前 Checklist

综合官方文档与仓库实际,向 ESLint 仓库提交一条核心规则前请逐项核对:

  1. 三文件齐全lib/rules/<rule-id>.js源码、tests/lib/rules/<rule-id>.js测试、docs/src/rules/<rule-id>.md文档,三者命名一致;
  2. 源码格式合规:文件头@fileoverview/@author"use strict"、JSDoc 类型标注Rule.RuleModulemetatype/docs.description/schema齐全;可修复规则声明fixable,可修复的messageId收拢在meta.messages中;
  3. 测试覆盖充分:使用RuleTester提供validinvalid两组用例,覆盖常见正确/错误代码、语法版本边界(如 ES6 class、ES2022 静态块)以及「不可自动修复」场景(output: null);
  4. 性能验证:使用npm run perfmain与规则分支间对比总运行时间,确认未突破性能预算;
  5. 命名与状态:遵循连字符命名与no-前缀约定;评估规则是否已达功能完备而可标记frozen: true,并遵守冻结规则不新增选项的约定。

按此清单推进,你的规则就能与仓库中现有 300 余条核心规则保持一致的工程质量,顺利进入审查与合并流程。

【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询