Code Generator

generate is the parser run backwards. Give it a Program and it gives you source text.

generate(program: Program, options?: GenerateOptions): GenerateResult
import { generate, parse } from "yuku-tsrx";

const { program } = parse(source, { lang: "tsx" });
const { code, errors, map } = generate(program);

The tree does not have to be the one you just parsed. You can walk it, change it, and print the result, which is what a codemod does.

What it accepts#

The first argument has to be a Program node from this parser. generate checks that before doing anything and throws a TypeError reading Expected a Program node from yuku-tsrx otherwise. It is not a printer for arbitrary ESTree.

Internally the tree goes back across the native boundary the same way it came: generate calls encode on the program and hands the buffer to the addon. The generator itself is src/dialect/codegen.zig.

GenerateOptions#

export interface GenerateOptions {
  strip?: boolean;
  minify?: boolean | { whitespace?: boolean; syntax?: boolean; quotes?: boolean };
  format?: "pretty" | "compact";
  indent?: number;
  quotes?: "preserve" | "double" | "single" | "shortest";
  comments?: boolean | "all" | "some" | "none" | "line" | "block";
}
Option What it does
strip Emit without the TypeScript-only syntax.
minify Size-reducing output. true means all three modes; an object turns them on one at a time.
format pretty is indented, with spaces around operators and after commas. compact emits no discretionary whitespace, only what the grammar requires.
indent Spaces per indentation level. Used only when format is pretty.
quotes preserve keeps each literal's original quote style, double and single force one, shortest picks the one that needs fewer escapes.
comments Which comments survive.

One current limitation: quotes: "shortest" and minify: true are declared in npm/yuku-tsrx/index.d.ts but fail at the native boundary today, because the Quotes enum in src/dialect/codegen.zig has only preserve, double, and single, so the addon rejects the request with invalid enum value for codegen.Quotes: 'shortest'. minify: true hits the same wall because its quotes mode sets shortest. Until the enum gains the value, use preserve, double, or single, and ask for minify: { whitespace: true, syntax: true } instead of minify: true.

Every option in the table is a control below, and the output is what the generator in your browser returns for the settings you pick.

Source

import type { Item } from "./item";
import { format } from './format';

/* The cart list, one row per item. */
export function Cart({ items }: { items: Item[] }) {
  // total is read by the attribute below
  @{ const total = items.length; }
  return (
    <ul class="cart" data-empty={total === 0}>
      @for (const item of items; key item.id) {
        <li>{format(item.label)}</li>
      }
    </ul>
  );
}

Generated

The generator runs when this figure scrolls into view.

the generator runs in your browser when this figure scrolls into view; with JavaScript off this stays the listing above

The figure parses with attachComments: true, because the comments option can only act on comments the parse kept.

comments#

none drops all comments, all emits every one, line emits only // ..., block emits only /* ... */, and some emits legal headers, JSDoc, and tree-shaking annotations such as __PURE__ and __NO_SIDE_EFFECTS__. A boolean is accepted as a shorthand: true becomes all and false becomes none. Without the option, the generator's own default applies, which is some.

minify is three switches, not one#

minify: true is shorthand for { whitespace: true, syntax: true, quotes: true }, and npm/yuku-tsrx/index.js expands each of the three into the option it actually controls:

Mode Effect
syntax Turns on the print-time size-reducing substitutions.
whitespace Sets format to compact.
quotes Sets quotes to shortest.

So { minify: { whitespace: true } } gives you compact output with ordinary syntax and ordinary quotes, and { minify: true } gives you all three. Setting format or quotes yourself alongside a minify mode that also sets them means the mode wins.

GenerateResult#

export interface GenerateResult {
  code: string;
  errors: Array<{ message: string; start: number; end: number }>;
  map: unknown | null;
}

code is the source text. errors is empty when codegen ran cleanly; an entry is a problem the generator found in the tree you handed it, with the offsets of the node it found it on. map is a Source Map V3 object, and it is null unless source maps were requested.

Round-tripping#

Printing a parsed tree and parsing the result again should give you the same structure back. That is the property the repository's own tests hold the generator to, across every valid fixture in test/parser/misc/tsrx/:

import { analyze, generate, parse } from "yuku-tsrx";

const analysis = analyze(source, { lang: "tsx" });
const generated = generate(analysis.program);
const reparsed = parse(generated.code, { lang: "tsx" });
// reparsed.diagnostics is empty, and the structure matches

This is the check that keeps TSRX from quietly degrading on the way out. A generator that printed a JSXCodeBlock as an ordinary expression would produce text that still parses, just not into the same tree, so "it parses" is not the bar. See TSRX Syntax Support for the constructs that have to survive the trip.