首页
/ ESLint 代码规范(Code Conventions):eslint-config-eslint 配置体系完全指南

ESLint 代码规范(Code Conventions):eslint-config-eslint 配置体系完全指南

2026-09-10 17:53:22作者:宣聪麟

本文以 ESLint 仓库中的 code-conventions.md 为骨架,结合仓库内 eslint-config-eslint 包的完整源码,系统梳理 ESLint 项目自身的代码规范:规范由谁定义、为什么采用这套规范、每个配置入口如何使用,以及每条规则的出处与底层实现。读完本文,你既能理解 ESLint 团队维护自己代码的方式,也能直接复用这套配置到自己的项目中。

规范从何而来:eslint-config-eslint

ESLint 项目的代码规范并非零散写在文档里,而是由一个独立发布的共享配置包 eslint-config-eslint 统一定义。文档 code-conventions.md 明确写道:代码规范由 eslint-config-eslint 决定("Code conventions for ESLint are determined by eslint-config-eslint")。

该包位于仓库 packages/eslint-config-eslint 目录下,其 package.json 中这样描述自己:

"Default ESLint configuration for ESLint projects."

即「ESLint 项目默认的 ESLint 配置」。这意味着:

  • ESLint 团队自己写代码时,就是被这套配置约束的;
  • 仓库根目录的 eslint.config.js 直接引用了该包;
  • 任何 ESLint 维护的项目(包括文档站、示例代码)都遵循同一套规范。

为什么规范由配置包而不是文档定义

把规范下沉到可执行的配置包,带来三个直接收益:

  1. 单一事实来源:规则清单、取值、参数都在源码里,而不是散落在人写的文档中,不会出现「文档说 A、代码用 B」的漂移;
  2. 可自动执行:规范不是给人「参考」的,而是由 ESLint 在 CI 中强制执行的;
  3. 可复用发布:包通过 npm 独立发布(版本号见 package.json,当前仓库中为 14.0.0),任何项目都能安装使用。

规范背后的依赖:六个插件与共享配置

eslint-config-eslint 并不是凭空规定几百条规则,而是站在多个高质量插件的基础上做减法与微调。其 package.jsondependencies 揭示了全部依赖:

