選擇器
某些規則和 API 允許使用選擇器來查詢 AST。本頁旨在
- 解釋什麼是選擇器
- 描述建立選擇器的語法
- 描述選擇器可以用來做什麼
什麼是選擇器?
選擇器是一個字串,可用於匹配抽象語法樹 (AST) 中的節點。這對於描述程式碼中的特定語法模式很有用。
AST 選擇器的語法與 CSS 選擇器 的語法相似。如果您之前使用過 CSS 選擇器,則 AST 選擇器的語法應該很容易理解。
最簡單的選擇器只是一個節點類型。節點類型選擇器將匹配所有具有給定類型的節點。例如,考慮以下程式
var foo = 1;
bar.baz();
選擇器「Identifier
」將匹配程式中所有 Identifier
節點。在這種情況下,選擇器將匹配 foo
、bar
和 baz
的節點。
選擇器不限於匹配單個節點類型。例如,選擇器 VariableDeclarator > Identifier
將匹配所有將 VariableDeclarator
作為直接父節點的 Identifier
節點。在上面的程式中,這將匹配 foo
的節點,但不匹配 bar
和 baz
的節點。
選擇器可以使用什麼語法?
支援以下選擇器
- AST 節點類型:
ForStatement
- 萬用字元(匹配所有節點):
*
- 屬性存在:
[attr]
- 屬性值:
[attr="foo"]
或[attr=123]
- 屬性正則表達式:
[attr=/foo.*/]
(帶有一些 已知問題) - 屬性條件:
[attr!="foo"]
、[attr>2]
、[attr<3]
、[attr>=2]
或[attr<=3]
- 巢狀屬性:
[attr.level2="foo"]
- 欄位:
FunctionDeclaration > Identifier.id
- 第一個或最後一個子節點:
:first-child
或:last-child
- 第 n 個子節點(不支援 ax+b):
:nth-child(2)
- 倒數第 n 個子節點(不支援 ax+b):
:nth-last-child(1)
- 後代:
FunctionExpression ReturnStatement
- 子節點:
UnaryExpression > Literal
- 後續兄弟節點:
VariableDeclaration ~ VariableDeclaration
- 相鄰兄弟節點:
ArrayExpression > Literal + SpreadElement
- 否定:
:not(ForStatement)
- 符合任一:
:matches([attr] > :first-child, :last-child)
- AST 節點類別:
:statement
、:expression
、:declaration
、:function
或:pattern
這種語法非常強大,可用於精確地選擇程式碼中的許多語法模式。
本節中的範例改編自 esquery 文件。
選擇器可以用來做什麼?
如果您正在編寫自訂 ESLint 規則,您可能會對使用選擇器來檢查 AST 的特定部分感興趣。如果您正在為您的程式碼庫設定 ESLint,您可能會對使用選擇器限制特定語法模式感興趣。
在規則中監聽選擇器
在編寫自訂 ESLint 規則時,您可以監聽與特定選擇器匹配的節點,因為 AST 會被遍歷。
module.exports = {
create(context) {
// ...
return {
// This listener will be called for all IfStatement nodes with blocks.
"IfStatement > BlockStatement": function(blockStatementNode) {
// ...your logic here
},
// This listener will be called for all function declarations with more than 3 parameters.
"FunctionDeclaration[params.length>3]": function(functionDeclarationNode) {
// ...your logic here
}
};
}
};
在選擇器末尾添加 :exit
將導致在遍歷期間退出匹配節點時調用偵聽器,而不是在進入它們時調用。
如果兩個或多個選擇器匹配同一個節點,它們的偵聽器將按照特異性遞增的順序調用。AST 選擇器的特異性與 CSS 選擇器的特異性相似
- 在比較兩個選擇器時,包含更多類別選擇器、屬性選擇器和偽類別選擇器(不包括
:not()
)的選擇器具有更高的特異性。 - 如果類別/屬性/偽類別計數相同,則包含更多節點類型選擇器的選擇器具有更高的特異性。
如果多個選擇器具有相同的特異性,則它們的偵聽器將按照節點的字母順序調用。
使用選擇器限制語法
使用 no-restricted-syntax 規則,您可以限制在程式碼中使用特定語法。例如,您可以使用以下設定來禁止使用主體中沒有區塊語句的 if
語句
{
"rules": {
"no-restricted-syntax": ["error", "IfStatement > :not(BlockStatement).consequent"]
}
}
…或等效地,您可以使用此設定
{
"rules": {
"no-restricted-syntax": ["error", "IfStatement[consequent.type!='BlockStatement']"]
}
}
作為另一個範例,您可以禁止呼叫 require()
{
"rules": {
"no-restricted-syntax": ["error", "CallExpression[callee.name='require']"]
}
}
或者您可以強制 setTimeout
的呼叫始終具有兩個參數
{
"rules": {
"no-restricted-syntax": ["error", "CallExpression[callee.name='setTimeout'][arguments.length!=2]"]
}
}
在 no-restricted-syntax
規則中使用選擇器可以讓您很好地控制程式碼庫中的問題模式,而無需編寫自訂規則來偵測每個模式。
已知問題
由於 esquery 中的 錯誤,包含正斜線字元 /
的正則表達式無法正確解析,因此 [value=/some\/path/]
將會是語法錯誤。作為 變通方案,您可以將 /
字元替換為其 Unicode 對應字元,如下所示:[value=/some\u002Fpath/]
。
例如,以下設定禁止從 some/path
導入
{
"rules": {
"no-restricted-syntax": ["error", "ImportDeclaration[source.value=/^some\\u002Fpath$/]"]
}
}
請注意,\
字元需要在 JSON 和字串文字中逸出 (\\
)。