自定义处理器

你还可以创建自定义处理器,告诉 ESLint 如何处理标准 JavaScript 以外的文件。例如,你可以编写一个自定义处理器来从 Markdown 文件中提取和处理 JavaScript(@eslint/markdown 为此包含一个自定义处理器)。

¥You can also create custom processors that tell ESLint how to process files other than standard JavaScript. For example, you could write a custom processor to extract and process JavaScript from Markdown files (@eslint/markdown includes a custom processor for this).

定制处理器规范

¥Custom Processor Specification

为了创建自定义处理器,从你的模块导出的对象必须符合以下接口:

¥In order to create a custom processor, the object exported from your module has to conform to the following interface:

const plugin = {

    meta: {
        name: "eslint-plugin-example",
        version: "1.2.3"
    },
    processors: {
        "processor-name": {
            meta: {
                name: "eslint-processor-name",
                version: "1.2.3"
            },
            // takes text of the file and filename
            preprocess(text, filename) {
                // here, you can strip out any non-JS content
                // and split into multiple strings to lint

                return [ // return an array of code blocks to lint
                    { text: code1, filename: "0.js" },
                    { text: code2, filename: "1.js" },
                ];
            },

            // takes a Message[][] and filename
            postprocess(messages, filename) {
                // `messages` argument contains two-dimensional array of Message objects
                // where each top-level array item contains array of lint messages related
                // to the text that was returned in array from preprocess() method

                // you need to return a one-dimensional array of the messages you want to keep
                return [].concat(...messages);
            },

            supportsAutofix: true // (optional, defaults to false)
        }
    }
};

// for ESM
export default plugin;

// OR for CommonJS
module.exports = plugin;

preprocess 方法将文件内容和文件名作为参数,并向 lint 返回代码块数组。代码块将被单独检查,但仍会注册到文件名中。

¥The preprocess method takes the file contents and filename as arguments, and returns an array of code blocks to lint. The code blocks will be linted separately but still be registered to the filename.

一个代码块有两个属性 textfilenametext 属性是块的内容,filename 属性是块的名称。块的名称可以是任何内容,但应包含文件扩展名,它告诉 ESLint 如何处理当前块。ESLint 检查项目配置中匹配的 files 条目,以确定是否应该对代码块进行 linted。

¥A code block has two properties text and filename. The text property is the content of the block and the filename property is the name of the block. The name of the block can be anything, but should include the file extension, which tells ESLint how to process the current block. ESLint checks matching files entries in the project’s config to determine if the code blocks should be linted.

由插件决定是否只需要返回非 JavaScript 文件的一部分或多部分。例如,在处理 .html 文件的情况下,你可能希望通过组合所有脚本只返回数组中的一项。但是,对于 .md 文件,你可以返回多个项目,因为每个 JavaScript 块可能是独立的。

¥It’s up to the plugin to decide if it needs to return just one part of the non-JavaScript file or multiple pieces. For example in the case of processing .html files, you might want to return just one item in the array by combining all scripts. However, for .md files, you can return multiple items because each JavaScript block might be independent.

postprocess 方法采用 lint 消息数组和文件名的二维数组。输入数组中的每一项对应于从 preprocess 方法返回的部分。postprocess 方法必须调整所有错误的位置以对应于原始的、未处理的代码中的位置,并将它们聚合到一个扁平数组中并返回它。

¥The postprocess method takes a two-dimensional array of arrays of lint messages and the filename. Each item in the input array corresponds to the part that was returned from the preprocess method. The postprocess method must adjust the locations of all errors to correspond to locations in the original, unprocessed code, and aggregate them into a single flat array and return it.

报告的问题在每条 lint 消息中具有以下位置信息:

¥Reported problems have the following location information in each lint message:

type LintMessage = {

  /// The 1-based line number where the message occurs.
  line?: number;

   /// The 1-based column number where the message occurs.
  column?: number;

  /// The 1-based line number of the end location.
  endLine?: number;

  /// The 1-based column number of the end location.
  endColumn?: number;

  /// If `true`, this is a fatal error.
  fatal?: boolean;

  /// Information for an autofix.
  fix: Fix;

  /// The error message.
  message: string;

  /// The ID of the rule which generated the message, or `null` if not applicable.
  ruleId: string | null;

  /// The severity of the message.
  severity: 0 | 1 | 2;

  /// Information for suggestions.
  suggestions?: Suggestion[];
};

type Fix = {
    range: [number, number];
    text: string;
}

type Suggestion = {
    desc?: string;
    messageId?: string;
    fix: Fix;
}

默认情况下,ESLint 在使用自定义处理器时不会执行自动修复,即使在命令行上启用了 --fix 标志也是如此。要允许 ESLint 在使用你的处理器时自动修复代码,你应该采取以下额外步骤:

