ESLint 的新配置系统,第 2 部分:扁平配置简介

ESLint 的新配置系统,昵称为平面配置,旨在既熟悉又比原来的配置系统更简单。

在我之前的帖子中,我谈到了 eslintrc 配置系统是如何通过一系列小的、渐进的变化变得比必要的更复杂的。另一方面,扁平配置系统从一开始就被设计为在多方面更简单。我们借鉴了过去六年 ESLint 开发中的所有经验,提出了一种整体的配置方法,将 eslintrc 的优点与其他 JavaScript 相关工具的配置方式结合起来。其结果希望让现有 ESLint 用户感到熟悉,同时比以前可能实现的功能更强大。

🌐 In my previous post, I talked about how the eslintrc config system had grown to be more complex than necessary through a series of small, incremental changes. The flat config system, on the other hand, was designed from the start to be simpler in a number of ways. We took all of the learnings from the previous six years of ESLint development to come up with a holistic approach to configuration that took the best of eslintrc and combined it with the way other JavaScript-related tools handled configuration. The result is something that hopefully feels familiar to existing ESLint users and is far more powerful than what was possible before.

文档:官方文档中了解有关平面配置系统的更多信息。

平面配置的目标

🌐 The goals of flat config

为了为平面配置的变更做好准备,我们有几个目标:

🌐 To set the stage for the changes in flat config, we had several goals:

  1. 逻辑默认值 - 人们编写 JavaScript 的方式在过去九年里发生了很大变化,我们希望新的配置系统能够反映我们当前的现实,而不是 ESLint 首次发布时我们所处的状态。
  2. 定义配置的一种方式 - 我们不希望人们有多种方式去做同样的事情。对于任何给定的项目,应该只有一种定义配置的方式。
  3. 规则配置应保持不变 - 我们认为规则的配置方式已经很好,所以为了更容易过渡到扁平配置,我们不想对规则配置做任何更改。在扁平配置中可以以相同的方式使用相同的 rules 键。
  4. 对所有内容使用本地加载 - 关于 eslintrc,我们最大的遗憾之一是以自定义方式重新实现 Node.js 的 require 解析。这是复杂性的一个重要来源,事后看来是不必要的。今后,我们希望直接利用 JavaScript 运行时的加载能力。
  5. 更有条理的顶层键 - 自从 ESLint 发布以来,eslintrc 顶层的键数量急剧增加。我们需要查看哪些键是必要的以及它们彼此之间的关系。
  6. 现有插件应能正常工作 - ESLint 生态系统中有数百个插件。确保这些插件能够继续工作非常重要。
  7. 向后兼容性应是优先考虑的——即使我们正在转向一个新的配置系统,我们也不想抛弃现有的生态系统。特别是,我们希望可共享的配置能够尽可能继续工作。虽然我们知道100%的兼容性可能不现实,但我们希望尽最大努力确保现有的可共享配置能够正常运行。

怀着这些目标,我们想出了新的扁平配置系统。

🌐 With these goals in mind, we came up with the new flat config system.

为代码检查设置逻辑默认值

🌐 Setting logical defaults for linting

当 ESLint 刚创建时,ECMAScript 5 是 JavaScript 的最新版本,大多数文件都是以“共享一切”的脚本或 CommonJS 模块(用于 Node.js)编写的。ECMAScript 6 已经在不远的未来,但没人知道它会被多快实现,或者模块(ESM)最终会如何被使用。因此,ESLint 的默认设置是假设所有文件都是 ECMAScript 5。我们最终提供了 ecmaVersion 解析器配置,以便人们在准备好时可以选择使用 ECMAScript 6。

🌐 When ESLint was first created, ECMAScript 5 was the most recent version of JavaScript and most files were written as “shared everything” scripts or CommonJS modules (for Node.js). ECMAScript 6 was on the horizon but no one knew how quickly it would be implemented or how modules (ESM) would end up being used. So ESLint’s defaults were to assume all files were ECMAScript 5. We ended up with the ecmaVersion parser configuration to allow people to opt-in to ECMAScript 6 when they were ready.

