news 2026/9/13 13:48:28

ESLint 配置组合实战:用 `extends` 合并配置对象与配置数组

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ESLint 配置组合实战:用 `extends` 合并配置对象与配置数组

ESLint 配置组合实战:用extends合并配置对象与配置数组

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

在实际项目中,eslint.config.js很少完全从零手写:你通常会将社区预定义配置、可共享配置(shareable config)与自己的覆写规则组合在一起,形成项目专属的 ESLint 配置。本文围绕 docs/src/use/configure/combine-configs.md 讲解的两种核心组合模式——应用配置对象应用配置数组——展开,并结合当前仓库源码,说明extends键在 flat config 体系下的求值顺序、合并语义与底层实现,让你能正确、灵活地组装自己的配置文件。

前置概念:配置对象与配置数组

在理解组合之前,需要先明确两个基础概念(详见 glossary 术语表):

  • 配置对象(Config Object):一个包含filespluginsruleslanguageOptions等键的普通对象,描述了 ESLint 在满足某些条件时如何解析与检查文件。
  • 配置数组(Config Array):配置对象的数组。eslint.config.js导出的就是一个配置数组。数组内的对象按顺序求值,后面的对象可以覆盖前面对象中同名规则的设置——这正是 flat config 区别于旧 eslintrcextends深层合并的关键语义。

每个配置文件都导出配置数组,例如下面这份只含一个配置对象的配置(来自 configuration-files.md 的示例):

// eslint.config.js import { defineConfig } from "eslint/config"; export default defineConfig([ { rules: { semi: "error", "prefer-const": "error", }, }, ]);

这里的defineConfig()eslint/config提供的辅助函数,它的作用仅仅是让编辑器获得更好的类型提示与自动补全,其返回值仍是一个配置数组,与直接写数组字面量等价。

defineConfigeslint/config入口

在 lib/config-api.js 中可以看到eslint/config模块的真实导出:

const { defineConfig, globalIgnores, includeIgnoreFile, } = require("@eslint/config-helpers"); module.exports = { defineConfig, globalIgnores, includeIgnoreFile, };

也就是说,eslint/config对外提供了三个辅助函数:defineConfig(定义配置数组)、globalIgnores(全局忽略)、includeIgnoreFile(引入.gitignore等忽略文件)。它们均来自@eslint/config-helpers包,ESLint 只是将其重新导出,方便统一从eslint/config引入。组合配置时我们主要使用defineConfig包裹整个配置数组。

