自定义规则

你可以创建自定义规则以与 ESLint 一起使用。如果 核心规则 不涵盖你的用例,你可能想要创建自定义规则。

You can create custom rules to use with ESLint. You might want to create a custom rule if the core rules do not cover your use case.

以下是自定义规则的基本格式:

Here’s the basic format of a custom rule:

// customRule.js

module.exports = {
    meta: {
        type: "suggestion",
        docs: {
            description: "Description of the rule",
        },
        fixable: "code",
        schema: [] // no options
    },
    create: function(context) {
        return {
            // callback functions
        };
    }
};

规则结构

Rule Structure

规则的源文件导出具有以下属性的对象。自定义规则和核心规则都遵循这种格式。

The source file for a rule exports an object with the following properties. Both custom rules and core rules follow this format.

meta:(object)包含规则的元数据:

meta: (object) Contains metadata for the rule:

  • type:(string)指示规则的类型,为 "problem""suggestion""layout" 之一:

    type: (string) Indicates the type of rule, which is one of "problem", "suggestion", or "layout":

    • "problem":该规则是识别将导致错误或可能导致混淆行为的代码。开发者应将此视为高度优先解决的问题。

      "problem": The rule is identifying code that either will cause an error or may cause a confusing behavior. Developers should consider this a high priority to resolve.

    • "suggestion":规则是确定可以以更好的方式完成的事情,但如果不更改代码就不会发生错误。

      "suggestion": The rule is identifying something that could be done in a better way but no errors will occur if the code isn’t changed.

    • "layout":该规则主要关注空格、分号、逗号和括号,程序的所有部分决定代码的外观而不是代码的执行方式。这些规则适用于 AST 中未指定的部分代码。

      "layout": The rule cares primarily about whitespace, semicolons, commas, and parentheses, all the parts of the program that determine how the code looks rather than how it executes. These rules work on parts of the code that aren’t specified in the AST.

  • docs:(object)通常用于文档生成和工具的属性。对于核心规则是必需的,对于自定义规则是可选的。自定义规则可以根据需要在此处包含其他属性。

    docs: (object) Properties often used for documentation generation and tooling. Required for core rules and optional for custom rules. Custom rules can include additional properties here as needed.

    • description:(string)提供规则的简短描述。对于核心规则,这是在 规则索引 中使用的。

      description: (string) Provides a short description of the rule. For core rules, this is used in rules index.

    • recommended:(boolean)对于核心规则,这指定规则是否由 @eslint/js 中的 recommended 配置启用。

      recommended: (boolean) For core rules, this specifies whether the rule is enabled by the recommended config from @eslint/js.

    • url:(string)指定可以访问完整文档的 URL。代码编辑器经常使用它来提供有关高亮的规则违规的有用链接。

      url: (string) Specifies the URL at which the full documentation can be accessed. Code editors often use this to provide a helpful link on highlighted rule violations.

  • fixable:(string)如果 命令行 上的 --fix 选项自动修复规则报告的问题,则为 "code""whitespace"

    fixable: (string) Either "code" or "whitespace" if the --fix option on the command line automatically fixes problems reported by the rule.

    重要:对于可修复规则,fixable 属性是强制性的。如果未指定此属性,ESLint 将在规则尝试生成修复时抛出错误。如果规则不可修复,则省略 fixable 属性。

    Important: the fixable property is mandatory for fixable rules. If this property isn’t specified, ESLint will throw an error whenever the rule attempts to produce a fix. Omit the fixable property if the rule is not fixable.

  • hasSuggestions:(boolean)指定规则是否可以返回建议(如果省略,则默认为 false)。

    hasSuggestions: (boolean) Specifies whether rules can return suggestions (defaults to false if omitted).

    重要:对于提供建议的规则,hasSuggestions 属性是强制性的。如果此属性未设置为 true,ESLint 将在规则尝试生成建议时抛出错误。如果规则不提供建议,则省略 hasSuggestions 属性。

    Important: the hasSuggestions property is mandatory for rules that provide suggestions. If this property isn’t set to true, ESLint will throw an error whenever the rule attempts to produce a suggestion. Omit the hasSuggestions property if the rule does not provide suggestions.

  • schema:(object | array | false)指定 选项,以便 ESLint 可以防止无效的 规则配置。当规则有选项时为强制。

    schema: (object | array | false) Specifies the options so ESLint can prevent invalid rule configurations. Mandatory when the rule has options.

  • deprecated:(boolean)指示规则是否已被弃用。如果规则没有被弃用,你可以省略 deprecated 属性。

    deprecated: (boolean) Indicates whether the rule has been deprecated. You may omit the deprecated property if the rule has not been deprecated.

  • replacedBy:(array)如果规则已弃用,请指定替换规则。

    replacedBy: (array) In the case of a deprecated rule, specify replacement rule(s).

create():返回一个对象,其中包含 ESLint 在遍历 JavaScript 代码的抽象语法树(ESTree 定义的 AST)时调用到 “visit” 节点的方法:

create(): Returns an object with methods that ESLint calls to “visit” nodes while traversing the abstract syntax tree (AST as defined by ESTree) of JavaScript code:

  • 如果键是节点类型或 选择器,ESLint 在沿着树向下移动时会调用该访问者函数。

    If a key is a node type or a selector, ESLint calls that visitor function while going down the tree.

  • 如果键是节点类型或 选择器:exit,则 ESLint 在向上查找树时会调用该访问者函数。

    If a key is a node type or a selector plus :exit, ESLint calls that visitor function while going up the tree.

  • 如果键是事件名称,ESLint 会调用 代码路径分析 的处理函数。

    If a key is an event name, ESLint calls that handler function for code path analysis.

规则可以使用当前节点及其周围的树来报告或修复问题。

A rule can use the current node and its surrounding tree to report or fix problems.

以下是 array-callback-return 规则的方法:

Here are methods for the array-callback-return rule:

function checkLastSegment (node) {
    // report problem for function if last code path segment is reachable
}

module.exports = {
    meta: { ... },
    create: function(context) {
        // declare the state of the rule
        return {
            ReturnStatement: function(node) {
                // at a ReturnStatement node while going down
            },
            // at a function expression node while going up:
            "FunctionExpression:exit": checkLastSegment,
            "ArrowFunctionExpression:exit": checkLastSegment,
            onCodePathStart: function (codePath, node) {
                // at the start of analyzing a code path
            },
            onCodePathEnd: function(codePath, node) {
                // at the end of analyzing a code path
            }
        };
    }
};

上下文对象

The Context Object

context 对象是规则中 create 方法的唯一参数。例如:

The context object is the only argument of the create method in a rule. For example:

// customRule.js

module.exports = {
    meta: { ... },
    // `context` object is the argument
    create(context) {
       // ...
    }
};

顾名思义,context 对象包含与规则上下文相关的信息。

As the name implies, the context object contains information that is relevant to the context of the rule.

context 对象具有以下属性:

The context object has the following properties:

  • id:(string)规则 ID。

    id: (string) The rule ID.

  • filename:(string)与源关联的文件名。

    filename: (string) The filename associated with the source.

  • physicalFilename:(string)对文件进行 linting 时,它会提供磁盘上文件的完整路径,而无需任何代码块信息。当检查文本时,如果未指定,它会提供传递给 —stdin-filename<text> 的值。

    physicalFilename: (string) When linting a file, it provides the full path of the file on disk without any code block information. When linting text, it provides the value passed to —stdin-filename or <text> if not specified.

  • cwd:(string)cwd 选项传递给 Linter。它是应被视为当前工作目录的目录的路径。

    cwd: (string) The cwd option passed to the Linter. It is a path to a directory that should be considered the current working directory.

  • options:(array)此规则的 配置选项 数组。此数组不包括规则严重性(请参阅 专用部分)。

    options: (array) An array of the configured options for this rule. This array does not include the rule severity (see the dedicated section).

  • sourceCode:(object)一个 SourceCode 对象,可用于处理传递给 ESLint 的源(请参阅 访问源代码)。

    sourceCode: (object) A SourceCode object that you can use to work with the source that was passed to ESLint (see Accessing the Source Code).

  • settings:(object)从配置上看是 共享的设置

    settings: (object) The shared settings from the configuration.

  • languageOptions:(object) 每个属性的更多详细信息 此处

    languageOptions: (object) more details for each property here

    • sourceType:('script' | 'module' | 'commonjs')当前文件的模式。

      sourceType: ('script' | 'module' | 'commonjs') The mode for the current file.

    • ecmaVersion:(number)用于解析当前文件的 ECMA 版本。

      ecmaVersion: (number) The ECMA version used to parse the current file.

    • parser:(object|string):用于解析扁平配置的当前文件的解析器或其旧配置的名称。

      parser: (object|string): Either the parser used to parse the current file for flat config or its name for legacy config.

    • parserOptions:(object)为此文件配置的解析器选项。

      parserOptions: (object) The parser options configured for this file.

    • globals:(object)指定的全局变量。

      globals: (object) The specified globals.

  • parserPath:(string,已删除,使用 context.languageOptions.parser 代替。)配置中 parser 的名称。

    parserPath: (string, Removed Use context.languageOptions.parser instead.) The name of the parser from the configuration.

  • parserOptions:(已弃用,请使用 context.languageOptions.parserOptions 代替。)为此运行配置的解析器选项(更多详细信息 此处)。

    parserOptions: (Deprecated Use context.languageOptions.parserOptions instead.) The parser options configured for this run (more details here).

