Commit Graph

96 Commits

Author SHA1 Message Date
camc314 32d00c55bc fix(codegen): preserve instantiation expression precedence (#26424)
Preserve parentheses around TypeScript instantiation expressions in postfix contexts. Previously, `(f<T>).x` and `(f<T>)[x]` were emitted as `f<T>.x` and `f<T>[x]`, producing invalid syntax or changing how the expression is parsed.

The printer now respects the surrounding precedence while retaining grouping for lower-precedence operands.
2026-09-07 18:20:11 +00:00
camc314 598618793c fix(packages/codegen): preserve private-in right operand precedence (#26420)
`packages/codegen` drops required parentheses around the right operand of a private-in expression when that operand is another relational expression. This changes which value the private-field check receives and can change observable behavior.

For example, this input throws a `TypeError`:

```js
class C {
  #x;
  test(a, b) {
    return #x in (a instanceof b);
  }
}
new C().test({}, Object);
```

`a instanceof b` evaluates to `true`, but a private-field check requires an object on its right-hand side.

The JavaScript printer previously emitted:

```js
return #x in a instanceof b;
```

Relational operators associate to the left, so that output is parsed as `(#x in a) instanceof b`. The private-field check on `{}` produces `false`, and `false instanceof Object` returns `false` instead of throwing.

The printer now preserves the original grouping:

```js
return #x in (a instanceof b);
```

`printPrivateInExpression` now prints its right operand with `PREC_COMPARE` instead of `PREC_EQUALS`. The expression printers wrap operands whose precedence is equal to or lower than the requested precedence, so the comparison threshold retains parentheses around `<`, `<=`, `>`, `>=`, `instanceof`, and nested private-in expressions. The now-unused `PREC_EQUALS` import is removed.

Equality and lower-precedence operands continue to retain their required grouping. Shift, arithmetic, and exponentiation operands can still omit unnecessary parentheses. The existing `CTX_FORBID_IN` handling remains in place.

This applies to both ordinary and source-map-enabled printer builds and brings the JavaScript implementation in line with the Rust fix in #26411. It addresses grouping of the private-in right operand, separately from the left-operand correction in #26403.
2026-09-07 17:54:59 +00:00
camc314 f8e6c6ccf8 fix(packages/codegen): preserve in restriction through yield arguments (#26421)
`packages/codegen` can turn valid JavaScript into unparseable output when a `yield` expression appears in a classic `for` loop initializer and its argument contains an `in` operator.

For example, this input is valid:

```js
function* g(o) {
  for (yield (1 in o); false;);
}
```

The JavaScript printer previously dropped the required grouping:

```js
function* g(o) {
  for (yield 1 in o; false;);
}
```

An unparenthesized `in` is not allowed at this position in a classic `for` initializer. The same problem affects `yield*`, nested yields, and assignments inside the yield argument.

The printer now preserves the parentheses:

```js
function* g(o) {
  for (yield (1 in o); false;);
}
```

The `for` printer already marks its initializer with `CTX_FORBID_IN`, but `printYieldExpression` discarded that context and printed its argument with `CTX_NONE`. The yield dispatch now passes its incoming context to `printYieldExpression`, which forwards only `CTX_FORBID_IN` to the argument when the yield expression is unwrapped. The argument retains its existing `PREC_YIELD` precedence.

When precedence requires parentheses around the entire yield expression, those parentheses already permit `in` inside. The argument context is cleared in that case, avoiding redundant inner parentheses:

```js
// Input
function* g(o) {
  for ((yield (1 in o)) + 1; false;);
}

// Output
function* g(o) {
  for ((yield 1 in o) + 1; false;);
}
```

Both `yield` and `yield*` use this path. Outside a restricted initializer, ordinary yields continue to omit unnecessary grouping: `yield (1 in o)` prints as `yield 1 in o`.

The correction applies to both ordinary and source-map-enabled builds and brings the JavaScript printer in line with the Rust fix in #26413.
2026-09-07 17:47:59 +00:00
camc314 6e15ad5cfb fix(packages/codegen): print matching quoted import names as identifiers (#26404)
Oxc codegen npm package emits invalid JavaScript when a quoted imported name matches its local binding. The printer compares the decoded names and omits the alias, but leaves the imported name quoted. A quoted import name requires an explicit `as` binding.

For this valid input:

```js
import { "foo" as foo } from "m";
```

The JavaScript printer previously emitted:

```js
import { "foo" } from "m";
```

It now prints the matching local binding as identifier shorthand:

```js
import { foo } from "m";
```

`printImportDeclaration` checks for a string-literal imported name whose decoded value equals the local binding name before printing the imported name. In that case, it prints the local identifier directly, including its source-map mapping. The local binding already supplies a valid identifier, so this requires neither a separate identifier-validity check nor an AST mutation.

Comparing the decoded value also handles escaped and Unicode import names:

```js
// Input
import { "a\u0062" as ab } from "m";
import { "π" as π } from "m";

// Output
import { ab } from "m";
import { π } from "m";
```

Declaration-level and inline TypeScript type imports use the same shorthand:

```ts
// Input
import type { "Foo" as Foo } from "m";
import { type "Bar" as Bar } from "m";

// Output
import type { Foo } from "m";
import { type Bar } from "m";
```

Quoted names that differ from their local bindings retain their quotes and aliases, including `"foo-bar" as foo`, `"" as foo`, and `"default" as foo`. Ordinary identifier imports keep their existing shorthand behavior. The correction applies to both the ordinary and source-map-enabled builds and brings the JavaScript printer in line with the Rust codegen fix in #26386.
2026-09-07 15:28:07 +00:00
camc314 bbbb4bc5c9 fix(packages/codegen): preserve private-in left operand precedence (#26403)
The codegen npm package drops required parentheses when a private-in expression is the left operand of a higher-precedence binary operator. The generated JavaScript can evaluate a different expression and throw a runtime error.

For example, this input returns `2`:

```js
class C {
  #x;
  test(o) {
    return (#x in o) + 1;
  }
}
console.log(new C().test(new C()));
```

The JavaScript printer previously emitted the return expression as:

```js
return #x in o + 1;
```

Because `+` binds more tightly than `in`, that output is parsed as `#x in (o + 1)`. Adding `1` to the object produces a primitive, so the private-field check throws a `TypeError` instead of producing the boolean used by the original addition.

The printer now preserves the grouping:

```js
return (#x in o) + 1;
```

The iterative binary-expression printer already computes the precedence required for each left operand. Its private-in special case discarded that value and passed `PREC_LOWEST` to `printPrivateInExpression`. Passing `v.leftPrecedence` instead lets the existing private-in printer insert the required parentheses.

This preserves grouping for arithmetic, exponentiation, and shift operators. Equal- and lower-precedence operators, such as `<`, `===`, and `&&`, continue to omit unnecessary parentheses. The correction applies to both the ordinary and source-map-enabled JavaScript printer builds and brings this path in line with the Rust codegen fix in #26383.
2026-09-07 15:21:31 +00:00
camc314 a111b5b340 fix(packages/codegen): print accessibility modifiers before abstract (#26402)
`packages/codegen` emits invalid TypeScript for abstract accessors with an explicit accessibility modifier. The JavaScript printer writes `abstract` before `public` or `protected`, but TypeScript requires accessibility to come first and reports TS1029 for the generated declaration.

For example, this valid input:

```ts
abstract class C {
  public abstract accessor x: number;
  protected abstract accessor y: string;
}
```

was printed as:

```ts
abstract class C {
  abstract public accessor x: number;
  abstract protected accessor y: string;
}
```

The printer now preserves the valid modifier order:

```ts
abstract class C {
  public abstract accessor x: number;
  protected abstract accessor y: string;
}
```

`printAccessorProperty` now emits accessibility before `abstract`, matching the neighboring method and property printers. The same ordering also handles declarations such as `protected abstract override accessor x: number` and applies to both the ordinary and source-map-enabled builds.

This brings the JavaScript printer in line with the Rust codegen correction in #26392. The change is confined to accessor modifier ordering; subsequent `static`, `override`, and `accessor` emission and source-map anchoring retain their existing behavior.
2026-09-07 15:13:55 +00:00
oxc-guard 60fa13878c release(crates): oxc v0.149.0 (#26397)
### 💥 BREAKING CHANGES

- 66744f0 parser: [**BREAKING**] Rename `panicked` to `fatal_error` in `ParserReturn` (#26382) (overlookmotel)
- 2c9a947 parser: [**BREAKING**] Reduce `MAX_LEN` to 256 bytes below `u32::MAX` (#26352) (overlookmotel)

### 🚀 Features

- 32bdc5b allocator: Construct `ArenaHashSet` with any `Default` hasher (#26372) (Dunqing)
- a17de58 minifier: Process of return/throw in non last position and fix async * inlining (#26214) (Armano)
- bfb4c57 parser: Distinguish unapplied no-side-effects comments (#26120) (碳苯 Carbon)
- 1d9b9d3 ast: Add `GetNodeId` trait (#26145) (camc314)

### 🐛 Bug Fixes

- b156333 codegen: Order accessibility before abstract on accessors (#26392) (camc314)
- 116c8b8 transformer/class-properties: Keep private methods in class-expression scope (#26308) (Changsu Seong)
- 1400a0f parser: Require a string source after `export ... from` (#26389) (camc314)
- 2da73b7 codegen: Print matching quoted import names as identifiers (#26386) (camc314)
- 0a81d29 codegen: Preserve private-in left operand precedence (#26383) (camc314)
- 9a02337 parser: Correctly round large nondecimal literals (#26379) (camc314)
- 3d95477 transformer: Gate async-only rewrites by owning transform (#26326) (Dunqing)
- f45a9ee transformer: Limit arguments capture to lowered async functions (#26317) (Dunqing)
- 789969f transformer: Gate this capture by owning async transform (#26227) (Dunqing)
- affeb14 minifier: Mangle class private members in Node API (#26118) (碳苯 Carbon)
- 8b35abd semantic: Detect duplicate private class elements (#26361) (camc314)
- 457ed57 semantic: Reject await and yield in rest parameter defaults (#26359) (camc314)
- 4b89c7b semantic: Reject jumps across arrow functions (#26357) (camc314)
- c966aca parser: Do not omit `<` token opening type argument list (#26328) (overlookmotel)
- f07dca5 ast_visit: Add the trimmed prefix length to translation table offsets (#26173) (Bharadwaj Pendyala)
- 3ab9bd1 parser: Do not re-lex template substitution tail after fatal error (#26230) (overlookmotel)
- 07851b9 parser: Fix debug assert failure when lexer error with tokens enabled (#26229) (overlookmotel)
- 00dea7a allocator: Gate `Allocator::data_end_ptr` behind fixed_size feature (#26248) (camc314)
- 853ffab ecmascript: Avoid `charAt` panic on 32-bit (#26244) (camc314)
- 34feccb runtime: Remove stale regenerator exports (#26243) (camc314)

### ⚡ Performance

- 34a242e minifier: Use `Ident` for property mangler (#26334) (sapphi-red)
- 5303f5c minifier: Unify merging of last expression into target sequence (#26264) (Armano)
- 2ca7d29 minifier: Consume nodes in minimize_statements in reverse order (#26258) (Armano)
- d2354b4 mangler: Use `Ident` for variable names to keep (#26333) (sapphi-red)
- 5b3e335 minifier: Use `Ident` instead of `Str` in `KeepVar` (#26332) (sapphi-red)
- 51366fb minifier: Use `IdentHashSet` in `PrivateMemberUsageStack` (#26331) (sapphi-red)
- d19c42a minifier: Merge nested if stmt in place instead of creating dummies (#26351) (Armano)
- 356def6 parser: Shrink annotation comment ranges (#26356) (overlookmotel)
- a5be474 parser: Shave instruction off `parse_jsx_element_name` (#26355) (overlookmotel)
- 9780663 parser: Remove fatal error guard from `parse_jsx_element_name` (#26354) (overlookmotel)
- 766e12f parser: Remove `token` field from `LexerCheckpoint` (#26350) (overlookmotel)

### 📚 Documentation

- d4b4e61 transformer: Document ES2015 target floor (#26378) (Dunqing)
- 67dd054 ast_visit: Correct out-of-date comments on `Utf8ToUtf16` converter (#26285) (overlookmotel)
- a8ed2a7 minifier: Fix stale validation instructions (#26251) (camc314)
2026-09-07 14:29:34 +00:00
camc314 949d7a9302 chore(deps): update vitest to v5 (#26299)
## Summary

- update workspace test dependencies to Vitest 5.0.0
- pin Vitest and related tooling to Vite 8.2.2
- migrate removed sequential and benchmark APIs
- update the pnpm lockfile
2026-09-03 19:05:05 +00:00
oxc-guard 894c8f9cd8 release(crates): oxc v0.148.0 (#26198)
### 💥 BREAKING CHANGES

- a31567a allocator: [**BREAKING**] Make `Allocator::cursor_ptr` and `data_end_ptr` private (#26081) (overlookmotel)

### 🚀 Features

- 784e9fa minifier: Invert `!0` and `!1` in place for boolean context to `1` and `0` (#26050) (Armano)
- 1e902cc minifier: Expand fold leading assignments into the var decl (#26142) (Armano)
- 3ed4f6a minifier: Expand de morgan's optimization to allow move of `!` (#25930) (Armano)
- 5672585 parser: Attach all comments to nodes (#25944) (camc314)

### 🐛 Bug Fixes

- 4adfb4c semantic: Validate chained continue labels (#26157) (camc314)
- b874f48 ecmascript: `Math.round` only exact half ties (#26150) (camc314)
- a625378 minifier: Coerce omitted `indexOf` search argument (#26149) (camc314)
- dc7398b ecmascript: Trim trailing whitespace in string to number (#26148) (camc314)
- 243b685 transformer/object-rest: Lower multiple declarators correctly (#26147) (camc314)
- dc09a3a parser: Avoid panic on escaped string export names (#26146) (camc314)
- e412cf2 linter: Clamp invalid JS plugin locations (#26144) (camc314)
- c3dedc9 semantic: Skip function body bindings in parameters (#26099) (Dunqing)
- e74de61 transform-react: Match Babel diagnostic reporting (#26128) (Boshen)
- d5163d0 parser: Correctly classify unapplied pure annotations (#26084) (camc314)
- 8531b9b linter/plugins: Alter method for obtaining mutable `Program` when sending AST to JS plugins (#26077) (overlookmotel)
- 9b51658 regular_expression: Allow oversized decimal escape for Annex B (#26070) (leaysgur)
- c8de4df regular_expression: Reject oversized backreferences (#26055) (camc314)

### ⚡ Performance

- ac4785a diagnostics: Use fixed ANSI styles (#26130) (Boshen)
- 07a0793 packages/codegen: Flatten output in chunks (#26109) (overlookmotel)
- 487427a packages/codegen: Faster string flattening (#26108) (overlookmotel)
- 7785583 packages/codegen: Ensure indent strings are flattened (#26107) (overlookmotel)
- 0dd4db3 packages/codegen: Store mapping positions in an `Int32Array` (#26085) (overlookmotel)
2026-09-01 08:15:07 +00:00
overlookmotel 07a07933c7 perf(packages/codegen): flatten output in chunks (#26109)
Continuation of #26108.

Previously we built the whole output as a single rope, and then flattened it in one go at the end. This is fine for smaller files, but for larger files it has 2 problems:

1. Each `output += segment` append allocates a 32-byte cons string cell. When printing a large AST, so many of these are allocated that they can fill "new space" and trigger garbage collection. When that happens, all these little cons strings have to be copied into the other half of new space, which is slow. Even worse, if they survive 2 rounds of GC, they get promoted into "old space" where (a) they may get scattered across memory, ruining cache coherence, and (b) they sit there, taking up memory, until the next major GC round.
2. Flattening a rope string which contains any segments which are 2-byte strings (non-Latin1 characters) is way more expensive than flattening a rope which is pure Latin1/ASCII. If a single segment contains Unicode, the entire flatten operation deopts and becomes several times slower.

This PR attacks both these problems by flattening a large output in chunks.

- When output reaches 16 KiB, flatten that as a chunk and store it in an array.
- This leaves all the little cons string cells unreferenced, so garbage collection will discard them all, instead of copying them across to keep them alive.
- If file contains some non-Latin1 characters, that does still heavily impact the performance of flattening the chunk they're in, but only that chunk - other chunks which are pure ASCII still take the fast path.

The 16 KiB limit is a heuristic, chosen after experimentation with different values.

### Benchmarks

The effect of this change is minimal on small files, but dramatic on large ones (negative is faster):

| Fixture | Bytes | Change |
|:---| ---:| ---:|
| `tiny.js` | 26 | \+3.2% |
| `RadixUIAdoptionSection.jsx` | 2,424 | \-0.1% |
| `react.development.js` | 50,496 | \+2.0% |
| `binder.ts` | 126,212 | \-1.9% |
| `lodash.js` | 182,262 | \-32.5% |
| `App.tsx` | 298,130 | \-26.4% |
| `kitchen-sink.tsx` | 662,560 | \-14.2% |
| `antd.js` | 5,100,647 | \-28.4% |

### Total cost of flattening

After this PR, compared to where we were prior to #26106 (positive is slower):

| Fixture | Bytes | Change |
|:---| ---:| ---:|
| `tiny.js` | 26 | \+51.5% |
| `RadixUIAdoptionSection.jsx` | 2,424 | \+134.2% |
| `react.development.js` | 50,496 | \+35.5% |
| `binder.ts` | 126,212 | \+39.0% |
| `lodash.js` | 182,262 | \+45.3% |
| `App.tsx` | 298,130 | \+55.0% |
| `kitchen-sink.tsx` | 662,560 | \+51.3% |
| `antd.js` | 5,100,647 | \+2.2% |

The effect of flattening is still very negative, but (as mentioned in #26106) it was always a cost, just one that we were ignoring. At least we've now managed to mitigate it.

In the case of really large files (`antd.js`), benchmark is pretty much unchanged from before flattening came into the picture. But now our measure _includes_ the large flattening cost which was previously _on top of_ the cost of printing - so the real cost for the user is much reduced from where it was previously.

### Sourcemap builds

Printing with sourcemaps on large files actually gets faster after this change. It was already paying the cost of string flattening, now it pays less. Printing `lodash.js` with sourcemaps is about 15% faster now.
2026-08-26 17:51:49 +00:00
overlookmotel 487427aa20 perf(packages/codegen): faster string flattening (#26108)
As noted in #26106, user code will end up flattening the output of `printSync`, and it's slow.

Since that cost is inevitable, we might as well make flattening the string part of `printSync`s responsibility, and make sure the cost is as low as possible.

V8 has 2 different methods of flattening a string. One of them is optimized for for exactly the shape of string that codegen produces - a rope which has been generated by repeated `output += segment` appends. The other (which would be more likely to be triggered by user code) is about 2x slower for ropes of this shape.

So here we use the faster one, which we trigger with `output.indexOf(" ")`. Except in the weirdest of weird cases, a space will appear early in the output, so `indexOf` won't have to search far. Even if it _does_ have to search the whole string, that's still faster than the slower flatten path.

The change to benchmarks made in #26106 flattened the output string with `output.charCodeAt(0)` which is the slower flatten path. In comparison, using the faster flatten, has a large effect (smaller is faster):

| Fixture | Bytes | Change |
|:---| ---:| ---:|
| `tiny.js` | 26 | \-23.5% |
| `RadixUIAdoptionSection.jsx` | 2,424 | \-41.8% |
| `react.development.js` | 50,496 | \-24.2% |
| `binder.ts` | 126,212 | \-23.4% |
| `lodash.js` | 182,262 | \-17.6% |
| `App.tsx` | 298,130 | \-14.9% |
| `kitchen-sink.tsx` | 662,560 | \-15.6% |
| `antd.js` | 5,100,647 | \-16.8% |
2026-08-26 17:51:48 +00:00
overlookmotel 77855831a6 perf(packages/codegen): ensure indent strings are flattened (#26107)
Codegen pre-prepares strings for writing indentation and caches them.

This PR ensure that these strings are properly flattened. They already were - `indent.charCodeAt(0)` does that. But that's only the case as long as `growIndents` doesn't get tiered up to TurboFan. If it does, TurboFan treats `indent.charCodeAt(0)` as a no-op, and removes it as dead code, which removes the flattening.

`growIndents` is a cold path, so it's unlikely to get tiered up, but make sure that even if it does, we don't lose the flattening by using `indent = [indent, indentString].join("");` instead.

No effect on benchmarks, because TurboFan wasn't kicking in here.
2026-08-26 17:51:48 +00:00
overlookmotel 5e6d537fca test(packages/codegen): include string flattening in benchmarks (#26106)
Over the weekend, I asked Fable to work on optimizing write operations when assembling the output string.

This uncovered a disturbing finding.

### Unrealistic benchmarks

The output string is assembled with repeated `output += segment` calls. This creates "cons strings" - a rope where each append creates a new string which references the previous `output` as its left side, and `segment` as its right side. On each append the rope gets longer - forming a linked list of segments.

That rope-string is what `printSync` returns to the user. It is a string, so it's correct, but there's a problem. In order to actually use that string for anything (write it to a file, index into it, search it, run a regex on it, etc) it has to be flattened. V8 does that transparently, so it works fine, but it's a costly operation.

So the disturbing finding was that we were ignoring that cost - we pushed it onto the user, and didn't measure it in our benchmarks. So, once this hidden cost is included, the codegen isn't as fast as we thought it was.

### Including the flatten cost

This PR alters the benchmarks to include string flattening via the most common path that V8 takes in user code. The effect is pronounced (higher is worse):

| Fixture | Bytes | Change |
|:---| ---:| ---:|
| `tiny.js` | 26 | \+92.4% |
| `RadixUIAdoptionSection.jsx` | 2,424 | \+303.8% |
| `react.development.js` | 50,496 | \+75.2% |
| `binder.ts` | 126,212 | \+85.9% |
| `lodash.js` | 182,262 | \+161.5% |
| `App.tsx` | 298,130 | \+146.8% |
| `kitchen-sink.tsx` | 662,560 | \+116.8% |
| `antd.js` | 5,100,647 | \+72.5% |

`RadixUIAdoptionSection.jsx` suffers in particular (4x slower) because one of the `JSXText` elements contain non-ASCII characters. This means the string has to be flattened to a 2-byte string (UTF-16) instead of the faster 1-byte (Latin1) representation. This encoding is twice as large, and hits V8's slow paths (because its optimized for 1-byte strings).

This doesn't mean that codegen has got slower - we're just now measuring it more realistically.

### Source maps builds

Source map builds are not affected - their perf doesn't change. They already flattened the `output` string by calling `indexOf` on it, so they were already paying the cost, we just didn't realize it.

### What to do?

The good news is that there are some things we can do to mitigate the impact - see following PRs.
2026-08-26 16:37:15 +00:00
overlookmotel 0dd4db384f perf(packages/codegen): store mapping positions in an Int32Array (#26085)
Perf optimization to sourcemap builds.

Use an `Int32Array` to store sourcemap positions, instead of a plain `Array`.

This `Int32Array` is held in a top-level var and reused for every file. It is replaced by a larger one when required, but will soon reach steady state - a size large enough to service all files being printed - and then ceases growing. Once it reaches that steady state, all the "does it need to grow?" branches become perfectly predictable.

`Int32Array` has 3 advantages over plain arrays:

1. 4 bytes per entry, instead of 8 - lower memory usage and less cache misses.
2. Low garbage collection cost - during garbage collection, GC has to loop through every item in an `Array` checking if it contains heap values (e.g. an `Object`) which the array references, and therefore can't be garbage-collected. `Int32Array`s are statically known to only contain small integers, which don't live on the heap, so this scan is skipped entirely.
3. Recycling - due to not having to worry about GC burden of keeping a typed array live across a GC run, a single `Int32Array` can be kept live and reused over and over for each file.

This change produces a decent perf bump:

| Fixture | Maps end-to-end | Recording pass only |
| --- | --- | --- |
| tiny.js | −7.3% | −34.5% |
| radix | −5.4% | −10.5% |
| react | −2.9% | −6.6% |
| App.tsx | −2.4% | −6.2% |
| binder.ts | −3.3% | −5.4% |
| kitchen-sink | −2.9% | −4.9% |
| lodash | −2.1% | −5.8% |
| antd | −5.1% | −5.3% |

### Why `Int32Array` not `Uint32Array`?

V8's native small integer type ("SMI") is a _signed_ 32-bit integer. Generally speaking, it's preferable to store numbers in this same format, as when reading from an `Int32Array`, V8 statically knows that the value is an SMI, and skips conversion logic and "does this number fit in an SMI?" checks. Ditto when writing - no conversion required to write a known SMI into an `Int32Array`.
2026-08-25 13:54:40 +00:00
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
oxc-guard 4e258430cd release(crates): oxc v0.147.0 (#26046)
### 🚀 Features

- b6e9e5e minifier: Fold switch stmt whose parent is not block stmt (#25403) (Armano)

### 🐛 Bug Fixes

- 8a9bdbd estree: Include decorators in `FormalParameterRest ` spans (#26021) (camc314)
- 6f0c7cf minifier: Avoid merging `if` to `for` in sloppy mode when containing function declaration (#25638) (sapphi-red)
- 2cde1f6 rust: Address nightly deprecations (#25998) (Boshen)
- 6c5ad1b codegen: Only add mapping names for `PrivateIdentifier`s which have changed (#25958) (overlookmotel)
- 2dad1e0 parser: Track irregular line terminators in trivia (#25947) (camc314)
- 58ba651 minifier: Avoid merging `if` to `for` when the body contains a function declaration (#25637) (sapphi-red)
- a185ac3 codegen: Print `#` before private identifier in TS signature key (#25938) (overlookmotel)
- af82a07 minifier: Keep unary `+` when the other operand has side effects (#25645) (Kotaro Chikuba)

### ⚡ Performance

- 53f6270 packages/codegen: Eagerly initialize `mapNames` (#25979) (overlookmotel)
- 5b43c60 packages/codegen: Split recording mappings into multiple functions (#25978) (overlookmotel)
- cc947b5 packages/codegen: Reduce property lookups (#25977) (overlookmotel)
- c22a9cc packages/codegen: Combine condition checks (#25976) (overlookmotel)
- 215a6a5 packages/codegen: Add functions for writing/marking unnamed mappings (#25975) (overlookmotel)
- fe444cc codegen: Avoid allocations for single comment anchors (#25598) (camc314)
- a0aee81 minifier: Do not rebuild expression when substituting void to null (#25964) (Armano)
- 1004439 packages/codegen: Merge `mark` and `write` calls in `printImportAttributes` (#25972) (overlookmotel)
- 94c8a34 packages/codegen: Remove `node` param from `printString` and `printNonNegativeFloat` (#25971) (overlookmotel)
- 7e2b4b3 packages/codegen: `printNonNegativeFloat` write mapping directly (#25968) (overlookmotel)
- f09c8ab minifier: Avoid creation of unnecessary conditional expressions (#25931) (Armano)
- 53ff080 packages/codegen: Remove `lastWasPostfixClose` (#25888) (overlookmotel)

### 📚 Documentation

- 07de646 packages/codegen: Fix JSDoc comments on `write*` methods (#25966) (overlookmotel)
- 3b09c04 packages/codegen: Clarify comment (#25959) (overlookmotel)
2026-08-24 11:19:12 +00:00
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 5163b14d72 refactor(packages/codegen): re-order params of markMapAtStartOffset (#25985)
Pure refactor. Just re-order `markMapAtStartOffset`'s params, so `node` is last, matching all the other functions.
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 a78ed30bbf perf(packages/codegen): remove last param from writeWithMapNamed (#25983)
Optimization to sourcemap and no-sourcemap builds.

`writeWithMapNamed` now always receives an `Identifier`, so all call sites pass `last` as `CAT_IDENT`. Remove the param and just write `state.last = CAT_IDENT` directly. This avoids using a register to pass a value which is always the same, and makes setting `last` cheaper too.

Add a new `writeIdent` function which `writeWithMapNamed` is converted to in no-sourcemap builds, which also doesn't have a `last` param.
2026-08-24 09:20:17 +00:00
overlookmotel a6f83e9ec6 refactor(packages/codegen): rename code to name in named write functions (#25982)
Pure refactor. After #25981, `writeWithMapNamed` and `writeWithMapNamedNoLast` only receive `Identifier`s, so rename `code` param to the more specific `name`.
2026-08-24 09:20:17 +00:00
overlookmotel c1bcac3be0 refactor(packages/codegen): add writeWithMapNamedPrivate (#25981)
Continuation of #25980.

Add a specialized function `writeWithMapNamedPrivate` for writing a `PrivateIdentifier`. It feeds `hashLength: 1` to `markMapNamed`, which allows removing polymoprhic `node.type` access from `markMapNamed`.

The code being printed in paths that lead to `markMapNamed` is always the same as `node.name`, so just pass it in to `markMapNamed`, which removes a polymorphic `node.name` read there.

Additionally, `writeWithMapNamedNoLast` now only receives `Identifier` nodes, so becomes monomorphic.
2026-08-24 09:20:16 +00:00
overlookmotel c96a69fff8 refactor(packages/codegen): add writeWithMapNamedJSXNoLast (#25980)
Writing mappings for JSX identifiers requires different logic than for normal `Identifiers`.

- Add specialized function `writeWithMapNamedJSXNoLast` for writing `JSXIdentifier`s.
- Move the logic for getting original name of a `JSXIdentifier` into a separate function.
- Branch on an `isJSXIdentifier` param passed in to `markMapNamed`, to decide which function to use to get original identifier name.

The main motivation is to avoid having to pass `node` into `originalNameFromSource`, where reading `node.type` is a polymorphic access (because `node` could be an `Identifier`, an `PrivateIdentifier` or `JSXIdentifier`). This is part of a broader move to remove the reliance of all `write` and `markMap` functions on receiving polymorphic/megamorphic `node` params (later PRs in this stack).
2026-08-24 09:05:20 +00:00
overlookmotel 53f62701f8 perf(packages/codegen): eagerly initialize mapNames (#25979)
Previously `mapNames` property of state was lazily initialized - it started as `null`, and an array is only created when a named mapping is written.

Often lazy initialization is a good strategy, but in this case IMO it's a poor trade-off - it adds a check for every named mapping that's written in return for saving a single allocation per file, and only in files which produce a source map with no names. Allocating 1 empty array is very cheap.
2026-08-24 08:59:56 +00:00
overlookmotel 5b43c60386 perf(packages/codegen): split recording mappings into multiple functions (#25978)
Optimization to sourcemap builds.

Previously one `recordSourceMapping` function handled all types of mappings, and branched on `location` to decide how to behave.

Instead, break it into separate `markMapStart`, `markMapEnd`, `markMapAfter`, `markMapAtStartOffset`, and `markMapNamed` functions which each select the right point to make the mapping. The main functionality that was in `recordSourceMapping` is moved into 2 helper functions `hasMappableSpan` and `recordMapping`.

This removes branches, and removes the heavy code for named mappings from all the paths which don't produce named mappings. `markMapStart` and the rest become minimal and are more likely to be inlined.
2026-08-24 07:48:21 +00:00
overlookmotel cc947b5a37 perf(packages/codegen): reduce property lookups (#25977)
`state.mapPositions` is accessed multiple times in 2 functions. Extract it to a local var, to avoid repeated property accesses.
2026-08-24 07:31:35 +00:00
overlookmotel c22a9cc260 perf(packages/codegen): combine condition checks (#25976)
Small optimization to sourcemap builds.

- Before: `end < start || start === end`
- After: `end <= start`

The 2 are equivalent.
2026-08-24 07:31:35 +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 10044392d9 perf(packages/codegen): merge mark and write calls in printImportAttributes (#25972)
Small optimization to sourcemap builds.

Merge a `markWithMapAtStartOffset` call with offset 0 with a `write` call immediately following it into a single `writeWithMap` call. This is functionally equivalent.
2026-08-22 13:24:57 +00:00
overlookmotel 94c8a34376 perf(packages/codegen): remove node param from printString and printNonNegativeFloat (#25971)
Small optimization to no-sourcemap builds.

`printString` and `printNonNegativeFloat` receive `node` as their last param, but in no-sourcemap builds, all the code which uses `node` is removed anyway.

Use the mechanism added in #25970 to remove the `node` param from the function declarations, and the corresponding argument from all call sites. This avoids passing data into these functions pointlessly, freeing a register.
2026-08-22 13:24:57 +00:00
overlookmotel 4c650372cd refactor(packages/codegen): specify in TSDown plugin how many args to remove from call sites (#25970)
Alter how functions where are not used in no-sourcemap builds get removed from the build.

Previously, the plugin explicitly replaced calls to these functions with `void 0`. Instead:

1\. Add `if (SOURCEMAPS)` guards to these functions, so minifier sees that they're no-ops and removes the calls.
2\. Remove arguments from their call sites, so nothing gets left over by minifier.

Additionally, remove the params from the function declarations themselves.

The table in plugin specifies how many arguments to remove for each function.

These change does not alter the output code - the same stuff gets removed, just by a different mechanism. But this change enables the next PR #25971.
2026-08-22 10:23:43 +00:00
overlookmotel a0708f3950 refactor(packages/codegen): simplify TSDown plugin (#25969)
The TSDown `unmap_writes` plugin was a bit sloppy. It was over-engineered, doing multiple passes over the AST, printing it and re-parsing it. Condense this all down to a single AST pass.

In addition:

1\. Check the declarations of listed functions to make sure that `arity` accurately matches the actual declarations.
2\. Rename `plain` to `rename` - that better describes what this property is.

This does not alter the output code.
2026-08-22 10:11:53 +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 7f70606ea1 refactor(packages/codegen): remove unnecessary type assertions (#25967)
Pure refactor. Remove some unnecessary type assertions.
2026-08-22 09:24:31 +00:00
overlookmotel 07de64608c docs(packages/codegen): fix JSDoc comments on write* methods (#25966)
Only changes comments.

- Fix some inaccuracies in JSDoc comments on `write*` and `mark*` functions.
- Add missing `@param`s to JSDoc comments.
- Expand some comments.
2026-08-22 09:17:42 +00:00
overlookmotel 3b09c04fb3 docs(packages/codegen): clarify comment (#25959)
Follow-on after #25957. Not sure how this crazily-formatted comment snuck by me. Also clarify the wording - what's this weird obsession with the word "spelling" Mr Claude? Speak English bruv!
2026-08-21 14:08:12 +00:00
overlookmotel 6c5ad1b723 fix(codegen): only add mapping names for PrivateIdentifiers which have changed (#25958)
A `PrivateIdentifier`'s span includes the leading `#`, but the `name` the AST holds does not.

The source map name was read from the whole span and then compared to `name` from AST, so the check which omits the mapping's `name` when it wasn't renamed compared `#foo` against `foo`. It never matched, so a named mapping was created for _every_ private identifier, renamed or not.

This PR fixes that in both Rust `oxc_codegen` and JS `oxc-codegen`, by comparing correctly including the `#`.

This also makes mapping names correct when a normal `Identifier` has been replaced with a `PrivateIdentifier` (`this.foo` -> `this.#foo)`, or vice-versa (`this.#foo` -> `this.foo`). New tests cover both directions.
2026-08-21 14:08:12 +00:00
overlookmotel 4628044a29 refactor(packages/codegen): simplify recording named mappings (#25957)
Pure refactor.

`recordSourceMapping`'s logic for recording named mappings was convoluted, and was hard to follow. Refactor it to clarify what's going on, and add comments explaining it.

In the process, it became apparent there were unnecessary branches and operations:

* `name = printedName;` always set `name` to `undefined`, which it was already.
* `if (name !== undefined) { ... }` block was present in multiple code paths where `name` is always `undefined`.

This PR removes those unnecessary code paths, so may have a small perf benefit, but main motivation is greater clarity.
2026-08-21 13:37:37 +00:00
overlookmotel 516440d467 refactor(packages/codegen): combine imports (#25956)
Pure refactor. Just combine two imports from the same file into one.
2026-08-21 12:56:34 +00:00
overlookmotel a185ac3ff3 fix(codegen): print # before private identifier in TS signature key (#25938)
`TSPropertySignature` and `TSMethodSignature` were printing a `PrivateIdentifier` key as `key.name`, dropping the `#`, so `interface I { #x: number }` was printed as `interface I { x: number }`.

This PR switches `oxc_codegen` to delegating to the `PrivateIdentifier` printer, as every other property key site already does. It also applies the same thing to JS printer `oxc-codegen`.

This is not legal syntax anyway - TS rejects it with TS18016, and Oxc parser with "Private identifier '#x' is not allowed in property names" - so this is only reachable from a recovered parse or a hand-built AST. But, since we have this code, it might as well do what you'd expect it to.
2026-08-20 15:16:07 +00:00
Boshen 1193c9dbaf chore(oxfmt): dogfood operator position at line start (#25937)
dogfood
2026-08-20 14:36:47 +00:00
overlookmotel 53ff08068d perf(packages/codegen): remove lastWasPostfixClose (#25888)
Follow-on after #25585.

Previously `printExpression` checked whether last character written was a `)` or `]` by consulting `lastWasPostfixClose`. Every write kept `lastWasPostfixClose` up to date by reading the last character that was written (`updatePostfixClose`).

It's statically known everywhere that writes a `)` or `]`, so instead add a category `CAT_CLOSE_BRACKET`, and write that as `state.last` using the existing machinery. `printExpression` then does its check with `state.last === CAT_CLOSE_BRACKET`.

This has a large effect on perf because checking the last character that was written in `updatePostfixClose` was expensive and on a hot path.

Benchmark without sourcemap generation:

| Fixture | Bytes | Before | After | Faster by |
|:---| ---:| ---:| ---:| ---:|
| `RadixUIAdoptionSection.jsx` | 2,518 | 0\.01221ms | 0\.00716ms | \+41.4% |
| `react.development.js` | 72,141 | 0\.38326ms | 0\.25377ms | \+33.8% |
| `binder.ts` | 193,077 | 0\.6824ms | 0\.6458ms | \+5.4% |
| `App.tsx` | 415,340 | 1\.8782ms | 1\.7846ms | \+5.0% |
| `lodash.js` | 544,096 | 1\.6802ms | 1\.0902ms | \+35.1% |
| `kitchen-sink.tsx` | 732,222 | 5\.2654ms | 4\.7936ms | \+9.0% |
| `antd.js` | 6,683,633 | 32\.0742ms | 22\.4061ms | \+30.1% |

With sourcemap generation included:

| Fixture | Bytes | Before | After | Faster by |
|:---| ---:| ---:| ---:| ---:|
| `RadixUIAdoptionSection.jsx` | 2,518 | 0\.02126ms | 0\.01616ms | \+24.0% |
| `react.development.js` | 72,141 | 0\.52684ms | 0\.39368ms | \+25.3% |
| `binder.ts` | 193,077 | 1\.0708ms | 1\.0335ms | \+3.5% |
| `App.tsx` | 415,340 | 3\.8221ms | 3\.7476ms | \+2.0% |
| `lodash.js` | 544,096 | 2\.9815ms | 2\.4294ms | \+18.5% |
| `kitchen-sink.tsx` | 732,222 | 9\.8293ms | 9\.5644ms | \+2.7% |
| `antd.js` | 6,683,633 | 54\.3088ms | 44\.3745ms | \+18.3% |

The main reason for the large impact on JS/JSX fixtures is that the JS build gets poisoned by JSX text. Most strings that `updatePostfixClose` sees are interned strings (source literals like `"function "`) but in JSX files it was also seeing dynamic strings (JSX text) which turned it polymorphic. Once it's gone polymorphic, it never goes back, affecting all following JS fixtures (which use the JS build).

Removing the string accesses removes this cost.
2026-08-19 09:20:28 +00:00
camc314 8e2b3bebcc refactor(codegen): remove Buffer dependency from source maps (#25914)
## Summary

- replace Node.js `Buffer` storage with `Uint8Array` in the source-map encoder
- use `TextDecoder` to create the final mappings string
- use `Uint8Array#set` when growing the mappings buffer
- remove the `node:buffer` runtime dependency

This makes source-map generation runtime-neutral and removes a blocker for browser support for the codegen package.

## Performance

Indexed access, copying, and ASCII decoding have comparable performance. `Uint8Array` allocation requires zero-initialization, unlike `Buffer.allocUnsafe`, but the expected end-to-end impact is small.
2026-08-19 08:55:18 +00:00
camc314 cc2fab6fa5 refactor(codegen): use Uint8Array for base64 lookup table (#25913)
## Summary

Replace the `Buffer`-backed base64 character-code table with a `Uint8Array`.

`BASE64_CODES` is only used as a lookup table mapping VLQ digits from `0–63` to ASCII byte values. It does not require any `Buffer`-specific APIs, and `Uint8Array` provides the same indexed byte semantics in both Node.js and browsers.

This reduces unnecessary Node-specific usage and is a small step toward making `oxc-codegen` browser-compatible. The hot encoding loop remains unchanged; only the one-time lookup-table initialization differs.

This does not remove the remaining `Buffer` dependency used for the source-map output buffer.
2026-08-19 08:41:21 +00:00
oxc-guard 0389c4010e release(crates): oxc v0.146.0 (#25912)
### 🚀 Features

- 83cb516 minifier: Improve negation of expressions in boolean context (#25755) (Armano)

### 🐛 Bug Fixes

- 134fb22 isolated-declarations: Preserve unresolved parameter types (#25909) (Dunqing)
- 526c2b3 codegen: Remove deprecated test APIs (#25889) (camc314)
- 344a9b2 codegen: Align enum template sourcemaps (#25872) (camc314)

### ⚡ Performance

- 5ef0c93 minifier: Unwrap all unary exprs without creating dummy expressions (#25891) (Armano)
- 8783524 packages/codegen: Remove loop from `growMappingBuffer` (#25886) (overlookmotel)
- b6b14c0 packages/codegen: Remove redundant branch (#25885) (overlookmotel)
- be6adf0 packages/sourcemap: Make `location` passed to `recordSourceMapping` always a number (#25884) (overlookmotel)
- f720f15 packages/codegen: Take named-mapping only code off hot path in `recordSourceMapping` (#25883) (overlookmotel)
- a734af5 packages/codegen: Remove redundant setting of `last` in `printImportAttributes` and `printIf` (#25882) (overlookmotel)
- c6e44c6 packages/codegen: Combine 2 writes in `printJSXElement` (#25881) (overlookmotel)
- 8801662 packages/codegen: Remove redundant mapping from `printParams` (#25880) (overlookmotel)

### 📚 Documentation

- 5ad330e packages/codegen: Reformat comments (#25876) (overlookmotel)
2026-08-19 08:29:51 +00:00
camc314 b016583d78 refactor(codegen): use magicString to preserve mappings in strip-ts (#25870)
## Summary

Preserve source maps when `strip-ts` removes TypeScript-only printer regions from JS builds.

The plugin now uses Rolldown’s `magicString` transform context instead of a plain string replacement, so removed `/* IF TS */` regions retain correct mappings back to the original printer source.
2026-08-19 08:24:14 +00:00