语言
从 ESLint v9.7.0 开始,您可以通过插件扩展 ESLint 以支持其他语言。虽然 ESLint 最初是作为专门用于 JavaScript 的代码风格检查器而诞生的,但 ESLint 核心是通用的,可以用于检查任何编程语言的代码。每种语言都定义为一个对象,其中包含检查文件所需的所有解析、评估和遍历功能。这些语言随后被打包到插件中,以便在用户配置中使用。
语言要求
为了创建语言,您需要
- 解析器。 解析器是将纯文本转换为数据结构的部分。ESLint 对数据结构的格式没有具体要求,因此您可以使用任何现有的解析器,或编写自己的解析器。
SourceCode
对象。 ESLint 使用SourceCode
对象与 AST 交互。每个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 内部使用的 API,也是编写用于处理代码的规则使用的 API(通过 context.sourceCode
)。SourceCode
对象必须实现 @eslint/core
包中定义的 TextSourceCode
接口。
基本的 SourceCode
对象必须实现以下内容
ast
- 包含源代码的 AST 或 CST 的属性。text
- 源代码的文本。getLoc(nodeOrToken)
- 返回给定节点或标记位置的方法。这必须与 ESTree 使用的loc
结构匹配。getRange(nodeOrToken)
- 返回给定节点或标记范围的方法。这必须返回一个数组,其中第一个元素是起始索引,第二个元素是结束索引。traverse()
- 返回一个用于遍历 AST 或 CST 的可迭代对象的方法。迭代器必须返回实现@eslint/core
中VisitTraversalStep
或CallTraversalStep
的对象。
以下可选成员允许您自定义 ESLint 与对象交互的方式
visitorKeys
- 仅针对此SourceCode
对象的访问器键。通常不需要,因为Language#visitorKeys
大部分时候都会使用。applyLanguageOptions(languageOptions)
- 如果您有在解析后需要应用的特定语言选项,您可以在此方法中执行此操作。getDisableDirectives()
- 返回代码中的任何禁用指令。ESLint 使用此来应用禁用指令并跟踪未使用的指令。getInlineConfigNodes()
- 返回任何内联配置节点。ESLint 使用此来报告在启用noInlineConfig
时发生的错误。applyInlineConfig()
- 将内联配置元素返回给 ESLint。ESLint 使用此来更改正在检查的文件的配置。finalize()
- 此方法在 linting 开始之前调用,是您修改SourceCode
的最后机会。如果您已定义applyLanguageOptions()
或applyInlineConfig()
,那么您可能需要在SourceCode
对象准备就绪之前应用其他更改。
此外,以下成员在 SourceCode
对象中很常见,建议您实现这些成员
lines
- 源代码的各个行,作为字符串数组。getParent(node)
- 返回给定节点的父节点,如果节点是根节点,则返回undefined
。getAncestors(node)
- 返回节点祖先的数组,第一个元素是树的根节点,每个后续元素是通往node
的根节点的子孙节点。getText(node, beforeCount, afterCount)
- 返回表示给定节点的字符串,以及可选地在节点范围之前和之后指定数量的字符。
请参阅 JSONSourceCode
作为基本 SourceCode
类的示例。
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
对象。 由 ESLint 在parse()
之后内部调用,第二个参数是parse()
的确切返回值。
以下可选成员允许您自定义 ESLint 与对象交互的方式
visitorKeys
- 特定于 AST 或 CST 的访问者键。 这用于优化 ESLint 内部对 AST 或 CST 的遍历。 虽然不是必需的,但强烈建议,尤其是对于与 ESTree 格式有很大偏差的 AST 或 CST 格式。defaultLanguageOptions
- 使用语言时的默认languageOptions
。 计算正在检查的文件的配置时,用户指定的languageOptions
与此对象合并。matchesSelectorClass(className, node, ancestry)
- 允许您指定选择器类,例如:expression
,它匹配多个节点。 只要 esquery 选择器包含一个:
后跟一个标识符,就会调用此方法。
请参阅 JSONLanguage
作为基本 Language
类的示例。
在插件中发布语言
语言发布在类似于处理器和规则的插件中。 将插件中的 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"
}
];
有关更多详细信息,请参阅插件配置文档中的 指定语言。