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