Tree-sitter bindings for the web providing WebAssembly-based incremental parsing of source code
—
Syntax tree navigation and manipulation functionality. Trees represent the complete parsed structure of source code, while Nodes represent individual elements within the tree hierarchy.
Trees are created by parsers and represent immutable syntax structures.
/**
* Create a shallow copy of the syntax tree (very fast operation)
* @returns New Tree instance sharing internal structure
*/
copy(): Tree;
/**
* Delete the syntax tree and free its resources
*/
delete(): void;
/** The language that was used to parse the syntax tree */
language: Language;Access the root node of the syntax tree.
/**
* Get the root node of the syntax tree
*/
get rootNode(): Node;
/**
* Get the root node with its position shifted forward by the given offset
* @param offsetBytes - Byte offset to apply
* @param offsetExtent - Point offset to apply
* @returns Root node with adjusted position
*/
rootNodeWithOffset(offsetBytes: number, offsetExtent: Point): Node;Usage Example:
const tree = parser.parse("let x = 1 + 2;");
const root = tree.rootNode;
console.log(root.type); // "program"
console.log(root.toString()); // S-expression representation
// Get root with offset (useful for embedded parsing)
const offsetRoot = tree.rootNodeWithOffset(10, { row: 1, column: 0 });Synchronize trees with source code changes for incremental parsing.
/**
* Edit the syntax tree to keep it in sync with source code changes
* @param edit - Description of the edit in both byte offsets and row/column coordinates
*/
edit(edit: Edit): void;
/**
* Compare this tree to a new tree and get ranges where structure changed
* @param other - New tree representing the same document after edits
* @returns Array of ranges with syntactic structure changes
*/
getChangedRanges(other: Tree): Range[];Usage Example:
const tree = parser.parse("let x = 1;");
// Edit the tree to reflect source change: "let x = 1;" → "let x = 42;"
tree.edit({
startIndex: 8,
oldEndIndex: 9,
newEndIndex: 10,
startPosition: { row: 0, column: 8 },
oldEndPosition: { row: 0, column: 9 },
newEndPosition: { row: 0, column: 10 }
});
// Parse the updated source
const newTree = parser.parse("let x = 42;", tree);
// Get changed ranges
const changes = tree.getChangedRanges(newTree);
console.log("Changed ranges:", changes);Create tree cursors and access parsing ranges.
/**
* Create a new TreeCursor starting from the root of the tree
* @returns TreeCursor for efficient tree traversal
*/
walk(): TreeCursor;
/**
* Get the included ranges that were used to parse the syntax tree
* @returns Array of ranges that were included during parsing
*/
getIncludedRanges(): Range[];Access node identification and location information.
/** Unique numeric ID for this node within the tree */
id: number;
/** The byte index where this node starts */
startIndex: number;
/** The position where this node starts */
startPosition: Point;
/** The tree that this node belongs to */
tree: Tree;
/**
* Get the byte index where this node ends
*/
get endIndex(): number;
/**
* Get the position where this node ends
*/
get endPosition(): Point;Access node type and classification details.
/**
* Get this node's type as a numerical id
*/
get typeId(): number;
/**
* Get the node's type as a numerical id as it appears in the grammar, ignoring aliases
*/
get grammarId(): number;
/**
* Get this node's type as a string
*/
get type(): string;
/**
* Get this node's symbol name as it appears in the grammar, ignoring aliases
*/
get grammarType(): string;Usage Example:
const root = tree.rootNode;
const firstChild = root.firstChild;
if (firstChild) {
console.log("Type:", firstChild.type); // e.g., "lexical_declaration"
console.log("Type ID:", firstChild.typeId); // e.g., 42
console.log("Grammar type:", firstChild.grammarType);
console.log("Position:", firstChild.startPosition); // { row: 0, column: 0 }
console.log("Byte range:", firstChild.startIndex, "-", firstChild.endIndex);
}Check node properties and error states.
/**
* Check if this node is named (corresponds to named rules in grammar)
*/
get isNamed(): boolean;
/**
* Check if this node is extra (like comments, not required by grammar)
*/
get isExtra(): boolean;
/**
* Check if this node represents a syntax error
*/
get isError(): boolean;
/**
* Check if this node is missing (inserted by parser for error recovery)
*/
get isMissing(): boolean;
/**
* Check if this node has been edited
*/
get hasChanges(): boolean;
/**
* Check if this node contains any syntax errors anywhere within it
*/
get hasError(): boolean;Access node text content and parse state information.
/**
* Get the string content of this node
*/
get text(): string;
/**
* Get this node's parse state
*/
get parseState(): number;
/**
* Get the parse state after this node
*/
get nextParseState(): number;Compare nodes for equality.
/**
* Check if this node is equal to another node
* @param other - Node to compare with
* @returns True if nodes are equal
*/
equals(other: Node): boolean;Access child nodes by index and field.
/**
* Get the node's child at the given index (0-based)
* @param index - Child index to retrieve
* @returns Child node or null if index is out of bounds
*/
child(index: number): Node | null;
/**
* Get this node's named child at the given index
* @param index - Named child index to retrieve
* @returns Named child node or null if index is out of bounds
*/
namedChild(index: number): Node | null;
/**
* Get this node's child with the given numerical field id
* @param fieldId - Field ID to look up
* @returns Child node or null if no child has this field
*/
childForFieldId(fieldId: number): Node | null;
/**
* Get the first child with the given field name
* @param fieldName - Field name to look up
* @returns Child node or null if no child has this field
*/
childForFieldName(fieldName: string): Node | null;Usage Examples:
const statement = tree.rootNode.firstChild; // e.g., "lexical_declaration"
if (statement) {
// Access by index
const firstChild = statement.child(0); // e.g., "let" keyword
// Access by field name
const declarations = statement.childForFieldName("declarations");
// Get field names
const fieldName = statement.fieldNameForChild(1);
console.log("Field name for child 1:", fieldName);
}Get field names for child nodes.
/**
* Get the field name of this node's child at the given index
* @param index - Child index to check
* @returns Field name or null if child has no field name
*/
fieldNameForChild(index: number): string | null;
/**
* Get the field name of this node's named child at the given index
* @param index - Named child index to check
* @returns Field name or null if child has no field name
*/
fieldNameForNamedChild(index: number): string | null;Get arrays of children by field.
/**
* Get an array of this node's children with a given field name
* @param fieldName - Field name to filter by
* @returns Array of child nodes with the given field name
*/
childrenForFieldName(fieldName: string): (Node | null)[];
/**
* Get an array of this node's children with a given field id
* @param fieldId - Field ID to filter by
* @returns Array of child nodes with the given field ID
*/
childrenForFieldId(fieldId: number): (Node | null)[];Find children by byte offset.
/**
* Get the node's first child that contains or starts after the given byte offset
* @param index - Byte offset to search from
* @returns Child node or null if no such child exists
*/
firstChildForIndex(index: number): Node | null;
/**
* Get the node's first named child that contains or starts after the given byte offset
* @param index - Byte offset to search from
* @returns Named child node or null if no such child exists
*/
firstNamedChildForIndex(index: number): Node | null;Get child counts and direct child access.
/**
* Get this node's number of children
*/
get childCount(): number;
/**
* Get this node's number of named children
*/
get namedChildCount(): number;
/**
* Get this node's first child
*/
get firstChild(): Node | null;
/**
* Get this node's first named child
*/
get firstNamedChild(): Node | null;
/**
* Get this node's last child
*/
get lastChild(): Node | null;
/**
* Get this node's last named child
*/
get lastNamedChild(): Node | null;
/**
* Get array of all children
*/
get children(): (Node | null)[];
/**
* Get array of all named children
*/
get namedChildren(): (Node | null)[];Navigate between sibling nodes.
/**
* Get this node's next sibling
*/
get nextSibling(): Node | null;
/**
* Get this node's previous sibling
*/
get previousSibling(): Node | null;
/**
* Get this node's next named sibling
*/
get nextNamedSibling(): Node | null;
/**
* Get this node's previous named sibling
*/
get previousNamedSibling(): Node | null;Work with descendant nodes in the subtree.
/**
* Get the descendants of this node that are the given type(s)
* @param types - Node type name or array of type names to search for
* @param startPosition - Optional start position to limit search
* @param endPosition - Optional end position to limit search
* @returns Array of matching descendant nodes
*/
descendantsOfType(types: string | string[], startPosition?: Point, endPosition?: Point): (Node | null)[];
/**
* Get the node's number of descendants, including one for the node itself
*/
get descendantCount(): number;
/**
* Get the smallest node within this node that spans the given byte range
* @param start - Start byte index
* @param end - End byte index (optional, defaults to start)
* @returns Smallest node spanning the range or null
*/
descendantForIndex(start: number, end?: number): Node | null;
/**
* Get the smallest named node within this node that spans the given byte range
* @param start - Start byte index
* @param end - End byte index (optional, defaults to start)
* @returns Smallest named node spanning the range or null
*/
namedDescendantForIndex(start: number, end?: number): Node | null;
/**
* Get the smallest node within this node that spans the given point range
* @param start - Start position
* @param end - End position (optional, defaults to start)
* @returns Smallest node spanning the range or null
*/
descendantForPosition(start: Point, end?: Point): Node | null;
/**
* Get the smallest named node within this node that spans the given point range
* @param start - Start position
* @param end - End position (optional, defaults to start)
* @returns Smallest named node spanning the range or null
*/
namedDescendantForPosition(start: Point, end?: Point): Node | null;Usage Examples:
// Find all identifier nodes
const identifiers = tree.rootNode.descendantsOfType("identifier");
console.log("Found identifiers:", identifiers.length);
// Find node at specific position
const nodeAtCursor = tree.rootNode.descendantForPosition({ row: 0, column: 5 });
console.log("Node at cursor:", nodeAtCursor?.type);
// Find all function declarations
const functions = tree.rootNode.descendantsOfType("function_declaration");Navigate up the tree hierarchy.
/**
* Get this node's immediate parent
*/
get parent(): Node | null;
/**
* Get the node that contains the given descendant
* @param descendant - Descendant node to find container for
* @returns Node containing the descendant (may be descendant itself)
*/
childWithDescendant(descendant: Node): Node | null;Create efficient tree cursors from nodes.
/**
* Create a new TreeCursor starting from this node
* Note: The given node is considered the root of the cursor
* @returns TreeCursor for efficient traversal
*/
walk(): TreeCursor;Edit individual nodes to sync with source changes.
/**
* Edit this node to keep it in-sync with source code changes
* Only needed when keeping specific Node instances after tree edits
* @param edit - Description of the edit
*/
edit(edit: Edit): void;Get textual representation of nodes.
/**
* Get the S-expression representation of this node
* @returns String representation showing the tree structure
*/
toString(): string;Usage Example:
const tree = parser.parse("let x = 1 + 2;");
console.log(tree.rootNode.toString());
// Output: (program (lexical_declaration (let) (variable_declarator (identifier) (number))))Install with Tessl CLI
npx tessl i tessl/npm-web-tree-sitter