Tree-sitter bindings for the web providing WebAssembly-based incremental parsing of source code
—
Pattern-based querying for complex syntax tree analysis and extraction. Queries use S-expression patterns to match specific structures in syntax trees and extract relevant nodes with named captures.
Create queries from S-expression pattern strings.
/**
* Create a new query from S-expression patterns
* @param language - Language the query is associated with
* @param source - Query source containing one or more S-expression patterns
*/
constructor(language: Language, source: string);
/**
* Delete the query and free its resources
*/
delete(): void;Usage Example:
import { Query, Language } from "web-tree-sitter";
const language = await Language.load("/tree-sitter-javascript.wasm");
// Create query to find function declarations with their names
const query = new Query(language, `
(function_declaration
name: (identifier) @function-name
parameters: (formal_parameters) @params)
(variable_declarator
name: (identifier) @var-name
value: (arrow_function) @arrow-func)
`);Execute queries to find matching patterns in syntax trees.
/**
* Get all matches in the order they were found
* @param node - Root node to execute the query on
* @param options - Query execution options
* @returns Array of matches with pattern indices and captures
*/
matches(node: Node, options?: QueryOptions): QueryMatch[];
/**
* Get all individual captures in the order they appear
* @param node - Root node to execute the query on
* @param options - Query execution options
* @returns Array of captures without grouping by match
*/
captures(node: Node, options?: QueryOptions): QueryCapture[];Usage Examples:
const tree = parser.parse(`
function add(a, b) { return a + b; }
const multiply = (x, y) => x * y;
`);
// Get all matches
const matches = query.matches(tree.rootNode);
console.log(`Found ${matches.length} matches`);
matches.forEach((match, i) => {
console.log(`Match ${i} (pattern ${match.patternIndex}):`);
match.captures.forEach(capture => {
console.log(` ${capture.name}: ${capture.node.text}`);
});
});
// Get just the captures
const captures = query.captures(tree.rootNode);
captures.forEach(capture => {
console.log(`${capture.name}: ${capture.node.text}`);
});Access information about query patterns and captures.
/** The names of the captures used in the query */
readonly captureNames: string[];
/** The quantifiers of the captures used in the query */
readonly captureQuantifiers: CaptureQuantifier[][];
/** The predicates associated with each pattern index */
readonly predicates: QueryPredicate[][];
/** Properties for predicates with the operator 'set!' */
readonly setProperties: QueryProperties[];
/** Properties for predicates with the operator 'is?' */
readonly assertedProperties: QueryProperties[];
/** Properties for predicates with the operator 'is-not?' */
readonly refutedProperties: QueryProperties[];Usage Example:
console.log("Capture names:", query.captureNames);
console.log("Pattern count:", query.patternCount());
// Check predicates for first pattern
const predicates = query.predicatesForPattern(0);
console.log("Predicates for pattern 0:", predicates);Get detailed information about individual patterns.
/**
* Get the predicates for a given pattern
* @param patternIndex - Index of the pattern
* @returns Array of predicates for the pattern
*/
predicatesForPattern(patternIndex: number): QueryPredicate[];
/**
* Get the byte offset where the given pattern starts in the query's source
* @param patternIndex - Index of the pattern
* @returns Start byte offset in query source
*/
startIndexForPattern(patternIndex: number): number;
/**
* Get the byte offset where the given pattern ends in the query's source
* @param patternIndex - Index of the pattern
* @returns End byte offset in query source
*/
endIndexForPattern(patternIndex: number): number;
/**
* Get the number of patterns in the query
* @returns Total number of patterns
*/
patternCount(): number;Work with named captures in the query.
/**
* Get the index for a given capture name
* @param captureName - Name of the capture to look up
* @returns Index of the capture or -1 if not found
*/
captureIndexForName(captureName: string): number;
/**
* Disable a certain capture within a query
* @param captureName - Name of the capture to disable
*/
disableCapture(captureName: string): void;Usage Example:
// Disable a specific capture to improve performance
query.disableCapture("params");
// Get capture index
const nameIndex = query.captureIndexForName("function-name");
console.log("Function name capture index:", nameIndex);Control pattern execution and analysis.
/**
* Disable a certain pattern within a query
* @param patternIndex - Index of the pattern to disable
*/
disablePattern(patternIndex: number): void;
/**
* Check if a given pattern within a query has a single root node
* @param patternIndex - Index of the pattern to check
* @returns True if pattern has single root node
*/
isPatternRooted(patternIndex: number): boolean;
/**
* Check if a given pattern within a query is non-local
* @param patternIndex - Index of the pattern to check
* @returns True if pattern is non-local
*/
isPatternNonLocal(patternIndex: number): boolean;
/**
* Check if a given step in a query is 'definite'
* @param byteIndex - Byte index in the query source
* @returns True if step is guaranteed to match
*/
isPatternGuaranteedAtStep(byteIndex: number): boolean;Control query execution limits and check performance metrics.
/** The maximum number of in-progress matches for this query */
matchLimit?: number;
/**
* Check if, on its last execution, this cursor exceeded its maximum number of in-progress matches
* @returns True if match limit was exceeded
*/
didExceedMatchLimit(): boolean;Usage Example:
// Set match limit for performance
query.matchLimit = 1000;
const matches = query.matches(tree.rootNode);
if (query.didExceedMatchLimit()) {
console.warn("Query exceeded match limit, results may be incomplete");
}interface QueryOptions {
/** The start position of the range to query */
startPosition?: Point;
/** The end position of the range to query */
endPosition?: Point;
/** The start index of the range to query */
startIndex?: number;
/** The end index of the range to query */
endIndex?: number;
/** The maximum number of in-progress matches (1-65536) */
matchLimit?: number;
/** The maximum start depth for query cursor */
maxStartDepth?: number;
/** Maximum execution time in microseconds */
timeoutMicros?: number;
/** Progress callback for monitoring/cancelling execution */
progressCallback?: (state: QueryState) => void;
}
interface QueryState {
/** The byte offset in the document that the query is at */
currentOffset: number;
}
/** Key-value pairs associated with a pattern */
type QueryProperties = Record<string, string | null>;
interface QueryPredicate {
/** The operator of the predicate (match?, eq?, set!, etc.) */
operator: string;
/** The operands of the predicate (captures or strings) */
operands: PredicateStep[];
}
interface QueryCapture {
/** The index of the pattern that matched */
patternIndex: number;
/** The name of the capture */
name: string;
/** The captured node */
node: Node;
/** Properties for predicates declared with 'set!' */
setProperties?: QueryProperties;
/** Properties for predicates declared with 'is?' */
assertedProperties?: QueryProperties;
/** Properties for predicates declared with 'is-not?' */
refutedProperties?: QueryProperties;
}
interface QueryMatch {
/** @deprecated Use patternIndex instead */
pattern: number;
/** The index of the pattern that matched */
patternIndex: number;
/** The captures associated with the match */
captures: QueryCapture[];
/** Properties for predicates declared with 'set!' */
setProperties?: QueryProperties;
/** Properties for predicates declared with 'is?' */
assertedProperties?: QueryProperties;
/** Properties for predicates declared with 'is-not?' */
refutedProperties?: QueryProperties;
}
/** Quantifiers for captures */
const CaptureQuantifier: {
readonly Zero: 0;
readonly ZeroOrOne: 1;
readonly ZeroOrMore: 2;
readonly One: 3;
readonly OneOrMore: 4;
};
type CaptureQuantifier = typeof CaptureQuantifier[keyof typeof CaptureQuantifier];
/** Steps in predicates - either captures or strings */
type PredicateStep = CapturePredicateStep | StringPredicateStep;
interface CapturePredicateStep {
type: 'capture';
name: string;
}
interface StringPredicateStep {
type: 'string';
value: string;
}// Find all function calls
const callQuery = new Query(language, `
(call_expression
function: (identifier) @function-name
arguments: (arguments) @args)
`);
// Find all string literals
const stringQuery = new Query(language, `
(string) @string-literal
`);// Find functions with specific names
const specificFuncQuery = new Query(language, `
(function_declaration
name: (identifier) @name
(#match? @name "^(test|spec)"))
`);
// Find variable declarations with initialization
const varQuery = new Query(language, `
(variable_declarator
name: (identifier) @var-name
value: (_) @initial-value)
`);// Find nested function calls
const nestedQuery = new Query(language, `
(call_expression
function: (member_expression
object: (identifier) @object
property: (property_identifier) @method)
arguments: (arguments
(call_expression) @nested-call))
`);Install with Tessl CLI
npx tessl i tessl/npm-web-tree-sitter