此外,context 对象具有以下方法:

Additionally, the context object has the following methods:

  • getCwd():(已弃用:请改用 context.cwd。)返回传递给 Lintercwd 选项。它是应被视为当前工作目录的目录的路径。

    getCwd(): (Deprecated: Use context.cwd instead.) Returns the cwd option passed to the Linter. It is a path to a directory that should be considered the current working directory.

  • getFilename():(已弃用:请改用 context.filename。)返回与源关联的文件名。

    getFilename(): (Deprecated: Use context.filename instead.) Returns the filename associated with the source.

  • getPhysicalFilename():(已弃用:请改用 context.physicalFilename。)当对文件进行 linting 时,它会返回磁盘上文件的完整路径,而不包含任何代码块信息。当检查文本时,如果未指定,它会返回传递给 —stdin-filename<text> 的值。

    getPhysicalFilename(): (Deprecated: Use context.physicalFilename instead.) When linting a file, it returns the full path of the file on disk without any code block information. When linting text, it returns the value passed to —stdin-filename or <text> if not specified.

  • getSourceCode():(已弃用:请使用 context.sourceCode。)返回一个 SourceCode 对象,你可以使用该对象来处理传递给 ESLint 的源(请参阅 访问源代码)。

    getSourceCode(): (Deprecated: Use context.sourceCode instead.) Returns a SourceCode object that you can use to work with the source that was passed to ESLint (see Accessing the Source Code).

  • report(descriptor)。报告代码中的问题(参见 专用部分)。

    report(descriptor). Reports a problem in the code (see the dedicated section).

注意:ESLint 的早期版本支持 context 对象上的其他方法。这些方法已在新格式中删除,不应依赖。

Note: Earlier versions of ESLint supported additional methods on the context object. Those methods were removed in the new format and should not be relied upon.

报告问题

Reporting Problems

编写自定义规则时你将使用的主要方法是 context.report(),它会发布警告或错误(取决于所使用的配置)。此方法接受一个参数,该参数是一个包含以下属性的对象:

