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.
## Summary
Introduces `oxc_diagnostics::Diagnostics` — a `Vec<OxcDiagnostic>` newtype that knows the difference between errors and warnings — and makes it the standard diagnostics return value across oxc.
- **New type**: `Diagnostics` with `has_errors()`, `has_warnings()`, `errors()`, `warnings()`, plus `Vec`-like ergonomics (Deref, `IntoIterator`, `Extend`, `From`/`Into<Vec>`) so most call sites are unchanged.
- **Return types**: `ParserReturn`, `SemanticBuilderReturn`, `IsolatedDeclarationsReturn`, the React Compiler `TransformResult`/`LintResult`, and `TransformerReturn` now expose `diagnostics: Diagnostics` instead of `errors: Vec<OxcDiagnostic>` (the React Compiler's separate `errors`/`warnings` are merged into one severity-tagged list).
- **Behaviour**: a React Compiler **error** now stops the transform (no output), while **warnings** flow through and the transform continues. `compile()` aborts codegen only when `diagnostics.has_errors()` — warnings alone no longer abort (previously any diagnostic did).
## Breaking change
Consumers reading `*.errors` on these return values must read `*.diagnostics`. React Compiler consumers that read `.errors`/`.warnings` separately should use `diagnostics.has_errors()` / `diagnostics.errors()` / `diagnostics.warnings()`.
Parser/semantic/isolated-declarations fatality is otherwise unchanged. Builds on the React Compiler transform feature (#23201).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- `oxc_codegen` produced off-by-one V8 stack-trace and coverage columns for call-chain shapes. When a `(`/`.`/`[` is chained directly onto a call — or any expression ending in `)`/`]` — that punctuation was left unmapped, so the lookup fell back to the closing delimiter one column to the left. Surfaced as vitest failures in vite-ecosystem-ci: `test.each([1, 2])(…)` reporting column 17 instead of 18, and `await expect(…).resolves.toBe(4)` reporting 40 instead of 41.
- Root cause: such operands end in `)` or `]`, so there is no trailing identifier token for a source-map consumer to anchor on. The fix maps that punctuation from `print_expr` — keyed on postfix precedence — back to the operand's exclusive end, exactly where V8 reports the frame. This is the trailing end-mapping that Babel and TSC apply to every node, scoped here to just the no-anchor sites. The closing `)`/`}`/`]` keep their existing at-delimiter mapping, which `v8-to-istanbul` relies on for coverage range boundaries (#22001).
- Only these no-anchor chain sites are mapped — not the byte past every delimiter — so the added source-map tokens stay at **+0.6%** over baseline. (A blanket variant that mapped every delimiter regressed codegen ~5% on CodSpeed.)
- Verified end-to-end through `node --enable-source-maps`: each chained frame resolves to the correct column (the outer `(` for curried calls, the `.` for a getter/member on a call result), and regresses by exactly one column when the mapping is disabled. Covered by the `stacktrace_is_correct` integration test and unit tests in `sourcemap.rs`.
## Example
`factory()()` — V8 reports the outer call at the `(` after the inner `)`:
```
without this fix: at <anonymous> (input.js:7:9) ← the inner `)`
with this fix: at <anonymous> (input.js:7:10) ← the outer `(` ✓
```
The same one-column drift affected `expect(x).resolves` (the `.` getter access after the `)`).
## Compared with esbuild and tsc
Same input through each tool (identical generated code), visualized with [source-map-viewer](https://source-map-viewer.void.app):
```js
const outer = factory()(); // outer `(` of factory()()
const member = expect(x).resolves; // `.` of expect(x).resolves
const indexed = arr[i](); // `(` of arr[i]()
```
| Source token | oxc (this PR) | tsc | esbuild |
| --- | :--: | :--: | :--: |
| outer `(` of `factory()()` | ✓ | ✓ | ✗ |
| `.` of `expect(x).resolves` | ✓ | ✓ | ✗ |
| `(` of `arr[i]()` | ✓ | ✓ | ✗ |
- **vs esbuild** ([compare](https://source-map-viewer.void.app/compare?a=QCnJWx7H&b=Y4FwbiMW)): oxc adds the three chain mappings esbuild lacks — esbuild maps the `]` one column left of the `(`, the exact off-by-one. This PR resolves what esbuild still gets wrong.
- **vs tsc** ([compare](https://source-map-viewer.void.app/compare?a=UjsQoNTX&b=Y4FwbiMW)): all three positions match tsc exactly. tsc maps ~10 more positions because it gives every node a trailing end-mapping; this PR maps only the no-anchor sites (+0.6% vs tsc's full density).
---
This PR was prepared with AI assistance; I reviewed, tested, and verified the changes.
Independent of #22199 / #22200. Fixes a separate sourcemap-indent issue.
Five `gen.rs` impls for top-level declarations were calling `add_source_mapping(self.span)` *before* `print_indent()`:
- `Directive`
- `ImportDeclaration`
- `ExportNamedDeclaration`
- `ExportAllDeclaration`
- `ExportDefaultDeclaration`
The mapping then anchored at the line's indent column (col 0) rather than at the actual keyword column. Sourcemap consumers querying the keyword's gen position would land on the leading whitespace.
Every other indented statement in the codebase (`ExpressionStatement`, `IfStatement`, `VariableDeclaration`, etc.) uses the inverse order — `print_indent` first, then `add_source_mapping`. This PR brings the five outliers in line.
## Visual comparison
The bug surfaces when codegen is invoked with non-zero `initial_indent` (a common pattern for bundler callers that nest output in a wrapper). [Before vs After this PR](https://source-map-viewer.void.app/compare?a=5KX8dYtn&b=ZFhNIEAv) on a fixture with all five declaration kinds:
```js
"use strict";
import { x } from "x";
export { y } from "y";
export * from "z";
export default 42;
```
Each declaration's source position now maps to gen col 1 (the keyword) instead of gen col 0 (the leading tab).
## Test plan
- [x] New test `top_level_decl_mappings_start_after_generated_indent` — wraps a `"use strict"`, `import`, three `export` forms inside an indented `if (true) { ... }` block, asserts each maps from `src col 0` to `gen col 1` (after the tab), not `gen col 0`.
- [x] Verified the test fails without the fix.
- [x] All 99 codegen tests pass.
Stacked on #22199.
Extends the same fix shape from #22199 (move `add_source_mapping_end` before the closing `print_ascii_byte`) to the remaining close-delimiter sites: `print_block_end`, `print_curly_braces`, and the `]`/`}` of `ArrayExpression` / `ObjectExpression`.
## Why
#22199 fixed the call's `)`. The same root cause exists for `}` of blocks/objects and `]` of arrays — the end mapping was placed AFTER the close char, putting it at the position of whatever follows. The most visible case: `() => { ... };` — the source `}` mapped to the generated `;` rather than the generated `}`, so source-map consumers viewing this diff saw the `}` "point at the semicolon."
## How
Five sites total, four in this PR (one already in #22199):
| Site | File |
|---|---|
| `print_block_end` (the `}` of `BlockStatement`) | `lib.rs` |
| `print_curly_braces` (shared by class body, switch, TS enum/interface/typeliteral/module) | `lib.rs` |
| `ArrayExpression` (`]`) | `gen.rs` |
| `ObjectExpression` (`}`) | `gen.rs` |
Each: swap `print_ascii_byte; add_source_mapping_end` → `add_source_mapping_end; print_ascii_byte`. The mapping now lands at the gen position OF the close char.
## Visual comparisons
Fixture covering all 5 sites (classes, arrow bodies, arrays, objects, chained calls):
- **Pre-PR vs Full fix** (full picture): https://source-map-viewer.void.app/compare?a=j9GHxMTX&b=UcCYKDih
- **PR-only vs Full fix** (just the changes #22199 + #22200 add): https://source-map-viewer.void.app/compare?a=ngs4XAsh&b=UcCYKDih
- **esbuild vs Full fix** (alignment with esbuild): https://source-map-viewer.void.app/compare?a=26JVF9DI&b=UcCYKDih
## Reference
esbuild (`internal/js_printer/js_printer.go`):
```go
if e.CloseBraceLoc.Start > expr.Loc.Start {
p.addSourceMapping(e.CloseBraceLoc)
}
p.print("}")
```
(Same pattern as the call's `)` handling — see #22199.)
## Test plan
Each of the 4 modified sites has direct test coverage:
- [x] `block_end_mapping_lands_at_close_brace` — pins `}` of `() => { return 1 }` (exercises `print_block_end`)
- [x] `class_body_close_brace_lands_at_close_brace` — pins `}` of `class C { a; }` (exercises `print_curly_braces` — shared by classes, switch, TS enum/interface/typeliteral/module)
- [x] `array_close_bracket_lands_at_close_bracket` — pins `]` of `const a = [1, 2]` (exercises `ArrayExpression`)
- [x] `object_close_brace_lands_at_close_brace` — pins `}` of `const o = { a: 1 }` (exercises `ObjectExpression`)
- [x] `synthesized_block_closing_braces_are_mapped` (added by #22001) updated to assert `dst (2, 0)` instead of `(2, 1)` — same intent (the synthesized `}` resolves back to the wrapped if's last char), now anchored at the `}` itself
- [x] `cargo test -p oxc_codegen` passes (11 sourcemap tests, 100 integration total)
- [x] `stacktrace_is_correct` snapshot unchanged
Each new test was verified to fail without the corresponding swap and pass with it.
Follow-up on #22001.
Move `add_source_mapping_end(span)` to before `print_ascii_byte(b')')` in `print_arguments` so the call's end mapping lands at the generated position **of** `)` rather than one past it. Each generated paren now has at most one mapping at its exact position, matching the convention used by esbuild and Babel.
A second follow-up (#22200) extends the same fix to block-end `}`, curly-braces `}`, and the `]`/`}` of `ArrayExpression` / `ObjectExpression`.
## Why
After #22001, `add_source_mapping_end` correctly points the source side at `span.end - 1` (the closing source char), but the generated position was still placed AFTER `)` was printed. For chained calls like `factory()()`, this collided with the next AST node's start position:
- inner call's end mapping at `gen 7:9 → src 7:8` (just past inner `)`)
- the outer call's `(` lives at `gen 7:9` in the generated code
Result: a V8 stack-trace lookup at `gen 7:9` (the outer `(`) returned `src 7:8` (the inner `)`) — one column off.
## How
Emit the end mapping at the gen position **of** `)`, not after it. Now:
- `gen 7:8 → src 7:8` (inner `)`)
- `gen 7:9` has no direct mapping → falls back to inner end → `src 7:8`
- `gen 7:10 → src 7:10` (outer `)`)
The chained-call shadow at the outer `(` becomes a plain "no mapping here, fall back to inner `)`" — same accepted behavior as esbuild, Babel, and TypeScript. None of them special-case chained calls.
## Visual comparisons
Generated against a fixture covering classes, arrow bodies, arrays, objects, and chained calls in the [source-map viewer](https://source-map-viewer.void.app):
- **Pre-PR vs Full fix** (full picture): https://source-map-viewer.void.app/compare?a=j9GHxMTX&b=UcCYKDih
- **PR-only vs Full fix** (just the changes #22199 + #22200 add): https://source-map-viewer.void.app/compare?a=ngs4XAsh&b=UcCYKDih
- **esbuild vs Full fix** (alignment with esbuild): https://source-map-viewer.void.app/compare?a=26JVF9DI&b=UcCYKDih
## Reference
esbuild (`internal/js_printer/js_printer.go`):
```go
if e.CloseParenLoc.Start > expr.Loc.Start {
p.addSourceMapping(e.CloseParenLoc)
}
p.print(")")
```
Babel (`packages/babel-generator/src/printer.ts::rightParens`):
```ts
this.sourceWithOffset("end", node.loc, -1);
this.token(")");
```
## Test plan
- [x] New test `call_end_mapping_lands_at_close_paren` — pins both `)` mappings of `factory()()` to their close-paren positions
- [x] `cargo test -p oxc_codegen` passes (99 tests)
- [x] `stacktrace_is_correct` snapshot unchanged
This fixes incorrect sourcemap end mappings for closing delimiters
emitted by `oxc_codegen`.
This came up while investigating [Remix 3](https://remix.run/) test
coverage reporting for code transformed with Oxc via the Remix `assets`
package, which we were using in our `test` package to run in-browser
tests. V8 reported uncovered functions and branches correctly, but
`v8-to-istanbul` could not always resolve the end of those covered
ranges back through Oxc's sourcemap, which caused coverage to be
over-reported. This meant that we saw cases of 100% coverage being
incorrectly reported because `v8-to-istanbul` dropped the whole function
block when it could not map the closing `}` line of an uncalled
function.
`add_source_mapping_end` used `Span::end` directly even though spans are
end-exclusive, so the generated token could land one character past the
real source span. Block printers also did not emit end mappings for
closing `}` consistently. Consumers that resolve V8 coverage range ends
through the sourcemap can then miss those block boundaries and
over-report coverage.
This PR maps end tokens to `span.end - 1` and emits them for block
closers, then extends the sourcemap tests with:
- a runtime stacktrace regression for normal function and arrow block
bodies
- a direct regression for the synthesized wrapper-block path used to
preserve `else` binding
The snapshot updates in `stacktrace_is_correct` are expected for the
same reason. Those column numbers come from Node consuming the generated
sourcemap at runtime. For example, one frame changes from
`/project/input.js:10:12` to `/project/input.js:10:11` for the source
line `obj.fn({a})()`. In 1-based columns, that line is `o1 b2 j3 .4 f5
n6 (7 {8 a9 }10 )11 (12 )13`. The error happens in the first call
expression, so the end position should resolve to column 11, the first
`)`, which is the closing character of `obj.fn({a})`. Before this change
it resolved to column 12, the next `(`, because the end mapping pointed
one character too far to the right. After switching end mappings to the
final real source character and adding the missing block-closer
mappings, that frame moves left by one column to the correct source
position.
Please note, I created this PR with AI assistance, but I worked closely
with the agent to design, review, and iterate on the changes until it
was at a level I was happy with.
## Summary
move statement sourcemap anchors to the first emitted token after generated indentation
## Testing
- cargo test -p oxc_codegen indented_statement_mappings_start_after_generated_indent -- --nocapture
- cargo test -p oxc_codegen class_member_mappings_start_before_member_keys -- --nocapture
- cargo test -p oxc_transformer --test integrations -- --nocapture
## AI Usage
- Codex was used to help prepare this change.
This PR tries to fix the following test failure.
https://github.com/oxc-project/oxc/actions/runs/17313194902/job/49151067079#step:5:325
This failure is probably happening because node binary is missing in the docker image that the test is running. While we can add the node binary in the docker container, I think we can skip the test as I guess there aren't any endian-specific code.