语言
从 ESLint v9.7.0 开始,您可以通过插件使用其他语言扩展 ESLint。虽然 ESLint 最初是一个严格用于 JavaScript 的 linter,但 ESLint 核心是通用的,可以用于 lint 任何编程语言。每种语言都定义为一个对象,其中包含 lint 文件所需的所有解析、评估和遍历功能。然后,这些语言在插件中分发,以供用户配置使用。
语言要求
为了创建一种语言,您需要
- 解析器。 解析器是将纯文本转换为数据结构的组件。ESLint 对数据结构没有特定的格式要求,因此您可以使用任何现有的解析器,或编写自己的解析器。
- 一个
SourceCode
对象。 ESLint 使用 AST 的方式是通过SourceCode
对象。每个SourceCode
上都有一些必需的方法,您还可以添加更多您想要暴露给规则的方法或属性。 - 一个
Language
对象。Language
对象包含关于语言本身的信息以及用于解析和创建SourceCode
对象的方法。
语言的解析器要求
要开始使用,请确保您有一个可以从 JavaScript 调用的解析器。解析器必须返回一个表示已解析代码的数据结构。大多数解析器返回一个 抽象语法树 (AST) 来表示代码,但它们也可以返回一个 具体语法树 (CST)。返回 AST 还是 CST 对 ESLint 来说并不重要,重要的是存在一个数据结构可以遍历。
虽然 AST 或 CST 没有特定的结构必须遵循,但当树中的每个节点都包含以下信息时,更容易与 ESLint 集成
-
类型 - 每个节点上表示节点类型的属性是必需的。例如,在 JavaScript 中,
type
属性包含每个节点的此信息。ESLint 规则使用节点类型来定义访问者方法,因此重要的是每个节点都可以通过字符串来标识。属性的名称并不重要(下面进一步讨论),只要存在即可。此属性通常被大多数解析器命名为type
或kind
。 -
位置 - 每个节点上表示节点在原始源代码中的位置的属性是必需的。位置必须包含
- 节点开始所在的行
- 节点开始所在的列
- 节点结束所在的行
- 节点结束所在的列
与节点类型一样,属性名称并不重要。两个常见的属性名称是
loc
(如 ESTree 中)和position
(如 Unist 中)。ESLint 使用此信息来报告错误和规则违规。 -
范围 - 每个节点上表示节点源代码在源代码内部的位置的属性是必需的。范围指示第一个字符所在的索引和最后一个字符之后的索引,这样调用
code.slice(start, end)
返回节点表示的文本。再次强调,不需要特定的属性名称,此信息甚至可以与位置信息合并。ESTree 使用range
属性,而 Unist 将此信息与位置信息一起包含在position
上。ESLint 使用此信息来应用自动修复。
SourceCode
对象
ESLint 在 SourceCode
对象中保存有关源代码的信息。此对象是 ESLint 内部和为处理代码而编写的规则(通过 context.sourceCode
)使用的 API。SourceCode
对象必须实现 @eslint/core
包中定义的 TextSourceCode
接口。
一个基本的 SourceCode
对象必须实现以下内容
ast
- 包含源代码的 AST 或 CST 的属性。text
- 源代码的文本。getLoc(nodeOrToken)
- 返回给定节点或 token 位置的方法。这必须与 ESTree 使用的loc
结构匹配。getRange(nodeOrToken)
- 返回给定节点或 token 范围的方法。这必须返回一个数组,其中第一个项目是起始索引,第二个项目是结束索引。traverse()
- 返回用于遍历 AST 或 CST 的可迭代对象的方法。迭代器必须返回实现来自@eslint/core
的VisitTraversalStep
或CallTraversalStep
的对象。
以下可选成员允许您自定义 ESLint 与对象的交互方式
visitorKeys
- 仅特定于此SourceCode
对象的访问者键。通常不是必需的,因为大多数时候使用Language#visitorKeys
。applyLanguageOptions(languageOptions)
- 如果您有需要在解析后应用的特定语言选项,您可以在此方法中执行此操作。getDisableDirectives()
- 返回代码中的任何禁用指令。ESLint 使用它来应用禁用指令并跟踪未使用的指令。getInlineConfigNodes()
- 返回任何内联配置节点。当启用noInlineConfig
时,ESLint 使用它来报告错误。applyInlineConfig()
- 将内联配置元素返回给 ESLint。ESLint 使用它来更改正在 lint 的文件的配置。finalize()
- 此方法在 linting 开始之前调用,是您修改SourceCode
的最后机会。如果您已定义applyLanguageOptions()
或applyInlineConfig()
,那么您可能需要在SourceCode
对象准备就绪之前应用其他更改。
此外,以下成员在 SourceCode
对象上很常见,建议实现
lines
- 源代码的各个行,以字符串数组形式表示。getParent(node)
- 返回给定节点的父节点;如果节点是根节点,则返回undefined
。getAncestors(node)
- 返回节点祖先的数组,第一个项目是树的根节点,每个后续项目是根节点的后代,一直到node
。getText(node, beforeCount, afterCount)
- 返回表示给定节点的字符串,以及可选地,节点范围之前和之后指定数量的字符。
有关基本 SourceCode
类的示例,请参阅 JSONSourceCode
。
Language
对象
Language
对象包含关于编程语言的所有信息,以及与该语言编写的代码交互的方法。ESLint 使用此对象来确定如何处理特定文件。Language
对象必须实现 @eslint/core
包中定义的 Language
接口。
一个基本的 Language
对象必须实现以下内容
fileType
- 应为"text"
(将来,我们还将支持"binary"
)lineStart
- 0 或 1,指示 AST 如何表示文件中的第一行。ESLint 使用它来正确显示错误位置。columnStart
- 0 或 1,指示 AST 如何表示每行中的第一列。ESLint 使用它来正确显示错误位置。nodeTypeKey
- 指示节点类型的属性名称(通常为"type"
或"kind"
)。validateLanguageOptions(languageOptions)
- 验证语言的语言选项。当预期的语言选项没有正确的类型或值时,此方法应抛出验证错误。应静默忽略意外的语言选项,并且不应抛出任何错误。即使语言未指定任何选项,也需要此方法。parse(file, context)
- 将给定文件解析为 AST 或 CST,并且还可以包含供规则使用的其他值。由 ESLint 内部调用。createSourceCode(file, parseResult, context)
- 创建SourceCode
对象。在parse()
之后由 ESLint 内部调用,第二个参数是来自parse()
的确切返回值。
以下可选成员允许您自定义 ESLint 与对象的交互方式
visitorKeys
- 特定于 AST 或 CST 的访问者键。这用于优化 ESLint 内部 AST 或 CST 的遍历。虽然不是必需的,但强烈建议使用,特别是对于与 ESTree 格式显着不同的 AST 或 CST 格式。defaultLanguageOptions
- 使用该语言时的默认languageOptions
。在计算正在 lint 的文件的配置时,用户指定的languageOptions
将与此对象合并。matchesSelectorClass(className, node, ancestry)
- 允许您指定选择器类,例如:expression
,它可以匹配多个节点。每当 esquery 选择器包含后跟标识符的:
时,都会调用此方法。normalizeLanguageOptions(languageOptions)
- 接受经过验证的语言选项对象并规范化其值。当语言选项属性更改时,这有助于向后兼容。
有关基本 Language
类的示例,请参阅 JSONLanguage
。
在插件中发布语言
语言在插件中发布,类似于处理器和规则。在您的插件中将 languages
键定义为一个对象,其名称是语言名称,值是语言对象。这是一个例子
import { myLanguage } from "../languages/my.js";
const plugin = {
// preferred location of name and version
meta: {
name: "eslint-plugin-example",
version: "1.2.3"
},
languages: {
my: myLanguage
},
rules: {
// add rules here
}
};
// for ESM
export default plugin;
// OR for CommonJS
module.exports = plugin;
为了在配置文件中使用插件中的语言,导入插件并将其包含在 plugins
键中,指定一个命名空间。然后,使用该命名空间在 language
配置中引用该语言,如下所示
// eslint.config.js
import example from "eslint-plugin-example";
export default [
{
plugins: {
example
},
language: "example/my"
}
];
有关更多详细信息,请参阅插件配置文档中的指定语言。