快进到2022年:ECMAScript 不断发展,而 ESM 是每个人都在使用的标准模块格式。我们实际上无法更改 eslintrc 的默认设置,因为可能会破坏很多现有配置,但我们完全可以通过 flat 配置进行更改。

🌐 Fast forward to 2022: ECMAScript is constantly evolving and ESM is the standard module format everyone is using. We couldn’t really change the default settings of eslintrc without potentially breaking a lot of existing configurations, but we could definitely make a change with flat config.

平面配置具有以下默认设置:

🌐 Flat config features the following defaults:

  • ecmaVersion: "latest" for all JavaScript files - 没错,默认情况下,所有 JavaScript 文件都会设置为最新版本的 ECMAScript。这模拟了 JavaScript 运行时的工作方式,因为每次升级意味着你正在选择使用最新最好的 JavaScript 版本。这个变化意味着,除非你由于运行时限制想强制使用以前的版本,否则你可能不需要手动在配置中设置 ecmaVersion。如果有必要,你仍然可以将 ecmaVersion 设置到 3
  • sourceType: "module" for all .js and .mjs files - 默认情况下,flat 配置假定你正在编写 ESM。如果不是,你总是可以将 sourceType 设置回 "script"
  • sourceType: "commonjs" for .cjs files - 我们仍然处于过渡期,很多 Node.js 代码是用 CommonJS 编写的。为了支持这些用户,我们添加了一个新的 sourceType"commonjs",可以为该环境正确配置一切。
  • ESLint 会搜索 .js.mjs.cjs 文件 —— 在使用 eslintrc 时,ESLint 只有在你在命令行传递目录名称时才会搜索 .js 文件,并且你需要使用 --ext 标志来定义更多文件。使用平铺配置时,三种最常见的 JavaScript 文件扩展名都会被自动搜索。

我们对这些新的默认设置感到非常兴奋,因为我们认为这将帮助人们更快、更少困惑地上手 ESLint。

🌐 We are pretty excited about these new defaults as we think this will help people onboard to ESLint faster and with less confusion.

新的配置文件:eslint.config.js

🌐 The new config file: eslint.config.js

与 eslintrc 相比,eslintrc 允许在多个位置使用多个配置文件、多个配置文件格式,甚至允许基于 package.json 的配置,而平面配置(flat config)只有一个位置来存放你项目的所有配置:eslint.config.js 文件。通过将配置限制在一个位置和一种格式,我们可以直接利用 JavaScript 运行时的加载机制,避免对配置文件进行自定义解析的需求。

🌐 In contrast to eslintrc, which allowed multiple config files in multiple locations, multiple config file formats, and even package.json-based configs, flat config has just one location for all of your project’s configuration: the eslint.config.js file. By limiting configuration to one location and one format, we can take advantage of the JavaScript runtime’s loading mechanism directly and avoid the need for custom parsing of config files.

当使用 ESLint CLI 时,它会从当前工作目录开始搜索 eslint.config.js,如果未找到,则会继续向上搜索目录的祖级,直到找到该文件或到达根目录。那个 eslint.config.js 文件包含了该次 ESLint 运行的所有配置信息,因此相比 eslintrc,大大减少了所需的磁盘访问,eslintrc 需要从被检查文件的位置一路向上到根目录检查是否有其他配置文件。

🌐 When the ESLint CLI is used, it searches for eslint.config.js from the current working directory and if not found will continue the search up the directory’s ancestors until the file is found or the root directory is hit. That one eslint.config.js file contains all of the configuration information for that run of ESLint so it dramatically reduces the disk access required as compared to eslintrc, which had to check each directory from the linted file location up to the root for any additional config files.

此外,使用 JavaScript 文件使我们能够依赖用户加载其配置文件可能需要的额外信息。不再需要通过名称加载 extendsplugins,现在你只需根据需要使用 importrequire 来引入那些额外的资源。下面是一个 eslint.config.js 文件的示例:

🌐 Additionally, using a JavaScript file allowed us to rely on users to load additional information that their config file might need. Instead of extends and plugins loading things by name, you can now just use import and require as necessary to bring in those additional resources. Here’s an example of what an eslint.config.js file looks like:

