
当我们于四月份发布 ESLint v9.0.0 时,这是 30 个月以来的第一个主要版本,并正式引入了新的 配置 系统。 ESLint v9.0.0 还进行了一些规则 API 更改,为核心功能的未来发展做准备。发布后,我们花费了大量时间创建兼容性实用程序、配置迁移工具 和 规则 API 转换实用程序,以帮助生态系统迁移到 ESLint v9.0.0。所有这些工作对于我们继续进行 ESLint 即将到来的重大变更都是必要的。
语言插件
两年前,TSC 决定是时候开放 ESLint,使其可以检查 JavaScript 以外的语言。实际上,ESLint 核心做了很多与 JavaScript 无关的事情:查找和读取文件、加载特定于文件的配置、收集规则冲突、将结果输出到控制台等等。此外,我们不断发现一些插件在 ESLint 内部检查其他语言(例如 GraphQL 和 HTML),通过与 JavaScript 相同的逻辑传递不同的 AST。这是低效且容易出错的,必须有一种更好的方法。
2023 年,语言插件 RFC 获得正式批准。然而,为了实施此 RFC,我们需要正式发布新的配置并进行一些规则 API 更改。特别是规则 API 更改,对于分离 ESLint 核心正在做的事情以及语言插件将为自定义规则提供什么至关重要。在完成这两项更改后,我们开始了繁琐的重构 ESLint 核心的任务,将特定于 JavaScript 的部分从与语言无关的部分中提取出来。
以下是未来的预期(没有具体时间表,因为一切都取决于贡献者的可用性)
@eslint/js
- 我们将逐步将所有与 JavaScript 相关的功能移至@eslint/js
包中,包括规则和文档。我们将重用现有的 Espree repo 并将其转换为包含 Espree、eslint-scope
、eslint-visitor-keys
和所有核心 JavaScript 规则的 monorepo。这将使我们能够在单个 repo 中处理所有与 JavaScript 相关的内容。@eslint/json
- 我们的第一个语言插件将允许 ESLint 本地检查 JSON 文件。此插件将包含解析逻辑以及所有相关的规则和文档。与@eslint/js
类似,这将使我们拥有一个专注于 JSON 检查的单一 repo,进一步将任何特定于语言的功能从核心中移出。@eslint/markdown
-eslint-plugin-markdown
将重命名为@eslint/markdown
,以便更好地与其他包对齐。我们将添加检查 Markdown 的能力以及特定于 Markdown 的规则。
当前的计划是拥有这三个官方 ESLint 语言插件,希望它们可以作为生态系统开发更多语言插件的示例。
随着特定于语言的功能被提取到单独的存储库中,剩下的任务就是完全重写核心。
核心重写
自 11 年前 ESLint 首次创建以来,ESLint 存储库中代码的架构并没有发生太大变化。因此,积累了大量的技术债务,阻碍了我们进行一些我们想做的更改。我们目前面临的一些问题:
- 同步核心逻辑。
Linter
类是完全同步的,这意味着我们无法支持异步规则或异步解析,这是我们经常收到的两个功能请求。我们需要添加第二个异步工作的类,或者向Linter
添加异步工作的替代方法。这两种选择看起来都需要大量工作,实际上相当于重写核心的大部分内容。 - 有限的 API。 公共 API 是有限的,因为 ESLint 最初从未打算用作 API。最初的
linter
对象仅用于启用浏览器演示。后来它被Linter
类取代,甚至更晚又出现了ESLint
类,以完全模仿命令行界面。当我们想要公开新功能时,它需要存在于这两个听起来相似的 API 之一中,不幸的是,主要决策点是 API 是否需要在浏览器中可用。如果是,则需要放在Linter
中,否则需要放在ESLint
中。这既令人困惑,从长远来看也是不可持续的。 - 缺乏类型检查。 我们一直希望在存储库中添加类型检查以帮助捕捉潜在问题,但过去尝试这样做需要大量工作和协调。最终,我们得出结论,这不值得付出努力。
- 停留在 CommonJS 中。 与类型检查情况类似,虽然有人希望将代码库转换为 ESM,但这样做需要花费大量的工作。我们将花费大量时间进行转换,最终得到的只是等效的功能集。
在考虑了持续增量更改现有核心或从头开始的选项后,我们决定是时候从头开始了。虽然我们坚信完全重写代表着巨大的风险,但在经历了 11 年之后,我们可以诚实地说,我们从最初的架构中获得了最大的收益。
展望未来,以下是您可以期待的:
- 一个新的存储库。
eslint/rewrite
存储库是我们与语言无关的工作的新家。它是一个 monorepo,我们将在其中管理所有以某种方式直接与核心相关的包。 - 现代化的包。
eslint/rewrite
中的每个包都将达到现代标准,发布 ESM 入口点以及类型定义。如果可能,我们还将发布 CommonJS 入口点。所有包都发布了 provenance 到 npm 和 JSR(如果适用)。 - 运行时无关的核心。
@eslint/core
包将包含 ESLint 的运行时无关 API 以及核心的类型定义。我们将随着新 API 的设计,逐步构建此包,从eslint
包中提取功能片段。随着每年涌现出更多的 JavaScript 运行时,我们认为运行时无关的核心变得越来越重要。 - 可组合的 API。 新的 API 不会将所有功能捆绑到一个或两个类中,而是可组合的,许多专门构建的类旨在一起使用。类将遵循单一职责原则,从而更容易进行更改和在集成内部自定义 ESLint。
- 新的 CLI。 我们将在
@eslint/node
包中从头开始重写 CLI。我们将从最常见的用例开始,并重新评估每个标志,以确保它对于与语言无关的核心仍然有意义。使用不同的包名称使我们可以在不中断eslint
包使用的情况下试验新的 CLI。此包还将导出其他特定于 Node.js 的 API。 - 并行开发。 我们将继续并行维护
eslint
和重写,这样我们就不会为了全新的东西而牺牲现有的包。目标是使eslint
包逐渐变小,并利用@eslint/core
中包含的 API,以便我们尽可能地限制重复。
结论
我们对 ESLint 的未来发展方向感到非常兴奋,因为我们认为这代表了 ESLint 发展的下一个合乎逻辑的步骤。将 ESLint 转变为任何人都可以为其编写插件的与语言无关的 linter,将通过减少任何一个项目所需的 linter 工具和编辑器扩展的数量来简化开发。此外,将我们的核心重写为更可组合的 API 可以实现更轻松和更可定制的集成,并拥有我们自己的类型定义,以确保与 API 的兼容性。
ESLint 以其当前的架构经历了 11 年,我们希望这些变化能帮助我们度过未来的 11 年。