创建插件

ESLint 插件通过附加功能扩展了 ESLint。在大多数情况下,你将通过创建封装要在多个项目之间共享的附加功能的插件来扩展 ESLint。

¥ESLint plugins extend ESLint with additional functionality. In most cases, you’ll extend ESLint by creating plugins that encapsulate the additional functionality you want to share across multiple projects.

创建插件

¥Creating a plugin

插件是一个 JavaScript 对象,它向 ESLint 公开某些属性:

¥A plugin is a JavaScript object that exposes certain properties to ESLint:

  • meta - 有关插件的信息。

    ¥meta - information about the plugin.

  • configs - 包含命名配置的对象。

    ¥configs - an object containing named configurations.

  • rules - 包含自定义规则定义的对象。

    ¥rules - an object containing the definitions of custom rules.

  • processors - 包含命名处理器的对象。

    ¥processors - an object containing named processors.

首先,创建一个 JavaScript 文件并导出一个包含你希望 ESLint 使用的属性的对象。为了使你的插件尽可能易于维护,我们建议你将插件入口点文件格式化为如下所示:

¥To get started, create a JavaScript file and export an object containing the properties you’d like ESLint to use. To make your plugin as easy to maintain as possible, we recommend that you format your plugin entrypoint file to look like this:

const plugin = {
	meta: {},
	configs: {},
	rules: {},
	processors: {},
};

// for ESM
export default plugin;

// OR for CommonJS
module.exports = plugin;

如果你计划将插件作为 npm 包分发,请确保导出插件对象的模块是包的默认导出。这将使 ESLint 在 --plugin 选项.conf 文件中的命令行中指定插件时导入该插件。

¥If you plan to distribute your plugin as an npm package, make sure that the module that exports the plugin object is the default export of your package. This will enable ESLint to import the plugin when it is specified in the command line in the --plugin option.

插件中的元数据

¥Meta Data in Plugins

为了更容易调试和更有效地缓存插件,建议在插件的根目录下的 meta 对象中提供 nameversion,如下所示:

¥For easier debugging and more effective caching of plugins, it’s recommended to provide a name and version in a meta object at the root of your plugin, like this:

const plugin = {
	// preferred location of name and version
	meta: {
		name: "eslint-plugin-example",
		version: "1.2.3",
	},
	rules: {
		// add rules here
	},
};

// for ESM
export default plugin;

// OR for CommonJS
module.exports = plugin;

meta.name 属性应与插件的 npm 包名称匹配,meta.version 属性应与插件的 npm 包版本匹配。实现此目的的最简单方法是从 package.json 中读取此信息,如以下示例所示:

¥The meta.name property should match the npm package name for your plugin and the meta.version property should match the npm package version for your plugin. The easiest way to accomplish this is by reading this information from your package.json, as in this example:

import fs from "fs";

const pkg = JSON.parse(
	fs.readFileSync(new URL("./package.json", import.meta.url), "utf8"),
);

const plugin = {
	// preferred location of name and version
	meta: {
		name: pkg.name,
		version: pkg.version,
	},
	rules: {
		// add rules here
	},
};

export default plugin;

作为替代方案,你还可以在插件的根目录下公开 nameversion 属性,例如:

¥As an alternative, you can also expose name and version properties at the root of your plugin, such as:

const plugin = {
	// alternate location of name and version
	name: "eslint-plugin-example",
	version: "1.2.3",
	rules: {
		// add rules here
	},
};

// for ESM
export default plugin;

// OR for CommonJS
module.exports = plugin;

插件中的规则

¥Rules in Plugins

插件可以公开在 ESLint 中使用的自定义规则。为此,插件必须导出一个 rules 对象,其中包含规则 ID 到规则的键值映射。规则 ID 不必遵循任何命名约定,只是它不应包含 / 字符(因此,例如,它可以是 dollar-sign,但不能是 foo/dollar-sign)。要了解有关在插件中创建自定义规则的更多信息,请参阅 自定义规则

¥Plugins can expose custom rules for use in ESLint. To do so, the plugin must export a rules object containing a key-value mapping of rule ID to rule. The rule ID does not have to follow any naming convention except that it should not contain a / character (so it can just be dollar-sign but not foo/dollar-sign, for instance). To learn more about creating custom rules in plugins, refer to Custom Rules.