export default [
    {
        files: ["**/*.js"],
        rules: {
            "semi": "error",
            "no-unused-vars": "error"
        }
    }
];

一个 eslint.config.js 文件导出一个配置对象数组。继续阅读以更多地了解这个示例。

🌐 An eslint.config.js file exports an array of config objects. Read on to understand more about this example.

到处都是基于通配符的配置

🌐 Glob-based configs everywhere

虽然 eslintrc 中的 overrides 键是很多复杂性的来源,但有一件事非常清楚:人们非常喜欢能够在配置文件中通过全局模式定义配置。因为我们想要消除 eslintrc 的配置级联,所以我们必须使用全局模式来启用同类型的配置覆盖。我们使用 overrides 配置作为平面配置的基础。

🌐 While the overrides key in eslintrc was the source of a lot of complexity, one thing was very clear: people really liked being able to define configuration by glob patterns in their config file. Because we wanted to eliminate the config cascade of eslintrc, we had to use glob patterns to enable the same type of config overrides. We used the overrides configs as the basis for flat config.

每个配置对象可以包含可选的 filesignores 键,用于指定基于 minimatch 的全局匹配模式来匹配文件。只有当文件名匹配 files 中的某个模式时(或者如果没有 files 键,则会匹配所有文件),配置对象才会应用于该文件。ignores 键可以从 files 的列表中过滤掉文件,从而限制配置对象适用的文件。例如,可能你的测试文件与源文件位于同一目录,而你希望配置对象只应用于源文件。你可以这样做:

🌐 Each config object can have optional files and ignores keys specifying minimatch-based glob patterns to match files. A config object only applies to a file if the filename matches a pattern in files (or if there is no files key, in which case it will match all files). The ignores key filters out files from the list of files, so you limit which files the config object applies to. For instance, maybe your test files live in the same directory as your source file and you want a config object to apply only to the source files. You could do so like this:

export default [
    {
        files: ["**/*.js"],
        ignores: ["**/*.test.js"],
        rules: {
            "semi": "error",
            "no-unused-vars": "error"
        }
    }
];

在这里,config 对象将匹配所有 JavaScript 文件,然后过滤掉任何以 .test.js 结尾的文件。

🌐 Here, the config object will match all JavaScript files and then filter out any files ending with .test.js.

如果你想完全忽略文件怎么办?你可以通过指定一个只包含 ignores 键的配置对象来做到这一点,例如:

🌐 What if you want to ignore files completely? You can do that by specifying a config object that has only an ignores key, like this:

export default [
    {
        ignores: ["**/*.test.js"]
    },
    {
        files: ["**/*.js"],
        rules: {
            "semi": "error",
            "no-unused-vars": "error"
        }
    }
];

使用此配置,所有以 .test.js 结尾的 JavaScript 文件将被忽略。你可以将其视为 eslintrc 中 ignorePatterns 的等效方法,只是使用了 minimatch 模式。

🌐 With this config, all JavaScript files ending with .test.js will be ignored. You can think of this as the equivalent of ignorePatterns in eslintrc, albeit with minimatch patterns.

再见 extends,你好平滑级联

🌐 Goodbye extends, hello flat cascade

虽然我们想要摆脱基于目录的配置层叠,但扁平配置实际上仍然在你的 eslint.config.js 文件中直接定义了一个扁平层叠。在数组内部,ESLint 会找到所有与正在检查的文件匹配的配置对象,并像使用 eslintrc 时一样将它们合并在一起。唯一的真正区别是合并是从数组的顶部到底部进行,而不是使用目录结构中的文件。例如:

🌐 While we wanted to get rid of the directory-based config cascade, flat config actually still has a flat cascade defined directly in your eslint.config.js file. Inside of the array, ESLint finds all config objects that match the file being linted and merges them together in much the same way that eslintrc did. The only real difference is the merge happens from the top of the array down to the bottom instead of using files in a directory structure. For example:

export default [
    {
        files: ["**/*.js", "**/*.cjs"],
        rules: {
            "semi": "error",
            "no-unused-vars": "error"
        }
    },
    {
        files: ["**/*.js"],
        rules: {
            "no-undef": "error",
            "semi": "warn"
        }
    }
];

