### 💥 BREAKING CHANGES -c7d229btransformer: [**BREAKING**] Decode JSX entities to lone surrogates (#26712) (Dunqing) -e3c1b11ast: [**BREAKING**] Preserve lone surrogates in static property names (#27095) (Dunqing) -a0a987cast: [**BREAKING**] Preserve lone surrogates in static member names (#26677) (Dunqing) ### 🚀 Features -7bb2697minifier: Minimize array join expression (#27905) (Armano) -5d91db8ast: Generate node ID setters for enums (#27500) (camc314) -6b5057bminifier: Add de morgan optimization for void expressions (#27375) (Armano) ### 🐛 Bug Fixes -ce6a088minifier: Fold string indices by UTF-16 code unit (#27096) (Dunqing) -7c47246linter: Check module specifiers and string values that contain lone surrogates (#26717) (Dunqing) -782ab6elinter: Search string values without UTF-8 conversion (#26716) (Dunqing) -a5d8184jsdoc: Parse code spans after quotes in descriptions (#27955) (leaysgur) -bd37e78ecmascript: Look through sequence expressions in `to_primitive` (#27837) (baltasarblanco) -d92789dminifier: Drop debugger statements in unbraced bodies (#27867) (camc314) -863b359ecmascript: Correct parseFloat decimal prefix scanning (#27695) (camc314) -a2534c6minifier: Preserve lexical initializers in dead for loops (#27676) (camc314) -3543672ecmascript: Do not fold Number.isInteger / isSafeInteger of tiny non-integers to true (#27622) (Baltasar Blanco) -6987a61ecmascript: BigInt loose equality folding (unknown operands and `StringToBigInt`) (#27656) (Baltasar Blanco) -40e5644minifier: Preserve indirect access when folding into call targets (#27082) (waltu) -022306fecmascript: Do not fold `Math.cbrt` of a negative number to `NaN` (#27621) (Baltasar Blanco) ### ⚡ Performance -faa75d8transformer: Remove redundant scratch vectors (#27922) (camc314) -88b5c35minifier: Reduce code branches when evaluating roots (#27906) (Armano) -fe1d367codegen: Remove branches from loops printing arrays (#27840) (overlookmotel) -7bb6b62codegen: Reduce hashmap lookups in `print_annotation_comment` (#27845) (overlookmotel) -31ffd08codegen: Avoid unnecessary comment clone (#27843) (overlookmotel) -a6456b7codegen: Remove branch from `print_space_before_identifier` (#27842) (overlookmotel) -eeff416codegen: Simplify `print_space_before_identifier` (#27841) (overlookmotel) -5d877b8diagnostics: Speed up zero-context span lookups (#27770) (camc314) -2ab6b63packages/codegen: Remove redundant `printSpaceBeforeIdentifier` and `last` updates in `printParams` (#27652) (overlookmotel) -e533278codegen: Reduce writes in `printDecorators` (#27651) (overlookmotel) ### 📚 Documentation -29caafdcodegen: Format table in doc comment (#27839) (overlookmotel) -3de03c1packages/codegen: Correct outdated comments (#27838) (overlookmotel)
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.