const plugin = {
	meta: {
		name: "eslint-plugin-example",
		version: "1.2.3",
	},
	rules: {
		"dollar-sign": {
			create(context) {
				// rule implementation ...
			},
		},
	},
};

// for ESM
export default plugin;

// OR for CommonJS
module.exports = plugin;

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

¥In order to use a rule 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 rule in the rules configuration, like this:

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

export default defineConfig([
	{
		plugins: {
			example,
		},
		rules: {
			"example/dollar-sign": "error",
		},
	},
]);

插件中的处理器

¥Processors in Plugins

插件可以通过提供 processors 对象来公开 processors 以在配置文件中使用。与规则类似,processors 对象中的每个键都是处理器的名称,每个值都是处理器对象本身。这是一个例子:

¥Plugins can expose processors for use in configuration file by providing a processors object. Similar to rules, each key in the processors object is the name of a processor and each value is the processor object itself. Here’s an example:

const plugin = {
	meta: {
		name: "eslint-plugin-example",
		version: "1.2.3",
	},
	processors: {
		"processor-name": {
			preprocess(text, filename) {
				/* ... */
			},
			postprocess(messages, filename) {
				/* ... */
			},
		},
	},
};

// for ESM
export default plugin;

// OR for CommonJS
module.exports = plugin;

为了在配置文件中使用插件中的处理器,请导入插件并将其包含在 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 { defineConfig } from "eslint/config";
import example from "eslint-plugin-example";

export default defineConfig([
	{
		files: ["**/*.txt"],
		plugins: {
			example,
		},
		processor: "example/processor-name",
	},
]);

插件中的配置

¥Configs in Plugins

你可以通过在 configs 键下指定它们来将配置打包在插件中。当你想要将一组自定义规则与启用推荐选项的配置打包在一起时,这会很有用。每个插件支持多种配置。

¥You can bundle configurations inside a plugin by specifying them under the configs key. This can be useful when you want to bundle a set of custom rules with a configuration that enables the recommended options. Multiple configurations are supported per plugin.

你可以在配置中包含来自插件的单独规则,该配置也包含在插件中。在配置中,你必须在 plugins 对象中指定你的插件名称以及你想要启用的作为插件一部分的任何规则。任何插件规则都必须以插件命名空间为前缀。这是一个例子:

¥You can include individual rules from a plugin in a config that’s also included in the plugin. In the config, you must specify your plugin name in the plugins object as well as any rules you want to enable that are part of the plugin. Any plugin rules must be prefixed with the plugin namespace. Here’s an example:

const plugin = {
	meta: {
		name: "eslint-plugin-example",
		version: "1.2.3",
	},
	configs: {},
	rules: {
		"dollar-sign": {
			create(context) {
				// rule implementation ...
			},
		},
	},
};

// assign configs here so we can reference `plugin`
Object.assign(plugin.configs, {
	recommended: [
		{
			plugins: {
				example: plugin,
			},
			rules: {
				"example/dollar-sign": "error",
			},
			languageOptions: {
				globals: {
					myGlobal: "readonly",
				},
				parserOptions: {
					ecmaFeatures: {
						jsx: true,
					},
				},
			},
		},
	],
});

// for ESM
export default plugin;

// OR for CommonJS
module.exports = plugin;

该插件导出一个 recommended 配置,它是一个包含一个配置对象的数组。当只有一个配置对象时,你还可以仅导出该对象而不使用封闭数组。

¥This plugin exports a recommended config that is an array with one config object. When there is just one config object, you can also export just the object without an enclosing array.

为了在配置文件中使用插件中的配置,请导入插件并使用 extends 键引用配置的名称,如下所示:

¥In order to use a config from a plugin in a configuration file, import the plugin and use the extends key to reference the name of the config, like this:

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

export default defineConfig([
	{
		files: ["**/*.js"], // any patterns you want to apply the config to
		plugins: {
			example,
		},
		extends: ["example/recommended"],
	},
]);

旧配置的向后兼容性

¥Backwards Compatibility for Legacy Configs