¥By default, ESLint does not perform autofixes when a custom processor is used, even when the --fix flag is enabled on the command line. To allow ESLint to autofix code when using your processor, you should take the following additional steps:

  1. 更新 postprocess 方法以额外转换报告问题的 fix 属性。所有可自动修复的问题都有一个 fix 属性,它是一个具有以下架构的对象:

    ¥Update the postprocess method to additionally transform the fix property of reported problems. All autofixable problems have a fix property, which is an object with the following schema:

    {
        range: [number, number],
        text: string
    }
    

    range 属性在代码中包含两个索引,指的是将被替换的连续文本部分的开始和结束位置。text 属性引用将替换给定范围的文本。

    ¥The range property contains two indexes in the code, referring to the start and end location of a contiguous section of text that will be replaced. The text property refers to the text that will replace the given range.

    在最初的问题列表中,fix 属性将引用已处理 JavaScript 中的修复。postprocess 方法应该转换对象以引用原始未处理文件中的修复。

    ¥In the initial list of problems, the fix property will refer to a fix in the processed JavaScript. The postprocess method should transform the object to refer to a fix in the original, unprocessed file.

  2. 向处理器添加 supportsAutofix: true 属性。

    ¥Add a supportsAutofix: true property to the processor.

你可以在一个插件中同时拥有规则和自定义处理器。你还可以在一个插件中拥有多个处理器。要支持多个扩展,请将每个扩展添加到 processors 元素并将它们指向同一对象。

¥You can have both rules and custom processors in a single plugin. You can also have multiple processors in one plugin. To support multiple extensions, add each one to the processors element and point them to the same object.

如何使用 meta 对象

¥How meta Objects are Used

meta 对象帮助 ESLint 缓存使用处理器的配置并提供更友好的调试消息。

¥The meta object helps ESLint cache configurations that use a processor and to provide more friendly debug messages.

插件 meta 对象

¥Plugin meta Object

插件 meta 对象 提供有关插件本身的信息。当使用字符串格式 plugin-name/processor-name 指定处理器时,ESLint 会自动使用插件 meta 为处理器生成名称。这是处理器最常见的情况。

¥The plugin meta object provides information about the plugin itself. When a processor is specified using the string format plugin-name/processor-name, ESLint automatically uses the plugin meta to generate a name for the processor. This is the most common case for processors.

示例:

¥Example:

// eslint.config.js
import example from "eslint-plugin-example";

export default [
    {
        plugins: {
            example
        },
        processor: "example/processor-name"
    },
    // ... other configs
];

在此示例中,处理器名称为 "example/processor-name",这是用于序列化配置的值。

¥In this example, the processor name is "example/processor-name", and that’s the value that will be used for serializing configurations.

处理器 meta 对象

¥Processor meta Object

每个处理器还可以指定自己的 meta 对象。当在配置中将处理器对象直接传递给 processor 时,将使用此信息。在这种情况下,ESLint 不知道处理器属于哪个插件。meta.name 属性应与处理器名称匹配,meta.version 属性应与处理器的 npm 包版本匹配。完成此操作的最简单方法是从你的 package.json 读取此信息。

¥Each processor can also specify its own meta object. This information is used when the processor object is passed directly to processor in a configuration. In that case, ESLint doesn’t know which plugin the processor belongs to. The meta.name property should match the processor name and the meta.version property should match the npm package version for your processors. The easiest way to accomplish this is by reading this information from your package.json.

示例:

¥Example:

// eslint.config.js
import example from "eslint-plugin-example";

export default [
    {
        processor: example.processors["processor-name"]
    },
    // ... other configs
];

在此示例中,指定 example.processors["processor-name"] 直接使用处理器自己的 meta 对象,必须定义该对象以确保在未通过插件名称引用处理器时正确处理。

¥In this example, specifying example.processors["processor-name"] directly uses the processor’s own meta object, which must be defined to ensure proper handling when the processor is not referenced through the plugin name.

为什么需要两个元对象

¥Why Both Meta Objects are Needed

建议插件和每个处理器都提供各自的元对象。这可确保依赖于元对象的功能(例如 --print-config--cache)无论在配置中如何指定处理器都能正常工作。

¥It is recommended that both the plugin and each processor provide their respective meta objects. This ensures that features relying on meta objects, such as --print-config and --cache, work correctly regardless of how the processor is specified in the configuration.

在配置文件中指定处理器

¥Specifying Processor in Config Files

为了在配置文件中使用插件中的处理器,请导入插件并将其包含在 plugins 键中,指定命名空间。然后,使用该命名空间来引用 processor 配置中的处理器,如下所示:

¥In order to use a processor from a plugin in a configuration file, import the plugin and include it in the plugins key, specifying a namespace. Then, use that namespace to reference the processor in the processor configuration, like this:

// eslint.config.js
import example from "eslint-plugin-example";

export default [
    {
        plugins: {
            example
        },
        processor: "example/processor-name"
    }
];

有关详细信息,请参阅插件配置文档中的 指定处理器

¥See Specify a Processor in the Plugin Configuration documentation for more details.