wasm/

directory
v0.48.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 4, 2026 License: MIT

README

gotreesitter in the browser

gotreesitter has two JavaScript/WebAssembly entry points. Choose one target per .wasm file; they intentionally expose different APIs.

Target Input Best for
./wasm/runtime A generated gotreesitter grammar blob Production parsing, queries, and highlighting from pre-generated tables
./wasm/grammargen Tree-sitter grammar.json Interactive grammar development and generation in the browser

Both targets publish a synchronous globalThis.gotreesitter object after the module starts. loader.js initializes either build.

Build

Build the targets with Go's JavaScript WebAssembly port:

mkdir -p dist
GOOS=js GOARCH=wasm go build -tags grammar_blobs_external \
  -o dist/gotreesitter-runtime.wasm ./wasm/runtime
GOOS=js GOARCH=wasm go build -o dist/gotreesitter-grammargen.wasm ./wasm/grammargen
cp "$(go env GOROOT)/lib/wasm/wasm_exec.js" dist/wasm_exec.js
cp wasm/loader.js dist/loader.js

For a deployable single-language bundle, use the asset command instead. It:

  • selects only the requested language's tables, registry, and scanner support;
  • emits the browser-loaded grammar blob as a separate asset; and
  • writes a digest manifest alongside the compiler's matching bootstrap.
go run ./cmd/wasmassets -language go -output dist/go -compiler go
# Or use TinyGo and its matching wasm_exec.js:
go run ./cmd/wasmassets -language go -output dist/go-tiny -compiler tinygo

Serve these files over HTTP. WebAssembly.instantiateStreaming normally requires the server to send .wasm as application/wasm; the shared loader falls back to fetching an ArrayBuffer when streaming instantiation is not available.

wasm_exec.js must come from the same Go toolchain used to build the selected .wasm file. Mixing versions is unsupported and can fail during startup. Go 1.24 and later install it at $(go env GOROOT)/lib/wasm/wasm_exec.js; older Go toolchains place it under misc/wasm.

Runtime build

The runtime build consumes grammar blobs generated by gotreesitter. The repository's built-in blobs live under grammars/grammar_blobs; applications can serve only the languages they need. The grammar_blobs_external build tag shown above keeps the complete built-in blob set out of the WASM module. If the supported language set is known at build time, combine it with grammar_subset and the matching grammar_subset_<language> tags. This retains only that language's scanner and registry support.

When name resolves to a registered language with a custom token-source factory, the runtime uses that factory consistently for parsing, queries, and highlighting. Unknown out-of-tree names fall back to the blob's DFA tables.

API
  • loadBlob(name, blobUint8Array, highlightQuery, tagsQuery?) loads one grammar blob. Pass an empty string for either unavailable query.
  • parse(name, source) returns {ok, sexp, hasError, tree}. tree is a JSON string containing the structured syntax tree; parse it once with JSON.parse. Empty source returns an empty S-expression, hasError: false, and the JSON string "null".
  • query(name, source, queryText) compiles and runs a Tree-sitter query. It returns {ok, matches, truncated}; compile failures also include errorOffset when the compiler reports one.
  • highlight(name, source) returns {ok, ranges} for the highlight query supplied to loadBlob. Each range contains startByte, endByte, and capture.
  • open(name, documentID, source) retains a UTF-16 document tree and returns its initial highlights and tags.
  • update(documentID, source) computes a surrogate-safe minimal edit, incrementally reparses the document, and returns its new revision, highlights, tags, and edit span.
  • queryDocument(documentID, queryText) runs a bounded query over the retained tree without reparsing source.
  • close(documentID) releases the retained tree. Reopening an existing ID replaces and releases the prior document.

Tree nodes contain type, named, optional missing, error, and field properties, plus nested children. Their start/end positions are canonical UTF-8 byte offsets. start16/end16 are UTF-16 code-unit offsets suitable for indexing JavaScript strings. Query captures provide the same two coordinate systems along with name, type, and text. Highlight ranges are UTF-8 byte offsets.

Structured trees retain at most 20,000 nodes. The root has truncated: true only when additional nodes were omitted. Queries retain at most 500 matches and set truncated: true when another match exists. Execution is bounded by the streaming cursor rather than materializing every match first.

Browser example
<script src="/wasm/wasm_exec.js"></script>
<script src="/wasm/loader.js"></script>
<script type="module">
  const api = await loadGotreesitter("/wasm/gotreesitter-runtime.wasm");

  const blobResponse = await fetch("/grammars/go.bin");
  const blob = new Uint8Array(await blobResponse.arrayBuffer());
  const loaded = api.loadBlob("go", blob, "");
  if (!loaded.ok) throw new Error(loaded.error);

  const source = "package main\nfunc hello() {}\n";
  const parsed = api.parse("go", source);
  if (!parsed.ok) throw new Error(parsed.error);
  const root = JSON.parse(parsed.tree);
  console.log(root.type, root.start16, root.end16, parsed.sexp);

  const queried = api.query(
    "go",
    source,
    "(function_declaration name: (identifier) @name)",
  );
  if (!queried.ok) throw new Error(queried.error);
  console.log(queried.matches[0].captures[0]);
</script>

To highlight, pass the language's highlights.scm contents as the third loadBlob argument and then call api.highlight("go", source).

Persistent editor clients can share one tree across analysis operations:

const opened = api.open("go", "editor:main.go", source);
const updated = api.update("editor:main.go", source.replace("hello", "world"));
const matches = api.queryDocument(
  "editor:main.go",
  "(function_declaration name: (identifier) @name)",
);
api.close("editor:main.go");

Grammargen build

The grammargen target creates parse tables from grammar JSON inside the browser. It is useful for authoring and experiments; production applications that already have a generated blob should prefer the runtime target.

API
  • importGrammar(grammarJSON) imports a Tree-sitter grammar JSON string and returns its name.
  • generateLanguage(name) generates and caches the parser tables.
  • parse(name, source) returns {ok, sexp, hasError}; empty source succeeds with an empty S-expression and hasError: false.
  • highlight(name, source, highlightQuery?) returns UTF-8 byte ranges. When a query is omitted, the build uses a cached generated query when available.
  • highlightQueries(name) returns a cached generated highlight query.

Generate a language before parsing or highlighting it:

const api = await loadGotreesitter("/wasm/gotreesitter-grammargen.wasm");
const imported = api.importGrammar(grammarJSON);
if (!imported.ok) throw new Error(imported.error);

const generated = api.generateLanguage(imported.name);
if (!generated.ok) throw new Error(generated.error);

const parsed = api.parse(imported.name, source);
if (!parsed.ok) throw new Error(parsed.error);
console.log(parsed.sexp);

The grammargen target does not expose the runtime target's structured tree or query API. Build the runtime target and load the generated blob when those APIs are required.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL