Closes#22463
## Summary
- Add `try_print_require_call` to force plain string literals (no
backticks) for `require("...")` calls during minification
- Prevents `cjs-module-lexer` from failing to detect CJS reexport
sources
### Problem
When minifying, `StringLiteral::gen` passes `allow_backtick: true`,
which can convert:
```js
__exportStar(require("./decorators"), exports);
```
into:
```js
__exportStar(require(`./decorators`),exports);
```
Node.js uses `cjs-module-lexer` for CJS/ESM interop. This lexer uses a
fast heuristic parser that does **not** recognize template literals as
`require()` arguments, causing named reexports to silently disappear
from the module's export list.
### Fix
Add `try_print_require_call` (following the existing
`try_print_cjs_define_property_call` pattern) that detects
`require("...")` calls via the existing
`CallExpression::common_js_require()` helper and prints the string
argument with `allow_backtick: false`.
## Test plan
- [x] `require("./decorators")` stays quoted when minified
- [x] `require('./foo')` normalizes to double quotes (not backticks)
- [x] `require(foo)` (dynamic) is unaffected
- [x] Non-require calls like `foo("./bar")` still get backtick
optimization
- [x] `cargo test -p oxc_codegen` passes
- [x] Follows existing CJS special-case pattern in codegen
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: zennnnq <ennanzhou@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Dunqing <dengqing0821@gmail.com>
Stacked on #22400. Closes the second `cjs-module-lexer` gap.
After #22400, `Object.defineProperty(exports, "name", ...)` is fine, but `exports["name"] = …` / `module.exports["name"] = …` were still being emitted with backtick keys in minify. The lexer recognises this form too — it just couldn't see them past the template literal.
Valid-identifier keys (`exports["foo"]`) are already collapsed to dot access by the minifier earlier in the pipeline, so only non-identifier keys (`exports["has-dash"]`, `module.exports["__esModule"]`, …) survive to the codegen stage and were affected.
In `AssignmentExpression::gen_expr`, detect when the LHS is a computed member expression on `exports` / `module.exports` whose key is a `StringLiteral`, and print the key via `print_string_literal(key, false)`. Other computed access (`otherObj["x"]`) is unaffected.
### Where each cjs-module-lexer named-export form lands
| Pattern | Status |
|---|---|
| `exports.name = …` | identifier — never affected |
| `module.exports.name = …` | identifier — never affected |
| `module.exports = { "name": … }` | object property keys already use `allow_backtick: false` |
| `Object.defineProperty(exports, "name", …)` | fixed in #22400 |
| `exports["name"] = …` / `module.exports["name"] = …` | **fixed here** |
### Before / after
```js
// input
exports["has-dash"] = a;
module.exports["__esModule"] = true;
otherObj["not-exports"] = d;
```
```js
// before
exports[`has-dash`]=a,module.exports[`__esModule`]=!0,otherObj[`not-exports`]=d;
// after
exports["has-dash"]=a,module.exports.__esModule=!0,otherObj[`not-exports`]=d;
```
(`module.exports.__esModule` collapses to dot via a separate minifier pass; `otherObj["x"]` correctly stays backtick.)
Refs #22342
AI assisted.
if we are only printing a fragment of a ast e.g. a expression, we can't control if `is_jsx` is true.
this means generics can be printed incorrectly as they are missing a `,` in jsx code.
Fix `cjs-module-lexer` failing to detect `Object.defineProperty(exports, "name", ...)` named exports after oxc minify.
`calculate_quote_maybe_backtick` favored backtick when all three quote costs tied at zero, so a plain ASCII property name like `"getInclusionReasons"` was emitted as a backtick template. [`cjs-module-lexer`](https://github.com/nodejs/cjs-module-lexer) (used by Node for CJS/ESM interop) only matches plain string literals there, so the affected exports silently dropped out of its export set — downstream ESM consumers then failed with `Named export 'X' not found`.
In `CallExpression::gen_expr`, detect `Object.defineProperty` / `Reflect.defineProperty` and print the 2nd argument via `print_string_literal(name, false)` (the existing `allow_backtick: false` path). Other arguments — and nested strings inside the descriptor — still benefit from backtick optimization. Minify-only: outside minify, `print_string_literal` uses `self.quote` (never backtick), and `print_arguments` is needed there to preserve argument comments.
### Before / after
```js
// input
Object.defineProperty(exports, "getInclusionReasons", { ... });
```
```js
// before
Object.defineProperty(exports,`getInclusionReasons`,{...});
// after
Object.defineProperty(exports,"getInclusionReasons",{...});
```
Closes#22342
AI assisted.
## Summary
`Codegen::get_identifier_reference_name` and `Codegen::get_binding_identifier_name` are called once per identifier emission (every `IdentifierReference`, `BindingIdentifier`, import/export spec, etc.) but were not marked `#[inline]`. The bodies are short and in the common case (no scoping i.e. no mangling) compile to a couple of loads, so the function-call overhead is a noticeable share of codegen self-time on identifier-heavy inputs.
## Measurements
Wall-time on codegen-only loop (parse once, codegen N times), `--profile release-with-debug`, median of 3 runs:
| file | before | after | delta |
|---|---|---|---|
| antd.js (6.7M) | 14.49 ms/iter | 13.25 ms/iter | **-8.6%** |
| checker.ts (2.8M) | 4.35 ms/iter | 4.12 ms/iter | **-5.3%** |
| binder.ts (189K) | 155 µs/iter | 150 µs/iter | -3.2% |
| pdf.mjs (554K) | 961 µs/iter | 935 µs/iter | -2.7% |
| cal.com.tsx (1.0M) | 1.46 ms/iter | 1.48 ms/iter | noise |
xctrace Time Profiler self-time on `Codegen::get_identifier_reference_name` (antd.js): 0.50% → 0.33%; samples redistribute into `GenExpr::print_expr` (the inlining call site) as expected.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
`print_legal_orphans_before` carried the source-side `followed_by_newline = false` for inline orphans (e.g. `33/*!*/ ||`) over into the flush position. After printing the orphan, `print_comments` set `print_next_indent_as_space = true`, which then turned the closing-`}` indent into a single space — producing `\t/*!*/ }` on pass 1.
Pass 2 saw the comment at `}`-position, picked it up via `FunctionBody`'s `comments_at_end` branch (which calls `clear_pending_indent_space()`), and emitted `\t/*!*/}` without the space. The two passes diverged and [monitor-oxc's codegen idempotency check](https://github.com/oxc-project/monitor-oxc/actions/runs/25616176953/job/75194649734) failed on every cjs-module-lexer / es-module-lexer build.
Force `followed_by_newline = true` on the last orphan before the flush so `print_comments` takes its existing newline-emitting branch. Orphans now land block-level on both passes — matching the pass-2 `comments_at_end` path that already calls `clear_pending_indent_space()`.
The orphan-preservation behavior added in #21575 (issue #19750) is unaffected: `//! some license` whose anchor is DCE'd is still emitted at the next surviving statement.
## Test plan
- [x] `cargo test -p oxc_codegen` (105 tests, includes new `test_inline_legal_comment_in_function_body_is_idempotent`)
- [x] `cargo test -p oxc_minifier --tests` (492 tests, includes #21575's `preserve_legal_comment_when_dce_removes_anchor`)
- [x] Re-ran codegen idempotency on all 4 originally failing files (`cjs-module-lexer@1.4.3 / @2.2.0`, `es-module-lexer@1.7.0 / @2.0.0`) — all `same = true`.
Stacked on #22206. Pure refactor — no behavior change.
The `print_next_indent_as_space` flag was being read inline at **five** sites in `gen.rs` via copy-pasted `if flag { print_hard_space(); flag = false }` blocks. Two named private helpers replace those:
- `consume_pending_indent_space() -> bool` — emit space + clear flag, returns whether anything was emitted
- `clear_pending_indent_space()` — drop the flag without emitting
`print_indent` continues to call `consume_pending_indent_space` internally, preserving the flag-as-space contract at every existing `print_indent` site.
## Test plan
- [x] `cargo test -p oxc_codegen` passes (100 integration tests, no new tests added since the existing `top_level_decl_mappings_start_after_generated_indent` from #22206 already covers the `Directive` indent path)
- [x] `cargo test -p oxc_isolated_declarations` passes (the namespace / `declare module` snapshot fixtures that already exercise indented import/export decls)
- [x] No snapshot updates needed
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.
Closes#19750.
## Problem
Legal comments (`//!`, `/*!`, `@license`, `@preserve`) anchor to AST nodes via `attached_to`. When DCE removes the node, codegen's `Inline` mode silently drops the comment.
## Fix
Codegen-side. Two helpers in `oxc_codegen`:
- `print_legal_orphans_before(end)` — drains pending legal-comment orphans and emits them at the current cursor in source order.
- `has_legal_orphans_before(end)` — used by block emitters to keep an empty body multi-line so it can host the flush.
Wired into every statement-list site (Program / FunctionBody / BlockStatement / StaticBlock / SwitchCase / TSModuleBlock) via a single `print_stmts_with_orphan_flush` helper. Orphans land at the next surviving anchor in the same scope, preserving lexical order.
A sorted side index `legal_comment_keys: Vec<u32>` keeps the per-call cost at `O(log K)` (K = legal-comment count) rather than `O(M)` over the full comments map. This avoids the 19% codegen regression on `react.development.js` an earlier iteration of this PR introduced.
## Behaviour
Statement-level orphans match esbuild's `inline` mode: anchor surviving → original spot; anchor removed with sibling → next sibling in scope; anchor removed with no sibling → enclosing block (or program scope-end as last resort). Order against surviving legal comments preserved. Never silently dropped.
Out of scope: comments anchored to non-statement nodes (array elements, object properties, class members, expression positions). They still survive via the nearest enclosing statement-scope flush but won't always land at their exact original position. Closing those gaps would need an AST-level change (legal comments as first-class statements).
## Tests
`preserve_legal_comment_when_dce_removes_anchor` covers all four legal styles, no-DCE no-op, multi-orphan order, order against surviving legals, all-removed scope-end fallback, BlockStatement / FunctionBody / SwitchCase scopes, and the single-consequent SwitchCase regression flagged by Codex review.
All minifier + codegen tests pass. CodSpeed at baseline.
## Summary
- treat `Legal` and `JsdocLegal` comments as leading comments for the following token instead of allowing them to become trailing comments on the previous token
- apply that rule to both legal block comments and legal line comments after code
- add parser regressions for both cases and a codegen idempotency regression for comment-preserving minify mode
## Why
`monitor-oxc` whitespace found a repeated print/parse instability in comment-preserving whitespace removal. Some bundled files contain legal comments such as `@license`/`@preserve` or `/*! ... */` between two statements. After one print, Oxc could produce a shape where a legal comment appeared after code and before the next token. On the second parse, the trivia builder sometimes classified that legal comment as trailing trivia for the previous token.
That classification is wrong for legal-comment preservation. Oxc only treats legal comments as preservable legal comments when they are leading comments. So the parser should keep legal comments attachable to the following token instead of finalizing them as trailing comments.
This makes repeated parse/print cycles stable in modes that remove whitespace but keep comments, without changing executable JavaScript semantics. Ordinary non-legal comments still use the existing leading/trailing classification.
## esbuild comparison
I checked esbuild's implementation because Oxc's legal-comment behavior is intended to follow the same model. esbuild's lexer records legal comments in `LegalCommentsBeforeToken`, and the parser consumes that list before parsing the next statement by inserting `SComment { IsLegalComment: true }` statements. In other words, esbuild models legal comments as comments before the next token/statement, not as trailing trivia attached to the previous token.
For the concrete repro, esbuild with `minify: true` and `legalComments: "inline"` keeps the legal comments inline and is idempotent across repeated transforms:
```js
foo();/**
* @license MIT
**//*! #__NO_SIDE_EFFECTS__ */function bar(){}
```
The key invariant from esbuild is not the exact newline placement. It is that legal comments remain associated with the following statement/token for preservation. This change moves Oxc's parser classification toward that invariant while leaving ordinary non-legal trailing comments alone.
## Testing
- `cargo test -p oxc_parser trivia_builder -- --nocapture`
- `cargo test -p oxc_codegen comments -- --nocapture`
- `cargo fmt --all --check`
- `git diff --check`
- `monitor-oxc whitespace` filtered to the two previously failing files (`@vue-macros/devtools` and `@asamuzakjp/css-color`): both files were collected and processed with no `RemoveWhitespace` diagnostics. The local runtime phase then failed separately under Node.js v25.8.1 on old package imports.
## 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.
Fix some idempotency tests that failed in https://github.com/oxc-project/monitor-oxc/actions/runs/24652862465/job/72079552802
## Summary
When `/* @__PURE__ */` appears before an object literal (e.g. `export const X = /* @__PURE__ */ { a: 1 }`), the parser's arrow-function lookahead causes the comment to be added to the `comments` vec during speculative parsing. After rewind, the same comment is encountered again — the dedup check skips the push, but `parse_annotation` had already set `pure_comment` to `comments.len()`, which now points to a non-existent index. This causes `mark_pure_comment_not_applied` to silently fail, breaking codegen idempotency.
### Fix
- Move `pure_comment` / `has_no_side_effects_comment` assignment from `parse_annotation` to `add_comment`, **after** the dedup check
- In the dedup branch, set the index to the existing comment (`len - 1`)
- In the normal branch, set the index to where the comment will be pushed (`len`)
- Extract shared logic into `set_annotation_flags` helper
## Test plan
- Added parser test: `/* @__PURE__ */` before object literal is correctly marked `PureNotApplied`
- Added parser test: `/* @__PURE__ */` before call expression remains `Pure` after lookahead
- Added codegen idempotency test: parse→codegen→parse→codegen produces identical output
- `cargo test -p oxc_parser --lib -- trivia_builder` — 9 tests pass
- `cargo test -p oxc_codegen --test integration -- comments` — all pass
This PR contains the following updates:
| Package | Update | Change |
|---|---|---|
| [rust](https://redirect.github.com/rust-lang/rust) | minor | `1.94.1` → `1.95.0` |
---
### Release Notes
<details>
<summary>rust-lang/rust (rust)</summary>
### [`v1.95.0`](https://redirect.github.com/rust-lang/rust/compare/1.94.1...1.95.0)
[Compare Source](https://redirect.github.com/rust-lang/rust/compare/1.94.1...1.95.0)
</details>
---
### Configuration
📅 **Schedule**: (in timezone Asia/Shanghai)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Enabled.
♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/oxc-project/oxc).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMjMuOCIsInVwZGF0ZWRJblZlciI6IjQzLjEyMy44IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
For historical reasons, `oxc_span` crate was re-exporting string types which now live in `oxc_str` crate.
Remove these re-exports, and import these types directly from `oxc_str`.
Main motivation is just that it makes sense, but it should also have a minor positive effect on compile times - `oxc_span` and `oxc_str` crates can now be compiled in parallel.
Pure refactor.
Rename `Atom` to `Str`. The name "Atom" is a leftover from when we used to intern strings (years ago), and is misleading now.
Just call it `Str`. This follows the naming convention we have for arena-allocated versions of other types e.g. `Box`, `Vec`, `HashMap` - which we name the same as the native equivalents but with a lifetime (`Box<'a>` not `ArenaBox<'a>`, so `Str<'a>` not `ArenaStr<'a>`).
Rename all variables that contained "atom", and methods which relate to `Str`s to match the new type name.
Rename `as_atom` methods to `as_arena_str`. The longer name is required here to disambiguate from `as_str` methods which return a `&str`.
Some variables called "atom" still exist in `oxc_regular_expression` crate - that's a different meaning of "atom" - the meaning defined in regexp spec.
The diff of this PR is large but it's 100% renaming. No substantive or unrelated changes whatsoever.
## Summary
Adds `CommentContent::PureNotApplied` variant to flag `#__PURE__` / `@__PURE__` annotations that cannot be applied, aligning with [Rollup's behavior](https://rollupjs.org/configuration-options/#pure) for invalid pure comment positions.
### Changes
- `set_pure_on_call_or_new_expr` now returns `bool` so callers detect when the annotation couldn't be applied
- Track the specific pure comment index (`Option<usize>`) instead of a boolean flag, fixing a bug where the wrong comment could be retagged when multiple pure comments exist (e.g. `/*#__PURE__*/ foo + /*#__PURE__*/ bar()`)
- Mark pure comments as `PureNotApplied` in three contexts:
- **Expression-level**: annotation not before a call/new expression (e.g. `/* #__PURE__ */ a().b`)
- **Statement-level**: annotation before non-expression statements (e.g. `/* #__PURE__ */ function foo() {}`)
- **Variable declarator**: annotation before `=` (e.g. `const foo /* #__PURE__ */ = bar()`)
- Downstream consumers (codegen, minifier, Rolldown) can check for `PureNotApplied` to warn or strip
Closes#20334
Rolldown will add custom logic to emit the comments that are `PureNotApplied` so it does not affect the rest of the compiler pipeline.
## Test plan
- [x] Unit tests for all `PureNotApplied` cases (expression, statement, variable declarator)
- [x] Regression test for correct comment targeting with multiple pure comments
- [x] Codegen integration tests updated
- [x] `cargo test -p oxc_parser -p oxc_codegen` passes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- Print leading coverage ignore comments (e.g. `/* istanbul ignore next */`) before `ConditionalExpression`'s alternate expression
- Print leading coverage ignore comments before `case`/`default` keywords in `SwitchStatement`
Closes#20549🤖 Generated with [Claude Code](https://claude.com/claude-code)
FormalParameterRest was missing decorator printing, so decorators on
rest parameters (e.g. `@foo ...rest`) were silently dropped during
code generation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The codegen was missing the `static` keyword when printing `TSIndexSignature`
nodes, causing `static [key: string]: string` index signatures to lose the
`static` modifier during code generation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>