此配置有两个配置对象,它们的 files 模式存在重叠。第一个配置对象适用于所有 .js.cjs 文件,而第二个仅适用于 .js 文件。当对以 .js 结尾的文件进行 lint 时,ESLint 会将两个配置对象合并,以创建该文件的最终配置。因为第二个配置将 semi 设置为 "warn" 严重级别,所以它会覆盖第一个配置中设置的 "error"。在发生冲突时,最后匹配的配置总是优先。

🌐 This config has two config objects with overlapping files patterns. The first config object applies to all .js and .cjs files while the second applies only to .js files. When linting a file ending with .js, ESLint combines both config objects to create the final config for the file. Because the second config sets semi to a severity of "warn", that takes precedence over the "error" that was set in the first config. The last matching config always wins when there is a conflict.

这对可共享配置意味着你可以将它们直接插入数组中,而不是使用 extends,例如:

🌐 What this means for shareable configs is that you can insert them directly into the array instead of using extends, such as:

import customConfig from "eslint-config-custom";

export default [
    customConfig,
    {
        files: ["**/*.js", "**/*.cjs"],
        rules: {
            "semi": "error",
            "no-unused-vars": "error"
        }
    },
    {
        files: ["**/*.js"],
        rules: {
            "no-undef": "error",
            "semi": "warn"
        }
    }
];

这里,customConfig 首先被插入数组中,因此它成为此文件的配置基础。接下来的每个配置对象都在该基础上构建,以创建给定 JavaScript 文件的最终配置。

🌐 Here, customConfig is inserted first in the array so that it becomes the base of configuration for this file. Each of the following config objects builds upon that base to create the final config for a given JavaScript file.

重新构想的语言选项

🌐 Reimagined language options

ESLint 一直有一组奇怪的选项组合,会影响 JavaScript 的解释方式。顶部的 globals 键会修改可用的全局变量,还有 ecmaVersionsourceType 作为 parserOptions,更不用说 env 用于添加更多全局变量。也许最让人困惑的是,你必须同时设置 ecmaVersion 并添加一个像 es6 的环境,才能启用你想要的语法并确保正确的全局变量可用。

🌐 ESLint has always had a strange mix of options that affected how JavaScript was interpreted. There was the top-level globals key that modified available global variables, and ecmaVersion and sourceType as parserOptions, not to mention env to add more globals. Perhaps the most confusing is that you had to set both ecmaVersion and add an environment like es6 to enable both the syntax you wanted and ensure that the correct global variables would be available.

