版本

自定义规则教程

本教程涵盖如何为 ESLint 创建自定义规则并使用插件分发它。

您可以创建自定义规则来验证您的代码是否满足特定预期,并确定如果不满足该预期该怎么办。插件打包自定义规则和其他配置,使您能够轻松地在不同的项目中共享和重用它们。

要了解有关自定义规则和插件的更多信息,请参阅以下文档

为什么要创建自定义规则?

如果 ESLint 的内置规则和社区发布的自定义规则无法满足您的需求,则可以创建自定义规则。您可能会创建自定义规则来强制执行公司或项目的最佳实践,防止特定错误再次发生,或确保符合样式指南。

在创建不特定于您的公司或项目的自定义规则之前,值得搜索网络以查看是否有人发布了包含解决您的用例的自定义规则的插件。该规则很可能已经存在。

先决条件

在开始之前,请确保您的开发环境中已安装以下内容

本教程还假设您对 ESLint 和 ESLint 规则有基本的了解。

自定义规则

本教程中的自定义规则要求所有名为 fooconst 变量都分配字符串字面量 "bar"。该规则在文件 enforce-foo-bar.js 中定义。该规则还建议将分配给 const foo 的任何其他值替换为 "bar"

例如,假设您有以下 foo.js 文件

// foo.js

const foo = "baz123";

使用该规则运行 ESLint 会将 "baz123" 标记为变量 foo 的不正确值。如果 ESLint 正在自动修复模式下运行,则 ESLint 会修复文件以包含以下内容

// foo.js

const foo = "bar";

步骤 1:设置您的项目

首先,为您的自定义规则创建一个新项目。创建一个新目录,在其中启动一个新的 npm 项目,并为自定义规则创建一个新文件

mkdir eslint-custom-rule-example # create directory
cd eslint-custom-rule-example # enter the directory
npm init -y # init new npm project
touch enforce-foo-bar.js # create file enforce-foo-bar.js

步骤 2:创建规则文件

enforce-foo-bar.js 文件中,为 enforce-foo-bar 自定义规则添加一些脚手架。此外,添加一个包含有关规则的一些基本信息的 meta 对象。

// enforce-foo-bar.js

module.exports = {
    meta: {
       // TODO: add metadata
    },
    create(context) {
        return {
            // TODO: add callback function(s)
        };
    }
};

步骤 3:添加规则元数据

在编写规则之前,请向规则对象添加一些元数据。ESLint 在运行规则时使用此信息。

首先导出一个对象,该对象具有一个 meta 属性,其中包含规则的元数据,例如规则类型、文档和可修复性。在本例中,规则类型为“problem”,描述为“强制名为 foo 的变量只能分配值‘bar’。”,并且可以通过修改代码来修复该规则。

// enforce-foo-bar.js

module.exports = {
    meta: {
        type: "problem",
        docs: {
            description: "Enforce that a variable named `foo` can only be assigned a value of 'bar'.",
        },
        fixable: "code",
        schema: []
    },
    create(context) {
        return {
            // TODO: add callback function(s)
        };
    }
};

要了解有关规则元数据的更多信息,请参阅规则结构

步骤 4:添加规则访问器方法

定义规则的 create 函数,该函数接受一个 context 对象并返回一个对象,该对象为要处理的每个语法节点类型提供一个属性。在本例中,您想要处理 VariableDeclarator 节点。您可以选择任何ESTree 节点类型选择器

VariableDeclarator 访问器方法内部,检查该节点是否表示 const 变量声明,其名称是否为 foo,以及它是否未分配给字符串 "bar"。您可以通过评估传递给 VariableDeclaration 方法的 node 来做到这一点。

如果 const foo 声明分配的值为 "bar",则规则不执行任何操作。如果 const foo **未**分配值 "bar",则 context.report() 向 ESLint 报告错误。错误报告包括有关错误的信息以及如何修复错误的信息。

// enforce-foo-bar.js