模式一:应用一个配置对象(files+extends

当从其他模块导入一个配置对象时,可以创建一个带files键的新对象,并通过extends键把导入对象的其余属性合并进来,从而让该配置只作用于文件子集。

// eslint.config.js import js from "@eslint/js"; import { defineConfig } from "eslint/config"; export default defineConfig([ { files: ["**/*.js"], plugins: { js, }, extends: ["js/recommended"], rules: { "no-unused-vars": "warn", }, }, ]);

这段配置的含义是:

  1. files: ["**/*.js"]限定本配置对象只作用于所有.js文件;
  2. extends: ["js/recommended"]@eslint/js包内置的recommended预定义配置(一个配置对象)合并进来。当前仓库中该预定义配置定义在 packages/js/src/index.js:
configs: { all: require("./configs/eslint-all"), recommended: require("./configs/eslint-recommended"), },
  1. rules: { "no-unused-vars": "warn" }在合并后的基础上追加规则覆写。

由于filesextends位于同一个对象内,合并后的完整配置(预定义配置的规则 + 你自己的覆写)只会应用到"**/*.js"匹配的文件上。这是"先限定文件范围、再注入预定义配置"的标准写法。

为什么要显式声明plugins

注意上面的示例在extends之外还显式声明了plugins: { js }。原因是:在 flat config 中,规则所属的插件必须在每个使用它的配置对象里可见extends合并进来的是@eslint/js内部的规则定义,而js/recommended这样的引用字符串最终要解析为某个已注册插件下的规则集。保持插件对象与配置对象同处一个作用域,可以让合并与后续校验(见下文Config类)顺利进行。这是从当前仓库 Config 类的规则解析逻辑 可以观察到的约束:规则 ID 中的插件名会通过config.plugins查找对应插件。

模式二:应用一个配置数组(extends数组)

当从其他模块导入的是一个配置数组时,同样可以通过extends键把它应用到文件子集。例如从eslint-config-example包导入配置:

// eslint.config.js import exampleConfigs from "eslint-config-example"; import { defineConfig } from "eslint/config"; export default defineConfig([ { files: ["**/*.js"], extends: [exampleConfigs], rules: { "no-unused-vars": "warn", }, }, ]);

这里extends: [exampleConfigs]传入的是一个配置数组(数组中每个元素是配置对象)。行为与模式一完全一致:exampleConfigs中的全部配置对象先被展开合并,然后rules中的no-unused-vars: "warn"作为后续配置对象追加覆盖。

两种模式的本质区别

对比维度模式一:应用配置对象模式二:应用配置数组
extends中的元素类型配置对象(或引用该对象的字符串,如"js/recommended"配置对象、配置数组(或引用它们的字符串)
典型来源@eslint/jseslint-plugin-*导出的单个对象可共享配置包导出的数组
应用范围受外层files约束受外层files约束,行为一致

事实上,两种模式在底层走的是同一条路径:extends接受"字符串、配置对象或配置数组"构成的列表,这些元素会被依次展开后与当前对象的files合并。这与 configuration-files.md 中对extends键的官方定义一致:

extends- An array of strings, configuration objects, or configuration arrays that contain additional configuration to apply.

合并语义:数组按序求值,后者覆盖前者

理解组合配置最关键的一点是合并顺序。在 flat config 中:

  • 配置数组内的对象按顺序求值
  • 对于规则配置,后出现的对象会覆盖(override)先前对象中同名规则的设置;
  • 对于数组型配置(如languageOptions的某些字段),则按deepMergeArrays的策略合并——该实现位于 lib/shared/deep-merge-arrays.js(Config类构造时引入)。

以一个三段式配置为例:

export default defineConfig([ { files: ["**/*.js"], extends: ["js/recommended"], }, { files: ["**/*.js"], rules: { "no-unused-vars": "warn", // 覆盖 recommended 中该规则的默认级别 }, }, { files: ["**/*.test.js"], rules: { "no-unused-vars": "off", // 测试文件里彻底关闭 }, }, ]);

第一段注入js/recommended,第二段把no-unused-vars调整为warn,第三段只对测试文件关闭该规则。这正是"预定义配置 + 项目覆写 + 文件子集特例"的典型三段式结构,也是extends组合模式最常见的实际用法。

底层机制:FlatConfigArrayConfig类的处理流程

组合配置在运行时经历两个关键阶段(对应仓库源码):

  1. 展开与合并:配置文件导出的数组被包装成 FlatConfigArray(继承自@eslint/config-arrayConfigArray)。其中的preprocessConfig钩子(lib/config/flat-config-array.js#L182-L202)负责把extends中的字符串与数组元素替换为真实对象后再进入规范化流程。

  2. 校验与规范化:每个合并完成的配置对象交由 Config 类 处理:用ObjectSchema(flatConfigSchema)校验键的合法性、解析languageprocessor、合并languageOptions,最后通过#normalizeRulesConfigvalidateRulesConfig(lib/config/config.js#L608-L633)统一规则配置格式——例如把"warn"规范化为数字1,并依据规则的meta.schema校验选项。这也解释了为什么extends合并进来的规则在运行前就会被严格校验,拼写错误的规则名会直接抛出带插件提示的TypeError

与 eslintrcextends的差异

如果你熟悉旧的 eslintrc 配置体系,需要特别注意:flat config 中extends的行为与 eslintrc 时代的"继承链 + 深层对象合并"完全不同——这里不存在递归继承的层级,只有同一数组内对象的顺序覆盖。旧式 eslintrc 的envglobalsignorePatternsoverrides等键在 flat config 中均为非法键,见 flat-config-schema.js 中的 eslintrcKeys 清单(extends本身也在其中被列为 eslintrc 风格、应替换为 flat config 写法的键)。

组合配置的进阶实战模式

基于上述两条核心模式,可以组合出更复杂的实战写法:

(1)多个预定义配置叠加,并分别限定文件范围:

import js from "@eslint/js"; import ts from "typescript-eslint"; import { defineConfig } from "eslint/config"; export default defineConfig([ { files: ["**/*.js"], extends: ["js/recommended"], }, { files: ["**/*.ts"], extends: [...ts.configs.recommended], }, ]);

(2)命名配置对象,便于排查与调试:

export default defineConfig([ { name: "project/javascript", files: ["**/*.js"], extends: ["js/recommended"], rules: { "no-unused-vars": "warn" }, }, ]);

name是配置对象的可选元信息,运行eslint --print-config或使用调试工具时可以据此快速定位是哪一段配置影响了文件。

(3)配合globalIgnores组织全局忽略:

import { defineConfig, globalIgnores } from "eslint/config"; export default defineConfig([ { files: ["**/*.js"], extends: ["js/recommended"], }, globalIgnores(["dist/**", "node_modules/**"]), ]);

globalIgnores()生成一个只含ignores的配置对象,其模式会对所有其他配置对象生效——这正是在 lib/config-api.js 中重新导出的辅助函数之一,可作为组合配置的补充模块。

常见陷阱与排查建议

  • extends元素类型错误extends只接受字符串、配置对象或配置数组。直接传入函数、Promise等会在校验阶段被ObjectSchema拒绝,报错信息会指明非法键。
  • 漏声明插件:当extends引用的字符串(如"js/recommended")所依赖的插件对象没有在当前配置对象的plugins中注册时,运行时会在 Config 类的规则查找逻辑 处抛出Key "rules": Key "xxx": Could not find plugin类错误,并提示可能的拼写或插件名。
  • 覆盖顺序反了:想覆写预定义配置的规则,覆写对象必须排在extends所在对象之后(或与extends同处一个对象、位于其后),否则会被预定义配置"反覆盖"。
  • 调试手段:使用npx eslint --print-config <file>查看某个文件最终生效的合并结果,或借助--debug输出配置解析过程,都可以快速确认组合是否正确。

小结

组合配置是 flat config 体系下日常开发最常用的能力:通过files限定作用范围、用extends注入预定义或可共享的配置对象/数组、再叠加自己的规则覆写,即可拼装出结构清晰、按文件类型差异化的完整配置。其底层由 FlatConfigArray 负责展开合并、Config 负责规范化与校验,理解这两层的求值顺序与校验时机,你就能避免绝大多数配置组合过程中的"灵异问题"。

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

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 13:46:29

Abaqus焊接仿真Python接口:热源动态控制与多道次耦合实现

简介&#xff1a;这是一套面向ABAQUS初学者与焊接仿真工程师的专用插件工具包&#xff0c;即3DSwym Abaqus Welding Interface&#xff08;AWI&#xff09;6.14小版本全兼容焊接仿真接口&#xff0c;专为简化激光焊、氩弧焊、真空电子束焊及搅拌摩擦焊等多工艺热-力耦合仿真流程…

作者头像 李华
网站建设 2026/9/13 13:44:44

RAP开发实战:CDS + Fiori Elements实现主明细显示

做SAP S/4HANA扩展开发的人应该都碰到过这种需求&#xff1a;业务方拿着一张纸质单据过来&#xff0c;说“我要在这个页面上&#xff0c;上面显示抬头信息&#xff0c;下面能够维护明细行&#xff0c;还能增删改”。以前遇到这种主明细&#xff08;Master-Detail&#xff09;场…

作者头像 李华
网站建设 2026/9/13 13:38:25

多Agent系统可观测性实战:从埋点设计到故障排查的完整方法论

我们团队最近把三条业务线全部迁到了基于大模型的多Agent协作架构上&#xff0c;系统跑起来的那一瞬间&#xff0c;所有人都很兴奋。但等兴奋劲过了&#xff0c;真正让人头疼的事情才刚开始&#xff1a;系统在测试环境一切正常&#xff0c;一到生产环境就开始“抽风”&#xff…

作者头像 李华