如果你的插件需要导出可与当前(扁平配置)系统和旧(eslintrc)系统一起使用的配置,则可以从 configs 键导出这两种配置类型。导出旧配置时,我们建议在名称前加上 "legacy-"(例如,"legacy-recommended"),以明确如何使用配置。

¥If your plugin needs to export configs that work both with the current (flat config) system and the old (eslintrc) system, you can export both config types from the configs key. When exporting legacy configs, we recommend prefixing the name with "legacy-" (for example, "legacy-recommended") to make it clear how the config should be used.

如果你正在使用 ESLint v9.0.0 之前存在的插件,那么你可能已经拥有名称为 "recommended" 的旧配置。如果你不想更新配置名称,你还可以在 configs 对象中创建一个以 "flat/" 为前缀的附加条目(例如,"flat/recommended")。这是一个例子:

¥If you’re working on a plugin that has existed prior to ESLint v9.0.0, then you may already have legacy configs with names such as "recommended". If you don’t want to update the config name, you can also create an additional entry in the configs object prefixed with "flat/" (for example, "flat/recommended"). Here’s an example:

const plugin = {
	meta: {
		name: "eslint-plugin-example",
		version: "1.2.3",
	},
	configs: {},
	rules: {
		"dollar-sign": {
			create(context) {
				// rule implementation ...
			},
		},
	},
};

// assign configs here so we can reference `plugin`
Object.assign(plugin.configs, {
	// flat config format
	"flat/recommended": [
		{
			plugins: {
				example: plugin,
			},
			rules: {
				"example/dollar-sign": "error",
			},
			languageOptions: {
				globals: {
					myGlobal: "readonly",
				},
				parserOptions: {
					ecmaFeatures: {
						jsx: true,
					},
				},
			},
		},
	],

	// eslintrc format
	recommended: {
		plugins: ["example"],
		rules: {
			"example/dollar-sign": "error",
		},
		globals: {
			myGlobal: "readonly",
		},
		parserOptions: {
			ecmaFeatures: {
				jsx: true,
			},
		},
	},
});

// for ESM
export default plugin;

// OR for CommonJS
module.exports = plugin;

使用此方法,两个配置系统都可以识别 "recommended"。旧配置系统使用 recommended 键,而当前配置系统使用 flat/recommended 键。defineConfig() 助手首先查看 recommended 键,如果格式不正确,它会查找 flat/recommended 键。如果你以后不再需要支持旧配置系统时想将 flat/recommended 重命名为 recommended,这将为你提供升级路径。

¥With this approach, both configuration systems recognize "recommended". The old config system uses the recommended key while the current config system uses the flat/recommended key. The defineConfig() helper first looks at the recommended key, and if that is not in the correct format, it looks for the flat/recommended key. This allows you an upgrade path if you’d later like to rename flat/recommended to recommended when you no longer need to support the old config system.

测试插件

¥Testing a Plugin

ESLint 提供了 RuleTester 实用程序,可以轻松测试插件的规则。

¥ESLint provides the RuleTester utility to make it easy to test the rules of your plugin.

对插件进行代码检查

¥Linting a Plugin

ESLint 插件也应该被检查!建议使用以下 recommended 配置对你的插件进行 lint:

¥ESLint plugins should be linted too! It’s suggested to lint your plugin with the recommended configurations of:

共享插件

¥Share Plugins

为了使你的插件公开可用,你必须将其发布到 npm 上。这样做时,请务必:

¥In order to make your plugin available publicly, you have to publish it on npm. When doing so, please be sure to:

  1. 将 ESLint 列为对等依赖。由于插件旨在与 ESLint 一起使用,因此将 eslint 包添加为对等依赖非常重要。为此,请手动编辑 package.json 文件以包含 peerDependencies 块,如下所示:

    ¥List ESLint as a peer dependency. Because plugins are intended for use with ESLint, it’s important to add the eslint package as a peer dependency. To do so, manually edit your package.json file to include a peerDependencies block, like this:

    {
    	"peerDependencies": {
    		"eslint": ">=9.0.0"
    	}
    }
    
  2. 指定关键字。ESLint 插件应在 package.json 文件中将 eslinteslintplugineslint-plugin 指定为 keywords

    ¥Specify keywords. ESLint plugins should specify eslint, eslintplugin and eslint-plugin as keywords in your package.json file.