在平面配置中,我们将所有与 JavaScript 评估相关的键移到了一个名为 `languageOptions`` 的新的顶层键中。

🌐 In flat config, we moved all keys related to JavaScript evaluation into a new top-level key called languageOptions.

在平面配置中设置 ecmaVersion

🌐 Setting ecmaVersion in flat config

最大的变化是我们将 ecmaVersionparserOptions 移出,直接放入 languageOptions。这更好地反映了该键的新行为,即根据指定的 ECMAScript 版本启用语法和全局变量。例如:

🌐 The biggest change is that we moved ecmaVersion out of parserOptions and directly into languageOptions. This better reflects this key’s new behavior, which is to enable both syntax and global variables based on the specified version of ECMAScript. For example:

export default [
    {
        files: ["**/*.js"],
        languageOptions: {
            ecmaVersion: 6
        }
    }
];

此配置已将 ecmaVersion 降级为 6。这样做可以确保所有的 ES6 语法和所有的 ES6 全局变量都可用。(任何使用的自定义解析器仍将接收此 ecmaVersion 值。)

🌐 This config has downgraded ecmaVersion to 6. Doing so ensures that all of the ES6 syntax and all of the ES6 globals are available. (Any custom parsers used will still receive this value of ecmaVersion.)

在平面配置中设置 sourceType

🌐 Setting sourceType in flat config

接下来,我们将 sourceType 移动到 languageOptions。类似于 ecmaVersion,这个键不仅影响文件的解析方式,还影响 ESLint 对其作用域结构的评估。我们为 ESM 保留了传统的 "module",为脚本保留了 "script",并且还添加了 "commonjs",它让 ESLint 知道应将该文件视为 CommonJS(这也启用了 CommonJS 特有的全局变量)。如果你使用 ecmaVersion: 3ecmaVersion: 5,请务必设置 sourceType: script,如下所示:

🌐 Next, we moved sourceType into languageOptions. Similar to ecmaVersion, this key affects not just how a file is parsed, but also how ESLint evaluates its scope structure. We kept the traditional "module" for ESM and "script" for scripts, and also added "commonjs", which lets ESLint know that it should treat the file as CommonJS (which also enables CommonJS-specific globals). If you are using ecmaVersion: 3 or ecmaVersion: 5, be sure to set sourceType: script, like this:

export default [
    {
        files: ["**/*.js"],
        languageOptions: {
            ecmaVersion: 5,
            sourceType: "script"
        }
    }
];

再见环境,你好 globals

🌐 Goodbye environments, hello globals

eslintrc 中的环境提供了一组已知的全局变量,并且一直是用户困惑的常见来源。它们需要保持最新(特别是在 browser 的情况下),而且这种更新需要等待 ESLint 的发布。此外,我们在环境上挂载了一些额外的功能,以便更容易地使用 Node.js,但最终我们弄得一团糟。

🌐 Environments in eslintrc provided a known set of globals and were a constant source of confusion for users. They need to be kept up to date (especially in the case of browser) and that update needs to wait for ESLint releases. Plus, we had hooked some additional functionality onto environments to make it easier to work with Node.js, and in the end, we made a mess.

对于平面配置,我们决定完全删除 env 键。为什么?因为它不再需要。我们之前为 Node.js 使用环境钩子的所有自定义功能现在都由 sourceType: "commonjs" 覆盖,所以剩下的只是让环境管理全局变量。让 ESLint 在核心中处理这个事情没有意义,因此我们将这个责任交还给你们。

🌐 For flat config, we decided to remove the env key completely. Why? Because it’s no longer needed. All of the custom functionality we hooked onto environments for use with Node.js is now covered by sourceType: "commonjs", so all that was left was for environments to manage global variables. It doesn’t make sense for ESLint to do this in the core, so we are handing this responsibility back to you.

多年前,我们与 Sindre Sorhus 合作创建了 globals 包,该包从 ESLint 中提取所有环境信息,以便其他包可以使用。随后 ESLint 使用 globals 作为其环境的来源。

🌐 Years ago, we worked with Sindre Sorhus to create the globals package, which extracted all of the environment information from ESLint so that it would be available to other packages. ESLint then used globals as the source for its environments.

使用平面配置时,你可以直接使用 globals 包,并在任何时候更新它,以获得环境曾经提供的所有相同功能。例如,下面是如何将浏览器全局变量添加到你的配置中:

🌐 With flat config, you can use the globals package directly, updating it whenever you want, to get all of the same functionality that environments used to provide. For example, here is how you add browser globals into your configuration:

import globals from "globals";

export default [
    {
        files: ["**/*.js"],
        languageOptions: {
            globals: {
                ...globals.browser,
                myCustomGlobal: "readonly"
            }
        }
    }
];

languageOptions.globals 键的作用与在 eslintrc 中相同,只是现在你可以使用 JavaScript 动态插入你想要的任何全局变量。

🌐 The languageOptions.globals key works the same as it did in eslintrc, only now, you can use JavaScript to dynamically insert any global variables that you want.

自定义解析器和解析器选项大多相同

🌐 Custom parsers and parser options are mostly the same

parserparserOptions 键现在已经移动到 languageOptions 键中,但它们的工作方式大致与 eslintrc 中相同,有两个具体的区别:

🌐 The parser and parserOptions keys have now moved into the languageOptions key, but they mostly work the same as in eslintrc with two specific differences:

  1. 你现在可以直接将解析器对象插入到配置中。
  2. 解析器现在可以与插件打包,并且你可以为 parser 指定一个字符串值以使用来自插件的解析器。(在下一节中有更多描述。)

这是一个使用 Babel ESLint 解析器 的示例:

🌐 Here’s an example using the Babel ESLint parser:

import babelParser from "@babel/eslint-parser";

export default [
    {
        files: ["**/*.js", "**/*.mjs"],
        languageOptions: {
            parser: babelParser
        }
    }
];

此配置确保将使用 Babel 解析器,而不是默认解析器,来解析所有以 .js.mjs 结尾的文件。

🌐 This configuration ensures that the Babel parser, rather than the default, will be used to parse all files ending with .js and .mjs.

你也可以通过使用 parserOptions 键直接将选项传递给自定义解析器,其用法与在 eslintrc 中的用法相同:

🌐 You can also pass options directly to the custom parser by using the parserOptions key in the same way as it works in eslintrc:

import babelParser from "@babel/eslint-parser";

export default [
    {
        files: ["**/*.js", "**/*.mjs"],
        languageOptions: {
            parser: babelParser,
            parserOptions: {
                requireConfigFile: false,
                babelOptions: {
                    babelrc: false,
                    configFile: false,
                    // your babel options
                    presets: ["@babel/preset-env"],
                }
            }
        }
    }
];

更强大且可配置的插件

🌐 More powerful and configurable plugins

ESLint 的强大之处在于由个人和公司维护的插件生态系统,这些插件可以用来定制他们的代码检查策略。因此,我们希望确保现有的插件能够在不修改的情况下继续工作,同时也允许插件做一些过去无法做到的事情。

🌐 The strength of ESLint is the ecosystem of plugins that individuals and companies maintain to customize their linting strategy. As such, we wanted to be sure that existing plugins continued to work without modification as well as allowing plugins to do things they were never able to do in the past.

表面上,在 flat 配置中使用插件看起来与在 eslintrc 中使用插件非常相似。最大的区别在于 eslintrc 使用的是字符串,而 flat 配置使用的是对象。你不再指定插件的名称,而是直接导入插件并将其放入 plugins 键中,如下面的示例所示:

🌐 On the surface, using a plugin in flat config looks very similar to using a plugin in eslintrc. The big difference is that eslintrc used strings whereas flat configs uses objects. Instead of specifying the name of a plugin, you import the plugin directly and place it into the plugins key, as in this example:

import jsdoc from "eslint-plugin-jsdoc";

export default [
    {
        files: ["**/*.js"],
        plugins: {
            jsdoc
        },
        rules: {
            "jsdoc/require-description": "error",
            "jsdoc/check-values": "error"
        }
    }
];

此配置通过将 eslint-plugin-jsdoc 插件作为本地 jsdoc 变量导入,然后将其插入配置中的 plugins 键来使用该插件。之后,插件内的规则通过 jsdoc 命名空间进行引用。

🌐 This config uses the eslint-plugin-jsdoc plugin by importing it as a local jsdoc variable and then inserting it into the plugins key in the config. After that, the rules inside the plugin are referenced using the jsdoc namespace.

注意: 由于插件现在像其他 JavaScript 模块一样被导入,因此不再严格要求插件包的名称。你不再需要在包名称前加上 eslint-plugin- 作为前缀……但是如果你这样做,我们还是会很高兴的。

个性化插件命名空间

🌐 Personalized plugin namespaces

因为你配置中的插件名称现在已与插件包名称分离,所以你可以选择任何你想要的名称,如下例所示:

🌐 Because the name of the plugin in your config is now decoupled from the name of the plugin package, you can choose any name you want, as in this example:

import jsdoc from "eslint-plugin-jsdoc";

export default [
    {
        files: ["**/*.js"],
        plugins: {
            jsd: jsdoc
        },
        rules: {
            "jsd/require-description": "error",
            "jsd/check-values": "error"
        }
    }
];

在这里,插件在配置中被命名为 jsd,所以规则也使用 jsd 来表示它们来自哪个插件。

🌐 Here, the plugin is named jsd in the config, so the rules also use jsd to indicate which plugin they are coming from.

--rulesdir 到运行时插件

🌐 From --rulesdir to runtime plugins

使用 eslintrc 时,规则需要通过 CLI 直接加载,才能在配置文件中使用。这意味着要么将自定义规则打包到插件中,要么使用 --rulesdir 标志指定 ESLint 应该从哪个目录加载自定义规则。这两种方法都需要一些额外的设置工作,并且经常成为用户抱怨的原因。

🌐 With eslintrc, rules needed to be loaded by the CLI directly in order to be available inside of a config file. This means either bundling custom rules in a plugin or using the --rulesdir flag to specify the directory from which ESLint should load custom rules. Both approaches required some extra work to set up and were a frequent cause of frustration for our users.

通过扁平配置,你可以直接在配置文件中加载自定义规则。由于插件现在直接作为配置中的对象,你可以轻松创建仅存在于配置文件中的运行时插件,例如:

🌐 With flat config, you can load custom rules directly in the config file. Because plugins are now objects directly in the config, you can easily create runtime plugins that exist only in your config file, such as:

import myrule from "./custom-rules/myrule.js";

export default [
    {
        files: ["**/*.js"],
        plugins: {
            custom: {
                rules: {
                    myrule
                }
            }
        },
        rules: {
            "custom/myrule": "error"
        }
    }
];

在这里,自定义规则被导入为 myrule,然后创建了一个名为 custom 的运行时插件,以将该规则作为 custom/myrule 提供给配置。

🌐 Here, a custom rule is imported as myrule and then a runtime plugin is created named custom to provide that rule to the config as custom/myrule.

因此,一旦平面配置的过渡完成,我们将移除 --rulesdir

🌐 As a result, we will be removing --rulesdir once the transition to flat config is complete.

处理器的工作方式类似于 eslintrc

🌐 Processors works in a similar way to eslintrc

processor 顶层键的工作方式大致与 eslintrc 相同,主要用例是使用在插件中定义的处理器,例如:

🌐 The processor top-level key works mostly the same as in eslintrc, with the primary use case being to use a processor that is defined in a plugin, for example:

import markdown from "eslint-plugin-markdown";

export default [
    {
        files: ["**/*.md"],
        plugins: {
            markdown
        },
        processor: "markdown/markdown"
    }
];

该配置对象指定在名为 "markdown" 的插件中包含一个名为 "markdown" 的处理器,并将该处理器应用于所有以 .md 结尾的文件。

🌐 This configuration object specifies that there is a processor called "markdown" contained in the plugin named "markdown" and will apply the processor to all files ending with .md.

在扁平配置中的唯一新增是 processor 现在也可以是一个包含 preprocess()postprocess() 方法的对象。

🌐 The one addition in flat config is that processor can now also be an object containing both a preprocess() and a postprocess() method.

已组织的代码检查器选项

🌐 Organized linter options

在 eslintrc 中,有几个键直接与 linter 的运作相关,即 noInlineConfigreportUnusedDisableDirectives。这些已移到新的 linterOptions 键中,但其工作方式与在 eslintrc 中完全相同。下面是一个示例:

🌐 In eslintrc, there were a couple of keys that related directly to how the linter operated, namely noInlineConfig and reportUnusedDisableDirectives. These have moved into the new linterOptions key but work exactly the same as in eslintrc. Here’s an example:

export default [
    {
        files: ["**/*.js"],
        linterOptions: {
            noInlineConfig: true,
            reportUnusedDisableDirectives: true
        }
    }
];

共享设置完全相同

🌐 Shared settings are exactly the same

顶层 settings 键的行为与 eslintrc 中完全相同。你可以定义一个包含键值对的对象,这些键值对应该对所有规则都可用。下面是一个示例:

🌐 The top-level settings key behaves the exact same way as in eslintrc. You can define an object with key-value pairs that should be available to all rules. Here’s an example:

export default [
    {
        settings: {
            sharedData: "Hello"
        }
    }
];

使用预定义配置

🌐 Using predefined configs

ESLint 有两个预定义的 JavaScript 配置:

🌐 ESLint has two predefined configurations for JavaScript:

  • js.configs.recommended - 启用 ESLint 建议每个人使用的规则以避免潜在错误
  • js.configs.all - 启用 ESLint 所附带的所有规则

要包含这些预定义配置,请安装 @eslint/js 包,然后在后续的配置对象中对其他属性进行任何修改:

🌐 To include these predefined configurations, install the @eslint/js package and then make any modifications to other properties in subsequent configuration objects:

import js from "@eslint/js";

export default [
    js.configs.recommended,
    {
        rules: {
            semi: ["warn", "always"]
        }
    }
];

这里,首先应用 eslint:recommended 预定义配置,然后另一个配置对象为 semi 添加所需的配置。

🌐 Here, the eslint:recommended predefined configuration is applied first and then another configuration object adds the desired configuration for semi.

向后兼容性工具

🌐 Backwards compatibility utility

如前所述,我们觉得需要有相当多的对 eslintrc 的向后兼容性,以便简化过渡。@eslint/eslintrc 包提供了一个 FlatCompat 类,使得在一个扁平配置文件中继续使用 eslintrc 风格的共享配置和设置变得容易。下面是一个示例:

🌐 As mentioned previously, we felt like there needed to be a good amount of backwards compatibility with eslintrc in order to ease the transition. The @eslint/eslintrc package provides a FlatCompat class that makes it easy to continue using eslintrc-style shared configs and settings within a flat config file. Here’s an example:

import { FlatCompat } from "@eslint/eslintrc";
import path from "path";
import { fileURLToPath } from "url";

// mimic CommonJS variables -- not needed if using CommonJS
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const compat = new FlatCompat({
    baseDirectory: __dirname
});

export default [

    // mimic ESLintRC-style extends
    ...compat.extends("standard", "example"),

    // mimic environments
    ...compat.env({
        es2020: true,
        node: true
    }),

    // mimic plugins
    ...compat.plugins("airbnb", "react"),

    // translate an entire config
    ...compat.config({
        plugins: ["airbnb", "react"],
        extends: "standard",
        env: {
            es2020: true,
            node: true
        },
        rules: {
            semi: "error"
        }
    })
];

使用 FlatCompat 类可以让你继续使用所有现有的 eslintrc 文件,同时针对 flat config 进行优化。我们将其视为一个必要的过渡步骤,以便生态系统能够逐步转向 flat config。

🌐 Using the FlatCompat class allows you to continue using all of your existing eslintrc files while optimizing them for use with flat config. We envision this as a necessary transitional step to allow the ecosystem to slowly convert over to flat config.

结论

🌐 Conclusion

团队花了很长时间设计扁平配置,以便它既让现有用户感到熟悉,又提供对所有人都有益的新功能。我们保持了规则、设置和处理器不变,同时扩展了插件、语言选项和检查器选项,使其更统一。我们认为扁平配置在这两者之间找到了良好的平衡,并且一旦新的配置系统普遍可用,你会更喜欢使用 ESLint。在此期间,兼容性工具将允许你继续使用现有的共享配置。

🌐 The team spent a long time designing flat config so that it would both feel familiar to existing users and provide new functionality that would benefit everyone. We kept things like rules, settings, and processors the same while extending things like plugins, language options, and linter options to be more uniform. We think that flat config has found a good balance between these two poles and that you will enjoy using ESLint more once the new config system is generally available. In the meantime, the compatibility utility will allow you to continue using existing shared configs.

在本系列博客的下一部分中,你将学习如何从今天开始使用扁平配置。

🌐 In the next part of this blog series, you’ll learn how to start using flat config today.

更新(2024-08-12): 更新了 JavaScript 的预定义 ESLint 配置。

最新的 ESLint 新闻、案例研究、教程和资源。

ESLint v10.3.0 发布
1 min read

ESLint v10.3.0 发布

我们刚刚发布了 ESLint v10.3.0,这是 ESLint 的一次小版本升级。此版本添加了一些新功能,并修复了上一版本中发现的几个错误。

ESLint v10.2.1 发布
1 min read

ESLint v10.2.1 发布

我们刚刚发布了 ESLint v10.2.1,这是 ESLint 的一个补丁版本升级。本次发布修复了上一版本中发现的几个错误。

ESLint v10.2.0 发布
2 min read

ESLint v10.2.0 发布

我们刚刚发布了 ESLint v10.2.0,这是 ESLint 的一次小版本升级。此版本添加了一些新功能,并修复了上一版本中发现的几个错误。