or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

index.mddocs/

ESQuery

ESQuery is a library for querying ECMAScript Abstract Syntax Trees (ASTs) using CSS-style selectors. It enables developers to search through JavaScript code structures using familiar CSS selector syntax, making it an essential tool for static code analysis, linting, and code transformation utilities.

Package Information

  • Package Name: esquery
  • Package Type: npm
  • Language: JavaScript (ES6+)
  • Installation: npm install esquery

Core Imports

ESQuery exports a default function with attached methods. The package provides both UMD and ES module builds.

// ES6 module import (uses dist/esquery.esm.min.js)
import esquery from "esquery";

// Individual method access
const results = esquery(ast, 'selector');
const parsed = esquery.parse('selector');
const matches = esquery.matches(node, parsed, ancestry);

For CommonJS:

// CommonJS require (uses dist/esquery.min.js - UMD build)
const esquery = require("esquery");

// Access methods as properties
const results = esquery(ast, 'selector');
const parsed = esquery.parse('selector');

Basic Usage

import esquery from "esquery";
import * as esprima from "esprima";

// Parse JavaScript code into an AST
const ast = esprima.parse('for (let i = 0; i < arr.length; i++) { console.log(arr[i]); }');

// Query for all ForStatement nodes
const forLoops = esquery(ast, 'ForStatement');

// Query for all function calls
const functionCalls = esquery(ast, 'CallExpression');

// Query for console.log calls specifically
const consoleLogs = esquery(ast, 'CallExpression[callee.object.name="console"][callee.property.name="log"]');

// Check if a specific node matches a selector
const selector = esquery.parse('IfStatement');
const matches = esquery.matches(someNode, selector, ancestry || []);

Architecture

ESQuery is built around several key components:

  • CSS-style Selector Engine: Supports comprehensive CSS selector syntax adapted for AST nodes
  • Parser: Converts selector strings into internal AST representations for efficient matching
  • Matcher System: Core engine that tests nodes against selector criteria with caching for performance
  • Traversal System: Built on estraverse for efficient AST navigation and visitor pattern support
  • Options System: Flexible configuration for custom node types, traversal patterns, and class matching

Capabilities

Query Execution

Execute CSS-style selectors against JavaScript ASTs to find matching nodes.

/**
 * Query the code AST using the selector string.
 * @param ast - The AST to query
 * @param selector - CSS-style selector string
 * @param options - Optional query configuration
 * @returns Array of matching AST nodes
 */
function query(ast: AST, selector: string, options?: ESQueryOptions): AST[];

Selector Parsing

Parse selector strings into internal selector AST format for reuse and analysis.

/**
 * Parse a selector string and return its AST.
 * @param selector - CSS-style selector string
 * @returns Parsed selector AST
 */
function parse(selector: string): SelectorAST;

Direct Matching

Test parsed selectors against AST nodes with ancestry context for complex queries.

/**
 * Determine if a node matches a selector.
 * @param node - The AST node to test
 * @param selector - Parsed selector AST
 * @param ancestry - Node ancestry chain for context (defaults to [] if not provided)
 * @param options - Optional query configuration
 * @returns True if node matches selector
 */
function matches(node: AST, selector: SelectorAST, ancestry: AST[], options?: ESQueryOptions): boolean;

Low-level Matching

Match parsed selectors against ASTs without string parsing overhead.

/**
 * Match AST nodes against a parsed selector AST.
 * @param ast - The AST to search
 * @param selector - Parsed selector AST
 * @param options - Optional query configuration
 * @returns Array of matching AST nodes
 */
function match(ast: AST, selector: SelectorAST, options?: ESQueryOptions): AST[];

Custom Traversal

Traverse ASTs with custom visitor functions for complex analysis workflows.

/**
 * Traverse AST and call visitor for matching nodes.
 * @param ast - The AST to traverse
 * @param selector - Parsed selector AST  
 * @param visitor - Callback function for matches
 * @param options - Optional query configuration
 */
