選擇器
某些規則和 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 和字串文字中逸出 (\\
)。