Commit Graph

17 Commits

Author SHA1 Message Date
leaysgur 5ff57f83c0 fix(formatter/sort-imports): handle custom side effect groups (#26217)
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
2026-09-01 03:56:36 +00:00
leaysgur c07fe7c217 feat(oxfmt): support experimentalOperatorPosition (#25643)
Fixes #16366

---

Option name requires `experimental` prefix for now.

Values are:

- `"start"`
- `"end"`(default)

This is also available on playground.
2026-08-14 08:12:16 +00:00
leaysgur fed6681eda docs(oxfmt): skip expanding overrides options (#25572)
Follow up on #25544.

Oxlint also omits `overrides[].rules`.
2026-08-13 02:35:53 +00:00
leaysgur 00f490d7f7 refactor(oxfmt,formatter): split sortImports validation and use type enum (#25432)
Reduce runtime validation.

Also, by using type `enum`, IDE and TS types are also covered.
2026-08-09 15:07:41 +00:00
leaysgur e6e584b9be fix(oxfmt): type jsdoc. enum options (#25008)
Fixes #24999
2026-07-28 01:22:23 +00:00
leaysgur 415fe1e7bb fix(oxfmt): error on ignorePatterns that cannot match files outside the config directory (#24286)
Patterns in `ignorePatterns` use gitignore-style matching rooted at the config file's directory, so a `..` component can never match anything.

Reject such patterns at config load time instead of silently accepting them, and document the scoping.

Refs: #24276
2026-07-08 05:31:23 +00:00
leaysgur f88961ae29 docs(oxfmt): annotate each config option with supported languages (#22953)
Currently, our config file is Prettier-compatible, which is not easy to tell which options apply to which languages.

(I've felt this way for a while, so) I've added some notes to clarify.
2026-06-04 08:28:02 +00:00
leaysgur 43b997847b fix(formatter/sort_imports): Treat subpath imports as internal (#22440)
Closes #22371, follows https://github.com/azat-io/eslint-plugin-perfectionist/pull/732

---

As a baseline, while the result effectively follows the behavior of a perfectionist, there is no fundamental reason why it must be done that way.

Node.js subpaths are more flexible and aren't necessarily "internal", you can also alias external modules.

However, I do think it's consistent in the sense that it treats them the same way as other conventions like `@`.
2026-05-15 00:45:24 +00:00
leaysgur 6e8e8185c0 feat(oxfmt): Experimental .svelte support (#21700)
Part of #19715

Note that:

> Users are required to perform 2 steps below
> 1️⃣ Install svelte/compiler@^5.0.0 and/or @astrojs/compiler@^2.9.1 manually
> 2️⃣ Enable option in config
2026-05-11 01:24:37 +00:00
leaysgur 330fe31f82 refactor(config): Update doc comment for GlobSet (#22197)
Follow up on #22147.
2026-05-07 02:13:27 +00:00
leaysgur 50c389b64f fix(oxfmt): Support .editorconfig quote_type (#20989)
Fixes #20563

---

- [x] Merge https://github.com/oxc-project/editorconfig-parser/pull/42
- [x] Release https://github.com/oxc-project/editorconfig-parser/pull/43
- [x] Update crates version
- [x] Implement
- [x] Test
- [x] Update schema, snapshots
2026-04-07 02:03:35 +00:00
leaysgur 4216380bc0 fix(oxfmt): Support .editorconfig tab_width fallback (#20988)
https://github.com/prettier/prettier/blob/90983f40dce5e20beea4e5618b5e0426a6a7f4f0/src/config/editorconfig/editorconfig-to-prettier.js#L25-L30
2026-04-03 02:28:39 +00:00
leaysgur 6ef440af18 feat(oxfmt): Support bool for object style options (#20853)
Fixes #20793
2026-03-30 02:05:35 +00:00
leaysgur 416865ac32 feat(formatter,oxfmt): Add doc comments for JsdocConfig (#20644)
Follow up #19828
2026-03-23 09:00:46 +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
leaysgur bc2021765c fix(oxlint,oxfmt): Omit useless | null for Option<T> field from schema (#20273)
Currently, fields defined as `key: Option<T>` are represented in TS as `key?: T | null`.

However, passing `null` actually just treats it as `None`, which only creates noise.
2026-03-12 09:48:46 +00:00
leaysgur ec91dce7b3 refactor(oxfmt): Generate TS types from schema (#19901)
Like oxlint does.

No public definition change.
It also prepares for dynamic configuration files.
2026-03-02 23:51:22 +00:00