Getting Started

There is no npm package yet. The package name is yuku-tsrx and the version in npm/yuku-tsrx/package.json is 0.0.0; nothing has been published, and neither are the twelve @yuku-tsrx/binding-* packages that carry the native addon. So getting started means building from source.

What you need#

  • Zig 0.16. build.zig.zon sets .minimum_zig_version = "0.16.0".
  • pnpm, for the JavaScript side.
  • A Yuku checkout of the branch in yuku-toolchain/yuku#164 (opens in new tab), in a sibling directory named yuku-minimal-seam.

That last one is the part that is not optional. The compile-time extension points yuku-tsrx builds against are the subject of that pull request, which is open, and build.zig.zon declares the dependency by path:

.yuku = .{ .path = "../yuku-minimal-seam" },

A path dependency resolves relative to this repository, so the two folders sit side by side:

dev/
  yuku-minimal-seam/     the Yuku branch from PR #164
  yuku-tsrx/             this repository
Why a path dependency rather than a pinned URL

The owner ruling recorded in goal.md is "Local link first, PR last": build against the local path the whole way, keep the Yuku-side diff under pressure to shrink, and open the upstream pull request only once the whole system is green. A path dependency is what makes that loop fast, and it is why the checkout is a prerequisite rather than something the build fetches for you. When PR #164 merges, the path dependency becomes a normal versioned one. See Upstreaming to Yuku.

Which route are you on#

Three things people come here to do, and they need different commands. Pick yours; the other two answers are not on your path.

What do you want to do with yuku-tsrx?

Consume it from Node

zig build writes a complete npm package layout into zig-out/npm/yuku-tsrx/. A consuming project points at that directory with "yuku-tsrx": "link:../yuku-tsrx/zig-out/npm/yuku-tsrx" and then does import { parseModule } from "yuku-tsrx". Use the built package from another project has the whole sequence.

Run it in a browser

pnpm run docs:wasm is zig build wasm -Doptimize=ReleaseSmall, and it writes zig-out/wasm/yuku-tsrx.wasm. node tools/wasm-smoke.mjs instantiates that module in Node and parses with it, so the build is proven before any page fetches it. docs/assets/yuku-wasm.js is the browser host for the same module, and the playground is that host running in your tab.

Hack on the dialect

The dialect is src/dialect/: parser_extension.zig declares the hooks Yuku calls, schema.zig the TSRX node records, and transfer.zig the buffer that crosses into JavaScript. Run zig build test for the Zig suite and pnpm test for the JavaScript one. The Yuku-side seam is yuku-toolchain/yuku#164 (opens in new tab); see Zig/Yuku Dialect Core and Upstreaming to Yuku.

The browser route is the shortest one to a working build, because it needs nothing from npm:

See it run
# the WebAssembly build the docs site and the playground load
zig build wasm -Doptimize=ReleaseSmall
# exit 0

# prove the module in Node before a page ever fetches it
node tools/wasm-smoke.mjs
wasm: zig-out/wasm/yuku-tsrx.wasm 1246 KiB
imports: none
hero: 92 nodes in 6.21 ms, 0 diagnostics
semantic: 9 scopes, 4 symbols, 5 references
generate: 338 bytes, 0 errors
fixtures: 15 parsed
ok
# exit 0
Captured on 2026-08-17 by tools/capture-transcripts.mjs on Darwin 25.5.0 arm64 (Apple M5 Pro), Node v24.15.0, Zig 0.16.0. Every line is what the command printed; long output is trimmed where marked.

Build#

zig build            # builds the addon and writes the package to zig-out/npm/yuku-tsrx/
zig build test       # the Zig test suite
pnpm test            # the JavaScript test suite
See it run
# build the addon and write the npm package layout
zig build
# exit 0

# zig build prints nothing on success, so look at what it wrote
ls zig-out/npm/yuku-tsrx
@yuku-tsrx
binding.js
decode-analyzer.js
decode.js
encode.js
index.d.ts
index.js
package.json
walk.js
# exit 0

# the Zig test suite; --summary all only adds the tree at the end
zig build test --summary all
Build Summary: 7/7 steps succeeded; 2/2 tests passed
test success
+- run test 1 pass (1 total) 7ms MaxRSS:3M
|  +- compile test Debug native cached 51ms MaxRSS:31M
|     +- options cached
|     +- options cached
+- run test 1 pass (1 total) 7ms MaxRSS:3M
   +- compile test Debug native cached 51ms MaxRSS:31M
      +- options (reused)
      +- options (reused)
# exit 0
Captured on 2026-08-17 by tools/capture-transcripts.mjs on Darwin 25.5.0 arm64 (Apple M5 Pro), Node v24.15.0, Zig 0.16.0. Every line is what the command printed; long output is trimmed where marked.

pnpm test is not in that recording. tools/capture-transcripts.mjs drops any command that did not exit zero rather than editing its output, and on the day above two checks in test/m1.test.ts, which assert things about the build environment rather than about the parser, were failing. Run it yourself and read what it says.

zig build is the one that produces something you can import. It writes a complete npm package layout into zig-out/npm/yuku-tsrx/: index.js, index.d.ts, package.json, the generated decoders (decode.js, decode-analyzer.js, encode.js), walk.js, and a binding.js that loads the native addon built for your machine.

Run the two test suites before you trust the result. zig build test covers the dialect itself; pnpm test covers the JavaScript surface, including the fixtures in test/parser/misc/tsrx/.

Use the built package from another project#

zig-out/npm/yuku-tsrx/ is a real package directory, so a consuming project points at it with a link: dependency:

{
  "dependencies": {
    "yuku-tsrx": "link:../yuku-tsrx/zig-out/npm/yuku-tsrx"
  }
}

Then import it the way you would any ESM package:

import { parseModule, walk } from "yuku-tsrx";

const program = parseModule(source, "Cart.tsrx");

walk(program, {
  JSXCodeBlock(node) {
    // every TSRX code block in the file
  },
});

That is how Markless consumes it. Markless parses .tsrx today through @tsrx/core, and parseModule(source, filename, options) is shaped as a drop-in for @tsrx/core's parseModule, so the link is the only change at the call site. On 2026-08-17, against the head of PR #164, Markless's node test suite (229 files, 1832 tests) passed with yuku-tsrx swapped in for @tsrx/core and no test edits, and its typescript-plugin completion matrix was 47/47.

Rebuild with zig build after any change to the dialect or to the sibling Yuku checkout. A link: dependency reads the directory on disk, so the consuming project picks the new build up without reinstalling.

Where to go next#

  • TSRX Syntax Support for what the parser accepts and what it rejects.
  • Parser for parse, parseModule, options, and diagnostics.
  • API for every export with its signature.
  • Limitations for what this library deliberately does not do.