function traverse(ast: AST, selector: SelectorAST, visitor: TraverseVisitor, options?: ESQueryOptions): void;

/**
 * Visitor callback for traverse function
 * @param node - Current matching node
 * @param parent - Parent node in AST
 * @param ancestry - Full ancestry chain from root
 */
type TraverseVisitor = (node: AST, parent: AST, ancestry: AST[]) => void;

Types

/**
 * Configuration options for ESQuery operations
 */
interface ESQueryOptions {
  /** Key for node type identification (default: "type") */
  nodeTypeKey?: string;
  /** Custom visitor keys mapping for extended AST node types */
  visitorKeys?: { [nodeType: string]: string[] };
  /** Fallback function for unknown node types during traversal */
  fallback?: (node: AST) => string[];
  /** Custom class matcher function for :class() selectors */
  matchClass?: (className: string, node: AST, ancestry: AST[]) => boolean;
}

/**
 * AST representation of parsed selector
 * Generated from grammar.pegjs parsing rules
 */
interface SelectorAST {
  type: string;
  [key: string]: any;
}

/**
 * ECMAScript AST node
 * Follows ESTree specification format
 */
interface AST {
  type: string;
  [key: string]: any;
}

Supported Selector Syntax

ESQuery supports comprehensive CSS selector syntax adapted for AST queries:

Basic Selectors

  • AST node type: ForStatement, IfStatement, FunctionDeclaration
  • Wildcard: * (matches any node)
  • Attribute existence: [attr] (node has property)
  • Attribute value: [attr="value"], [attr=123] (exact match)
  • Attribute regex: [attr=/pattern/], [attr=/pattern/flags] (regex match)
  • Attribute conditions: [attr!="value"], [attr>2], [attr<=3] (comparison operators)
  • Nested attributes: [obj.prop="value"] (deep property access)

Structural Selectors

  • Field selector: FunctionDeclaration > Identifier.id (specific child property)
  • Child combinator: parent > child (direct child relationship)
  • Descendant combinator: ancestor descendant (any descendant)
  • Adjacent sibling: node + adjacent (immediately following sibling)
  • General sibling: node ~ sibling (any following sibling)

Pseudo-selectors

  • Position: :first-child, :last-child, :nth-child(2), :nth-last-child(1)
  • Logic: :not(selector) (negation)
  • Containment: :has(selector), :has(> selector) (contains matching descendant)
  • Grouping: :matches(sel1, sel2) (matches any of multiple selectors)
  • Subject: !selector (subject indicator for complex queries)

Built-in Classes

  • :statement: Any statement node type
  • :expression: Any expression node type
  • :declaration: Any declaration node type
  • :function: Function declarations and expressions
  • :pattern: Any pattern node type

Usage Examples:

// Find all variable declarations
esquery(ast, 'VariableDeclaration');

// Find function calls to specific methods
esquery(ast, 'CallExpression[callee.property.name="forEach"]');

// Find nested if statements
esquery(ast, 'IfStatement IfStatement');

// Find functions with specific parameter counts
esquery(ast, 'FunctionDeclaration[params.length=2]');

// Find return statements that are not in functions
esquery(ast, ':not(FunctionDeclaration) ReturnStatement');

// Find identifiers that are function names
esquery(ast, 'FunctionDeclaration > Identifier.id');

Error Handling

ESQuery functions throw Error objects for:

  • Unknown selector types: Invalid selector AST node types
  • Unknown operators: Invalid attribute comparison operators
  • Unknown class names: Invalid built-in class names (:statement, etc.)
  • Unknown value types: Invalid attribute value types in selectors
  • Parse errors: Invalid selector syntax (via parser)

Common error handling:

try {
  const results = esquery(ast, 'InvalidSelector[badOperator~="value"]');
} catch (error) {
  console.error('Query failed:', error.message);
  // Handle invalid selector syntax
}