module.exports = {
    meta: {
        type: "problem",
        docs: {
            description: "Enforce that a variable named `foo` can only be assigned a value of 'bar'."
        },
        fixable: "code",
        schema: []
    },
    create(context) {
        return {

            // Performs action in the function on every variable declarator
            VariableDeclarator(node) {

                // Check if a `const` variable declaration
                if (node.parent.kind === "const") {

                    // Check if variable name is `foo`
                    if (node.id.type === "Identifier" && node.id.name === "foo") {

                        // Check if value of variable is "bar"
                        if (node.init && node.init.type === "Literal" && node.init.value !== "bar") {

                            /*
                             * Report error to ESLint. Error message uses
                             * a message placeholder to include the incorrect value
                             * in the error message.
                             * Also includes a `fix(fixer)` function that replaces
                             * any values assigned to `const foo` with "bar".
                             */
                            context.report({
                                node,
                                message: 'Value other than "bar" assigned to `const foo`. Unexpected value: {{ notBar }}.',
                                data: {
                                    notBar: node.init.value
                                },
                                fix(fixer) {
                                    return fixer.replaceText(node.init, '"bar"');
                                }
                            });
                        }
                    }
                }
            }
        };
    }
};

步骤 5:设置测试

编写规则后,您可以对其进行测试以确保其按预期工作。

ESLint 提供了内置的RuleTester 类来测试规则。您不需要使用第三方测试库来测试 ESLint 规则,但 RuleTester 可以与 Mocha 和 Jest 等工具无缝协作。

接下来,创建测试文件 enforce-foo-bar.test.js

touch enforce-foo-bar.test.js

您将在测试文件中使用 eslint 包。将其安装为开发依赖项

npm install eslint --save-dev

并向您的 package.json 文件添加一个测试脚本以运行测试

// package.json
{
    // ...other configuration
    "scripts": {
        "test": "node enforce-foo-bar.test.js"
    },
    // ...other configuration
}

步骤 6:编写测试

要使用 RuleTester 编写测试,请将该类和您的自定义规则导入 enforce-foo-bar.test.js 文件。

RuleTester#run() 方法针对有效和无效的测试用例测试规则。如果规则未能通过任何测试场景,则此方法会抛出错误。RuleTester 要求至少存在一个有效和一个无效的测试场景。

// enforce-foo-bar.test.js
const {RuleTester} = require("eslint");
const fooBarRule = require("./enforce-foo-bar");

const ruleTester = new RuleTester({
  // Must use at least ecmaVersion 2015 because
  // that's when `const` variables were introduced.
  languageOptions: { ecmaVersion: 2015 }
});

// Throws error if the tests in ruleTester.run() do not pass
ruleTester.run(
  "enforce-foo-bar", // rule name
  fooBarRule, // rule code
  { // checks
    // 'valid' checks cases that should pass
    valid: [{
      code: "const foo = 'bar';",
    }],
    // 'invalid' checks cases that should not pass
    invalid: [{
      code: "const foo = 'baz';",
      output: 'const foo = "bar";',
      errors: 1,
    }],
  }
);

console.log("All tests passed!");

使用以下命令运行测试

npm test

如果测试通过,您应该在控制台中看到以下内容

All tests passed!

步骤 7:将自定义规则捆绑到插件中

现在您已经编写了自定义规则并验证了它是否有效,您可以将其包含在插件中。使用插件,您可以将规则共享到 npm 包中,以便在其他项目中使用。

创建插件文件

touch eslint-plugin-example.js

现在编写插件代码。插件只是导出的 JavaScript 对象。要将规则包含到插件中,请将其包含在插件的 rules 对象中,该对象包含规则名称及其源代码的键值对。

要了解有关创建插件的更多信息,请参阅创建插件

// eslint-plugin-example.js

const fooBarRule = require("./enforce-foo-bar");
const plugin = { rules: { "enforce-foo-bar": fooBarRule } };
module.exports = plugin;

步骤 8:在本地使用插件

您可以使用本地定义的插件在您的项目中执行自定义规则。要使用本地插件,请在 ESLint 配置文件的 plugins 属性中指定插件的路径。

您可能希望在以下场景之一中使用本地定义的插件

  • 您想在将插件发布到 npm 之前对其进行测试。
  • 您想使用插件,但不想将其发布到 npm。

在将插件添加到项目之前,请使用扁平化配置文件 eslint.config.js 为您的项目创建 ESLint 配置

touch eslint.config.js

然后,将以下代码添加到 eslint.config.js

// eslint.config.js
"use strict";

// Import the ESLint plugin locally
const eslintPluginExample = require("./eslint-plugin-example");

module.exports = [
    {
        files: ["**/*.js"],
        languageOptions: {
            sourceType: "commonjs",
            ecmaVersion: "latest",
        },
        // Using the eslint-plugin-example plugin defined locally
        plugins: {"example": eslintPluginExample},
        rules: {
            "example/enforce-foo-bar": "error",
        },
    }
]