The main method you’ll use when writing custom rules is context.report(), which publishes a warning or error (depending on the configuration being used). This method accepts a single argument, which is an object containing the following properties:

  • messageId:(string)消息的 ID(参见 messageIds)(在 message 上推荐)。

    messageId: (string) The ID of the message (see messageIds) (recommended over message).

  • message:(string)问题消息(messageId 的替代)。

    message: (string) The problem message (alternative to messageId).

  • node:(可选的 object)与问题相关的 AST 节点。如果存在且未指定 loc,则将节点的起始位置用作问题的位置。

    node: (optional object) The AST node related to the problem. If present and loc is not specified, then the starting location of the node is used as the location of the problem.

  • loc:(可选的 object)指定问题的位置。如果同时指定了 locnode,则使用 loc 而不是 node 的位置。

    loc: (optional object) Specifies the location of the problem. If both loc and node are specified, then the location is used from loc instead of node.

    • start:起始位置的对象。

      start: An object of the start location.

      • line:(number)发生问题的从 1 开始的行号。

        line: (number) The 1-based line number at which the problem occurred.

      • column:(number)发生问题的从 0 开始的列号。

        column: (number) The 0-based column number at which the problem occurred.

    • end:结束位置的对象。

      end: An object of the end location.

      • line:(number)发生问题的从 1 开始的行号。

        line: (number) The 1-based line number at which the problem occurred.

      • column:(number)发生问题的从 0 开始的列号。

        column: (number) The 0-based column number at which the problem occurred.

  • data:(可选的 objectmessage占位符 数据。

    data: (optional object) Placeholder data for message.

  • fix(fixer):(可选的 function)应用 fix 来解决问题。

    fix(fixer): (optional function) Applies a fix to resolve the problem.

请注意,至少需要 nodeloc 之一。

Note that at least one of node or loc is required.

最简单的示例是仅使用 nodemessage

The simplest example is to use just node and message:

context.report({
    node: node,
    message: "Unexpected identifier"
});

该节点包含找出违规文本的行号和列号以及代表该节点的源文本所需的所有信息。

The node contains all the information necessary to figure out the line and column number of the offending text as well as the source text representing the node.

使用消息占位符

Using Message Placeholders

你还可以在消息中使用占位符并提供 data

You can also use placeholders in the message and provide data:

context.report({ node: node, message: “Unexpected identifier: {{ identifier }}”, data: { identifier: node.name } });

请注意,前导和尾随空格在消息参数中是可选的。

Note that leading and trailing whitespace is optional in message parameters.

该节点包含找出违规文本的行号和列号以及代表该节点的源文本所需的所有信息。

The node contains all the information necessary to figure out the line and column number of the offending text as well as the source text representing the node.

messageId

messageId 是在 context.report() 调用中报告消息的推荐方法,因为它具有以下优点:

messageIds are the recommended approach to reporting messages in context.report() calls because of the following benefits:

  • 规则违规消息可以存储在中央 meta.messages 对象中,以便于管理

    Rule violation messages can be stored in a central meta.messages object for convenient management

  • 规则违规消息不需要在规则文件和规则测试文件中重复

    Rule violation messages do not need to be repeated in both the rule file and rule test file

  • 因此,更改违规消息的障碍较低,鼓励更频繁的贡献来改进和优化它们,以获得最大的清晰度和实用性

    As a result, the barrier for changing rule violation messages is lower, encouraging more frequent contributions to improve and optimize them for the greatest clarity and usefulness

规则文件:

Rule file:

// avoid-name.js

module.exports = { meta: { messages: { avoidName: “Avoid using variables named ‘{{ name }}’” } }, create(context) { return { Identifier(node) { if (node.name === “foo”) { context.report({ node, messageId: “avoidName”, data: { name: “foo”, } }); } } }; } };

在要 lint 的文件中:

In the file to lint:

// someFile.js

var foo = 2;
//  ^ error: Avoid using variables named 'foo'

在你的测试中:

In your tests:

// avoid-name.test.js

var rule = require("../../../lib/rules/avoid-name");
var RuleTester = require("eslint").RuleTester;

var ruleTester = new RuleTester();
ruleTester.run("avoid-name", rule, {
    valid: ["bar", "baz"],
    invalid: [
        {
            code: "foo",
            errors: [
                {
                    messageId: "avoidName"
                }
            ]
        }
    ]
});

应用修复

Applying Fixes

如果你希望 ESLint 尝试修复你报告的问题,你可以通过在使用 context.report() 时指定 fix 函数来实现。fix 函数接收一个参数,一个 fixer 对象,你可以使用它来应用修复。例如:

If you’d like ESLint to attempt to fix the problem you’re reporting, you can do so by specifying the fix function when using context.report(). The fix function receives a single argument, a fixer object, that you can use to apply a fix. For example:

context.report({
    node: node,
    message: "Missing semicolon",
    fix(fixer) {
        return fixer.insertTextAfter(node, ";");
    }
});

在这里,fix() 函数用于在节点后插入一个分号。请注意,修复不会立即应用,如果与其他修复存在冲突,则可能根本不会应用。应用修复后,ESLint 将在修复的代码上再次运行所有启用的规则,可能会应用更多修复。此过程将重复最多 10 次,或直到找不到更多可修复的问题。之后,将照常报告任何剩余问题。

Here, the fix() function is used to insert a semicolon after the node. Note that a fix is not immediately applied, and may not be applied at all if there are conflicts with other fixes. After applying fixes, ESLint will run all the enabled rules again on the fixed code, potentially applying more fixes. This process will repeat up to 10 times, or until no more fixable problems are found. Afterward, any remaining problems will be reported as usual.

重要:meta.fixable 属性对于可修复规则是必需的。如果实现 fix 功能的规则没有 export meta.fixable 属性,ESLint 将抛出错误。

Important: The meta.fixable property is mandatory for fixable rules. ESLint will throw an error if a rule that implements fix functions does not export the meta.fixable property.

fixer 对象具有以下方法:

The fixer object has the following methods:

  • insertTextAfter(nodeOrToken, text):在给定的节点或标记之后插入文本。

    insertTextAfter(nodeOrToken, text): Insert text after the given node or token.

  • insertTextAfterRange(range, text):在给定范围后插入文本。

    insertTextAfterRange(range, text): Insert text after the given range.

  • insertTextBefore(nodeOrToken, text):在给定节点或标记之前插入文本。

    insertTextBefore(nodeOrToken, text): Insert text before the given node or token.

  • insertTextBeforeRange(range, text):在给定范围之前插入文本。

    insertTextBeforeRange(range, text): Insert text before the given range.

  • remove(nodeOrToken):删除给定的节点或令牌。

    remove(nodeOrToken): Remove the given node or token.

  • removeRange(range):删除给定范围内的文本。

    removeRange(range): Remove text in the given range.

  • replaceText(nodeOrToken, text):替换给定节点或标记中的文本。

    replaceText(nodeOrToken, text): Replace the text in the given node or token.

  • replaceTextRange(range, text):替换给定范围内的文本。

    replaceTextRange(range, text): Replace the text in the given range.

range 是一个包含源代码内部字符索引的两项数组。第一项是范围的开始(包括),第二项是范围的结束(不包括)。每个节点和令牌都有一个 range 属性来标识它们所代表的源代码范围。

A range is a two-item array containing character indices inside the source code. The first item is the start of the range (inclusive) and the second item is the end of the range (exclusive). Every node and token has a range property to identify the source code range they represent.

上述方法返回一个 fixing 对象。fix() 函数可以返回以下值:

The above methods return a fixing object. The fix() function can return the following values:

  • 一个 fixing 对象。

    A fixing object.

  • 包含 fixing 个对象的数组。

    An array which includes fixing objects.

  • 枚举 fixing 个对象的可迭代对象。特别是,fix() 函数可以是一个生成器。

    An iterable object which enumerates fixing objects. Especially, the fix() function can be a generator.

如果你创建一个返回多个 fixing 对象的 fix() 函数,则这些 fixing 对象不得重叠。

If you make a fix() function which returns multiple fixing objects, those fixing objects must not overlap.

修复的最佳实践:

Best practices for fixes:

  1. 避免任何可能改变代码运行时行为并导致其停止工作的修复。

    Avoid any fixes that could change the runtime behavior of code and cause it to stop working.

  2. 使修复尽可能小。过大的修复可能会与其他修复发生冲突,并阻止应用它们。

    Make fixes as small as possible. Fixes that are unnecessarily large could conflict with other fixes, and prevent them from being applied.

  3. 每条消息只修复一次。这是强制执行的,因为你必须从 fix() 返回修复程序操作的结果。

    Only make one fix per message. This is enforced because you must return the result of the fixer operation from fix().

  4. 由于在应用第一轮修复后所有规则都会再次运行,因此规则没有必要检查修复的代码风格是否会导致另一条规则报告错误。

    Since all rules are run again after the initial round of fixes is applied, it’s not necessary for a rule to check whether the code style of a fix will cause errors to be reported by another rule.

    • 例如,假设修复者想用引号将对象键括起来,但不确定用户更喜欢单引号还是双引号。

      For example, suppose a fixer would like to surround an object key with quotes, but it’s not sure whether the user would prefer single or double quotes.

      ({ foo : 1 })
      
      // should get fixed to either
      
      ({ 'foo': 1 })
      
      // or
      
      ({ "foo": 1 })
      
    • 这个固定器可以任意选择一个引号类型。如果猜错了,结果代码会自动上报,并被 quotes 规则修复。

      This fixer can just select a quote type arbitrarily. If it guesses wrong, the resulting code will be automatically reported and fixed by the quotes rule.

注意:使修复尽可能小是最佳做法,但在某些情况下,为了有意防止其他规则在同一遍中在周围范围内进行修复,扩展修复范围可能是正确的。例如,如果替换文本声明了一个新变量,则可以防止变量范围内的其他更改很有用,因为它们可能会导致名称冲突。

Note: Making fixes as small as possible is a best practice, but in some cases it may be correct to extend the range of the fix in order to intentionally prevent other rules from making fixes in a surrounding range in the same pass. For instance, if replacement text declares a new variable, it can be useful to prevent other changes in the scope of the variable as they might cause name collisions.

以下示例替换了 node,并确保在同一遍中不会在 node.parent 的范围内应用其他修复:

The following example replaces node and also ensures that no other fixes will be applied in the range of node.parent in the same pass:

context.report({
    node,
    message,
    *fix(fixer) {
        yield fixer.replaceText(node, replacementText);

        // extend range of the fix to the range of `node.parent`
        yield fixer.insertTextBefore(node.parent, "");
        yield fixer.insertTextAfter(node.parent, "");
    }
});

冲突修复

Conflicting Fixes

冲突修复是对源代码的同一部分应用不同更改的修复。无法指定应用了哪些冲突修复程序。

Conflicting fixes are fixes that apply different changes to the same part of the source code. There is no way to specify which of the conflicting fixes is applied.

例如,如果有两个修复要修改字符 0 到 5,则只应用一个。

For example, if two fixes want to modify characters 0 through 5, only one is applied.

提供建议

Providing Suggestions

在某些情况下,修复不适合自动应用,例如,如果修复可能会更改功能,或者根据实现意图有多种有效方法来修复规则(请参阅上面列出的 应用修复 的最佳实践)。在这些情况下,context.report() 上有一个替代的 suggest 选项,它允许其他工具(例如编辑器)向用户公开辅助程序以手动应用建议。

In some cases fixes aren’t appropriate to be automatically applied, for example, if a fix potentially changes functionality or if there are multiple valid ways to fix a rule depending on the implementation intent (see the best practices for applying fixes listed above). In these cases, there is an alternative suggest option on context.report() that allows other tools, such as editors, to expose helpers for users to manually apply a suggestion.

要提供建议,请在带有建议对象数组的报告参数中使用 suggest 键。建议对象表示可以应用的个人建议,需要一个 desc 键字符串来描述应用建议会做什么或 messageId 键(参见 below),以及一个 fix 键,它是一个定义建议结果的函数。此 fix 函数遵循与常规修复相同的 API(在上面的 应用修复 中描述)。

To provide suggestions, use the suggest key in the report argument with an array of suggestion objects. The suggestion objects represent individual suggestions that could be applied and require either a desc key string that describes what applying the suggestion would do or a messageId key (see below), and a fix key that is a function defining the suggestion result. This fix function follows the same API as regular fixes (described above in applying fixes).

context.report({ node: node, message: “Unnecessary escape character: \{{character}}.”, data: { character }, suggest: [ { desc: “Remove the \\. This maintains the current functionality.”, fix: function(fixer) { return fixer.removeRange(range); } }, { desc: “Replace the \\ with \\\\ to include the actual backslash character.”, fix: function(fixer) { return fixer.insertTextBeforeRange(range, “\”); } } ] });

重要:meta.hasSuggestions 属性对于提供建议的规则是必需的。如果规则试图产生一个建议但没有 export 这个属性,ESLint 将抛出一个错误。

Important: The meta.hasSuggestions property is mandatory for rules that provide suggestions. ESLint will throw an error if a rule attempts to produce a suggestion but does not export this property.

注意:建议作为独立更改应用,不会触发多通道修复。每个建议都应该集中在代码的单一变化上,而不应该试图符合用户定义的样式。例如,如果一个建议是在代码库中添加一个新语句,它不应该尝试匹配正确的缩进或符合用户对分号存在/不存在的偏好。当用户触发它时,所有这些事情都可以通过多通道自动修复来纠正。

Note: Suggestions are applied as stand-alone changes, without triggering multipass fixes. Each suggestion should focus on a singular change in the code and should not try to conform to user-defined styles. For example, if a suggestion is adding a new statement into the codebase, it should not try to match correct indentation or conform to user preferences on the presence/absence of semicolons. All of those things can be corrected by multipass autofix when the user triggers it.

建议的最佳实践:

Best practices for suggestions:

  1. 不要尝试做太多,也不要建议可能会引入大量重大更改的大型重构。

    Don’t try to do too much and suggest large refactors that could introduce a lot of breaking changes.

  2. 如上所述,不要试图符合用户定义的样式。

    As noted above, don’t try to conform to user-defined styles.

建议旨在提供修复。如果建议的 fix 函数返回 null 或空数组/序列,ESLint 会自动从 linting 输出中删除整个建议。

Suggestions are intended to provide fixes. ESLint will automatically remove the whole suggestion from the linting output if the suggestion’s fix function returned null or an empty array/sequence.

建议 messageId

Suggestion messageIds

可以使用 messageId 代替建议,而不是使用 desc 键。对于总体误差,其工作方式与 messageId 相同(参见 messageIds)。以下是如何在规则中使用建议 messageId 的示例:

Instead of using a desc key for suggestions a messageId can be used instead. This works the same way as messageIds for the overall error (see messageIds). Here is an example of how to use a suggestion messageId in a rule:

module.exports = { meta: { messages: { unnecessaryEscape: “Unnecessary escape character: \{{character}}.”, removeEscape: “Remove the \\. This maintains the current functionality.”, escapeBackslash: “Replace the \\ with \\\\ to include the actual backslash character.” }, hasSuggestions: true }, create: function(context) { // … context.report({ node: node, messageId: ‘unnecessaryEscape’, data: { character }, suggest: [ { messageId: “removeEscape”, // suggestion messageId fix: function(fixer) { return fixer.removeRange(range); } }, { messageId: “escapeBackslash”, // suggestion messageId fix: function(fixer) { return fixer.insertTextBeforeRange(range, “\”); } } ] }); } };

建议消息中的占位符

Placeholders in Suggestion Messages

你还可以在建议消息中使用占位符。这与整体错误的占位符的工作方式相同(请参阅 使用消息占位符)。

You can also use placeholders in the suggestion message. This works the same way as placeholders for the overall error (see using message placeholders).

请注意,你必须在建议的对象上提供 data。建议消息不能使用总体错误的 data 中的属性。

Please note that you have to provide data on the suggestion’s object. Suggestion messages cannot use properties from the overall error’s data.

module.exports = { meta: { messages: { unnecessaryEscape: “Unnecessary escape character: \{{character}}.”, removeEscape: “Remove \\ before {{character}}.”, }, hasSuggestions: true }, create: function(context) { // … context.report({ node: node, messageId: “unnecessaryEscape”, data: { character }, // data for the unnecessaryEscape overall message suggest: [ { messageId: “removeEscape”, data: { character }, // data for the removeEscape suggestion message fix: function(fixer) { return fixer.removeRange(range); } } ] }); } };

访问传递给规则的选项

Accessing Options Passed to a Rule

某些规则需要选项才能正常运行。这些选项出现在配置中(.eslintrc、命令行接口或注释)。例如:

Some rules require options in order to function correctly. These options appear in configuration (.eslintrc, command line interface, or comments). For example:

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

本例中的 quotes 规则有一个选项,"double"error 是错误级别)。你可以使用 context.options 检索规则的选项,context.options 是一个包含规则的每个配置选项的数组。在这种情况下,context.options[0] 将包含 "double"

The quotes rule in this example has one option, "double" (the error is the error level). You can retrieve the options for a rule by using context.options, which is an array containing every configured option for the rule. In this case, context.options[0] would contain "double":

module.exports = {
    meta: {
        schema: [
            {
                enum: ["single", "double", "backtick"]
            }
        ]
    },
    create: function(context) {
        var isDouble = (context.options[0] === "double");

        // ...
    }
};

由于 context.options 只是一个数组,你可以使用它来确定已传递的选项数量以及检索实际选项本身。请记住,错误级别不是 context.options 的一部分,因为无法从规则内部获知或修改错误级别。

Since context.options is just an array, you can use it to determine how many options have been passed as well as retrieving the actual options themselves. Keep in mind that the error level is not part of context.options, as the error level cannot be known or modified from inside a rule.

使用选项时,请确保你的规则具有一些逻辑默认值,以防未提供选项。

When using options, make sure that your rule has some logical defaults in case the options are not provided.

带有选项的规则必须指定 schema

Rules with options must specify a schema.

访问源代码

Accessing the Source Code

SourceCode 对象是获取有关正在检查的源代码的更多信息的主要对象。你可以随时使用 context.sourceCode 属性检索 SourceCode 对象:

The SourceCode object is the main object for getting more information about the source code being linted. You can retrieve the SourceCode object at any time by using the context.sourceCode property:

module.exports = {
    create: function(context) {
        var sourceCode = context.sourceCode;

        // ...
    }
};

已弃用:context.getSourceCode() 方法已弃用;确保改用 context.sourceCode 属性。

Deprecated: The context.getSourceCode() method is deprecated; make sure to use context.sourceCode property instead.

拥有 SourceCode 的实例后,你可以在其上使用以下方法来处理代码:

Once you have an instance of SourceCode, you can use the following methods on it to work with the code:

  • getText(node):返回给定节点的源代码。省略 node 以获得完整的源代码(参见 专用部分)。

    getText(node): Returns the source code for the given node. Omit node to get the whole source (see the dedicated section).

  • getAllComments():返回源中所有注释的数组(参见 专用部分)。

    getAllComments(): Returns an array of all comments in the source (see the dedicated section).

  • getCommentsBefore(nodeOrToken):返回直接出现在给定节点或标记之前的注释标记数组(请参阅 专用部分)。

    getCommentsBefore(nodeOrToken): Returns an array of comment tokens that occur directly before the given node or token (see the dedicated section).

  • getCommentsAfter(nodeOrToken):返回在给定节点或标记之后直接出现的注释标记数组(请参阅 专用部分)。

    getCommentsAfter(nodeOrToken): Returns an array of comment tokens that occur directly after the given node or token (see the dedicated section).

  • getCommentsInside(node):返回给定节点内所有注释标记的数组(请参阅 专用部分)。

    getCommentsInside(node): Returns an array of all comment tokens inside a given node (see the dedicated section).

  • isSpaceBetween(nodeOrToken, nodeOrToken):如果两个标记之间有空白字符,或者如果给定一个节点,第一个节点的最后一个标记和第二个节点的第一个标记,则返回 true。

    isSpaceBetween(nodeOrToken, nodeOrToken): Returns true if there is a whitespace character between the two tokens or, if given a node, the last token of the first node and the first token of the second node.

  • getFirstToken(node, skipOptions):返回表示给定节点的第一个标记。

    getFirstToken(node, skipOptions): Returns the first token representing the given node.

  • getFirstTokens(node, countOptions):返回代表给定节点的前 count 个标记。

    getFirstTokens(node, countOptions): Returns the first count tokens representing the given node.

  • getLastToken(node, skipOptions):返回表示给定节点的最后一个标记。

    getLastToken(node, skipOptions): Returns the last token representing the given node.

  • getLastTokens(node, countOptions):返回表示给定节点的最后 count 个令牌。

    getLastTokens(node, countOptions): Returns the last count tokens representing the given node.

  • getTokenAfter(nodeOrToken, skipOptions):返回给定节点或标记之后的第一个标记。

    getTokenAfter(nodeOrToken, skipOptions): Returns the first token after the given node or token.

  • getTokensAfter(nodeOrToken, countOptions):返回给定节点或标记后的 count 个标记。

    getTokensAfter(nodeOrToken, countOptions): Returns count tokens after the given node or token.

  • getTokenBefore(nodeOrToken, skipOptions):返回给定节点或标记之前的第一个标记。

    getTokenBefore(nodeOrToken, skipOptions): Returns the first token before the given node or token.

  • getTokensBefore(nodeOrToken, countOptions):返回给定节点或标记之前的 count 个标记。

    getTokensBefore(nodeOrToken, countOptions): Returns count tokens before the given node or token.

  • getFirstTokenBetween(nodeOrToken1, nodeOrToken2, skipOptions):返回两个节点或标记之间的第一个标记。

    getFirstTokenBetween(nodeOrToken1, nodeOrToken2, skipOptions): Returns the first token between two nodes or tokens.

  • getFirstTokensBetween(nodeOrToken1, nodeOrToken2, countOptions):返回两个节点或令牌之间的第一个 count 令牌。

    getFirstTokensBetween(nodeOrToken1, nodeOrToken2, countOptions): Returns the first count tokens between two nodes or tokens.

  • getLastTokenBetween(nodeOrToken1, nodeOrToken2, skipOptions):返回两个节点或令牌之间的最后一个令牌。

    getLastTokenBetween(nodeOrToken1, nodeOrToken2, skipOptions): Returns the last token between two nodes or tokens.

  • getLastTokensBetween(nodeOrToken1, nodeOrToken2, countOptions):返回两个节点或令牌之间的最后 count 个令牌。

    getLastTokensBetween(nodeOrToken1, nodeOrToken2, countOptions): Returns the last count tokens between two nodes or tokens.

  • getTokens(node):返回给定节点的所有标记。

    getTokens(node): Returns all tokens for the given node.

  • getTokensBetween(nodeOrToken1, nodeOrToken2):返回两个节点之间的所有令牌。

    getTokensBetween(nodeOrToken1, nodeOrToken2): Returns all tokens between two nodes.

  • getTokenByRangeStart(index, rangeOptions):返回其范围从源中的给定索引开始的标记。

    getTokenByRangeStart(index, rangeOptions): Returns the token whose range starts at the given index in the source.

  • getNodeByRangeIndex(index):返回包含给定源索引的 AST 中最深的节点。

    getNodeByRangeIndex(index): Returns the deepest node in the AST containing the given source index.

  • getLocFromIndex(index):返回具有 linecolumn 属性的对象,对应于给定源索引的位置。line 从 1 开始,column 从 0 开始。

    getLocFromIndex(index): Returns an object with line and column properties, corresponding to the location of the given source index. line is 1-based and column is 0-based.

  • getIndexFromLoc(loc):返回源代码中给定位置的索引,其中 loc 是具有基于 1 的 line 键和基于 0 的 column 键的对象。

    getIndexFromLoc(loc): Returns the index of a given location in the source code, where loc is an object with a 1-based line key and a 0-based column key.

  • commentsExistBetween(nodeOrToken1, nodeOrToken2):如果两个节点之间存在注释,则返回 true

    commentsExistBetween(nodeOrToken1, nodeOrToken2): Returns true if comments exist between two nodes.

  • getAncestors(node):返回给定节点的祖级数组,从 AST 的根开始,一直到给定节点的直接父节点。该数组不包括给定节点本身。

    getAncestors(node): Returns an array of the ancestors of the given node, starting at the root of the AST and continuing through the direct parent of the given node. This array does not include the given node itself.

  • getDeclaredVariables(node):返回给定节点声明的 variables 列表。此信息可用于跟踪对变量的引用。

    getDeclaredVariables(node): Returns a list of variables declared by the given node. This information can be used to track references to variables.

    • 如果节点是 VariableDeclaration,则返回声明中声明的所有变量。

      If the node is a VariableDeclaration, all variables declared in the declaration are returned.

    • 如果节点是 VariableDeclarator,则返回声明符中声明的所有变量。

      If the node is a VariableDeclarator, all variables declared in the declarator are returned.

    • 如果节点是 FunctionDeclarationFunctionExpression,则除了函数参数的变量外,还返回函数名称的变量。

      If the node is a FunctionDeclaration or FunctionExpression, the variable for the function name is returned, in addition to variables for the function parameters.

    • 如果节点是 ArrowFunctionExpression,则返回参数的变量。

      If the node is an ArrowFunctionExpression, variables for the parameters are returned.

    • 如果节点是 ClassDeclarationClassExpression,则返回类名的变量。

      If the node is a ClassDeclaration or a ClassExpression, the variable for the class name is returned.

    • 如果节点是 CatchClause,则返回异常变量。

      If the node is a CatchClause, the variable for the exception is returned.

    • 如果节点是 ImportDeclaration,则返回其所有说明符的变量。

      If the node is an ImportDeclaration, variables for all of its specifiers are returned.

    • 如果节点是 ImportSpecifierImportDefaultSpecifierImportNamespaceSpecifier,则返回声明的变量。

      If the node is an ImportSpecifier, ImportDefaultSpecifier, or ImportNamespaceSpecifier, the declared variable is returned.

    • 否则,如果节点没有声明任何变量,则返回一个空数组。

      Otherwise, if the node does not declare any variables, an empty array is returned.

  • getScope(node):返回给定节点的 scope。此信息可用于跟踪对变量的引用。

    getScope(node): Returns the scope of the given node. This information can be used to track references to variables.

  • markVariableAsUsed(name, refNode):将给定引用节点指示的范围内具有给定名称的变量标记为已使用。这会影响 no-unused-vars 规则。如果找到具有给定名称的变量并将其标记为已使用,则返回 true,否则返回 false

    markVariableAsUsed(name, refNode): Marks a variable with the given name in a scope indicated by the given reference node as used. This affects the no-unused-vars rule. Returns true if a variable with the given name was found and marked as used, otherwise false.

skipOptions 是一个具有 3 个属性的对象;skipincludeCommentsfilter。默认为 {skip: 0, includeComments: false, filter: null}

skipOptions is an object which has 3 properties; skip, includeComments, and filter. Default is {skip: 0, includeComments: false, filter: null}.

  • skip:(number)正整数,跳过标记的数量。如果同时给出 filter 选项,则不会将已过滤的标记计为已跳过。

    skip: (number) Positive integer, the number of skipping tokens. If filter option is given at the same time, it doesn’t count filtered tokens as skipped.

  • includeComments:(boolean)将注释标记包含到结果中的标志。

    includeComments: (boolean) The flag to include comment tokens into the result.

  • filter(token):获取标记作为第一个参数的函数。如果函数返回 false,则结果不包括标记。

    filter(token): Function which gets a token as the first argument. If the function returns false then the result excludes the token.

countOptions 是一个具有 3 个属性的对象;countincludeCommentsfilter。默认为 {count: 0, includeComments: false, filter: null}

countOptions is an object which has 3 properties; count, includeComments, and filter. Default is {count: 0, includeComments: false, filter: null}.

  • count:(number)正整数,返回 token 的最大数量。

    count: (number) Positive integer, the maximum number of returning tokens.

  • includeComments:(boolean)将注释标记包含到结果中的标志。

    includeComments: (boolean) The flag to include comment tokens into the result.

  • filter(token):获取标记作为第一个参数的函数,如果函数返回 false,则结果不包括标记。

    filter(token): Function which gets a token as the first argument, if the function returns false then the result excludes the token.

rangeOptions 是具有 1 个属性 includeComments 的对象。默认为 {includeComments: false}

rangeOptions is an object that has 1 property, includeComments. Default is {includeComments: false}.

  • includeComments:(boolean)将注释标记包含到结果中的标志。

    includeComments: (boolean) The flag to include comment tokens into the result.

你还可以访问一些属性:

There are also some properties you can access:

  • hasBOM:(boolean)该标志指示源代码是否具有 Unicode BOM。

    hasBOM: (boolean) The flag to indicate whether the source code has Unicode BOM.

  • text:(string)正在检查的代码的全文。Unicode BOM 已从本文中删除。

    text: (string) The full text of the code being linted. Unicode BOM has been stripped from this text.

  • ast:(object)正在检查的代码的 AST 的 Program 节点。

    ast: (object) Program node of the AST for the code being linted.

  • scopeManager:代码的 ScopeManager 对象。

    scopeManager: ScopeManager object of the code.

  • visitorKeys:(object)遍历此 AST 的访问者键。

    visitorKeys: (object) Visitor keys to traverse this AST.

  • parserServices:(object)包含解析器提供的规则服务。默认解析器不提供任何服务。但是,如果规则旨在与自定义解析器一起使用,则它可以使用 parserServices 来访问该解析器提供的任何内容。(例如,TypeScript 解析器可以提供获取给定节点的计算类型的能力。)

    parserServices: (object) Contains parser-provided services for rules. The default parser does not provide any services. However, if a rule is intended to be used with a custom parser, it could use parserServices to access anything provided by that parser. (For example, a TypeScript parser could provide the ability to get the computed type of a given node.)

  • lines:(array)行数组,根据规范的换行符定义进行分割。

    lines: (array) Array of lines, split according to the specification’s definition of line breaks.

每当你需要获取有关被检查代码的更多信息时,你都应该使用 SourceCode 对象。

You should use a SourceCode object whenever you need to get more information about the code being linted.

访问源文本

Accessing the Source Text

如果你的规则需要获取实际的 JavaScript 源代码才能使用,请使用 sourceCode.getText() 方法。此方法的工作原理如下:

If your rule needs to get the actual JavaScript source to work with, then use the sourceCode.getText() method. This method works as follows:


// get all source
var source = sourceCode.getText();

// get source for just this AST node
var nodeSource = sourceCode.getText(node);

// get source for AST node plus previous two characters
var nodeSourceWithPrev = sourceCode.getText(node, 2);

// get source for AST node plus following two characters
var nodeSourceWithFollowing = sourceCode.getText(node, 0, 2);

这样,当 AST 未提供适当数据(例如逗号、分号、括号等的位置)时,你可以在 JavaScript 文本本身中查找模式。

In this way, you can look for patterns in the JavaScript text itself when the AST isn’t providing the appropriate data (such as the location of commas, semicolons, parentheses, etc.).

访问注释

Accessing Comments

虽然注释在技术上不是 AST 的一部分,但 ESLint 提供了 sourceCode.getAllComments()sourceCode.getCommentsBefore()sourceCode.getCommentsAfter()sourceCode.getCommentsInside() 来访问它们。

While comments are not technically part of the AST, ESLint provides the sourceCode.getAllComments(), sourceCode.getCommentsBefore(), sourceCode.getCommentsAfter(), and sourceCode.getCommentsInside() to access them.

sourceCode.getCommentsBefore()sourceCode.getCommentsAfter()sourceCode.getCommentsInside() 对于需要检查与给定节点或令牌相关的注释的规则很有用。

sourceCode.getCommentsBefore(), sourceCode.getCommentsAfter(), and sourceCode.getCommentsInside() are useful for rules that need to check comments in relation to a given node or token.

请记住,这些方法的结果是按需计算的。

Keep in mind that the results of these methods are calculated on demand.

你还可以使用 includeComments 选项通过 sourceCode 的许多方法访问注释。

You can also access comments through many of sourceCode’s methods using the includeComments option.

选项模式

Options Schemas

带有选项的规则必须指定 meta.schema 属性,它是规则选项的 JSON 结构 格式描述,ESLint 将使用它来验证配置选项并防止无效或意外的输入,然后再将其传递给 context.options 中的规则。

Rules with options must specify a meta.schema property, which is a JSON Schema format description of a rule’s options which will be used by ESLint to validate configuration options and prevent invalid or unexpected inputs before they are passed to the rule in context.options.

如果你的规则有选项,强烈建议你指定选项验证的架构。但是,可以通过设置 schema: false 来选择退出选项验证,但不鼓励这样做,因为它会增加出现错误和错误的机会。

If your rule has options, it is strongly recommended that you specify a schema for options validation. However, it is possible to opt-out of options validation by setting schema: false, but doing so is discouraged as it increases the chance of bugs and mistakes.

对于未指定 meta.schema 属性的规则,ESLint 在传递任何选项时都会引发错误。如果你的规则没有选项,请不要设置 schema: false,而只需省略 schema 属性或使用 schema: [],这两者都会阻止传递任何选项。

For rules that don’t specify a meta.schema property, ESLint throws errors when any options are passed. If your rule doesn’t have options, do not set schema: false, but simply omit the schema property or use schema: [], both of which prevent any options from being passed.

验证规则的配置时,有五个步骤:

When validating a rule’s config, there are five steps:

  1. 如果规则配置不是数组,则将值封装到数组中(例如 "off" 变为 ["off"]);如果规则配置是一个数组,则直接使用它。

    If the rule config is not an array, then the value is wrapped into an array (e.g. "off" becomes ["off"]); if the rule config is an array then it is used directly.

  2. ESLint 将规则配置数组的第一个元素验证为严重性("off""warn""error"012

    ESLint validates the first element of the rule config array as a severity ("off", "warn", "error", 0, 1, 2)

  3. 如果严重性为 off0,则禁用规则并停止验证,忽略规则配置数组的任何其他元素。

    If the severity is off or 0, then the rule is disabled and validation stops, ignoring any other elements of the rule config array.

  4. 如果启用规则,则严重性之后的数组中的任何元素都会复制到 context.options 数组中(例如 ["warn", "never", { someOption: 5 }] 的配置会导致 context.options = ["never", { someOption: 5 }]

    If the rule is enabled, then any elements of the array after the severity are copied into the context.options array (e.g. a config of ["warn", "never", { someOption: 5 }] results in context.options = ["never", { someOption: 5 }])

  5. 规则的架构验证在 context.options 数组上运行。

    The rule’s schema validation is run on the context.options array.

注意:这意味着规则架构无法验证严重性。规则架构仅验证规则配置中严重性之后的数组元素。规则无法知道其配置的严重性。

Note: this means that the rule schema cannot validate the severity. The rule schema only validates the array elements after the severity in a rule config. There is no way for a rule to know what severity it is configured at.

规则的 schema 有两种格式:

There are two formats for a rule’s schema:

  • JSON Schema 对象的数组

    An array of JSON Schema objects

    • 将根据 context.options 数组中的相同位置检查每个元素。

      Each element will be checked against the same position in the context.options array.

    • 如果 context.options 数组的元素少于模式,则忽略不匹配的模式

      If the context.options array has fewer elements than there are schemas, then the unmatched schemas are ignored

    • 如果 context.options 数组的元素多于模式,则验证失败

      If the context.options array has more elements than there are schemas, then the validation fails

    • 使用这种格式有两个重要的后果:

      There are two important consequences to using this format:

      • 用户不向你的规则提供任何选项始终有效(超出严重性)

        It is always valid for a user to provide no options to your rule (beyond severity)

      • 如果你指定一个空数组,那么用户为你的规则提供任何选项始终是一个错误(超出严重性)

        If you specify an empty array, then it is always an error for a user to provide any options to your rule (beyond severity)

  • 将验证 context.options 数组的完整 JSON 架构对象

    A full JSON Schema object that will validate the context.options array

    • 即使你的规则只接受一个选项,架构也应该假设一组选项进行验证。

      The schema should assume an array of options to validate even if your rule only accepts one option.

    • 该模式可以任意复杂,因此你可以通过 oneOfanyOf 等验证完全不同的潜在选项集。

      The schema can be arbitrarily complex, so you can validate completely different sets of potential options via oneOf, anyOf etc.

    • 支持的 JSON Schemas 版本是 Draft-04,因此一些较新的功能(例如 if$data)不可用。

      The supported version of JSON Schemas is Draft-04, so some newer features such as if or $data are unavailable.

      • 目前,由于生态系统兼容性问题,明确计划不更新超出此级别的架构支持。请参阅 此提交 了解更多背景信息。

        At present, it is explicitly planned to not update schema support beyond this level due to ecosystem compatibility concerns. See this comment for further context.

例如,yoda 规则接受 "always""never" 的主模式参数,以及带有可选属性 exceptRange 的额外选项对象:

For example, the yoda rule accepts a primary mode argument of "always" or "never", as well as an extra options object with an optional property exceptRange:

// Valid configuration:
// "yoda": "warn"
// "yoda": ["error"]
// "yoda": ["error", "always"]
// "yoda": ["error", "never", { "exceptRange": true }]
// Invalid configuration:
// "yoda": ["warn", "never", { "exceptRange": true }, 5]
// "yoda": ["error", { "exceptRange": true }, "never"]
module.exports = {
    meta: {
        schema: [
            {
                enum: ["always", "never"]
            },
            {
                type: "object",
                properties: {
                    exceptRange: { type: "boolean" }
                },
                additionalProperties: false
            }
        ]
    }
};

这是等效的基于对象的模式:

And here is the equivalent object-based schema:

// Valid configuration:
// "yoda": "warn"
// "yoda": ["error"]
// "yoda": ["error", "always"]
// "yoda": ["error", "never", { "exceptRange": true }]
// Invalid configuration:
// "yoda": ["warn", "never", { "exceptRange": true }, 5]
// "yoda": ["error", { "exceptRange": true }, "never"]
module.exports = {
    meta: {
        schema: {
            type: "array",
            minItems: 0,
            maxItems: 2,
            items: [
                {
                    enum: ["always", "never"]
                },
                {
                    type: "object",
                    properties: {
                        exceptRange: { type: "boolean" }
                    },
                    additionalProperties: false
                }
            ]
        }
    }
};

对象模式在允许的范围内可以更加精确和严格。例如,下面的模式始终要求指定第一个选项(0 到 10 之间的数字),但第二个选项是可选的,并且可以是显式设置某些选项的对象,也可以是 "off""strict"

Object schemas can be more precise and restrictive in what is permitted. For example, the below schema always requires the first option to be specified (a number between 0 and 10), but the second option is optional, and can either be an object with some options explicitly set, or "off" or "strict".

// Valid configuration:
// "someRule": ["error", 6]
// "someRule": ["error", 5, "strict"]
// "someRule": ["warn", 10, { someNonOptionalProperty: true }]
// Invalid configuration:
// "someRule": "warn"
// "someRule": ["error"]
// "someRule": ["warn", 15]
// "someRule": ["warn", 7, { }]
// "someRule": ["error", 3, "on"]
// "someRule": ["warn", 7, { someOtherProperty: 5 }]
// "someRule": ["warn", 7, { someNonOptionalProperty: false, someOtherProperty: 5 }]
module.exports = {
    meta: {
        schema: {
            type: "array",
            minItems: 1, // Can't specify only severity!
            maxItems: 2,
            items: [
                {
                    type: "number",
                    minimum: 0,
                    maximum: 10
                },
                {
                    anyOf: [
                        {
                            type: "object",
                            properties: {
                                someNonOptionalProperty: { type: "boolean" }
                            },
                            required: ["someNonOptionalProperty"],
                            additionalProperties: false
                        },
                        {
                            enum: ["off", "strict"]
                        }
                    ]
                }
            ]
        }
    }
}

请记住,规则选项始终是一个数组,因此请注意不要在顶层指定非数组类型的架构。如果你的架构未在顶层指定数组,则用户永远无法启用你的规则,因为启用规则后他们的配置将始终无效。

Remember, rule options are always an array, so be careful not to specify a schema for a non-array type at the top level. If your schema does not specify an array at the top-level, users can never enable your rule, as their configuration will always be invalid when the rule is enabled.

这是一个总是验证失败的示例模式:

Here’s an example schema that will always fail validation:

// Possibly trying to validate ["error", { someOptionalProperty: true }]
// but when the rule is enabled, config will always fail validation because the options are an array which doesn't match "object"
module.exports = {
    meta: {
        schema: {
            type: "object",
            properties: {
                someOptionalProperty: {
                    type: "boolean"
                }
            },
            additionalProperties: false
        }
    }
}

注意:如果你的规则架构使用 JSON 架构 $ref 属性,则必须使用完整的 JSON 架构对象而不是位置属性架构数组。这是因为 ESLint 将数组速记转换为单个模式,而没有更新使它们不正确的引用(它们被忽略)。

Note: If your rule schema uses JSON schema $ref properties, you must use the full JSON Schema object rather than the array of positional property schemas. This is because ESLint transforms the array shorthand into a single schema without updating references that makes them incorrect (they are ignored).

要了解有关 JSON 架构的更多信息,我们建议查看 JSON 结构网站 上的一些示例,或阅读免费的 理解 JSON 模式 电子书。

To learn more about JSON Schema, we recommend looking at some examples on the JSON Schema website, or reading the free Understanding JSON Schema ebook.

访问 Shebang

Accessing Shebangs

Shebangs (#!)"Shebang" 类型的唯一标记表示。它们被视为注释,可以通过 访问注释 部分中概述的方法访问,例如 sourceCode.getAllComments()

Shebangs (#!) are represented by the unique tokens of type "Shebang". They are treated as comments and can be accessed by the methods outlined in the Accessing Comments section, such as sourceCode.getAllComments().

访问变量范围

Accessing Variable Scopes

SourceCode#getScope(node) 方法返回给定节点的范围。这是查找有关给定范围内的变量以及它们在其他范围内的使用方式的信息的有用方法。

The SourceCode#getScope(node) method returns the scope of the given node. It is a useful method for finding information about the variables in a given scope and how they are used in other scopes.

范围类型

Scope types

下表包含 AST 节点类型列表以及它们对应的范围类型。有关作用域类型的更多信息,请参阅 Scope 对象文档

The following table contains a list of AST node types and the scope type that they correspond to. For more information about the scope types, refer to the Scope object documentation.

AST 节点类型 范围类型
Program global
FunctionDeclaration function
FunctionExpression function
ArrowFunctionExpression function
ClassDeclaration class
ClassExpression class
BlockStatement ※1 block
SwitchStatement ※1 switch
ForStatement ※2 for
ForInStatement ※2 for
ForOfStatement ※2 for
WithStatement with
CatchClause catch
其他的 ※3

※1 仅当配置的解析器提供块作用域功能时。如果 parserOptions.ecmaVersion 不小于 6,则默认解析器提供块作用域功能。※2 仅当 for 语句将迭代变量定义为块作用域变量时(例如 for (let i = 0;;) {})。※3 拥有自己作用域的最近祖级节点的作用域。如果最近的祖级节点有多个范围,那么它会选择最里面的范围(例如,如果 Program#sourceType"module",则 Program 节点具有 global 范围和 module 范围。最里面的作用域是 module 作用域。)。

※1 Only if the configured parser provided the block-scope feature. The default parser provides the block-scope feature if parserOptions.ecmaVersion is not less than 6.
※2 Only if the for statement defines the iteration variable as a block-scoped variable (E.g., for (let i = 0;;) {}).
※3 The scope of the closest ancestor node which has own scope. If the closest ancestor node has multiple scopes then it chooses the innermost scope (E.g., the Program node has a global scope and a module scope if Program#sourceType is "module". The innermost scope is the module scope.).

范围变量

Scope Variables

Scope#variables 属性包含 Variable 对象 的数组。这些是在当前范围内声明的变量。你可以使用这些 Variable 对象来跟踪整个模块中对变量的引用。

The Scope#variables property contains an array of Variable objects. These are the variables declared in current scope. You can use these Variable objects to track references to a variable throughout the entire module.

在每个 Variable 内部,Variable#references 属性包含一个 Reference 对象 数组。Reference 数组包含模块源代码中引用变量的所有位置。

Inside of each Variable, the Variable#references property contains an array of Reference objects. The Reference array contains all the locations where the variable is referenced in the module’s source code.

同样在每个 Variable 内部,Variable#defs 属性包含一个 Definition 对象 数组。你可以使用 Definitions 查找定义变量的位置。

Also inside of each Variable, the Variable#defs property contains an array of Definition objects. You can use the Definitions to find where the variable was defined.

全局变量具有以下附加属性:

Global variables have the following additional properties:

  • Variable#writeable (boolean | undefined) …如果是 true,这个全局变量可以赋任意值。如果是 false,这个全局变量是只读的。

    Variable#writeable (boolean | undefined) … If true, this global variable can be assigned arbitrary value. If false, this global variable is read-only.

  • Variable#eslintExplicitGlobal (boolean | undefined) …如果为 true,则此全局变量由源代码文件中的 /* globals */ 指令注释定义。

    Variable#eslintExplicitGlobal (boolean | undefined) … If true, this global variable was defined by a /* globals */ directive comment in the source code file.

  • Variable#eslintExplicitGlobalComments (Comment[] | undefined) …在源代码文件中定义此全局变量的 /* globals */ 指令注释数组。如果没有 /* globals */ 指令注释,则此属性为 undefined

    Variable#eslintExplicitGlobalComments (Comment[] | undefined) … The array of /* globals */ directive comments which defined this global variable in the source code file. This property is undefined if there are no /* globals */ directive comments.

  • Variable#eslintImplicitGlobalSetting ("readonly" | "writable" | undefined) …配置文件中配置的值。如果有 /* globals */ 指令注释,这可以与 variable.writeable 不同。

    Variable#eslintImplicitGlobalSetting ("readonly" | "writable" | undefined) … The configured value in config files. This can be different from variable.writeable if there are /* globals */ directive comments.

使用 SourceCode#getScope() 跟踪变量的例子,参考源码,内置规则如下:

For examples of using SourceCode#getScope() to track variables, refer to the source code for the following built-in rules:

  • no-shadow:在 Program 节点调用 sourceCode.getScope() 并检查所有子作用域以确保变量名称未在较低作用域中重用。(no-shadow 文档)

    no-shadow: Calls sourceCode.getScope() at the Program node and inspects all child scopes to make sure a variable name is not reused at a lower scope. (no-shadow documentation)

  • no-redeclare:在每个作用域调用 sourceCode.getScope() 以确保变量未在同一作用域中声明两次。(no-redeclare 文档)

    no-redeclare: Calls sourceCode.getScope() at each scope to make sure that a variable is not declared twice in the same scope. (no-redeclare documentation)

将变量标记为已使用

Marking Variables as Used

某些 ESLint 规则,例如 no-unused-vars,检查是否使用了变量。ESLint 本身只知道变量访问的标准规则,因此访问变量的自定义方式可能不会注册为 “used”。

Certain ESLint rules, such as no-unused-vars, check to see if a variable has been used. ESLint itself only knows about the standard rules of variable access and so custom ways of accessing variables may not register as “used”.

为此,你可以使用 sourceCode.markVariableAsUsed() 方法。此方法有两个参数:要标记为已使用的变量的名称和指示你正在工作的范围的选项参考节点。这是一个例子:

To help with this, you can use the sourceCode.markVariableAsUsed() method. This method takes two arguments: the name of the variable to mark as used and an option reference node indicating the scope in which you are working. Here’s an example:

module.exports = {
    create: function(context) {
        var sourceCode = context.sourceCode;

        return {
            ReturnStatement(node) {

                // look in the scope of the function for myCustomVar and mark as used
                sourceCode.markVariableAsUsed("myCustomVar", node);

                // or: look in the global scope for myCustomVar and mark as used
                sourceCode.markVariableAsUsed("myCustomVar");
            }
        }
        // ...
    }
};

这里,myCustomVar 变量被标记为相对于 ReturnStatement 节点已使用,这意味着 ESLint 将从最接近该节点的范围开始搜索。如果省略第二个参数,则使用顶层范围。(对于 ESM 文件,顶层作用域是模块作用域;对于 CommonJS 文件,顶层作用域是第一个函数作用域。)

Here, the myCustomVar variable is marked as used relative to a ReturnStatement node, which means ESLint will start searching from the scope closest to that node. If you omit the second argument, then the top-level scope is used. (For ESM files, the top-level scope is the module scope; for CommonJS files, the top-level scope is the first function scope.)

访问代码路径

Accessing Code Paths

ESLint 在遍历 AST 时分析代码路径。你可以通过与代码路径相关的七个事件来访问代码路径对象。有关详细信息,请参阅 代码路径分析

ESLint analyzes code paths while traversing AST. You can access code path objects with seven events related to code paths. For more information, refer to Code Path Analysis.

弃用的 SourceCode 方法

Deprecated SourceCode Methods

请注意,以下 SourceCode 方法已被弃用,并将在 ESLint 的未来版本中删除:

Please note that the following SourceCode methods have been deprecated and will be removed in a future version of ESLint:

  • getTokenOrCommentBefore():由带有 { includeComments: true } 选项的 SourceCode#getTokenBefore() 替换。

    getTokenOrCommentBefore(): Replaced by SourceCode#getTokenBefore() with the { includeComments: true } option.

  • getTokenOrCommentAfter():由带有 { includeComments: true } 选项的 SourceCode#getTokenAfter() 替换。

    getTokenOrCommentAfter(): Replaced by SourceCode#getTokenAfter() with the { includeComments: true } option.

  • isSpaceBetweenTokens():替换为 SourceCode#isSpaceBetween()

    isSpaceBetweenTokens(): Replaced by SourceCode#isSpaceBetween()

  • getJSDocComment()

规则单元测试

Rule Unit Tests

ESLint 提供了 RuleTester 实用程序,可以轻松编写规则测试。

ESLint provides the RuleTester utility to make it easy to write tests for rules.

规则命名约定

Rule Naming Conventions

虽然你可以为自定义规则指定任何名称,但核心规则具有命名约定。将这些相同的命名约定应用于你的自定义规则可能会更清楚。要了解更多信息,请参阅 核心规则命名约定 文档。

While you can give a custom rule any name you’d like, the core rules have naming conventions. It could be clearer to apply these same naming conventions to your custom rule. To learn more, refer to the Core Rule Naming Conventions documentation.

运行时规则

Runtime Rules

ESLint 与其他 linter 的不同之处在于能够在运行时定义自定义规则。这非常适合特定于你的项目或公司的规则,并且对于 ESLint 附带或包含在插件中没有意义。只需编写你的规则并在运行时包含它们。

The thing that makes ESLint different from other linters is the ability to define custom rules at runtime. This is perfect for rules that are specific to your project or company and wouldn’t make sense for ESLint to ship with or be included in a plugin. Just write your rules and include them at runtime.

运行时规则的编写格式与所有其他规则相同。像创建任何其他规则一样创建你的规则,然后按照以下步骤操作:

Runtime rules are written in the same format as all other rules. Create your rule as you would any other and then follow these steps:

  1. 将所有运行时规则放在同一目录中(例如,eslint_rules)。

    Place all of your runtime rules in the same directory (e.g., eslint_rules).

  2. 创建一个 配置文件 并在 rules 键下指定你的规则 ID 错误级别。除非配置文件中的值为 "warn""error",否则你的规则将不会运行。

    Create a configuration file and specify your rule ID error level under the rules key. Your rule will not run unless it has a value of "warn" or "error" in the configuration file.

  3. 使用 --rulesdir 选项运行 命令行接口 以指定运行时规则的位置。

    Run the command line interface using the --rulesdir option to specify the location of your runtime rules.

配置文件规则性能

Profile Rule Performance

ESLint 有一个内置的方法来跟踪单个规则的性能。设置 TIMING 环境变量将在 linting 完成后触发显示十个运行时间最长的规则,以及它们各自的运行时间(规则创建 + 规则执行)和相对性能影响占总规则处理时间(规则创建)的百分比 + 规则执行)。

ESLint has a built-in method to track the performance of individual rules. Setting the TIMING environment variable will trigger the display, upon linting completion, of the ten longest-running rules, along with their individual running time (rule creation + rule execution) and relative performance impact as a percentage of total rule processing time (rule creation + rule execution).

$ TIMING=1 eslint lib
Rule                    | Time (ms) | Relative
:-----------------------|----------:|--------:
no-multi-spaces         |    52.472 |     6.1%
camelcase               |    48.684 |     5.7%
no-irregular-whitespace |    43.847 |     5.1%
valid-jsdoc             |    40.346 |     4.7%
handle-callback-err     |    39.153 |     4.6%
space-infix-ops         |    35.444 |     4.1%
no-undefined            |    25.693 |     3.0%
no-shadow               |    22.759 |     2.7%
no-empty-class          |    21.976 |     2.6%
semi                    |    19.359 |     2.3%

要显式测试一条规则,请组合 --no-eslintrc--rule 选项:

To test one rule explicitly, combine the --no-eslintrc, and --rule options:

$ TIMING=1 eslint --no-eslintrc --rule "quotes: [2, 'double']" lib
Rule   | Time (ms) | Relative
:------|----------:|--------:
quotes |    18.066 |   100.0%

要查看更长的结果列表(超过 10 个),请将环境变量设置为另一个值,例如 TIMING=50TIMING=all

To see a longer list of results (more than 10), set the environment variable to another value such as TIMING=50 or TIMING=all.