### 💥 BREAKING CHANGES -66744f0parser: [**BREAKING**] Rename `panicked` to `fatal_error` in `ParserReturn` (#26382) (overlookmotel) -2c9a947parser: [**BREAKING**] Reduce `MAX_LEN` to 256 bytes below `u32::MAX` (#26352) (overlookmotel) ### 🚀 Features -32bdc5ballocator: Construct `ArenaHashSet` with any `Default` hasher (#26372) (Dunqing) -a17de58minifier: Process of return/throw in non last position and fix async * inlining (#26214) (Armano) -bfb4c57parser: Distinguish unapplied no-side-effects comments (#26120) (碳苯 Carbon) -1d9b9d3ast: Add `GetNodeId` trait (#26145) (camc314) ### 🐛 Bug Fixes -b156333codegen: Order accessibility before abstract on accessors (#26392) (camc314) -116c8b8transformer/class-properties: Keep private methods in class-expression scope (#26308) (Changsu Seong) -1400a0fparser: Require a string source after `export ... from` (#26389) (camc314) -2da73b7codegen: Print matching quoted import names as identifiers (#26386) (camc314) -0a81d29codegen: Preserve private-in left operand precedence (#26383) (camc314) -9a02337parser: Correctly round large nondecimal literals (#26379) (camc314) -3d95477transformer: Gate async-only rewrites by owning transform (#26326) (Dunqing) -f45a9eetransformer: Limit arguments capture to lowered async functions (#26317) (Dunqing) -789969ftransformer: Gate this capture by owning async transform (#26227) (Dunqing) -affeb14minifier: Mangle class private members in Node API (#26118) (碳苯 Carbon) -8b35abdsemantic: Detect duplicate private class elements (#26361) (camc314) -457ed57semantic: Reject await and yield in rest parameter defaults (#26359) (camc314) -4b89c7bsemantic: Reject jumps across arrow functions (#26357) (camc314) -c966acaparser: Do not omit `<` token opening type argument list (#26328) (overlookmotel) -f07dca5ast_visit: Add the trimmed prefix length to translation table offsets (#26173) (Bharadwaj Pendyala) -3ab9bd1parser: Do not re-lex template substitution tail after fatal error (#26230) (overlookmotel) -07851b9parser: Fix debug assert failure when lexer error with tokens enabled (#26229) (overlookmotel) -00dea7aallocator: Gate `Allocator::data_end_ptr` behind fixed_size feature (#26248) (camc314) -853ffabecmascript: Avoid `charAt` panic on 32-bit (#26244) (camc314) -34feccbruntime: Remove stale regenerator exports (#26243) (camc314) ### ⚡ Performance -34a242eminifier: Use `Ident` for property mangler (#26334) (sapphi-red) -5303f5cminifier: Unify merging of last expression into target sequence (#26264) (Armano) -2ca7d29minifier: Consume nodes in minimize_statements in reverse order (#26258) (Armano) -d2354b4mangler: Use `Ident` for variable names to keep (#26333) (sapphi-red) -5b3e335minifier: Use `Ident` instead of `Str` in `KeepVar` (#26332) (sapphi-red) -51366fbminifier: Use `IdentHashSet` in `PrivateMemberUsageStack` (#26331) (sapphi-red) -d19c42aminifier: Merge nested if stmt in place instead of creating dummies (#26351) (Armano) -356def6parser: Shrink annotation comment ranges (#26356) (overlookmotel) -a5be474parser: Shave instruction off `parse_jsx_element_name` (#26355) (overlookmotel) -9780663parser: Remove fatal error guard from `parse_jsx_element_name` (#26354) (overlookmotel) -766e12fparser: Remove `token` field from `LexerCheckpoint` (#26350) (overlookmotel) ### 📚 Documentation -d4b4e61transformer: Document ES2015 target floor (#26378) (Dunqing) -67dd054ast_visit: Correct out-of-date comments on `Utf8ToUtf16` converter (#26285) (overlookmotel) -a8ed2a7minifier: Fix stale validation instructions (#26251) (camc314)
oxc-codegen
Fast, synchronous code generation for JavaScript and TypeScript ASTs.
oxc-codegen turns an ESTree or
TS-ESTree AST into formatted source
code. It supports JavaScript, JSX, TypeScript, and TSX.
The printer is a port of Oxc's Rust oxc_codegen crate. With the default options, both printers
produce byte-identical output: tab indentation, double-quoted strings, and no comments.
Installation
npm install oxc-codegen
oxc-codegen is ESM-only and requires Node.js ^20.19.0 or >=22.12.0.
Quick start
Pair it with oxc-parser to parse and print source code:
import { printSync } from "oxc-codegen";
import { parseSync } from "oxc-parser";
const { program } = parseSync("input.js", "const answer=6*7");
const { code } = printSync(program);
console.log(code);
// const answer = 6 * 7;
You can also print a manually constructed AST:
const program = {
type: "Program",
sourceType: "script",
body: [
{
type: "ExpressionStatement",
expression: {
type: "CallExpression",
callee: {
type: "MemberExpression",
object: { type: "Identifier", name: "console" },
property: { type: "Identifier", name: "log" },
computed: false,
optional: false,
},
arguments: [{ type: "Literal", value: "Hello!" }],
optional: false,
},
},
],
};
console.log(printSync(program).code);
// console.log("Hello!");
TypeScript and TSX
Set ts when the AST can contain TypeScript nodes. For TSX, set both ts and jsx:
const { program } = parseSync("component.tsx", "const Box = <T,>(value: T) => <div>{value}</div>");
const { code } = printSync(program, {
ts: true,
jsx: true,
});
API
printSync(node, options?)
function printSync(
node: ESTree.Program | ESTree.Statement,
options?: Options,
): {
code: string;
map: SourceMap | null;
};
Prints a complete Program or a single statement and returns the generated source code,
and (when requested) a standard Source Map v3 object.
import { printSync } from "oxc-codegen";
import { parseSync } from "oxc-parser";
const sourceText = "const answer=6*7";
const { program } = parseSync("input.js", sourceText);
const { code, map } = printSync(program, {
sourcemap: true,
sourceFilename: "input.js",
sourceText,
});
Source-map mappings require sourceText and nodes with valid Oxc start / end offsets.
A manually constructed AST without offsets can still be printed, but its source map has
an empty mappings string.
Options
| Option | Type | Default | Description |
|---|---|---|---|
indent |
string |
"\t" |
Non-empty string of spaces and/or tabs used for one indent level |
startingIndentLevel |
number |
0 |
Starting indent level, from 0 to 1000 |
jsx |
boolean |
false |
Enable TSX-safe printing for ambiguous TypeScript syntax |
ts |
boolean |
false |
Select the printer that supports TypeScript nodes |
sourcemap |
boolean |
false |
Return a Source Map v3 object in map |
sourceFilename |
string |
"" |
Original source filename recorded in the source map |
sourceText |
string |
- | Original text required for source-map mappings and content |
Why pure JavaScript?
Most Oxc packages use native bindings. This package deliberately does not: when an AST already
lives in JavaScript, passing the entire object graph across a JS/native boundary can cost more than
printing it in place. oxc-codegen avoids that serialization and uses specialized printer builds
for JavaScript and TypeScript workloads.
See DESIGN.md for the implementation details and performance constraints.
Current limitations
- Comments are not printed.
- Minified output is not supported.
Benchmarks
Representative time per printSync call:
| Fixture | Bytes | Time |
|---|---|---|
tiny.js |
26 | 0.0001 ms |
RadixUIAdoptionSection.jsx |
2,518 | 0.0070 ms |
react.development.js |
72,141 | 0.1518 ms |
binder.ts |
193,077 | 0.3364 ms |
App.tsx |
415,340 | 1.2912 ms |
lodash.js |
544,096 | 0.7977 ms |
kitchen-sink.tsx |
732,222 | 4.2924 ms |
antd.js |
6,683,633 | 16.9652 ms |
These figures come from one machine and are illustrative, not a regression baseline.
Results vary between runs, most noticeably for large fixtures such as antd.js.