@r4ai/typst-ast-web
v0.3.0
Published
A WebAssembly library that parses [Typst](https://typst.app/) source code and returns its CST/AST as a JSON-serializable object. Built with Rust and [`typst-syntax`](https://crates.io/crates/typst-syntax), distributed as npm packages via `wasm-pack`.
Readme
typst-ast
A WebAssembly library that parses Typst source code and returns its CST/AST as a JSON-serializable object. Built with Rust and typst-syntax, distributed as npm packages via wasm-pack.
Packages
| Package | Target |
| ---------------------------------------------------------------------------- | ------------- |
| @r4ai/typst-ast-web | Web (ESM, Browser) |
| @r4ai/typst-ast-node | Node.js |
Usage
Browser
import init, { parse, parseAst } from "@r4ai/typst-ast-web";
await init();
// CST (Concrete Syntax Tree)
const cst = parse("= Hello\n\nThis is *typst*.", { mode: "markup" });
console.log(cst);
// {
// root: { kind: "Markup", range: [0, 25], children: [...] },
// errors: []
// }
// AST (Abstract Syntax Tree)
const ast = parseAst("= Hello\n\nThis is *typst*.", { mode: "markup" });
console.log(ast);
// {
// root: [
// { kind: "heading", ... },
// { kind: "parbreak", ... },
// ...
// ],
// errors: []
// }Node.js
import { parse, parseAst } from "@r4ai/typst-ast-node";
const cst = parse("#let x = 1 + 2", { mode: "code" });
const ast = parseAst("#let x = 1 + 2", { mode: "code" });API
Both functions accept the same parameters:
text: string— Typst source code to parseoptions.mode?: "markup" | "code" | "math"— Parse mode (default:"markup")
parse(text, options?)
Returns the CST (Concrete Syntax Tree) — a lossless syntax tree that preserves all tokens including whitespace and punctuation.
interface ParseResult {
root: SyntaxNode;
errors: ParseError[];
}
interface SyntaxNode {
kind: string;
range: [number, number];
text?: string;
children: SyntaxNode[];
}parseAst(text, options?)
Returns the AST (Abstract Syntax Tree) — a typed, semantic tree where each node is a tagged union discriminated by kind. Unlike the CST, the AST extracts semantic information (e.g. heading depth, function callee, binary operator) into dedicated fields.
interface ParseAstResult {
root: AstExpr[];
errors: ParseError[];
}
// AstExpr is a discriminated union of 59 node types.
// Examples:
type AstExpr =
| { kind: "text"; range: [number, number] | null; text: string }
| { kind: "heading"; range: [number, number] | null; depth: number; body: AstExpr[] }
| { kind: "strong"; range: [number, number] | null; body: AstExpr[] }
| { kind: "funcCall"; range: [number, number] | null; callee: AstExpr; args: AstArg[] }
| { kind: "binary"; range: [number, number] | null; op: AstBinOp; lhs: AstExpr; rhs: AstExpr }
| // ... and 54 more variantsSee src/types.ts for the full type definitions.
Development
Prerequisites
- Rust (stable) with
wasm32-unknown-unknowntarget - Node.js 24
- pnpm 10
wasm-pack
Commands
# Install Node.js and pnpm via mise
mise install
# Install JS dependencies
pnpm install
# Build WASM packages (outputs to dist/web and dist/node)
pnpm run build:lib
# Lint
cargo fmt --check
cargo clippy -- -D warnings
pnpm run checkRelease
# Bump version and create a release commit + tag
bash scripts/release.sh 1.2.3Examples
React app
A React-based example app is located at examples/app.
cd examples/app
pnpm install
pnpm devLicense
MIT
