ESLint's new config system - part one 直译

132 阅读9分钟

ESLint的新配置系统-1(背景)

写在最前面的话:本文是对 Nicholas C. Zakas (eslint 作者)关于elint新配置系统说明的直译,翻译未经校准,看不懂的地方请配合原文食用,谢谢!!!


ESLint 的配置系统在 2013 年开始时相当简单。从那时起它变得更加复杂,是时候做出改变了。

当 ESLint 于 2013 年首次发布时,配置系统相当简单。您可以在 .eslintrc 文件中定义要启用或禁用的规则。当文件被 linted 时,ESLint 将首先在与该文件相同的目录中查找 .eslintrc 文件,然后继续沿目录层次结构向上直到到达根目录,合并沿途找到的所有 .eslintrc 文件中的配置。这个系统,我们称之为配置级联,允许您轻松覆盖特定目录的规则,这是 JSHint 无法做到的。您还可以在 package.json 内的 eslintConfig 字段中添加更多配置。

然而,多年来,配置系统变得一团糟。这就是为什么我在 2019 年建议创建一个新的配置系统,以便在 JavaScript 项目变得越来越复杂的世界中更轻松地配置 ESLint。新配置系统的一个重要部分已经被合并到主分支中,所以是时候开始学习如何在未来配置ESLint了。但为了做到这一点,回顾一下我们是如何进入当前状态的是有帮助的。

增量变化导致最大的复杂性

回顾当前的配置系统(称为 eslintrc 系统)是如何演变的,每一步对于我们当时的情况都具有逻辑意义。ESLint 始终采用渐进式开发方法,我们寻找改进现有技术的方法,而不是扔掉东西重新开始。 eslintrc 系统也不例外。

extends 字段

eslintrc 的第一个重大变化是引入了 extends 键。从 JSHint 借用的 extends 密钥允许用户导入另一个配置,然后对其进行扩充,例如:

{
  "extends": ["./other-config.json"],
  "rules": {
    "semi": "warn"
  }
}

因此,假设 ./other-config.json 有一些配置数据,您可以导入它,然后在其上添加您自己的规则设置。出于多种原因,这对 ESLint 来说是向前迈出的一大步。

首先,扩展实际上先于可通过 npm 分发的可共享配置的想法。在扩展的实现过程中,我们意识到可共享的配置是可能的。extends 中指定的文件是通过 Node.js require() 函数加载的,因此 Node.js 可以通过该函数加载的任何内容也可以作为扩展的配置。

其次,扩展允许我们实现 eslint:recommended,我们认为对每个人来说启用的一组规则都很重要。最初,ESLint 默认启用了几条规则,但这成为用户的负担。因此,我们默认关闭所有规则,这对于没有看到任何规则的新用户来说也很困惑。添加 eslint:recommended 允许我们明确表示您包含了我们推荐的一系列规则,但如果您不想,也可以删除它们。

事后看来,如果我们多考虑一下,我们就会在此时删除配置级联。引入扩展启用了许多与级联相同的用例,而保留这两个用例结果却是一团糟,我们需要花费数年时间来尝试修复。

个人配置

当人们要求我们在 ~/.eslintrc 添加个人配置文件的功能时,又增加了一层复杂性。因此,我们添加了一项额外的检查:如果我们在文件位置的祖先中没有找到配置文件,那么我们将自动查找个人配置文件。

多种配置文件格式

作为重构的一部分,我们发现允许不同的配置文件格式是微不足道的。我们可以将 JSON 格式形式化为 .eslintrc.json,并添加对 YAML(.eslintrc.yml 或 .eslintrc.yaml)和 JavaScript(.eslintrc.js)的支持,而不是强迫每个人都使用非标准 .eslintrc 文件。为了向后兼容,我们继续支持 .eslintrc,因为需要保留的代码量很少。

事后看来,这也不是一个好主意。添加 JavaScript 配置文件格式导致它与非 JS 格式之间不兼容:任何 JavaScript 对象都可以传递到配置中并在规则中可用。因为我们没有正确验证配置以完全匹配非 ​​JS 格式,所以我们最终得到了一些规则,要求传入正则表达式对象才能正确配置。虽然这可以在 JS 配置文件格式中工作,但无法在非 JS 配置文件中正确配置规则。不幸的是,由于插件规则依赖于此功能,因此我们无法在不破坏内容的情况下返回并修复它。

可共享的配置和依赖项

也许我们早期面临的最大问题是 npm 决定停止在 v3 中安装对等依赖项。在此之前,我们建议可共享配置包含它们作为对等依赖项而不是常规依赖项所依赖的任何插件。这是扩展实现方式的一个怪癖:使用 require()。

