Commit Graph

6 Commits

Author SHA1 Message Date
overlookmotel d50c985fc2 refactor(packages/codegen): split categories into separate file (#26083)
Pure refactor. Move the category consts (`CAT_IDENT` etc) into a separate file.

2 reasons:

1. `write.ts` had got too large.
2. `index.ts`, which is not part of printer builds, imports `CAT_OTHER`. Importing it from a file containing only consts avoids any problems with minifier not being able to recognise dead code and remove it.
2026-08-25 13:46:38 +00:00
overlookmotel 29a774930a perf(packages/codegen): pass start and end to write and mark functions (#25986)
Optimization to sourcemap builds. This is what the rest of this stack has been working towards.

All the `writeWithMap*` and `markMap*` functions received a `node` param which they're extract `start` and `end` from. The problem is that these functions receive a range of different node types, so accessing `node.start` / `node.end` are megamorphic, involving an expensive dispatch.

Instead, extract `start` and `end` at call sites, where the type of `node` is already known (monomorphic), and pass them in to `writeWithMap*` / `markMap*` functions.

These functions still keep their `node` param, as it's needed for debug asserts, but it's never touched in release builds. #25989 removes it entirely from release builds too.

This has quite a large effect on perf. The cumulative perf gain of this stack (#25968 onwards) in the "without generation" sourcemaps benchmark is:

<table>
<tr>
<th align="left">Fixture</th>
<th align="right">Bytes</th>
<th align="right">Before</th>
<th align="right">After</th>
<th align="right">Faster by</th>
</tr>
<tr>
<td align="left">

`RadixUIAdoptionSection.jsx`

</td>
<td align="right">2,518</td>
<td align="right">0.0077 ms</td>
<td align="right">0.0059 ms</td>
<td align="right">+31.5%</td>
</tr>
<tr>
<td align="left">

`react.development.js`

</td>
<td align="right">72,141</td>
<td align="right">0.2549 ms</td>
<td align="right">0.2015 ms</td>
<td align="right">+26.9%</td>
</tr>
<tr>
<td align="left">

`lodash.js`

</td>
<td align="right">544,096</td>
<td align="right">1.14 ms</td>
<td align="right">0.9289 ms</td>
<td align="right">+23.5%</td>
</tr>
<tr>
<td align="left">

`App.tsx`

</td>
<td align="right">415,340</td>
<td align="right">1.84 ms</td>
<td align="right">1.51 ms</td>
<td align="right">+21.2%</td>
</tr>
<tr>
<td align="left">

`binder.ts`

</td>
<td align="right">193,077</td>
<td align="right">0.6598 ms</td>
<td align="right">0.5483 ms</td>
<td align="right">+20.6%</td>
</tr>
<tr>
<td align="left">

`kitchen-sink.tsx`

</td>
<td align="right">732,222</td>
<td align="right">5.12 ms</td>
<td align="right">4.33 ms</td>
<td align="right">+17.7%</td>
</tr>
<tr>
<td align="left">

`antd.js`

</td>
<td align="right">6,683,633</td>
<td align="right">23.52 ms</td>
<td align="right">20.30 ms</td>
<td align="right">+15.7%</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>

The vast majority of that gain is down to this PR, and the ones preceding it which also remove polymorphic/megamorphic accesses.

---

Note: Actually this change is unnecessary for `writeWithMapNamed`, `writeWithMapNamedPrivate`, and `writeWithMapNamedJSXNoLast` - they only receive a single type of node so are already monomorphic. But it's easier to be consistent, and this uniformity enables the further optimization in #25989. We could pare this back in a later PR, so we only pass in `start` and `end` where it's beneficial to do so.
2026-08-24 09:20:18 +00:00
overlookmotel 8ae741ba4a perf(packages/codegen): replace write calls with writeIdent (#25984)
Optimization to sourcemap and no-sourcemap builds.

#25983 introduced a `writeIdent` function. Use it everywhere which was previously calling `write` with `CAT_IDENT`. This has the same advantages as #25983 - avoids using a register just to pass a static value, and makes setting `last` cheaper in this hot path.
2026-08-24 09:20:17 +00:00
overlookmotel 3216cbb04c refactor(packages/codegen): rename write* and mark* functions (#25973)
Pure refactor. Just rename functions:

- `markWithMap` -> `markMap` for brevity.
- All functions which can produce a named mapping called `*Named`.

This mainly prepares the way for next PR #25975, which introduces separate `write` functions for where a mapping can never be named.
2026-08-22 13:24:58 +00:00
overlookmotel 7e2b4b36a1 perf(packages/codegen): printNonNegativeFloat write mapping directly (#25968)
Small perf optimization and code simplification.

`printNonNegativeFloat` previously passed the `node` parameter through a long chain of helper functions, so that they could call `writeWithMap` with `node` on their first write.

Instead create the sourcemapping at the start of the chain, so there's no need to pass `node` down and helpers can just use `write` / `writeLast`.
2026-08-22 09:30:31 +00:00
overlookmotel a4478e960a feat(codegen): add oxc-codegen package (#25488)
Add `oxc-codegen` package - a printer written in TS.

### Features

- Prints JS, JSX, TS, TSX.
- Matches `oxc_codegen` exactly on all Test262, TypeScript, Acorn-JSX fixtures.
- Very fast.

### Deficiencies

- Pretty-printing only (no compact/minified output).
- No support for printing comments.
- Source map support is semi-there, but largely untested, and poor API (design doc has some ideas on how to improve it).

### This PR adds

- The printer code.
- Conformance testing infra.
- Further tests for edge cases.
- README.
- Design doc - how it differs from `oxc_codegen` and why, what produces the perf.

### This PR doesn't add

- Not integrated into CI or release pipeline.

### Notes

This started as a slop port of `oxc_codegen`. I've iterated on that base, cleaned it up a lot, and at least skimmed through all of it. I think it's in fairly decent shape.

The design doc is I think worth a read. It explains the rationale for the choices, in particular the "1 codebase, 4 builds" approach.

I've put it in a new top-level directory called `packages`. It's not an app, so didn't seem to me to fit alongside `apps/oxlint` and `apps/oxfmt`. It's pure JS, so not like e.g. `napi/parser`. And it felt too heavyweight to put into `npm` dir. But maybe there's a better place for it.

Co-authored-by: Cameron <cameron.clark@hey.com>
2026-08-12 13:41:31 +00:00