依赖 作用
@eslint/js ESLint 官方核心规则的 flat config 预设(js.configs.recommended
eslint-plugin-jsdoc JSDoc 注释规范(类型、标签、描述、缩进)
eslint-plugin-unicorn 现代 JavaScript 风格建议(数组、字符串 API 偏好)
@eslint-community/eslint-plugin-eslint-comments ESLint 指令注释(eslint-disable 等)的规范
eslint-plugin-regexp 正则表达式规范
eslint-plugin-n Node.js 环境专属规范(回调、require、协议前缀)

此外还有两个关键元信息:

  • peerDependencies 声明 eslint: ^10.0.0(可选的 peer 依赖,即使用方自己安装 ESLint);
  • engines 要求 node: ^20.19.0 || ^22.13.0 || >=24,说明这套配置基于现代 Node.js 与 ESLint 10 的 flat config 体系。

从源码结构看,index.js 是包的主入口,它将 base 配置与 ESM/CJS 专属配置组合后导出:

module.exports = [
    ...baseConfigs,
    ...esmConfigs.map(config => ({
        files: ["**/*.js"],   // ESM 专属配置作用于 .js 文件
        ...config,
    })),
    ...cjsConfigs.map(config => ({
        files: ["**/*.cjs"],  // CommonJS 专属配置作用于 .cjs 文件
        ...config,
    })),
];

可以推断:在 "type": "module" 的项目里 .js 按 ESM 处理,.cjs 显式按 CommonJS 处理;在 CommonJS 项目里则直接使用 cjs 入口。

规则的出处:为什么「看规则文档」就够了

文档强调,任何一条规则的 rationale(设计理由)都可以通过查阅该规则的文档获得:

  • 若规则是 ESLint 核心规则,看仓库 docs/src/rules 下的对应页面,例如 no-varprefer-const 等规则页都解释了「为什么不允许这种写法」;
  • 若规则来自第三方插件(如 unicorn/*jsdoc/*n/*),则查阅对应插件的官方文档。

这一点对贡献者尤其重要:当你想修改某条规则或对某个配置提出异议时,第一步不是改配置,而是先理解该规则背后要解决的问题。ESLint 仓库中所有核心规则的实现在 lib/rules 下,例如 prefer-const 的检测逻辑在 lib/rules/prefer-const.js,文档在 docs/src/rules/prefer-const.md,二者一一对应。

配置入口总览:base / cjs / formatting / 默认

eslint-config-eslint 通过 package.jsonexports 暴露了四个入口:

入口 文件 适用场景
eslint-config-eslint(默认) index.js Node.js 项目,同时覆盖 .js(ESM)与 .cjs(CJS)
eslint-config-eslint/base base.js 非 Node.js 环境(浏览器脚本等),不含 Node 专属规则
eslint-config-eslint/cjs cjs.js CommonJS 项目(或文件),base + Node CJS 规则
eslint-config-eslint/formatting formatting.js 格式化类规则,与任意上述配置叠加使用

其中默认入口与 cjs 入口都复用了 base

  • index.jsbase + ESM 配置(.js)+ CJS 配置(.cjs)
  • cjs.jsbase + CJS 配置

formatting 入口独立成包,是因为文档明确说明:上述所有配置都不包含格式化规则"none of the above configurations includes formatting rules"),是否需要格式化由项目自行决定、按需叠加。

base 配置:核心规则的逐条解读

base.js 是整套规范的「地基」,由五个区块组成,按 index.js 的导出顺序 分别是:基础 linter 选项、unicorn 配置、jsdoc 配置、eslint-comments 配置、regexp 配置、核心 JS 规则。

1. 基础 linter 选项

{
    name: "eslint-config-eslint/base",
    linterOptions: {
        reportUnusedDisableDirectives: "error",
        reportUnusedInlineConfigs: "error",
    },
}

ESLint 团队把「无用指令」当作错误处理:如果代码里存在多余的 // eslint-disable-line 或内联配置(inline config),直接报错。这保证了仓库里不会残留过期的禁用注释——这是对 @eslint-community/eslint-plugin-eslint-comments 规则的补充,从 Linter 层面兜底。

2. 核心 JS 规则(js.configs.recommended 之上)

区块 eslint-config-eslint/js@eslint/jsrecommended 预设为基底,再叠加约 60 条额外规则,全部为 error 级别。按主题分组如下:

变量与声明

  • no-varprefer-constprefer-destructuring 不在列表但继承自 recommended;显式补充 no-undef: ["error", { typeof: true }](即使 typeof x 也要求 x 已声明)、no-undef-initno-undefinedno-use-before-define
  • no-unused-vars 配置为 { vars: "all", args: "after-used", caughtErrors: "all" },即所有变量、函数名都检查,参数「使用之后的位置」才算使用,catch 子句错误参数也必须被使用;
  • no-underscore-dangle 默认禁止下划线开头/结尾,但允许 this._foo 这种类私有字段惯例(allowAfterThis: true)。

函数与作用域

  • func-style: ["error", "declaration"]:强制函数声明而非函数表达式;
  • consistent-returndefault-casedefault-case-lastdefault-param-lastno-param-reassignno-inner-declarationsno-loop-funcno-shadow
  • prefer-arrow-callbackprefer-rest-paramsprefer-spread:现代化函数书写。

严格相等与类型安全

  • eqeqeq: "error":强制 === / !==
  • yoda: ["error", "never", { exceptRange: true }]:变量在左、字面量在右,但区间判断 1 < x && x < 5 除外;
  • radixrequire-unicode-regexpprefer-numeric-literalsprefer-exponentiation-operatorno-loss-of-precision(继承自 recommended)。

对象与字符串

  • object-shorthand: ["error", "always", { avoidExplicitReturnArrows: true }]:强制简写,但对象方法简写里不鼓励返回箭头函数;
  • prefer-templateno-useless-concatno-useless-computed-keyprefer-regex-literals

安全与危险 API

  • no-evalno-implied-evalno-new-funcno-new-wrappersno-extend-nativeno-protono-callerno-iteratorno-alertno-consoleno-script-urlno-return-assignno-sequencesno-throw-literal

一个体现项目风格的细节:no-restricted-properties

该规则禁止使用 assert 模块的宽松相等方法,并给出明确的替代提示:

"no-restricted-properties": [
    "error",
    { object: "assert", property: "equal", message: "Use assert.strictEqual instead of assert.equal." },
    { object: "assert", property: "notEqual", message: "Use assert.notStrictEqual instead of assert.notEqual." },
    { object: "assert", property: "deepEqual", message: "Use assert.deepStrictEqual instead of assert.deepEqual." },
    { object: "assert", property: "notDeepEqual", message: "Use assert.notDeepStrictEqual instead of assert.notDeepEqual." },
],

这体现了 ESLint 团队的一个核心约定:所有测试断言必须使用严格相等。对应地,根目录 eslint.config.js 中的 eslint/tests 区块还额外禁止了 assert.doesNotThrow()(要求用代码旁注释替代),两条规则共同约束着 tests 目录下几百个测试文件。

其他值得注意的取舍

  • no-process-exit: "off":Node 工具脚本中允许直接 process.exit()
  • strict: ["error", "global"]:全局严格模式(现代 ESM 天然严格,CJS 文件通过全局指令开启);
  • no-unneeded-ternaryno-else-return: ["error", { allowElseIf: false }]prefer-promise-reject-errors 等规整控制流。

3. JSDoc 规范(eslint-plugin-jsdoc)

ESLint 是一个重度使用 JSDoc 的项目——所有核心规则文件都以 /** @type {import("eslint").Linter.Rule} */ 之类的注释开头。eslint-config-eslint/jsdoc 区块在插件的 flat/recommended 之上做了三件事:

设置(settings)mode: "typescript"(按 TypeScript 风格解析 JSDoc);标签偏好 file -> fileoverviewaugments -> extendsclass -> constructor;类型偏好里最值得注意的一条——不鼓励 *anyobjectfunction 等泛泛类型,要求写具体类型:

preferredTypes: {
    "*": { message: "Use a more precise type or if necessary use `any` or `ArbitraryCallbackResult`", replacement: "any" },
    object: { message: "Use the specific object type or `Object` if truly arbitrary", replacement: "Object" },
    function: { message: "Point to a `@callback` namepath or `Function` if truly arbitrary in form", replacement: "Function" },
    Promise: { message: "Specify the specific Promise type, including, if necessary, the type `any`" },
    array: "Array",
    ...
}

规则开关jsdoc/check-syntaxjsdoc/no-bad-blocksjsdoc/require-asterisk-prefixjsdoc/require-descriptionjsdoc/require-throwsjsdoc/tag-lines 等设为 error;而 no-defaultsreject-any-typeno-undefined-typesrequire-yieldsrequire-throws-type 显式关闭,避免过度约束。

格式细节jsdoc/require-hyphen-before-param-description: ["error", "never"](参数描述前要连字符)、jsdoc/tag-lines 要求 @example 与描述之间留空行、@fileoverview 可任意,其余标签行内不留空行。

4. ESLint 指令注释规范(eslint-comments)

区块 eslint-config-eslint/eslint-comments 采用插件的 recommended 预设,并额外要求:

  • disable-enable-pair// eslint-disable 必须成对出现(不能只关不开);
  • require-description:每条 eslint-disable 指令必须写明原因,例如 // eslint-disable-next-line no-console -- 调试输出

结合前面 reportUnusedDisableDirectives: "error",ESLint 仓库对「禁用指令」的管理堪称苛刻:必须有原因、必须成对、必须真的被用到

5. 正则与 Unicorn 规则

  • regexp.configs.recommended:采用 eslint-plugin-regexp 的推荐预设,规范正则写法(配合核心规则 require-unicode-regexp,所有正则必须带 u 标志);
  • unicorn 区块只开启 11 条「现代 API 偏好」规则,例如 prefer-array-find(用 find 而非 filter()[0])、prefer-array-flatprefer-includesprefer-set-hasprefer-string-starts-ends-withprefer-at 等——只取最通用、争议最小的子集,而非全量开启 unicorn。

Node.js 专属配置:ESM 与 CJS 的差异

nodejs.js 定义了 ESM 与 CJS 两套配置,二者共享三条规则:

const sharedRules = {
    "n/callback-return": ["error", ["cb", "callback", "next"]],  // 回调后必须 return
    "n/handle-callback-err": ["error", "err"],                   // 回调错误参数必须处理
    "n/prefer-node-protocol": "error",                           // 用 node: 前缀导入内置模块
};

差异在于 CJS 额外启用了三条 require 相关规则(CommonJS 才有的 require 机制):

  • n/no-mixed-requires:同一语句中禁止混用同步 require 与变量声明;
  • n/no-new-require:禁止 new require(...)
  • n/no-path-concat:禁止用字符串拼接构造模块路径(应用 path.join)。

ESM/CJS 的基底分别来自 eslint-plugin-n 的 flat/recommended-moduleflat/recommended-script。这套「共享 + 差异」的设计在默认入口 index.js 中被具象化为:.js 文件挂 ESM 配置、.cjs 文件挂 CJS 配置。

formatting 配置:格式化规则的逐条解读

formatting.js 独立导出一个 rules 对象,集中了全部纯排版规则,且全部为 error 级。最核心的几组:

缩进与换行

indent: ["error", 4, { SwitchCase: 1 }],   // 4 空格缩进,switch case 再缩进 1 级
"max-len": ["error", 160, { ignoreComments: true, ignoreUrls: true, ignoreStrings: true, ignoreTemplateLiterals: true, ignoreRegExpLiterals: true }],
"no-multiple-empty-lines": ["error", { max: 2, maxBOF: 0, maxEOF: 0 }],  // 最多连续 2 空行,文件首尾禁止空行

引号与分号

quotes: ["error", "double", { avoidEscape: true }],  // 双引号;字符串内含双引号时允许用单引号
semi: "error",                                        // 必须分号
"comma-dangle": "error",                              // 多行时必须有尾逗号
"quote-props": ["error", "as-needed"],                // 对象键只有在必须时才加引号

括号与空格

"arrow-parens": ["error", "as-needed"],                // 单参数箭头函数省略括号
"space-before-function-paren": ["error", { anonymous: "never", named: "never", asyncArrow: "always" }],
"object-curly-spacing": ["error", "always"],           // 对象花括号内侧必须有空格
"object-curly-newline": ["error", { consistent: true, multiline: true }],
"template-curly-spacing": ["error", "never"],          // 模板字符串 ${} 内不留空格
"spaced-comment": ["error", "always", { exceptions: ["-"] }],  // 注释 // 或 /* 后必须有空格,允许 "-" 例外

分组排版约定

padding-line-between-statements 强制「变量声明块与后续语句之间必须有空行,但连续变量声明之间可无空行」:

"padding-line-between-statements": [
    "error",
    { blankLine: "always", prev: ["const", "let", "var"], next: "*" },
    { blankLine: "any", prev: ["const", "let", "var"], next: ["const", "let", "var"] },
],

另外还有 brace-style: ["error", "1tbs"](大括号一行式)、operator-linebreakwrap-iifeyield-star-spacinggenerator-star-spacing 等常规排版规则。这套格式化配置与仓库实际代码风格完全一致——例如 base.js 本身就用 4 空格缩进、双引号、尾逗号。

在项目中使用:安装与配置示例

安装

先安装 ESLint,再安装本配置(README.md 提供了 npm / yarn / pnpm / bun 四种方式):

npm install eslint -D
npm install eslint-config-eslint -D

注意 eslint-config-eslintpeerDependencies 要求 ESLint ^10.0.0,因此请使用支持 flat config 的 ESLint 10 及以上版本,Node 版本不低于 ^20.19.0 || ^22.13.0 || >=24

场景一:ESM 项目("type": "module")

// eslint.config.js
import { defineConfig } from "eslint/config";
import eslintConfigESLint from "eslint-config-eslint";

export default defineConfig([eslintConfigESLint]);

默认入口会自动处理 .js(ESM)与 .cjs(CJS)两类文件。

场景二:CommonJS 项目

// eslint.config.js
const { defineConfig } = require("eslint/config");
const eslintConfigESLintCJS = require("eslint-config-eslint/cjs");

module.exports = defineConfig([eslintConfigESLintCJS]);

场景三:base 配置(非 Node.js 文件)

base 配置不含任何 Node.js 专属规则,适合浏览器端脚本。README 给出的混合示例非常典型——浏览器脚本用 base,Node 工具与配置文件用 cjs:

const { defineConfig } = require("eslint/config");
const eslintConfigESLintBase = require("eslint-config-eslint/base");
const eslintConfigESLintCJS = require("eslint-config-eslint/cjs");

module.exports = defineConfig([
    {
        files: ["scripts/*.js"],                 // 浏览器脚本:base
        extends: [eslintConfigESLintBase],
    },
    {
        files: ["eslint.config.js", ".eleventy.js", "tools/*.js"],  // Node 环境:cjs
        extends: [eslintConfigESLintCJS],
    },
]);

场景四:叠加格式化规则

import { defineConfig } from "eslint/config";
import eslintConfigESLint from "eslint-config-eslint";
import eslintConfigESLintFormatting from "eslint-config-eslint/formatting";

export default defineConfig([eslintConfigESLint, eslintConfigESLintFormatting]);

formatting 是可选的最后一块拼图——ESLint 官方将此与「编辑器格式化」职责区分:格式化规则交给 ESLint 执行,但默认不强制。

真实案例:ESLint 仓库如何自用这套规范

仓库根目录 eslint.config.js 是这套配置的最佳实战范本。它先引入 eslint-config-eslint/cjs 作为全量基底:

const eslintConfigESLintCJS = require("eslint-config-eslint/cjs");
// ...
module.exports = defineConfig([
    {
        name: "eslint/cjs",
        files: [ALL_JS_FILES],   // "**/*.js"
        extends: [eslintConfigESLintCJS],
    },
    // ... 后续区块
]);

然后基于不同文件类型叠加数百行「项目专属」规则,可看到对上述配置体系的典型用法:

  • 忽略策略globalIgnores 排除 docstemplatestests/fixtures 等目录,因为示例代码、生成文件不参与 lint;
  • 规则叠加与豁免eslint/tools 区块对 tools/*.jsdocs/tools/*.js 关闭 no-consolen/no-process-exit(工具脚本允许打印与退出);eslint/deprecated-rules 对已废弃规则文件放宽插件约束;
  • 插件式扩展:对 lib/rules/*.js 挂载 eslint-plugin 插件的 rules-recommended,要求每条核心规则文件具备规范的 meta 信息与文档描述格式(如 report-message-format 要求报错信息首字母大写、以句号结尾);
  • 目录隔离:用 n/no-restricted-require 限制 lib 内部模块的跨目录 require,保证分层架构不被破坏;
  • 多语言文件:对 JSON/JSONC/JSON5 使用 @eslint/json 的语言级配置,对 *.ts 使用 TypeScript parser,对 YAML 挂载 eslint-plugin-yml。

这套「通用基底 + 按目录/语言微调」的写法,正是 ESLint 团队推荐给大型仓库的实践:公共规范由 eslint-config-eslint 统一维护,项目专属规则放在自己的配置文件中覆盖。

修改规范的正确姿势:从理解到提议

文档对「想改规范的人」给出了明确指引:先看规则的官方文档理解其设计意图(核心规则见 docs/src/rules,插件规则见插件文档),再做决定。这与仓库中 propose-rule-change.md 等流程文档的理念一脉相承——规范是集体共识的执行化,不是个人的口味偏好。

如果改动涉及 package.json,文档特别提示参见 package-json-conventions.md。这份配套规范同样非常具体,例如:脚本名只允许小写字母、:(分隔部分)、-(分隔单词)、+(分隔文件扩展名);脚本必须按字母序排列;linttestbuildfetchfmtreleasestart 各有明确语义;lint:fixfmt:check 等修饰符有严格顺序(如 lint:fix:js:watch 而不是 lint:watch:js:fix)。代码规范(怎么写字)与包工程规范(怎么组织脚本)共同构成了 ESLint 的完整开发约定。

小结

  • 规范主体eslint-config-eslintpackages/eslint-config-eslint),由文档 code-conventions.md 指定为唯一权威来源;
  • 四个入口:默认(ESM+CJS 自动分流)、/base(非 Node 环境)、/cjs(CommonJS)、/formatting(可选格式化);
  • 六类规则来源@eslint/js 推荐预设 + jsdoc + unicorn + eslint-comments + regexp + eslint-plugin-n,再叠加约百条定制规则;
  • 三大特色约定:严格相等断言(禁 assert.equal 等)、禁用指令必须有原因且不得冗余、JSDoc 类型必须具体;
  • 实战范本:根目录 eslint.config.js 展示了如何在自己的仓库中组合这套配置并叠加项目专属规则。

无论你是 ESLint 贡献者还是普通使用者,都可以把 eslint-config-eslint 当作一套「高质量 JavaScript 工程默认值」来参考或直接采用——它既是 ESLint 团队的自律工具,也是一份可迁移到任意项目的规范模板。

热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
900
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
927
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.94 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
603
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
396
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
527