Commit Graph

9 Commits

Author SHA1 Message Date
overlookmotel 20711f4995 perf(packages/codegen): remove node param from write and markMap functions in sourcemap builds (#25989)
Optimization to sourcemaps builds.

#25986 made the `node` param passed to `writeWithMap` etc redundant in sourcemap release builds.

Alter the TSDown plugin to remove the param from these functions in sourcemap release builds, and also remove the `node` argument from all call sites.

It was already removed in no-sourcemap builds - this only affects sourcemaps builds.
2026-08-24 09:20:19 +00:00
overlookmotel 6253b2ccc5 refactor(packages/codegen): convert unmap_writes TSDown plugin to a function (#25988)
Pure refactor, in preparation for #25989.

Turn `unmap_writes` TSDown plugin into a factory function. This allows passing it arguments to control the plugin's behavior.

This change split into a separate PR to avoid a massive diff in #25989.
2026-08-24 09:20:18 +00:00
overlookmotel b6c60361d2 refactor(packages/codegen): TSDown plugin strip all debugAssert* calls (#25987)
Refactor in preparation for #25989.

`remove_asserts` TSDown plugin removes debug assertions in release builds, but only when the assertion functions are imported from `asserts.ts`.

We have various other `debugAssert*` functions which are defined elsewhere. Make the plugin remove them too in release builds.

The reason this is needed is that many of these `debugAssert` functions receive `node`. #25989 removes `node` param from functions containing these assertions, which means `node` becomes a global. Minifier has to assume accessing a global may have side-effects and therefore does not remove these debug assertions. We need the plugin to do it.
2026-08-24 09:20:18 +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 215a6a51ad perf(packages/codegen): add functions for writing/marking unnamed mappings (#25975)
Small optimization to sourcemap builds.

Add functions for writing with a source mapping, where it's known from what's being written that the mapping cannot need a name, because `node.name` is never a string, and change all call sites to use the appropriate function.

This allows removing 2 checks from ``recordSourceMapping``:

- `typeof node.name === "string"` - because that's now statically known.
- `printedName.length > 0` - because identifiers cannot be length.

All `write` and `markMap` functions take a `NamedMappableNode` or `UnnamedMappableNode`, which makes it a type error to call the wrong function.

`markMapNamed` becomes dead code, and is removed.
2026-08-24 07:24:06 +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 ea2880170a test(package/codegen): add benchmark with sourcemaps (#25772)
Add benchmarks for source maps in `oxc-codegen`.

2 benchmarks:

1. Full end-to-end.
2. Without the actual sourcemap generation at the end.

The 2nd is to measure the overhead that pushing to the sourcemap arrays adds while printing in isolation.
2026-08-18 16:17:46 +00:00
camc314 5703d7a44f docs(codegen): correct stale printer comments (#25561)
## Summary

- Correct the three stale codegen comments identified in review.
2026-08-12 17:16:55 +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