在测试规则之前,您必须创建一个文件来测试规则。

创建文件 example.js

touch example.js

将以下代码添加到 example.js

// example.js

function correctFooBar() {
  const foo = "bar";
}

function incorrectFoo(){
  const foo = "baz"; // Problem!
}

现在您已准备好使用本地定义的插件测试自定义规则。

example.js 上运行 ESLint

npx eslint example.js

这会在终端中产生以下输出

/<path-to-directory>/eslint-custom-rule-example/example.js
  8:11  error  Value other than "bar" assigned to `const foo`. Unexpected value: baz  example/enforce-foo-bar

✖ 1 problem (1 error, 0 warnings)
  1 error and 0 warnings potentially fixable with the `--fix` option.

步骤 9:发布插件

要将包含规则的插件发布到 npm,您需要配置 package.json。在相应的字段中添加以下内容

  1. "name":包的唯一名称。npm 上没有其他包可以具有相同的名称。
  2. "main":插件文件的相对路径。根据此示例,路径为 "eslint-plugin-example.js"
  3. "description":npm 上可见的包描述。
  4. "peerDependencies":添加 "eslint": ">=9.0.0" 作为对等依赖项。使用该插件需要任何大于或等于该版本的版本。将 eslint 声明为对等依赖项要求用户单独将该包添加到项目中,而不是从插件中添加。
  5. "keywords":包含标准关键字 ["eslint", "eslintplugin", "eslint-plugin"] 以便于查找该包。您还可以添加任何其他与您的插件相关的关键字。

插件的 package.json 文件应具有的完整带注释示例

// package.json
{
  // Name npm package.
  // Add your own package name. eslint-plugin-example is taken!
  "name": "eslint-plugin-example",
  "version": "1.0.0",
  "description": "ESLint plugin for enforce-foo-bar rule.",
  "main": "eslint-plugin-example.js", // plugin entry point
  "scripts": {
    "test": "node enforce-foo-bar.test.js"
  },
  // Add eslint>=9.0.0 as a peer dependency.
  "peerDependencies": {
    "eslint": ">=9.0.0"
  },
  // Add these standard keywords to make plugin easy to find!
  "keywords": [
    "eslint",
    "eslintplugin",
    "eslint-plugin"
  ],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "eslint": "^9.0.0"
  }
}

要发布该包,请运行 npm publish 并按照 CLI 提示操作。

您应该在 npm 上看到该包上线了!

步骤 10:使用已发布的自定义规则

接下来,您可以使用已发布的插件。

在您的项目中运行以下命令以下载该包

npm install --save-dev eslint-plugin-example # Add your package name here

更新 eslint.config.js 以使用插件的打包版本

// eslint.config.js
"use strict";

// Import the plugin downloaded from npm
const eslintPluginExample = require("eslint-plugin-example");

// ... rest of configuration

现在您已准备好测试自定义规则。

在步骤 8 中创建的 example.js 文件上运行 ESLint,现在使用已下载的插件

npx eslint example.js

这会在终端中产生以下输出

/<path-to-directory>/eslint-custom-rule-example/example.js
  8:11  error  Value other than "bar" assigned to `const foo`. Unexpected value: baz  example/enforce-foo-bar

✖ 1 problem (1 error, 0 warnings)
  1 error and 0 warnings potentially fixable with the `--fix` option.

如上文所示,您实际上可以使用 --fix 标志修复该问题,将变量赋值更正为 "bar"

使用 --fix 标志再次运行 ESLint

npx eslint example.js --fix

当您运行此命令时,终端中没有错误输出,但是您可以在 example.js 中看到已应用的修复。您应该看到以下内容

// example.js

// ... rest of file

function incorrectFoo(){
  const foo = "bar"; // Fixed!
}

总结

在本教程中,您创建了一个自定义规则,该规则要求所有名为 fooconst 变量都分配字符串 "bar",并建议将分配给 const foo 的任何其他值替换为 "bar"。您还将该规则添加到插件中,并在 npm 上发布了该插件。

通过这样做,您学习了以下实践,您可以应用这些实践来创建其他自定义规则和插件

  1. 创建自定义 ESLint 规则
  2. 测试自定义规则
  3. 将规则打包到插件中
  4. 发布插件
  5. 从插件中使用规则

查看教程代码

您可以在此处查看本教程的带注释的源代码 这里

更改语言