`ParserReturn` contains a property `panicked`.
The name `panicked` is misleading. The parser did _not_ panic. When `true`, it means that the parser encountered a fatal error and is unable to parse the source, because it contains an unrecoverable syntax error.
Rename this property to `fatal_error`, to better describe what it means.
This is a breaking change, but one that's trivial to fix in downstream code - the property doesn't change its meaning or behavior, just its name.
All the changes in this PR in other crates are just reacting to this change and making everywhere use the term "fatal error" instead of "panicked", so the codebase is consistent.
Historically, only JS formatter has had the `detect_code_removal` feature, which uses CI to check for missing nodes or comments.
While this would be useful in other languages (especially CSS), but setting up the mechanism is difficult. For now, just start with something simple that just guarantees the number of comments.
That said, even with a mechanism in place, it can't detect anything unless the cases are fully covered by fixtures, so this only tells us that the current state is problem-free.
```js
// input (semi: true)
// oxfmt-ignore
const a = x
// before
// oxfmt-ignore
const a = x
// after
// oxfmt-ignore
const a = x;
```
```js
// input (semi: false)
let a = 1;
// oxfmt-ignore
[b].sort();
// before
let a = 1
// oxfmt-ignore
[b].sort(); // <- ASI failure
// after
let a = 1
// oxfmt-ignore
;[b].sort() // <- OK
```
Fixes#26165
- `side_effect` is predefined selector, it cannot be used as custom group name
- respect custom group which uses `side_effect` as side-effect-import correctly
- fix metadata detection bug
Fixes#26202
---
The trigger for this issue is the presence of a suppressed typecast.
```js
// oxfmt-ignore
const cast = /** c */ (x)
```
Because a comment had already been printed in a subsequent typecast check without going through the casting mechanism, it failed to find its own cast comment.
Refine comment position preserving rules, for statements and binary casts. Fixes idempotency issues for several sites.
---
Trailing is the same-line run only.
A newline-preceded comment is never trailing. It defers to the next node's leading pass, staying own-line with its blank lines.
Comments around `as`/`satisfies` keep their source side and line-start side, within the grammar-defined slots (line terminator is not allowed between `as` and before).
- Extract "known divergences" section from `AGENTS.md` to explicit `DIVERGENCES.md` with code example
- Update all anchors
- Add missing pinned fixtures
Fixes#25825, #26026.
---
If the formatter adjusts the wrapping and causes list-like characters to appear at the beginning of a line, those characters will be mistakenly converted into a list in the next formatting step.
Therefore, it is necessary to guard meaningful characters so that they do not appear at the beginning of a line.
Also, fixed an issue where character that should have originally been interpreted as lists were not, aligning with the CommonMark behavior.
Fixes#25911
---
As syntax, these two forms are equivalent:
```ts
@deco export class X {}
export @deco class X {}
```
However, in the former, the decorators lie outside the span of both the `ExportDeclaration` and the `Class`, they are inside no node's span.
The formatter's suppression path prints the `ExportDeclaration`'s span verbatim, so the decorators were silently dropped.
The latter form is unaffected because the decorators fall inside the export node's span.
Fixes comment-placement bugs, idempotency and also inconsistency around unions/intersections, type aliases, conditional types, and assignment-likes.
- Fix invariant violations where comments crossed `=`, the value, or `;`
- removed the relocation logic emulating old Prettier 3.8 behavior
- Normalize a union's leading comments to one canonical form behind the synthesized leading `|`, independent of the source shape
- Comments around a formatter-added `(` keep their source side of the `(`
- Hug conditional-type branch comments behind `?`/`:` with the content one level under the operator, matching conditional expressions
- Extend `= // c` position preservation from type aliases to all of JS
Fixes#25705
---
JS formatter drops `;` (`EmptyStatement`) in a statement list.
However, this raises a question: how are the surrounding comments handled when it is dropped?
This happens because when the comment cursor retrieves that information from the `span`s of the preceding and following nodes, the `EmptyStatement`, which no longer exists, was the one reporting that position.
Found by real-world repo testing for `operatorPosition`.
- Check the nearest nested `await` is enough
- `needs_parens` check is not needed
Now aligned with Prettier's output.
NOTE: For now, behavior is the same as Prettier.
- Default is `end`
- This works on intersection(`&`) type, but has no effect on union(`|`) type
For future refs: https://github.com/oxc-project/oxc/issues/16367
Fixes#25410
Syntactically, this `this` is written inside the function parameters.
However, in the AST, `this_param` and `params` are adjacent fields, and since the inclusion relationship of the spans is broken, exception handling is required.
- dispatcher
- string_embedder
- tailwind_sorter
Group these 3 together as `SessionServices`, make them available to `FormatSession`, and pass them around.
Fixes#21916, As it turned out, it served as a follow-up to #16320.
---
When a `group` inside a `fill` item re-measures (after a preceding hard line reset `measured_group_fits`), the fits walk escapes the item's entry and measures the following separator with the fill's own expanded mode, materializing expanded-only content like the raw JSX whitespace `{" "}` and over-counting the width.
Prettier avoids this because its command stack holds the decided separator command before the item prints.
The fix: `print_fill_item` exposes that mode via `PrinterState::fill_separator_mode`, and the fits walk applies it (single-shot) to the first entry at the printed fill's own level, exactly that separator.
Skipping the re-measure instead (as Biome upstream does unconditionally) was rejected: it was removed in #16320 for diverging from Prettier.
NOTE: As noted in comment, there is a known limitation for now. This PR covers only 1-level nested fill. But in practice, I couldn't find actual code snippet hits this.
## Summary
- add focused formatter coverage for declaration-bearing exports, local
named exports, and source re-exports
- cover comment attachment and prettier-ignore behavior for each export
shape
- include TypeScript type exports and re-export attributes
- covers edge cases to prep for #25095
## Summary
- Introduces `ArrowFunctionBody` to represent concise expression and block bodies explicitly.
- Removes the `ArrowFunctionExpression::expression` boolean.
- Makes invalid arrow body combinations unrepresentable.
## Breaking Change
The Rust AST changes from:
```rust
pub struct ArrowFunctionExpression<'a> {
pub expression: bool,
pub body: Box<'a, FunctionBody<'a>>,
}
```
to:
```rust
pub enum ArrowFunctionBody<'a> {
FunctionBody(Box<'a, FunctionBody<'a>>) = 64,
INHERIT(Expression<'a>),
}
pub struct ArrowFunctionExpression<'a> {
pub body: ArrowFunctionBody<'a>,
}
```
Concise arrow bodies are now stored directly as expressions instead of synthetic `FunctionBody` and `ExpressionStatement` nodes. Block bodies continue to use `FunctionBody`.
## Why?
The previous representation required consumers to maintain this implicit invariant:
```text
expression == true
→ no directives
→ exactly one statement
→ that statement is an ExpressionStatement
```
Invalid combinations could cause panics, skipped work, or fallback behavior. The enum encodes the grammar directly and makes those invalid states unrepresentable.
Concise expressions also now have `ArrowFunctionExpression` as their direct traversal ancestor, without synthetic `FunctionBody` or `ExpressionStatement` nodes.
## How to migrate
Use the arrow body accessors or match on `ArrowFunctionBody` rather than inspecting `expression` and synthetic statements.
Common field migrations:
| Previous representation | New representation |
| --- | --- |
| `arrow.expression` | `arrow.is_expression()` |
| `arrow.get_expression()` | `arrow.get_expression()` |
| `&arrow.body.statements` | `arrow.get_function_body().map(\|body\| &body.statements)` |
| `&mut arrow.body.statements` | `arrow.get_function_body_mut().map(\|body\| &mut body.statements)` |
| Synthetic expression statement in `arrow.body` | Direct `ArrowFunctionBody` expression variant |
| `arrow.body = function_body` | `arrow.body = ArrowFunctionBody::FunctionBody(function_body)` |
| Expression-to-block conversion | Create a `FunctionBody` containing a return statement |
| Block-to-expression conversion | Assign the expression directly to `arrow.body` |
| Builder `(expression, body)` arguments | Pass a single `ArrowFunctionBody` |
closes https://github.com/oxc-project/backlog/issues/211
Audited every formatter crate against the trailing-comment width discipline.
- same-line trailing line comments ride `line_suffix` and are excluded from the fits measurement
- while trailing block comments print inline and count
Corrected the prose that misstated it.
Adds formatter regression coverage for concise arrow bodies involving
nested `await` expressions, assignment chains, and AMD `define()` calls.
This locks existing parent-context behavior ahead of the
`ArrowFunctionBody` AST migration.
I may have caused a regression in #24987, so this PR adds test coverage
to make sure I don't break it.