Commit Graph

30 Commits

Author SHA1 Message Date
Dunqing 8ab883afaa fix(codegen): preserve property key annotations (#25766)
`@__KEY__` and `#__KEY__` are currently treated as normal comments. Codegen can drop them before a later property-mangling pass. The property may then be renamed while the annotated string keeps its old name.

This classifies the marker as a property-key annotation. Codegen keeps it next to string and no-substitution template literals when annotation comments are enabled, and the property mangler reads that classification instead of scanning source text. Final output still follows the normal comment options, so whitespace-minified or annotation-disabled output removes the marker.

## Example

Input:

```js
const object = { _field: 1 };
const key = /* #__KEY__ */ "_field";
object[key];
```

Before, an intermediate codegen pass could remove the marker:

```js
const object = { _field: 1 };
const key = "_field";
object[key];
```

After, the marker survives for the later property-mangling pass.

Verified with the affected crate suites, `just minsize`, workspace lint, docs, AST generation, and independent review. `just ready` is only blocked locally by existing Node-version and terminal-color snapshot differences.

Implemented with AI assistance (OpenAI Codex). I reviewed and understand the changes and remain responsible for this contribution under the repository AI usage policy.
2026-08-17 09:23:04 +00:00
overlookmotel bbd6ed38df fix(codegen): wrap string literal in parens after directives (#25435)
When printing statements (`Codegen::print_directives_and_statements`), if first statement is an `ExpressionStatement` containing just a `StringLiteral`, it has to be parenthesized to avoid it becoming a directive.

Fix 4 different bugs in this logic.

## 1\. Only protected when there were no directives at all

The check was gated on `directives.is_empty()`, but the statement at risk is the first non-directive one, whatever its index. The second statement below printed bare, and re-parsing turned it into a directive - switching the program from sloppy to strict mode.

```js
// Input
"use server";
("use strict");

// Output, before
"use server";
"use strict"; // Now a directive - program is in strict mode

// Output, after this PR
"use server";
("use strict");
```

## 2\. No indent on the protected path

The path printed the statement itself instead of going through `ExpressionStatement`'s printer, and never printed the indent.

```js
// Output, before
function f() {
("x");
}

// Output, after this PR
function f() {
  ("x");
}
```

## 3\. Leading comments dropped on the protected path

Same cause - the statement's own printer is what prints its comments.

```js
// Input
// keep me
("use strict");

// Output, before
("use strict");

// Output, after this PR
// keep me
("use strict");
```

## 4\. Unprotected in minify mode

Protection was skipped entirely when minifying. A string usually minifies to a template literal, which cannot be a directive - but only where that is the shortest form. One backtick or `${` makes quotes shorter, and then it's printed as a directive.

```js
// Input
("`");

// Output, before
"`"; // Now a directive

// Output, after this PR
`\``;
```

Minified output is now always a template literal:

```js
("`");   // -> `\``;
("${}"); // -> `\${}`;
```

A template is never a directive, costs no more than `("...")`, and survives a round trip.

In the common case (no backticks or `${` in the string), printing as a template literal (`` `use strict`; ``) is more compact than a parenthesized quoted string (` ("use strict"); `).

Note: this visibly changes minified output for this construct - a template literal where there was a parenthesized string. Behavior is unchanged.
2026-08-09 13:26:29 +00:00
Dunqing 0126aba1d9 fix(codegen): preserve orphaned file coverage comments (#24815)
File-level V8 and Istanbul coverage directives attach to the first AST statement. When TypeScript import removal or another upstream pass deletes that statement, codegen never visits the comment attachment position and silently drops the directive. Coverage tooling then instruments a file that was explicitly excluded.

The lexer now classifies `v8 ignore file` and `istanbul ignore file` separately, including line-comment forms and explanatory suffixes. Codegen lets those directives share the existing legal-comment orphan flush, preserving source order without scanning source text during printing. Position-sensitive directives such as `ignore next`, `start`, and `stop` remain unchanged.

The recovery path is named and documented around orphaned attachments. Hashbang ordering remains intact, and disabling annotation comments still suppresses these directives. The transformer regression stays in the Rust integration harness because transform conformance deliberately clears comments before codegen.

## Example

Input:

```ts
/* v8 ignore file */
import type { Foo } from "./types";

export default { typed: "stuff" } satisfies Foo;
```

Before:

```js
export default { typed: "stuff" };
```

After:

```js
/* v8 ignore file */
export default { typed: "stuff" };
```

Covered by direct codegen regressions for removed anchors, hashbang ordering, disabled annotation comments, and position-sensitive negative cases, plus a TypeScript transformer integration test. Verified with `env -u NO_COLOR just ready`, including all-feature tests, linting, documentation, generated-code checks, formatting, and a clean-tree check.

closes #23667

This PR was assisted by OpenAI Codex.
2026-07-24 15:26:48 +00:00
Dunqing 70994ae544 fix(codegen): preserve comments before expression operands (#24510)
Coverage directives attached to expression operands were silently dropped by codegen in logical right-hand sides, conditional consequents, object property values, and template literal interpolations. The AST expressions survive, but those emitters never consume their leading comment groups.

Print the comment group at each surviving expression boundary, probing parenthesized layers (`a || /* c */ (x)` anchors the comment at the `(`, `a || (/* c */ x)` at `x`). Parenthesized function and arrow IIFEs keep comments inside their generated wrapper.

Logical right-hand sides and conditional consequents only print groups containing annotations. This is codegen policy for mutated ASTs: comments are anchored by source position, and mutating consumers (minifier statement merging, rolldown finalization) move statements into exactly these positions — a dissolved statement's leading normal-comment group can coincide with the moved operand's span start, and printing it there misplaces statement-level trivia and breaks minify-twice idempotency (test262 `language/asi/S7.9_A5.8_T1.js` and friends). Annotations are the one comment kind with expression-level meaning, so they pass through.

Printing comments at expression boundaries also exposed three latent spacing bugs in the shared comment printer, surfaced by monitor-oxc over real node_modules: a full indent was injected before mid-line groups (doubling indentation on every pass, invisible at indent 0), a group ending in a newline left the next operand at column 0, and minify-with-comments mode emitted a glue space or not depending on which printer consumed the group. All three are fixed with regression tests.

## Example

Input:

```js
const value = a ?? /* istanbul ignore next */ [];
```

Before:

```js
const value = a ?? [];
```

After:

```js
const value = a ?? /* istanbul ignore next */ [];
```

Verified with `cargo test -p oxc_codegen` (134 tests), full `cargo coverage` (minifier at baseline, zero snapshot drift), `--twice` idempotency over webpack/typescript/eslint-utils/storybook/sigstore files in plain, dce, and minify-with-comments modes, and monitor-oxc green on codegen/dce/whitespace/transformer (remaining failures are main's pre-existing baseline).

closes #21301
closes #22049
closes #24324

This PR was assisted by OpenAI Codex and Claude.
2026-07-15 16:01:29 +00:00
Dunqing 837a395fba fix(parser): treat a line comment after ':' as leading, not trailing (#23515)
## Summary

A line comment immediately after a `:` on the same line was treated as a *trailing* comment in `should_be_treated_as_trailing_comment`, which anchors it to position `0` and drops it during codegen. The `=` and `(` tokens were already exempted from this rule for the same reason; `:` was not.

This surfaced as a transformer **idempotency** failure (caught by [monitor-oxc](https://github.com/oxc-project/monitor-oxc/actions/runs/27659196490/job/81800055426)): lowering optional chaining emits `? void 0 : // comment\n_obj.method()`, so the leading comment of the original expression lands right after the `:`. Codegen kept it on the first pass but dropped it on re-parse, so `transform(transform(x)) != transform(x)`.

Treating such a comment as **leading** anchors it to the following operand — preserving it and restoring idempotency. Comments after `:` in switch cases and labeled statements are likewise no longer dropped. (Object-property values and type-annotation colons are unaffected: still printed where they already were, and verified idempotent.)

## Example

Input:

```js
x = cond ? a : // c
b;
```

Before this PR the comment was dropped (`x = cond ? a : b;`); now it is preserved.

## Tests

- `leading_comments_after_colon` (`oxc_parser`) — the comment anchors to the following token.
- `test_line_comment_after_conditional_colon` (`oxc_codegen`) — exact output + idempotency, including the lowered optional-chaining shape.
- Verified idempotent across every `:` context (ternary, object value, switch case, label, TS type annotation); no committed transform-conformance fixture contains the affected pattern, so no snapshots change.
2026-06-17 07:10:45 +00:00
Boshen 187e1a52c1 fix(codegen): don't leak space after comment-only JSX expression container (#23006)
## What

A comment-only JSX expression container such as `{/* Hello */}` prints its comment via `print_comments`, which sets `print_next_indent_as_space = true` (the mechanism that turns the next indent into a single space for inline comments like `/* c */ code`).

But the container's closing `}` is printed directly with `print_ascii_byte`, not through `print_indent`, so the flag is never consumed there. It leaks until the **next statement's** `print_indent`, producing a stray leading space.

```jsx
x = <div>{/* Hello */}</div>;
y = 1;
```

Before:
```jsx
x = <div>{/* Hello */}</div>;
 y = 1;
```

After:
```jsx
x = <div>{/* Hello */}</div>;
y = 1;
```

## Fix

Clear `print_next_indent_as_space` after printing the empty expression's comments — the comment is the entire content of `{...}` and is always immediately followed by `}`, so the flag should never carry past it. This is the same class of leak already handled for orphaned legal comments.
2026-06-06 08:07:59 +00:00
Dunqing 2f7b21051b fix(codegen): emit pife-arrow/function leading comments inside the wrap (#22720)
close: https://github.com/oxc-project/oxc/pull/22710

## Summary

When a `pife`-flagged arrow or function expression is the alternate of a `ConditionalExpression`, codegen wraps it in parens but was emitting any leading comment **before** the wrap — moving the comment from inside the parens (where the user wrote it) to outside on every codegen pass.

```js
// Input
export const x = foo ? bar : (
  // explanatory comment   ← INSIDE the parens
  (a, b) => a + b
);

// Before fix (single codegen pass) — comment moved OUTSIDE the parens
export const x = foo ? bar :
// explanatory comment
((a, b) => a + b);
```

The mis-positioning then breaks idempotency: re-parsing the output anchors the comment at the outer `(` (different from the original parse's inner-arrow anchor), and the next codegen's exact-match `get_comments(alternate.span.start)` lookup misses, silently dropping the comment on the second pass.

## Fix

`ConditionalExpression::gen_expr` defers leading-comment printing when the alternate is a `pife` arrow/function — those nodes know they will emit a wrap and now print their own leading comment from inside the wrap, before the keyword/params:

```rust
p.wrap(precedence >= Precedence::Assign || self.pife, |p| {
    if self.pife
        && let Some(comments) = p.get_comments(self.span.start)
    {
        p.print_comments(&comments);
        p.consume_pending_indent_space();
    }
    // ... rest of arrow/function printing
});
```

The same handling is added to `Function::gen` for the function-expression `pife` case. Both non-`pife` and statement-start wraps are unchanged.

After the fix:
```js
// Output (single pass, and stable on every re-pass)
export const x = foo ? bar : (
// explanatory comment   ← still inside the parens
(a, b) => a + b);
```

## Why this rather than a comment-lookup workaround

A range-tolerant comment lookup (or comment re-anchoring on paren-strip) can make pass 2 *find* the dropped comment again — but the first pass would still emit the comment in the wrong source position. This fix addresses the actual first-pass codegen bug, so the comment stays where the user wrote it.

## Impact

End-to-end via monitor-oxc DCE runner on the 116,429-file npm-top-3000 corpus: **9 → 1 idempotency failures**. The remaining 1 is an unrelated unused-variable fixed-point bug (`var __webpack_exports__ = {};` in `@jest/globals/build/index.js`).

- `cargo test -p oxc_codegen` → 107 passed (was 105; +2 new tests)
- `cargo test -p oxc_minifier` → 504 passed, 0 failed
- `just minsize` → snapshots unchanged
- `just allocs` → snapshots unchanged

Tests:
- `comments::test_comment_inside_pife_arrow_alternate_of_conditional`
- `comments::test_comment_inside_pife_function_alternate_of_conditional`

## Closes / supersedes

This supersedes the workaround approach in #22710 (which re-anchored comments on paren-strip from inside `Normalize`). The pife-wrap fix is at the actual layer where the wrong decision was made — codegen choosing to emit a comment outside a wrap it's about to add — and preserves source-text fidelity rather than tolerating its loss.
2026-05-25 14:29:53 +00:00
Dunqing 6a8852d910 fix(codegen): emit newline after legal-comment orphan flush (#22304)
`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`.
2026-05-11 08:44:25 +00:00
Dunqing aaabde4574 fix(parser): attach legal comments to following token (#21670)
## 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.
2026-04-29 14:16:53 +00:00
Dunqing 67d40f8b36 fix(parser): set pure comment index after dedup check to handle lookahead/rewind (#21570)
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
2026-04-20 12:26:33 +00:00
bab 00fc13620d fix(codegen): preserve coverage comments before object properties (#21312)
Closes #21302

---------

Co-authored-by: Dunqing <dengqing0821@gmail.com>
2026-04-20 12:51:01 +08:00
Boshen 77abf546de fix(codegen): preserve coverage comments before ConditionalExpression alternate and between SwitchCases (#20718)
## 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)
2026-03-25 00:07:22 +00:00
Boshen 3b96f41211 fix(codegen): print comments in JSX expression containers and spread attributes (#19701)
## Summary

Closes #17266

- Print leading comments attached to expressions inside `JSXExpressionContainer` (e.g. `{/*before*/ x}`, `{/*before*/ "y"}`, `{/*before*/ true}`)
- Print comments between `{` and `...` in `JSXSpreadAttribute` (e.g. `{/*before*/ ...x}`)
- Add `print_comments_in_range` helper for finding comments attached to positions not directly represented in the AST (needed for the `...` token in spread attributes)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-02-25 09:04:15 +00:00
camc314 7422b7ea25 fix(parser/trivia): correctly mark whether a block comment is on a newline (#17754)
CI is green(er) https://github.com/oxc-project/monitor-oxc/actions/runs/20788815777/job/59705840652
2026-01-08 12:34:40 +00:00
camc314 ef7e014c0b fix(parser): preserve @__NO_SIDE_EFFECTS__ annotation with parenthesized expressions (#17711)
Fixes #17670

Two issues caused the annotation to be lost:
1. LexerCheckpoint did not save/restore trivia flags, so lookahead operations would reset them
2. parse_parenthesized_expression did not capture the annotation before bumping `(`

monitor oxc https://github.com/oxc-project/monitor-oxc/actions/runs/20745400998/job/59561563786

CI is not yet green, but the codegen diff looks a lot better
2026-01-07 08:18:00 +00:00
夕舞八弦 67e9f9e671 feat(codegen): keep comments on the export specifiers (#16943)
resolve https://github.com/rolldown/rolldown/issues/6938

Co-authored-by: Boshen <boshenc@gmail.com>
2025-12-16 20:45:43 +08:00
copilot-swe-agent a607cc453e feat(codegen): preserve comments between CatchClause's param and body (#16167)
Fixes oxc-project/oxc#16076
2025-12-05 06:29:52 +00:00
sapphi-red b3d6f45d37 test(all): check parse errors existence (#15865)
Added `assert!(ret.errors.is_empty());` in many places so that invalid inputs are caught properly.
2025-11-19 13:56:55 +00:00
Boshen 490471025e fix(codegen): print legal comments above directives (#14993)
## Summary

Fixes #14953

Legal comments (e.g., `/*! ... */`) that appear before directives like `"use strict"` are now properly preserved in the generated code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2025-10-27 13:14:00 +00:00
Boshen 2c5fa581db feat(codegen): allow attaching comment to the top of the file. (#13048)
closes #12982
2025-08-13 12:18:09 +08:00
Boshen 8b30a5bbd6 feat(codegen)!: introduce CommentOptions (#12114)
Control printing of comments more precisely via more options:

The logic change here is that jsdoc is no longer part of annotation
comments.

```rust
pub struct CommentOptions {
    /// Print normal comments that do not have special meanings.
    ///
    /// At present only statement level comments are printed.
    ///
    /// Default is `true`.
    pub normal: bool,

    /// Print jsdoc comments.
    ///
    /// * jsdoc: `/** jsdoc */`
    ///
    /// Default is `true`.
    pub jsdoc: bool,

    /// Print annotation comments.
    ///
    /// * pure: `/* #__PURE__ */` and `/* #__NO_SIDE_EFFECTS__ */`
    /// * webpack: `/* webpackChunkName */`
    /// * vite: `/* @vite-ignore */`
    /// * coverage: `v8 ignore`, `c8 ignore`, `node:coverage`, `istanbul ignore`
    ///
    /// Default is `true`.
    pub annotation: bool,

    /// Print legal comments.
    ///
    /// * starts with `//!` or `/*!`.
    /// * contains `/* @license */` or `/* @preserve */`
    ///
    /// Default is [LegalComment::Inline].
    pub legal: LegalComment,
}
```

closes #12030
related https://github.com/rolldown/tsdown/issues/357
2025-07-07 08:57:59 +00:00
Boshen 92af75954b fix(codegen): wrap comment and object literal with parens to avoid invalid syntax (#12111)
fixes #12086
2025-07-07 06:10:58 +00:00
Boshen 28fca3c9b7 feat(codegen): preserve comments for lingui /*i18n*/ (#12047)
closes https://github.com/rolldown/rolldown/issues/5118
2025-07-03 06:32:27 +00:00
Yuji Sugiura d565ed79b9 chore(codegen): Fix typo jsodc → jsdoc in codegen tests (#11451) 2025-06-03 13:13:32 +08:00
Boshen 1a4fec09dc fix(codegen)!: a legal comment can also be a jsdoc comment (#11158)
Breaking change: Changed the `CommentAnnotation` to `CommentContent` to
remove ambiguity on what an annotation is.

e.g. `/** @license */`

fixes #11093
2025-05-19 14:02:56 +00:00
Boshen 4a72b58e77 feat(codegen): print comments inside JSXEmptyExpression (#11022)
fixes #11005

```
<div>
  {/* Hello */}
</div>
```

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
2025-05-14 17:11:14 +08:00
Boshen 1673ffb05a feat(codegen): rework printing normal / legal / annotation comments (#10997)
fixes #10948

This PR changes how comment options are interpreted.

`CodegenOptions::comments`, `CodegenOptions::annotation_comments` and
`CodegenOptions::legal_comments` are now independent of each other.

### `CodegenOptions::comments`

Controls printing of normal comments. At present, only some leading
comments are preserved. It does not control legal and annotation
comments.


### `CodegenOptions::annotation_comments`

Controls printing of annotation comments

* jsdoc: `/** jsdoc */`                                                 
* pure: `/* #__PURE__ */` and `/* #__NO_SIDE_EFFECTS__ */`              
* webpack: `/* webpackChunkName */`                                     
* vite: `/* @vite-ignore */`                                            
* coverage: `v8 ignore`, `c8 ignore`, `node:coverage`, `istanbul ignore`

### `CodegenOptions::legal_comments`

Controls printing of legal comments

* starts with `//!` or `/*!`.                   
* contains `/* @license */` or `/* @preserve */`

---

Comment generation is also changed:

codegen first builds a comments map by keeping only the needed comments.
Printing functions will then print out comments in their respective
positions.

---

This PR reveals some incorrect comment interpretation:  

* https://github.com/oxc-project/oxc/issues/10998
2025-05-13 19:24:42 +08:00
Boshen b01cb45578 feat(codegen): a way to keep legal comments after minification (#10689)
fixes https://github.com/rolldown/rolldown/issues/4118
fixes #10626

At present, there is no option to keep legal comments when minification is turn on.

This PR allows Codegen to set legal comments with minfication turned on, with the following behavior:

* Inline: only if the attached code is not removed
* Eof: move all legal comments to the end of file, regardless of where they appear
* Linked and External: saved into `CodegenReturn::legal_comments` for the caller to use, regardless of where they appear

Code gets moved around or removed if minifier is turned on, so some legal comments may disappear from the final output.

The recommended legal comment option is Eof.

---

For Rolldown to keep legal comments, it should set `LegalComment::Inline` for chunk codegen and `LegalComment::Eof` for minification codegen.
2025-05-01 06:31:34 +00:00
Boshen 5ba02b0e49 feat(parser): set pure on typescript wrapped AST nodes (#10520)
Fixes https://github.com/rolldown/rolldown/issues/4166

Previously these inner call expressions were not annotated with `pure`.

```js
const Foo = /* @__PURE__ */ (((() => {})()))
const Foo = /* @__PURE__ */ (() => { })() as unknown as { new (): any };
const Foo = /* @__PURE__ */ (() => {})() satisfies X"
const Foo = /* @__PURE__ */ (() => {})()<X>"
const Foo = /* @__PURE__ */ <Foo>(() => {})()!
const Foo = /* @__PURE__ */ <Foo>(() => {})()! as X satisfies Y
```
2025-04-21 08:09:06 +00:00
Boshen 2c66ac2bcf feat(codegen): preserve code coverage ignore comments (e.g. v8 ignore) (#10338)
fixes #10091

This PR attempts to preserve any comments with a start match of

```
/* v8 ignore */
/* c8 ignore */
/* node:coverage */
/* istanbul ignore */
```

Added a special handle for coverage comment before `catch`:

```
try {
  something();
}
/* istanbul ignore next */
catch(e) {
  // should never happen
}
```
2025-04-09 16:24:20 +00:00