版本

選擇器

某些規則和 API 允許使用選擇器查詢 AST。本頁旨在

  1. 解釋什麼是選擇器
  2. 描述建立選擇器的語法
  3. 描述選擇器可以用於什麼

什麼是選擇器?

選擇器是一個可以用來匹配抽象語法樹 (AST) 中節點的字串。這對於描述程式碼中的特定語法模式很有用。

AST 選擇器的語法與 CSS 選擇器的語法類似。如果您以前使用過 CSS 選擇器,那麼 AST 選擇器的語法應該很容易理解。

最簡單的選擇器只是一個節點類型。節點類型選擇器將匹配具有給定類型所有節點。例如,考慮以下程式

var foo = 1;
bar.baz();

選擇器「Identifier」將匹配程式中所有 Identifier 節點。在這種情況下,選擇器將匹配 foobarbaz 的節點。

選擇器不限於匹配單個節點類型。例如,選擇器 VariableDeclarator > Identifier 將匹配所有以 VariableDeclarator 作為直接父節點的 Identifier 節點。在上面的程式中,這將匹配 foo 的節點,但不匹配 barbaz 的節點。

選擇器可以有哪些語法?

支援下列選擇器

  • 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 和字串文字中逸出 (\\)。

變更語言