Learn/Engineering · 6 min · updated 2026-04-26

tree_sitter_typescript::language_tsx in Rust — the right way to wire it up

How to use tree_sitter_typescript::language_tsx() from Rust without crashing on JSX-heavy files. Crate version compatibility, parser init, and how Aura uses it.

The tree-sitter-typescript crate exposes two grammars: language_typescript() for plain .ts files, and tree_sitter_typescript::language_tsx() for .tsx files containing JSX. Pick the wrong one and the parser silently truncates every JSX subtree to ERROR nodes, which is exactly what Aura\'s AST-diff engine handled wrong in v0.4 and now treats as a hard precondition.

Cargo.toml

[dependencies]
tree-sitter        = "0.22"
tree-sitter-typescript = "0.21"

Minimal parser setup

use tree_sitter::Parser;

fn parse_tsx(source: &str) -> tree_sitter::Tree {
    let mut parser = Parser::new();
    parser
        .set_language(&tree_sitter_typescript::language_tsx())
        .expect("load tsx grammar");
    parser.parse(source, None).expect("parse")
}

Pitfalls Aura hit (so you don\'t have to)

  • One parser, one grammar. Re-using the same Parser across .ts and .tsx files without calling set_language again leaves you parsing TSX with the TypeScript grammar: most JSX becomes ERROR nodes and your diff is meaningless.
  • Version skew. tree-sitter 0.22 ABI requires a tree-sitter-typescript built against the same major. Mixing 0.20 + 0.22 compiles fine, then segfaults at runtime when the parser walks a sufficiently deep tree.
  • UTF-16 source. parse assumes UTF-8. If you\'re feeding it bytes from VS Code over LSP, decode first or use parse_with_options.
  • Cancellation timeouts. Set parser.set_timeout_micros(…) for adversarial inputs: a hand-crafted JSX file can blow up the parser for > 30s otherwise.

How Aura uses it

Aura\'s AST-diff engine runs language_tsx() against every .tsx commit, extracts named function nodes, and hashes their structural skeleton (rename-proof identity). When Claude rewrites <UserCard> from a class component to a functional one, Aura sees one node updated, not 200 lines deleted + 180 added, and the intent log says exactly that.

Reference

The grammar source lives at tree-sitter/tree-sitter-typescript. The Aura wrapper that picks the right grammar per file extension is in aura-stackgraph/src/lang/typescript.rs (Apache 2.0).

Questions

How do you parse TSX with Tree-sitter in Rust?

Use the language_tsx() function from the tree-sitter-typescript crate rather than the plain TypeScript grammar — TSX is a separate grammar because JSX syntax is ambiguous with type assertions. Set it on the parser before parsing any .tsx source.

Why does Tree-sitter fail on JSX-heavy TypeScript files?

Almost always because the TypeScript grammar was set instead of the TSX one. They are distinct languages in the crate, and the TypeScript grammar reads a JSX element as a comparison, producing error nodes rather than a usable tree.

What causes a Tree-sitter version mismatch panic in Rust?

A grammar crate compiled against a different tree-sitter ABI than the runtime you linked. The versions must be compatible; pinning both in Cargo.toml and rebuilding cleanly is the fix, since a stale build artefact will keep reproducing it.