Commit Graph

9 Commits

Author SHA1 Message Date
leaysgur 0fa91283fd refactor(formatter,formatter_tests): move out prettier JS,TS,JSDoc conformance from tasks/ (#25477)
Migration from tasks/prettier_conformance to each crate test using oxc_formatter_tests, for JS/TS and JSDoc.
2026-08-12 00:48:32 +00:00
leaysgur ab52a593f3 fix(formatter): format xxx-in-js inside JSDoc js fence (#25414)
Such as CSS-in-JS-in-JSDoc_Markdown-in-JS. 😇
2026-08-09 14:49:15 +00:00
leaysgur 1a2c64a356 fix(formatter,oxfmt): apply effective print width for JSDoc fence (#25413)
As JSDoc is always contained within the ` * ` fenced comments, the `printWidth` option should not have been used as is.

The dedicated `jsdocPrintWidth` option has not yet been implemented. #24633
2026-08-09 14:49:14 +00:00
leaysgur 567089f3c5 refactor(oxfmt): route JSDoc fence through the dispatcher (#25334)
Markdown code fences can also be written in JSDoc, and other languages can be formatted as inline code within them.
2026-08-09 13:54:12 +00:00
leaysgur 0ccd8a14b7 feat(formatter_graphql): update oxc-graphql-parser 0.0.5 (#24106)
Part of #23923

Supports more syntax like:

- Fragment arguments
- Extends directives
2026-07-03 10:11:20 +00:00
leaysgur 259e0cdf60 feat(oxfmt,formatter_graphql)!: support draft syntax with removing prettier fallback (#23326)
- Fork `apollo-rs` to be compatible with Prettier(`latest` for now)
  - https://github.com/leaysgur/apollo-rs
- By doing this, Prettier fallback is no longer needed
- And now all prettier_conformance are passed, no parse error skip

> [!WARNING]
> TODO: Move fork under `oxc-project` org and replace refs
2026-06-29 03:15:25 +00:00
leaysgur 9d2a9579c1 refactor(oxfmt): use Rust formatters for jsdoc code fence (#23322)
In addition to basic embedded (e.g. css-in-js), JSDoc also contain code fence as markdown.

This PR makes JSDoc code fence also formatted by `oxc_formatter_(css|graphql)` if specified.
2026-06-29 03:15:24 +00:00
leaysgur 098db4649e test(oxfmt): Add css-in-jsdoc-in-js-in-vue tests (#20646)
😂
2026-03-23 09:00:47 +00:00
Dunqing 4fec907ad5 feat(formatter): add JSDoc comment formatting support (#19828)
## Summary

Implements native JSDoc comment formatting in oxfmt, porting behavior from [prettier-plugin-jsdoc](https://github.com/hosseinmd/prettier-plugin-jsdoc) (v1.8.0). Tracked in #19702.

### Conformance

**145/145 (100%)** compatibility with prettier-plugin-jsdoc test suite.

- 1 fixture intentionally skipped (`descriptions/032-jsx-tsx-css`) — requires embedded CSS/HTML formatter callbacks not available in the standalone conformance runner
- Run with: `cargo run -p oxc_prettier_conformance -- --jsdoc`

Prettier conformance (non-JSDoc):
- **JS**: 746/753 (99.07%)
- **TS**: 591/601 (98.34%)

### Real-World Repository Testing

Validated against 5 real-world repositories (7,542 JSDoc tags total) using Prettier 3.8.1 + prettier-plugin-jsdoc 1.8.0 as baseline:

| Repository | JSDoc Tags | Files with Diffs |
|------------|-----------|-----------------|
| evolu      | 134       | 13              |
| wxt        | 1,183     | 1               |
| typedoc    | 792       | 11              |
| Chart.js   | 1,276     | 4               |
| svelte     | 4,157     | 5               |
| **Total**  | **7,542** | **34**          |

All 34 remaining diffs are **intentional design differences**, not bugs:
- `{@link}` kept atomic (upstream breaks across lines) — ~11 files
- `{@includeCode}` placed on own line (upstream splits tag syntax) — 3 files
- Sub-list continuation indent normalized to 4-space — 5 files
- `?Type` expanded to `Type | null` — 1 file
- Minor line-break position differences — 5 files
- Double-space normalized to single space — 1 file

10 upstream bugs documented in `tasks/prettier_conformance/jsdoc/upstream-jsdoc-bugs.md` — oxfmt intentionally produces correct output where the upstream plugin has known issues.

### Performance

Measured with `hyperfine` using `oxfmt --check` on real-world repos (16 threads):

| Repository | Without JSDoc | With JSDoc | Overhead    |
|------------|--------------|------------|-------------|
| evolu      | 434ms        | 427ms      | ~0% (noise) |
| wxt        | 410ms        | 410ms      | ~0% (noise) |
| typedoc    | 532ms        | 567ms      | ~7%         |
| Chart.js   | 497ms        | 479ms      | ~0% (noise) |
| svelte     | 510ms        | 549ms      | ~8%         |

typedoc and svelte (highest JSDoc density) show measurable ~7-8% wall-clock overhead. The rest are within noise. CPU time overhead is ~1-2% as JSDoc work parallelizes across threads.

### Architecture

The formatting pipeline processes each JSDoc comment through these stages:

1. **Parse** — Extract JSDoc structure via `oxc_jsdoc` (description, tags with type/name/comment)
2. **Normalize** — Canonicalize tag aliases (`@return` → `@returns`), normalize types (`Array.<T>` → `T[]`, `?Type` → `Type | null`), normalize markdown emphasis (`__bold__` → `**bold**`, `*italic*` → `_italic_`)
3. **Sort & Group** — Sort tags by upstream priority weights within `@typedef`/`@callback` groups; reorder `@param` tags to match function signature
4. **Format** — Route each tag to its specific formatter (type+name+comment, type+comment, generic, example); format descriptions via markdown AST with word wrapping; format embedded code blocks
5. **Serialize** — Assemble final comment with `/** ... */` wrapper, `* ` prefixes, and single-line optimization

**Key modules:**
- `serialize.rs` — Main pipeline orchestrator (`JsdocFormatter` struct)
- `tag_formatters.rs` — Tag-specific formatters (type+name+comment, type+comment, generic, example)
- `normalize.rs` — Type/emphasis/capitalization normalization
- `mdast_serialize.rs` — Markdown AST → formatted text serialization with `tag_string_length` offset
- `wrap.rs` — Word wrapping with `first_line_offset`, atomic JSDoc link tokens, table formatting
- `embedded.rs` — Embedded JS/TS/JSX/TSX code formatting in @example blocks
- `imports.rs` — `@import` tag parsing, merging, and sorting
- `param_order.rs` — `@param` tag reordering to match function signature
- `line_buffer.rs` — Single-allocation line accumulator

### Options Support

All 11 options from prettier-plugin-jsdoc are implemented:

| Option | Default | Status |
|--------|---------|--------|
| `capitalizeDescriptions` | `true` | ✅ |
| `commentLineStrategy` | `"singleLine"` | ✅ |
| `separateTagGroups` | `false` | ✅ |
| `separateReturnsFromParam` | `false` | ✅ |
| `bracketSpacing` | `false` | ✅ |
| `descriptionWithDot` | `false` | ✅ |
| `addDefaultToDescription` | `true` | ✅ |
| `preferCodeFences` | `false` | ✅ |
| `lineWrappingStyle` | `"greedy"` | ✅ (greedy + balance) |
| `descriptionTag` | `false` | ✅ |
| `keepUnparsableExampleIndent` | `false` | ✅ |

**Not yet ported** (no conformance fixtures): `jsdocVerticalAlignment`, `tsdoc` mode — can be added when needed.

### Features

- **Description formatting**: Capitalize first letter, normalize markdown emphasis, wrap text to print width
- **Markdown AST processing**: Parse descriptions via mdast for heading normalization, list formatting, reference links, code blocks, blockquotes, tables
- **Tag description wrapping**: Matches upstream's `tagStringLength` architecture — full description passes through markdown AST with first-line offset
- **Tag normalization**: 17 tag synonyms, sort by upstream priority weights within groups
- **Type formatting**: Normalize JSDoc types, format via the formatter's TS parser (simulating upstream's `formatType()`), handle rest params, bracket spacing
- **@example formatting**: Format embedded JS/TS/JSX/TSX code; fenced code blocks with language detection
- **@import handling**: Parse, merge by module path, sort (third-party before relative), format consolidated imports
- **@param reordering**: Reorder tags to match function signature parameter order
- **Default value handling**: Extract `[name=value]` defaults with "Default is \`value\`" suffix

### Upstream Fidelity

Verified against [prettier-plugin-jsdoc](https://github.com/hosseinmd/prettier-plugin-jsdoc) v1.8.0 source:

- **Identical**: Tag synonyms (17), sort priorities (46 tags), grouping logic (`TAGS_GROUP_HEAD`, `TAGS_GROUP_CONDITION`), type normalization pipeline, rest param handling, JSDoc link protection, emphasis normalization, capitalization rules
- **Architecture aligned**: Tag description wrapping uses `tag_string_length` / `first_line_offset` (equivalent to upstream's `!...?` prefix trick), balance mode effective width calculation, greedy fallback
- **10 upstream bugs documented** — oxfmt intentionally produces correct output where the plugin has known issues (HTML comment wrapping, `{@link}` off-by-one, `{@includeCode}` splitting, `@type` capitalization inconsistency, etc.)

### Optimization Techniques

- Single `LineBuffer` allocation per comment (one contiguous `String`, not `Vec<String>`)
- `Cow<'_, str>` throughout normalization — borrows when unchanged, allocates only when modified
- Fast-path skip of mdast parsing for plain-text descriptions (`needs_mdast_parsing()` heuristic)
- Fast-path skip of TS formatter for simple types (`needs_formatter_pass()` check)
- `SmallVec<[usize; 4]>` for import tag indices (stack allocation for common case)

## Test plan

- [x] `cargo test -p oxc_formatter` — 335 tests pass (76 unit + 259 fixture)
- [x] `cargo run -p oxc_prettier_conformance -- --jsdoc` — 145/145 (100%)
- [x] Prettier conformance: JS 746/753 (99.07%), TS 591/601 (98.34%)
- [x] Real-world repos: 34 diffs across 5 repos (7,542 JSDoc tags) — all design differences
- [x] Performance: 0-8% overhead across 5 real-world repos (typedoc ~7%, svelte ~8%, rest within noise)
- [x] `cargo clippy -p oxc_formatter --all-targets` — clean
- [x] `cargo fmt -- --check` — clean

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-03-23 07:44:47 +00:00