版本

選擇器

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

更改語言