由于可共享配置仅包含数据,无法直接引用 Node.js 依赖项,因此 require() 不会自动将直接依赖项加载到 ESLint 解析它们的路径中。另一方面,仅使用 require() 就可以完美地工作,因为这些依赖项安装在正常包查找工作的位置。

当 npm v3 默认停止安装对等依赖项时,所有依赖此行为的共享配置都将停止正常工作。有一个长期存在的问题,要求允许可共享配置直接使用依赖项,但 eslintrc 的架构不允许这样做。我们本质上必须在 ESLint 内部重新创建整个 require() 功能,以解决可共享配置的设计方式。我们建议可共享配置创建一个安装后脚本来安装其对等依赖项。无论怎么想都不是理想的。

我们添加了 --resolve-plugins-relative-to 命令行选项来尝试帮助解决这个问题,但这还不够。我们的 Discord #help 频道中最受欢迎的帮助请求与配置文件中的插件解析不当有关。

npm 最终在 v7 中改回默认安装对等依赖项,但此时对 ESLint 生态系统的损害已经造成。

root 字段

随着时间的推移,配置级联继续给用户带来问题。最常见的是,人们不会意识到他们正在处理的项目的祖先目录中有一个配置文件。这会造成混乱,因为他们将获得他们看似未配置的 ESLint 设置。

为了帮助解决这个问题,我们引入了配置文件的 root 属性。当在配置中指定 root: true 时,对进一步配置文件的搜索不会继续到祖先目录。这消除了一些混乱,我们最终自动在 ESLint 通过旧的 --init 命令生成的配置中包含 root: true ,以帮助用户以最少的混乱开始。

overrides 字段

ESLint 继续收到关于更强大的方式来配置其项目的请求。更具体地说,有人请求从现有配置文件中提供基于全局的配置。这导致了一个覆盖键的创建,它可以让您进一步修改 ESLint 检测的特定文件子集的配置。这是一个例子:

{
  "rules": {
    "quotes": ["error", "double"]
  },

  "overrides": [
    {
      "files": ["bin/*.js", "lib/*.js"],
      "excludedFiles": "*.test.js",
      "rules": {
        "quotes": ["error", "single"]
      }
    }
  ]
}

在这种情况下,bin 和 lib 中的 JavaScript 文件更喜欢单引号,而不是其他地方首选的双引号。

事实证明,基于全局配置的覆盖键是完成配置级联尝试执行的操作的更好方法。后看来,这再次是尝试移除级联的最佳时机……但我们没有。复杂性还不止于此。

向 overrides 中添加 extends

eslintrc 开发的最后一步是添加 extends 键来覆盖配置,允许用户将额外的配置数据注入到基于 glob 的配置对象中,如下所示:

{
  "rules": {
    "quotes": ["error", "double"]
  },

  "overrides": [
    {
      "files": ["bin/*.js", "lib/*.js"],
      "excludedFiles": "*.test.js",
      "extends": ["eslint:recommended"],
      "rules": {
        "quotes": ["error", "single"]
      }
    }
  ]
}

此添加还引入了很多额外的复杂性,因为我们必须弄清楚如何合并两个不同配置之间的全局模式。最终结果是覆盖配置内部的扩展将使用 AND 运算符来合并文件和排除文件。如果您不确定这到底意味着什么,那么您并不孤单。就连我们自己也感到困惑。

简化的必要性

2019 年新年前后,我越来越担心 eslintrc 系统的复杂性。我们收到越来越多的问题,涉及与加载无法找到其他配置文件或插件的配置文件相关的晦涩错误消息。此外,团队集体开始害怕接触任何与配置系统有关的东西。没有人真正理解计算任何给定文件的最终配置的所有不同排列。我们陷入了许多软件项目所犯的陷阱:我们不断添加新功能,而没有退后一步从整体上审视问题(和解决方案)。这导致我们的代码库中出现了几乎无法维护的部分。

正是在这个时候,我做了一个思想实验:如果我今天从头开始,了解我现在所知道的关于 ESLint 的一切,配置系统会是什么样子?接下来是 ESLint 历史上最具争议的 RFC 提案。当时,团队中几乎一半人想要抛弃 eslintrc 并从头开始,另一部分人认为 eslintrc 可以通过更多迭代来保存。最终,经过 18 个月的修改和辩论,我们决定是时候开始考虑当今的现实而构建全新的配置系统了。

前进的道路

现在是 2022 年,我们终于在 v8.21.0 中发布了新配置系统的第一个实现。新系统被我们戏称为“平面配置”,旨在让现有 ESLint 用户感到熟悉,同时极大地简化设置配置文件的过程。随着我们继续处理错误和收集反馈,平面配置尚无法通过 CLI 获得,但直接使用 API 的开发人员可以使用它。我将在本系列的第二部分中讨论平面配置的设计。

Tags

Configuration Command Line