Yuku
Codegen
The codegen prints a tree back to source in one walk, so its output is always valid and faithful to the tree.
JavaScript
npm install yuku-codegen
import { generate } from "yuku-codegen";
import { parse } from "yuku-parser";
const { code, map, diagnostics } = generate(parse("const x = 1 + 2;").program);
The same printer in plain JavaScript, for any ESTree AST, with byte-identical output, source maps included. It runs 2.6× faster than @babel/generator, or 3× with source maps on. See yuku-codegen on npm for the full API.
Zig
var tree = try parser.parse(allocator, "const x: number = 1;", .{ .lang = .ts });
defer tree.deinit();
const result = try parser.codegen.generate(allocator, &tree, .{ .strip = true });
defer result.deinit(allocator);
std.debug.print("{s}\n", .{result.code}); // const x = 1;
generate leaves the tree unchanged and returns the code, its diagnostics as ast.Diagnostic, and the source map, owned by the allocator it was given and freed with result.deinit. It fails only with error.OutOfMemory.
Options
| Field | Default | Description |
|---|---|---|
strip | false | Drop TypeScript-only syntax, see Type stripping |
minify | false | Size-reducing syntax rewrites, see Minification |
format | .pretty | .compact emits only the whitespace the grammar requires |
indent | 2 | Spaces per level |
quotes | .preserve | .preserve, .double, .single, or .shortest |
comments | .some | Which attached comments to print, .some for legal headers, JSDoc, and annotations, or .all, .none, .line, .block |
source_map | null | Emit a Source Map V3, see Source maps |
Every option is independent, so they compose freely. Comments print from the nodes they are attached to, so parse with .comments = .attached or .both to keep them.
Type stripping
strip prints TypeScript as JavaScript in the same walk, skipping the nodes and fields the parser already marked as TypeScript. Types, interfaces, generics, assertions, satisfies, and declare strip cleanly. Features that emit runtime code, such as enum, namespace, and parameter properties, are reported in diagnostics and left out, since converting them is transpilation, not stripping.
Minification
minify rewrites the syntax, and combined with .compact and .shortest it is full minification.
const result = try parser.codegen.generate(allocator, &tree, .{
.minify = true,
.format = .compact,
.quotes = .shortest,
});
// const enabled = true; const size = 1000000;
// prints as const enabled=!0;const size=1e6
trueandfalsebecome!0and!1.- Numbers take their shortest form, so
1000000becomes1e6and0.5becomes.5. obj["foo"]becomesobj.foo, and{ "foo": x }becomes{ foo: x }.</script,<!--, and-->are escaped, so the output is safe inside a<script>tag.
Source maps
const result = try parser.codegen.generate(allocator, &tree, .{
.source_map = .{
.source = source, // required for a map
.file = "out.js",
.source_file_name = "in.js",
.source_root = null,
.sources_content = source,
},
});
defer result.deinit(allocator);
const map = result.map.?; // version, file, source_root, sources, sources_content, names, mappings
Mappings come from the same walk that prints the code, with columns in UTF-16 code units, the convention of browser devtools and source map libraries.
A build that never needs maps can compile them out, for a smaller binary. map is then always null.
const yuku = b.dependency("yuku", .{ .@"codegen-source-maps" = false });