Commit Graph

129 Commits

Author SHA1 Message Date
Dunqing 2f5cdb1231 feat(minifier): add property name mangling (#24740)
Oxc can shorten variable names and private class names, but it cannot shorten property names such as `obj._field`. This change adds optional property mangling. It replaces #23772 and gives bundlers a way to use the same property names across several files.

### Feature

- `include` selects names to mangle. `exclude` and `reserved` keep names unchanged.
- The cache can give a property a fixed output name or keep it unchanged.
- The mangler supports member access, object and class keys, destructuring, optional chains, the left side of `in`, and JSX property names.
- `mangle_quoted` also handles strings and template literals used directly as property keys. `/* @__KEY__ */` and `/* #__KEY__ */` can mark one other string or template literal as a property key.
- Short names are assigned in a stable order. More common properties get shorter names. Existing and reserved names are avoided.
- Bundlers can collect names from several programs, create one shared map, and apply it to every program.
- Numeric keys, `__proto__`, `constructor`, `prototype`, TypeScript-only keys, and private names are never changed by this pass.

### Design

Property names are part of JavaScript syntax, not local scope. For this reason, the code lives in `oxc_minifier`. It only reuses the short-name generator from `oxc_mangler`.

For one program, the order is:

```text
collect names → assign names → rewrite once → compress → mangle variables and private names
```

Property names are rewritten before compression because compression can remove information about quotes and comments.

For several programs, a bundler collects names from each program, merges the results, assigns one shared map, and rewrites each program once. Collection can run in parallel. All programs must use the same options.

The shared map keeps property names consistent inside one build. The cache has a different job: it keeps fixed names and reservations across separate minify calls.

### Example

With `include: "^_"` and `mangle_quoted: true`:

```js
obj[`_field`] = 1;
obj._field = 2;
```

becomes:

```js
obj[`e`] = 1;
obj.e = 2;
```

### Limitation

- Property mangling is unsafe when other code still uses the old property name. Public APIs and names shared with external code, host APIs, imported namespace objects, or separate builds must be excluded or reserved.
- Quote handling is per use. Without `mangle_quoted`, `obj._field` may change while `obj["_field"]` does not.
- The mangler cannot find names built at runtime or stored in arbitrary strings. Code that uses `eval`, `Function`, or `with` must reserve any affected names.
- Compression may create a matching property after this pass has finished. For example, `'f' + 'oo_'` may later become `foo_` and will not be mangled.
- TypeScript enum reverse mappings are not supported because the transform creates quoted strings.
- Multi-program support is a caller API. The caller must collect every program that shares mangled properties before assigning names.

The public `oxc-minify` API is stacked in #24741. The downstream multi-chunk integration is shown in rolldown/rolldown#10374.

Supersedes #23772.

AI assistance: OpenAI Codex and Claude were used for research, implementation, review, and testing. The contributor is responsible for reviewing and understanding the changes before submission.
2026-08-17 07:50:51 +00:00
leaysgur db446518ca fix(napi): disable reuseWorker in browser bindings (#25640)
Fixes #25610

AI assistance: Codex was used to implement this change.
2026-08-14 11:11:20 +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
renovate[bot] b91dc3a1f1 chore(deps): update napi (#25457)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Adoption](https://docs.renovatebot.com/merge-confidence/) |
[Passing](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) | Type |
Update |
|---|---|---|---|---|---|---|---|
| [@napi-rs/cli](https://napi.rs/)
([source](https://redirect.github.com/napi-rs/napi-rs)) | [`3.8.3` →
`3.8.5`](https://renovatebot.com/diffs/npm/@napi-rs%2fcli/3.8.3/3.8.5) |
![age](https://developer.mend.io/api/mc/badges/age/npm/@napi-rs%2fcli/3.8.5?slim=true)
|
![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@napi-rs%2fcli/3.8.5?slim=true)
|
![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@napi-rs%2fcli/3.8.3/3.8.5?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@napi-rs%2fcli/3.8.3/3.8.5?slim=true)
| pnpm.catalog.default | patch |
| [napi](https://redirect.github.com/napi-rs/napi-rs) | `3.12.0` →
`3.12.1` |
![age](https://developer.mend.io/api/mc/badges/age/crate/napi/3.12.1?slim=true)
|
![adoption](https://developer.mend.io/api/mc/badges/adoption/crate/napi/3.12.1?slim=true)
|
![passing](https://developer.mend.io/api/mc/badges/compatibility/crate/napi/3.12.0/3.12.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/crate/napi/3.12.0/3.12.1?slim=true)
| workspace.dependencies | patch |
| [napi-build](https://redirect.github.com/napi-rs/napi-rs) | `2.4.0` →
`2.4.1` |
![age](https://developer.mend.io/api/mc/badges/age/crate/napi-build/2.4.1?slim=true)
|
![adoption](https://developer.mend.io/api/mc/badges/adoption/crate/napi-build/2.4.1?slim=true)
|
![passing](https://developer.mend.io/api/mc/badges/compatibility/crate/napi-build/2.4.0/2.4.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/crate/napi-build/2.4.0/2.4.1?slim=true)
| workspace.dependencies | patch |
| [napi-derive](https://redirect.github.com/napi-rs/napi-rs) | `3.6.2` →
`3.6.3` |
![age](https://developer.mend.io/api/mc/badges/age/crate/napi-derive/3.6.3?slim=true)
|
![adoption](https://developer.mend.io/api/mc/badges/adoption/crate/napi-derive/3.6.3?slim=true)
|
![passing](https://developer.mend.io/api/mc/badges/compatibility/crate/napi-derive/3.6.2/3.6.3?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/crate/napi-derive/3.6.2/3.6.3?slim=true)
| workspace.dependencies | patch |

---

### Release Notes

<details>
<summary>napi-rs/napi-rs (@&#8203;napi-rs/cli)</summary>

###
[`v3.8.5`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.4...a5b5f277ab56e7e7e903b392b15c79da7a6520ce)

[Compare
Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.4...@napi-rs/cli@3.8.5)

###
[`v3.8.4`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.3...@napi-rs/cli@3.8.4)

[Compare
Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.3...@napi-rs/cli@3.8.4)

</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.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMi4wIiwidXBkYXRlZEluVmVyIjoiNDQuMTIuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Cameron <cclark@cloudflare.com>
2026-08-10 08:39:38 +00:00
leaysgur 465b723d04 refactor(formatter,oxfmt): unify host services as SessionServices on FormatSession (#25388)
- dispatcher
- string_embedder
- tailwind_sorter

Group these 3 together as `SessionServices`, make them available to `FormatSession`, and pass them around.
2026-08-09 14:32:10 +00:00
renovate[bot] d0b8d6ff22 chore(deps): update dependency @napi-rs/cli to v3.8.3 (#25434)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Adoption](https://docs.renovatebot.com/merge-confidence/) |
[Passing](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|---|---|
| [@napi-rs/cli](https://napi.rs/)
([source](https://redirect.github.com/napi-rs/napi-rs)) | [`3.8.2` →
`3.8.3`](https://renovatebot.com/diffs/npm/@napi-rs%2fcli/3.8.2/3.8.3) |
![age](https://developer.mend.io/api/mc/badges/age/npm/@napi-rs%2fcli/3.8.3?slim=true)
|
![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@napi-rs%2fcli/3.8.3?slim=true)
|
![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@napi-rs%2fcli/3.8.2/3.8.3?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@napi-rs%2fcli/3.8.2/3.8.3?slim=true)
|

---

### Release Notes

<details>
<summary>napi-rs/napi-rs (@&#8203;napi-rs/cli)</summary>

###
[`v3.8.3`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.2...@napi-rs/cli@3.8.3)

[Compare
Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.2...@napi-rs/cli@3.8.3)

</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:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMi4wIiwidXBkYXRlZEluVmVyIjoiNDQuMTIuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Cameron Clark <cameron.clark@hey.com>
2026-08-09 13:25:05 +00:00
camc314 253075e0cb fix(playground): include generated runtime files (#25246)
- add an explicit `files` allowlist for `oxc-playground`
- include generated JS/types, WASM binaries, and worker modules
- fix `publint` failures under pnpm 11.17.0 affecting oxc-project/playground#414
2026-08-03 10:28:30 +00:00
renovate[bot] b937e3e2f1 chore(deps): update napi (#25019)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Adoption](https://docs.renovatebot.com/merge-confidence/) |
[Passing](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) | Type |
Update |
|---|---|---|---|---|---|---|---|
| [@napi-rs/cli](https://napi.rs/)
([source](https://redirect.github.com/napi-rs/napi-rs)) | [`3.7.4` →
`3.8.2`](https://renovatebot.com/diffs/npm/@napi-rs%2fcli/3.7.4/3.8.2) |
![age](https://developer.mend.io/api/mc/badges/age/npm/@napi-rs%2fcli/3.8.2?slim=true)
|
![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@napi-rs%2fcli/3.8.2?slim=true)
|
![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@napi-rs%2fcli/3.7.4/3.8.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@napi-rs%2fcli/3.7.4/3.8.2?slim=true)
| pnpm.catalog.default | minor |
| [napi](https://redirect.github.com/napi-rs/napi-rs) | `3.11.0` →
`3.12.0` |
![age](https://developer.mend.io/api/mc/badges/age/crate/napi/3.12.0?slim=true)
|
![adoption](https://developer.mend.io/api/mc/badges/adoption/crate/napi/3.12.0?slim=true)
|
![passing](https://developer.mend.io/api/mc/badges/compatibility/crate/napi/3.11.0/3.12.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/crate/napi/3.11.0/3.12.0?slim=true)
| workspace.dependencies | minor |
| [napi-build](https://redirect.github.com/napi-rs/napi-rs) | `2.3.2` →
`2.4.0` |
![age](https://developer.mend.io/api/mc/badges/age/crate/napi-build/2.4.0?slim=true)
|
![adoption](https://developer.mend.io/api/mc/badges/adoption/crate/napi-build/2.4.0?slim=true)
|
![passing](https://developer.mend.io/api/mc/badges/compatibility/crate/napi-build/2.3.2/2.4.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/crate/napi-build/2.3.2/2.4.0?slim=true)
| workspace.dependencies | minor |
| [napi-derive](https://redirect.github.com/napi-rs/napi-rs) | `3.6.0` →
`3.6.2` |
![age](https://developer.mend.io/api/mc/badges/age/crate/napi-derive/3.6.2?slim=true)
|
![adoption](https://developer.mend.io/api/mc/badges/adoption/crate/napi-derive/3.6.2?slim=true)
|
![passing](https://developer.mend.io/api/mc/badges/compatibility/crate/napi-derive/3.6.0/3.6.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/crate/napi-derive/3.6.0/3.6.2?slim=true)
| workspace.dependencies | patch |

---

### Release Notes

<details>
<summary>napi-rs/napi-rs (@&#8203;napi-rs/cli)</summary>

###
[`v3.8.2`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.1...@napi-rs/cli@3.8.2)

[Compare
Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.1...@napi-rs/cli@3.8.2)

###
[`v3.8.1`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.0...@napi-rs/cli@3.8.1)

[Compare
Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.0...@napi-rs/cli@3.8.1)

###
[`v3.8.0`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.7.4...@napi-rs/cli@3.8.0)

[Compare
Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.7.4...@napi-rs/cli@3.8.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.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0My4yODAuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjMuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Cameron <cameron.clark@hey.com>
2026-08-03 11:21:03 +01:00
Boshen 3d223072c2 feat(parser): add ParseOptions::enable_ident_hashes (#24491)
Adds `ParseOptions::enable_ident_hashes` (default `true`) and `Ident::new_unhashed` in `oxc_str`, and turns it off in the parse-only paths of the `oxc-parser` napi crate and the JS/JSON formatters.

### Why

`Ident`'s precomputed hash (#19143) is a parse-time investment that pays off in semantic analysis — but parse-only consumers (parse + serialize, formatting) pay it for nothing. Profiling against other native parsers showed identifier hashing at 3.5-4.6% of parse time.

Measured on M-series (yuku-style native harness, 50 warmup + 300 runs, median):

| fixture | default | option off |
|---|---|---|
| react.development.js | 0.156 ms | 0.152 ms (−2.3%) |
| binder.ts | 0.383 ms | 0.370 ms (−3.5%) |
| App.tsx | 1.140 ms | 1.111 ms (−2.5%) |
| checker.ts | 7.862 ms | 7.499 ms (−4.6%) |
| typescript.js | 25.07 ms | 24.16 ms (−3.6%) |

### Contract

Unhashed `Ident`s store hash `0`; `Eq`/`Hash`/`ContentEq` include the stored hash, so unhashed and hashed `Ident`s of the same string do not compare equal. Semantic analysis (or anything relying on `Ident` hashing) must not run on an AST parsed with the option disabled. Default behavior is unchanged.

Every `Ident` the parser creates funnels through the `ParserImpl::ident()` helper, so the option applies uniformly. This covers identifiers lexed directly (`parse_identifier_kind`, `parse_private_identifier`) as well as those reconstructed from a `&str`/`Str` — import/export specifier locals, JSX element/member names, the TS `intrinsic` type name, TS import-type option keys (`with`/`assert`), and the `import x = this` recovery path — which would otherwise always be hashed through `Into<Ident>`. Exhaustively checked by parsing the test262/babel/typescript/misc/prettier corpora (96.5k files, 5.6M identifiers) in both option states and walking every `Ident` in the AST.

On 64-bit platforms `Ident::new_unhashed` compiles to a no-op: an unhashed `Ident` has the same layout and bit representation as `&str`.

### Opt-outs

- **napi `oxc-parser`** ties `enable_ident_hashes` to whether semantic errors are requested. Both parse entry points run `SemanticBuilder` only when `showSemanticErrors` is set, so the common parse-and-serialize path skips hashing while the semantic-errors path keeps it. Serialized output is unchanged — the hash is internal to `Ident` and never emitted.
- **JS and JSON formatters** turn it off: they never run semantic analysis and only read `Ident::as_str` / compare idents against string literals. Exception: the JS formatter keeps hashes when built with the `detect_code_removal` feature, whose check runs `SemanticBuilder` on the formatter-parsed AST.
2026-07-14 15:55:27 +00:00
Dunqing 4eb074e69d feat(mangler): add reserved option for names that must not be mangled (#24041)
Adds `MangleOptions::reserved` — equivalent to terser / uglify-js / swc `mangle.reserved`: names in the set are never used as mangled names, and bindings that already carry them keep them. Default empty; default output is byte-identical to main (minsize snapshot unchanged).

## Motivation

Node's cjs-module-lexer detects a CommonJS module's named exports by **lexically** scanning for `exports.<name> =` / `module.exports` token patterns — no scope analysis ([its README documents renamed identifiers as "DETECTS: NO EXPORTS"](https://github.com/nodejs/cjs-module-lexer)). UMD / CommonJS wrappers bind `exports` / `module` as ordinary function parameters, and the mangler renames them, erasing every named export the lexer can see.

Surfaced by the monitor-oxc mangler runtime test ([failing run](https://github.com/oxc-project/monitor-oxc/actions/runs/28569108563/job/84702922472), previously masked by #24033 / #24036): `archiver` does `import { queue } from "async"`, and `async`'s dist is a UMD bundle — mangled, the factory parameter `exports` became `e` and node fails with `Named export 'queue' not found`. With `reserved: ["exports", "module"]` the output imports correctly (verified end-to-end in node, including running a queue task).

## Why an opt-in list instead of always keeping these names

Renaming them is ecosystem-standard. Verified empirically — minifying the same UMD sample and importing it in node:

| minifier | renames `exports` param? | `import { queue }` from its output | escape hatch |
| --- | --- | --- | --- |
| terser 5.48 | yes | fails | `mangle.reserved` (verified working) |
| uglify-js 3.19 | yes | fails | `reserved` (verified working) |
| esbuild 0.28 `--minify` | yes | fails | none for identifier mangling |
| swc | yes | fails | terser-style `mangle.reserved` ([swc#3424](https://github.com/swc-project/swc/issues/3424)) |

An earlier revision of this PR kept `exports` / `module` unconditionally; the Minification Size check showed the real cost: ~1% minified on UMD-heavy files (d3: +3.3 kB from ~590 `exports` references, flipping it to *larger than esbuild*), because the point is keeping the literal 7-char token at every export site. No other minifier pays this by default — and rolldown bundles bind `exports` / `module` in every CJS interop wrapper, where node's lexer never looks. So oxc gets the same escape hatch as terser/swc, with the same name.

`MangleOptions` loses `Copy` (it now holds the set); construction sites switch to `..MangleOptions::default()` / `clone()`. Exposed in `oxc-minify` NAPI options (`mangle.reserved: string[]`) and the mangler example (`--reserve-exports`).

monitor-oxc will pass `["exports", "module"]` for its mangler runtime test (its corpus imports prebuilt CJS/UMD dists directly).

For reference, esbuild solves this for its *own* node-platform CJS bundles differently — a dead-code annotation `0 && (module.exports = { a, b })` that cjs-module-lexer recognizes ([esbuild#960](https://github.com/evanw/esbuild/issues/960), [cjs-module-lexer#44](https://github.com/nodejs/cjs-module-lexer/issues/44)); verified that pattern also works appended to fully-mangled output. That could be a future zero-size-cost alternative at the minifier level.
2026-07-02 09:31:28 +00:00
overlookmotel 7a76cd3dc3 feat(estree)!: make whether to include TS fields a runtime option (#23574)
Previously, whether to serialize AST to ESTree JSON with/without TS fields was a compile-time option - via 2 different configs `CompactTSSerializer` and `CompactJSSerializer` (and their pretty-printing and with-fixes counterparts).

Turn this into a runtime option instead -

This make serialization a little slower (more branches in either mode, and dead code in JS-only mode) but reduces the size of `oxc-parser` binary as only one `ESTreeSerializer` is generated, instead of 2 (JS-only, and with-TS).
2026-06-17 23:22:37 +00:00
renovate[bot] e745892d4c chore(deps): update napi (#23394)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Adoption](https://docs.renovatebot.com/merge-confidence/) |
[Passing](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) | Type |
Update |
|---|---|---|---|---|---|---|---|
| [@napi-rs/cli](https://napi.rs/)
([source](https://redirect.github.com/napi-rs/napi-rs)) | [`3.7.1` →
`3.7.2`](https://renovatebot.com/diffs/npm/@napi-rs%2fcli/3.7.1/3.7.2) |
![age](https://developer.mend.io/api/mc/badges/age/npm/@napi-rs%2fcli/3.7.2?slim=true)
|
![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@napi-rs%2fcli/3.7.2?slim=true)
|
![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@napi-rs%2fcli/3.7.1/3.7.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@napi-rs%2fcli/3.7.1/3.7.2?slim=true)
| pnpm.catalog.default | patch |
| [napi](https://crates.io/crates/napi) | `3.9.1` → `3.9.2` |
![age](https://developer.mend.io/api/mc/badges/age/crate/napi/3.9.2?slim=true)
|
![adoption](https://developer.mend.io/api/mc/badges/adoption/crate/napi/3.9.2?slim=true)
|
![passing](https://developer.mend.io/api/mc/badges/compatibility/crate/napi/3.9.1/3.9.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/crate/napi/3.9.1/3.9.2?slim=true)
| workspace.dependencies | patch |

---

### Release Notes

<details>
<summary>napi-rs/napi-rs (@&#8203;napi-rs/cli)</summary>

###
[`v3.7.2`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.7.1...@napi-rs/cli@3.7.2)

[Compare
Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.7.1...@napi-rs/cli@3.7.2)

</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.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0My4yMTkuMCIsInVwZGF0ZWRJblZlciI6IjQzLjIxOS4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Cameron Clark <cameron.clark@hey.com>
2026-06-14 14:10:12 +01:00
Boshen 777f02ae10 refactor(diagnostics)!: return a Diagnostics type instead of Vec<OxcDiagnostic> (#23217)
## Summary

Introduces `oxc_diagnostics::Diagnostics` — a `Vec<OxcDiagnostic>` newtype that knows the difference between errors and warnings — and makes it the standard diagnostics return value across oxc.

- **New type**: `Diagnostics` with `has_errors()`, `has_warnings()`, `errors()`, `warnings()`, plus `Vec`-like ergonomics (Deref, `IntoIterator`, `Extend`, `From`/`Into<Vec>`) so most call sites are unchanged.
- **Return types**: `ParserReturn`, `SemanticBuilderReturn`, `IsolatedDeclarationsReturn`, the React Compiler `TransformResult`/`LintResult`, and `TransformerReturn` now expose `diagnostics: Diagnostics` instead of `errors: Vec<OxcDiagnostic>` (the React Compiler's separate `errors`/`warnings` are merged into one severity-tagged list).
- **Behaviour**: a React Compiler **error** now stops the transform (no output), while **warnings** flow through and the transform continues. `compile()` aborts codegen only when `diagnostics.has_errors()` — warnings alone no longer abort (previously any diagnostic did).

## Breaking change

Consumers reading `*.errors` on these return values must read `*.diagnostics`. React Compiler consumers that read `.errors`/`.warnings` separately should use `diagnostics.has_errors()` / `diagnostics.errors()` / `diagnostics.warnings()`.

Parser/semantic/isolated-declarations fatality is otherwise unchanged. Builds on the React Compiler transform feature (#23201).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-10 14:46:52 +00:00
Boshen 1260f03b4f refactor(semantic): don't build the AstNodes store by default (#23005)
## What

Make the full `AstNodes` store **opt-in**, so the compiler pipeline (transformer / codegen / minifier) can run semantic analysis without building it at all.

Previously every `SemanticBuilder` run allocated and populated the full `AstNodes` store, even for consumers that only need scoping + the syntax checker. This separates the node-related builder state into three independent pieces:

- a standalone `NodeId` counter — the source of truth for node ids, valid in either mode;
- an **ancestry stack** — an upward-walk-only stack of the current node's ancestors, the only ancestry the syntax checker and binder actually read;
- the full **`AstNodes`** store — random access to every node, needed by the linter and mangler.

## How

- A new `AstNodeStore` (in `crates/oxc_semantic/src/node/store.rs`) bundles the node-id counter, the current-node cursor/flags, and the storage. The storage is an enum (`AstNodeStoreKind`) with two variants — `Full(AstNodes)` and `Ancestry(AncestryStack)` — so the full store and the ancestry stack are **mutually exclusive by construction**; they can never both be maintained at once.
- The checker and binder read ancestry through an `Ancestry` view (`node/ancestry.rs`) that dispatches between the full store (linter / mangler) and the stack (compiler pipeline). They no longer do random `NodeId` access into the store — all ancestry access is upward-walk-only.
- `SemanticBuilder` gains `with_build_nodes(bool)`. The default is **off** (ancestry stack only). `new_linter()` turns it on (the linter needs the full store); `new_compiler()` leaves it off.
- `CompilerInterface::build_semantic_nodes()` lets `oxc::Compiler` consumers opt back in if their `after_semantic` hook reads `Semantic::nodes`.

## Notes

- Behaviour-neutral: semantic conformance snapshots are unchanged.
- Allocation snapshots updated — the default (ancestry) path allocates slightly less (the growing `Stack` trades a couple of fixed allocations for a few reallocs).
- Builds on the already-merged `SymbolFlags`-based redeclaration checks (#23060) and the `new_linter`/`new_compiler` preset adoption (#23057), which removed the last random node reads from the redeclaration path so the checker works without the store.
2026-06-08 16:13:56 +00:00
Boshen 963146f4b6 refactor(semantic): use SemanticBuilder presets at remaining call sites (#23057)
Use the `SemanticBuilder::new_linter()` preset at linter call sites that built semantic with ad-hoc `new().with_cfg(true)`-style chains, so they build semantic the same way the linter does instead of replicating its options by hand.

- linter test helpers: `disable_directives`, `utils/{regex,comment,jest,react}`
- playground's lint path

Behaviour-neutral; all linter tests pass.
2026-06-07 05:14:26 +00:00
Boshen 336413bf5d refactor(semantic): use new_linter/new_compiler presets at callsites (#23037)
Replaces ad-hoc `SemanticBuilder::new().with_*()` chains with the `new_linter()` / `new_compiler()` presets where the preset already expands to the exact same option set, so the config lives in one place.

- linter example + linter benchmark → `new_linter()` (cfg + class table + syntax)
- semantic benchmark, cfg example, integration test util → `new_compiler()` (syntax)

Behavior-neutral — each preset matches the options the callsite set by hand. Carved out of the larger store-optional refactor so it can land independently on `main` first.
2026-06-06 19:09:57 +00:00
leaysgur 5778a2dfb5 refactor(formatter_core): purify core and define rules (#22952)
The Printer never consumes these; they belong with each language formatter.

- Evict `Quote` (each formatter owns `QuoteStyle`)
  - `normalize_string` takes a quote byte
- Evict `Expand` / `BracketSpacing` / `TrailingCommas` into oxc_formatter and oxc_formatter_json
- JSON's `TrailingCommas` is now two-variant (`Always`/`Never`)
  - collapse Prettier's `all`/`es5` at each translation layer
- Rename `util/` -> `spec/` (pure shared helpers)
- Split `SourceText`: mechanical access stays in core, JS/TS lexis moves to `SourceTextExt`
- Drop the `oxc_syntax` dependency from core

All behaviros, Prettier conformance are unchanged.
2026-06-04 07:03:56 +00:00
Boshen 85efabfe0a feat(semantic): make building the class table optional, off by default (#22862)
Rolldown runs semantic multiple times, some runs do not require building the class table.
2026-06-01 16:36:38 +00:00
leaysgur 705c0e969b refactor(formatter): introduce text-in public APIs (#22872)
Align `oxc_formatter` with `oxc_formatter_json`, both now expose `format(...) -> Result<Formatted<'a, _>, OxcDiagnostic>`.

- Add text-in `format` and `format_fragment`, also `format_program`, `parse_for_format` and the `FragmentContext` enum
  - Parse options, JSX enabling, js-in-xxx context aware handlings are applied internally
  - Make the `Formatter` struct and AST/IR primitives crate-internal
- Fold `get_parse_options` / `enable_jsx_source_type` and the `service` module into `parse_for_format`
2026-06-01 06:40:34 +00:00
leaysgur 5733868456 refactor(formatter_core,formatter,formatter_json): share Expand and BracketSpacing options (#22821)
We chose `core` as the location for duplicate `struct`s, but the final options will continue to be built by each formatter.
2026-05-29 09:13:38 +00:00
leaysgur c323187f37 refactor(formatter): Split oxc_formatter and oxc_formatter_core (#22611)
Split `oxc_formatter` into:

- `oxc_formatter(_js)`: JS/TS specific formatter, comment handling, sortImports, jsdoc, etc
- `oxc_formatter_core`: Language agnostic IR, printer, etc

The naming convention is different from Biome or Ruff (they do not have `_core`, and `_formatter` is equivalent to the core), but this is because in Oxc, items without `_js` are the default.

As a basic task, I have added `Context` as a generic to all relevant parts to ensure support for multiple languages.

There might still be other components that could be refactored into the core, but I would like to finish them as I implement support for other languages.

Also moved some shared utilities to the core in advance.
2026-05-29 00:49:49 +00:00
renovate[bot] b7568d54d7 chore(deps): update dependency @napi-rs/cli to v3.7.0 (#22771)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Adoption](https://docs.renovatebot.com/merge-confidence/) |
[Passing](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|---|---|
| [@napi-rs/cli](https://napi.rs/)
([source](https://redirect.github.com/napi-rs/napi-rs)) | [`3.6.2` →
`3.7.0`](https://renovatebot.com/diffs/npm/@napi-rs%2fcli/3.6.2/3.7.0) |
![age](https://developer.mend.io/api/mc/badges/age/npm/@napi-rs%2fcli/3.7.0?slim=true)
|
![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@napi-rs%2fcli/3.7.0?slim=true)
|
![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@napi-rs%2fcli/3.6.2/3.7.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@napi-rs%2fcli/3.6.2/3.7.0?slim=true)
|

---

### Release Notes

<details>
<summary>napi-rs/napi-rs (@&#8203;napi-rs/cli)</summary>

###
[`v3.7.0`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.6.2...@napi-rs/cli@3.7.0)

[Compare
Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.6.2...@napi-rs/cli@3.7.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:eyJjcmVhdGVkSW5WZXIiOiI0My4xOTQuMCIsInVwZGF0ZWRJblZlciI6IjQzLjE5NC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Cameron Clark <cameron.clark@hey.com>
2026-05-28 10:06:39 +01:00
camc314 339a9fb1ce refactor(linter): simplify context sub host options (#22056)
Simplifies ContextSubHost construction by replacing constructor variants with a single options struct.
2026-05-01 20:34:56 +00:00
anthropic-code-agent 245273ef0a feat(napi/playground): add optimizeEnums and optimizeConstEnums options (#21815)
## Summary
This PR adds `optimizeEnums` and `optimizeConstEnums` options to the playground's transformer configuration, enabling TypeScript enum optimization features that are already available in the transformer.

## Changes
- Added `optimize_enums: bool` and `optimize_const_enums: bool` fields to the `OxcTransformerOptions` struct in `napi/playground/src/options.rs`
- Updated `apply_transformations` method in `napi/playground/src/lib.rs` to apply these options to `options.typescript.optimize_enums` and `options.typescript.optimize_const_enums`

## Reference
Resolves https://github.com/oxc-project/oxc/blob/d1cf22efc2bc9cf9fa82e887c34965d2ce3004c3/napi/playground/src/options.rs#L59

These options enable:
- `optimize_enums`: Optimize regular (non-const) enums by inlining their member accesses at usage sites when the member value is statically known
- `optimize_const_enums`: Optimize const enums by inlining their values at usage sites and removing the enum declaration
2026-04-27 04:10:29 +00:00
Dunqing 5e755d14f9 chore(transformer, playground): enable enum_eval in semantic for transformer (#21671)
close: https://github.com/oxc-project/oxc/issues/21667
2026-04-23 10:27:58 +00:00
renovate d8076c9780 chore(deps): update rolldown (#21639)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|---|---|
| [rolldown](https://rolldown.rs/) ([source](https://redirect.github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)) | [`1.0.0-rc.16` → `1.0.0-rc.17`](https://renovatebot.com/diffs/npm/rolldown/1.0.0-rc.16/1.0.0-rc.17) | ![age](https://developer.mend.io/api/mc/badges/age/npm/rolldown/1.0.0-rc.17?slim=true) | ![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/rolldown/1.0.0-rc.17?slim=true) | ![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/rolldown/1.0.0-rc.16/1.0.0-rc.17?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/rolldown/1.0.0-rc.16/1.0.0-rc.17?slim=true) |
| [tsdown](http://tsdown.dev/) ([source](https://redirect.github.com/rolldown/tsdown)) | [`0.21.9` → `0.21.10`](https://renovatebot.com/diffs/npm/tsdown/0.21.9/0.21.10) | ![age](https://developer.mend.io/api/mc/badges/age/npm/tsdown/0.21.10?slim=true) | ![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/tsdown/0.21.10?slim=true) | ![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/tsdown/0.21.9/0.21.10?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/tsdown/0.21.9/0.21.10?slim=true) |

---

### Release Notes

<details>
<summary>rolldown/rolldown (rolldown)</summary>

### [`v1.0.0-rc.17`](https://redirect.github.com/rolldown/rolldown/blob/HEAD/CHANGELOG.md#100-rc17---2026-04-22)

[Compare Source](https://redirect.github.com/rolldown/rolldown/compare/v1.0.0-rc.16...v1.0.0-rc.17)

##### 🐛 Bug Fixes

- link: error on missing export between TS modules ([#&#8203;9197](https://redirect.github.com/rolldown/rolldown/issues/9197)) by [@&#8203;IWANABETHATGUY](https://redirect.github.com/IWANABETHATGUY)
- rolldown\_plugin\_vite\_import\_glob: import path should not be affected by absolute base option ([#&#8203;9145](https://redirect.github.com/rolldown/rolldown/issues/9145)) by [@&#8203;kermanx](https://redirect.github.com/kermanx)
- `this.resolve()` returns null for bare relative paths without importer ([#&#8203;9142](https://redirect.github.com/rolldown/rolldown/issues/9142)) by [@&#8203;Copilot](https://redirect.github.com/Copilot)
- collect destructured bindings in HMR module exports ([#&#8203;9146](https://redirect.github.com/rolldown/rolldown/issues/9146)) by [@&#8203;h-a-n-a](https://redirect.github.com/h-a-n-a)
- esbuild-tests: handle 0.28.0 test cases ([#&#8203;9149](https://redirect.github.com/rolldown/rolldown/issues/9149)) by [@&#8203;sapphi-red](https://redirect.github.com/sapphi-red)
- plugin/copy-module: honor external resolutions from other plugins ([#&#8203;9139](https://redirect.github.com/rolldown/rolldown/issues/9139)) by [@&#8203;TheAlexLichter](https://redirect.github.com/TheAlexLichter)
- allow undefined in sourcesContent type ([#&#8203;9136](https://redirect.github.com/rolldown/rolldown/issues/9136)) by [@&#8203;jurijzahn8019](https://redirect.github.com/jurijzahn8019)
- reduce false positives in chunk optimizer circular dependency detection ([#&#8203;9049](https://redirect.github.com/rolldown/rolldown/issues/9049)) by [@&#8203;AlonMiz](https://redirect.github.com/AlonMiz)

##### 🚜 Refactor

- chunk-optimizer: extract runtime-module placement into rehome\_runtime\_module ([#&#8203;9163](https://redirect.github.com/rolldown/rolldown/issues/9163)) by [@&#8203;IWANABETHATGUY](https://redirect.github.com/IWANABETHATGUY)

##### 📚 Documentation

- add design doc for sort\_modules execution ordering ([#&#8203;9169](https://redirect.github.com/rolldown/rolldown/issues/9169)) by [@&#8203;IWANABETHATGUY](https://redirect.github.com/IWANABETHATGUY)
- add document for `RenderedModule` ([#&#8203;9147](https://redirect.github.com/rolldown/rolldown/issues/9147)) by [@&#8203;sapphi-red](https://redirect.github.com/sapphi-red)

##### ⚡ Performance

- rolldown\_plugin\_vite\_import\_glob: skip self-import earlier using raw path comparison ([#&#8203;9193](https://redirect.github.com/rolldown/rolldown/issues/9193)) by [@&#8203;shulaoda](https://redirect.github.com/shulaoda)

##### 🧪 Testing

- lazy: add `playground/lazy-compilation` ([#&#8203;7974](https://redirect.github.com/rolldown/rolldown/issues/7974)) by [@&#8203;hyf0](https://redirect.github.com/hyf0)

##### ⚙️ Miscellaneous Tasks

- use app token for release PR ([#&#8203;9198](https://redirect.github.com/rolldown/rolldown/issues/9198)) by [@&#8203;Boshen](https://redirect.github.com/Boshen)
- upgrade oxc to 0.127.0 ([#&#8203;9194](https://redirect.github.com/rolldown/rolldown/issues/9194)) by [@&#8203;Dunqing](https://redirect.github.com/Dunqing)
- use oxc security action ([#&#8203;9196](https://redirect.github.com/rolldown/rolldown/issues/9196)) by [@&#8203;Boshen](https://redirect.github.com/Boshen)
- esbuild-tests: remove some tests from ignored list as enum inline is now supported ([#&#8203;9184](https://redirect.github.com/rolldown/rolldown/issues/9184)) by [@&#8203;sapphi-red](https://redirect.github.com/sapphi-red)
- deps: update dependency vite-plus to v0.1.19 ([#&#8203;9183](https://redirect.github.com/rolldown/rolldown/issues/9183)) by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot]
- use vp instead of pnpm in check-wasi-binding-deps ([#&#8203;9182](https://redirect.github.com/rolldown/rolldown/issues/9182)) by [@&#8203;shulaoda](https://redirect.github.com/shulaoda)
- verify wasm32-wasi binding deps match [@&#8203;rolldown/browser](https://redirect.github.com/rolldown/browser) before publish ([#&#8203;9162](https://redirect.github.com/rolldown/rolldown/issues/9162)) by [@&#8203;shulaoda](https://redirect.github.com/shulaoda)
- deps: update esbuild for tests to 0.28.0 ([#&#8203;9172](https://redirect.github.com/rolldown/rolldown/issues/9172)) by [@&#8203;sapphi-red](https://redirect.github.com/sapphi-red)
- deps: update rollup submodule for tests to v4.60.2 ([#&#8203;9173](https://redirect.github.com/rolldown/rolldown/issues/9173)) by [@&#8203;sapphi-red](https://redirect.github.com/sapphi-red)
- deps: update test262 submodule for tests ([#&#8203;9174](https://redirect.github.com/rolldown/rolldown/issues/9174)) by [@&#8203;sapphi-red](https://redirect.github.com/sapphi-red)
- sort\_modules: fix stale async-entry sort key comment ([#&#8203;9170](https://redirect.github.com/rolldown/rolldown/issues/9170)) by [@&#8203;IWANABETHATGUY](https://redirect.github.com/IWANABETHATGUY)
- deps: update npm packages ([#&#8203;9157](https://redirect.github.com/rolldown/rolldown/issues/9157)) by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot]
- deps: update dependency diff to v9 ([#&#8203;9158](https://redirect.github.com/rolldown/rolldown/issues/9158)) by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot]
- deps: update rust crates ([#&#8203;9156](https://redirect.github.com/rolldown/rolldown/issues/9156)) by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot]
- run Windows CI on PRs labeled with `ci: windows` ([#&#8203;9153](https://redirect.github.com/rolldown/rolldown/issues/9153)) by [@&#8203;hyf0](https://redirect.github.com/hyf0)
- update-test-dependencies: run setup-rust before file changes ([#&#8203;9151](https://redirect.github.com/rolldown/rolldown/issues/9151)) by [@&#8203;sapphi-red](https://redirect.github.com/sapphi-red)
- deps: update dependency rust to v1.95.0 ([#&#8203;9140](https://redirect.github.com/rolldown/rolldown/issues/9140)) by [@&#8203;renovate](https://redirect.github.com/renovate)\[bot]

##### ❤️ New Contributors

- [@&#8203;jurijzahn8019](https://redirect.github.com/jurijzahn8019) made their first contribution in [#&#8203;9136](https://redirect.github.com/rolldown/rolldown/pull/9136)
- [@&#8203;AlonMiz](https://redirect.github.com/AlonMiz) made their first contribution in [#&#8203;9049](https://redirect.github.com/rolldown/rolldown/pull/9049)

</details>

<details>
<summary>rolldown/tsdown (tsdown)</summary>

### [`v0.21.10`](https://redirect.github.com/rolldown/tsdown/releases/tag/v0.21.10)

[Compare Source](https://redirect.github.com/rolldown/tsdown/compare/v0.21.9...v0.21.10)

#####    🚀 Features

- Upgrade rolldown  -  by [@&#8203;sxzz](https://redirect.github.com/sxzz) [<samp>(8a449)</samp>](https://redirect.github.com/rolldown/tsdown/commit/8a4498d)

#####     [View changes on GitHub](https://redirect.github.com/rolldown/tsdown/compare/v0.21.9...v0.21.10)

</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.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0My4xMzkuNCIsInVwZGF0ZWRJblZlciI6IjQzLjEzOS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
2026-04-23 10:20:58 +00:00
renovate 611b3b69ef chore(deps): update dependency rust to v1.95.0 (#21509)
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-->
2026-04-17 14:43:16 +00:00
renovate 152b37a6e8 chore(deps): update napi (#20829)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | Type | Update |
|---|---|---|---|---|---|---|---|
| [@napi-rs/cli](https://napi.rs/) ([source](https://redirect.github.com/napi-rs/napi-rs)) | [`3.5.1` → `3.6.0`](https://renovatebot.com/diffs/npm/@napi-rs%2fcli/3.5.1/3.6.0) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@napi-rs%2fcli/3.6.0?slim=true) | ![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@napi-rs%2fcli/3.6.0?slim=true) | ![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@napi-rs%2fcli/3.5.1/3.6.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@napi-rs%2fcli/3.5.1/3.6.0?slim=true) | pnpm.catalog.default | minor |
| [napi](https://redirect.github.com/napi-rs/napi-rs) | `3.8.3` → `3.8.4` | ![age](https://developer.mend.io/api/mc/badges/age/crate/napi/3.8.4?slim=true) | ![adoption](https://developer.mend.io/api/mc/badges/adoption/crate/napi/3.8.4?slim=true) | ![passing](https://developer.mend.io/api/mc/badges/compatibility/crate/napi/3.8.3/3.8.4?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/crate/napi/3.8.3/3.8.4?slim=true) | workspace.dependencies | patch |
| [napi-derive](https://redirect.github.com/napi-rs/napi-rs) | `3.5.2` → `3.5.3` | ![age](https://developer.mend.io/api/mc/badges/age/crate/napi-derive/3.5.3?slim=true) | ![adoption](https://developer.mend.io/api/mc/badges/adoption/crate/napi-derive/3.5.3?slim=true) | ![passing](https://developer.mend.io/api/mc/badges/compatibility/crate/napi-derive/3.5.2/3.5.3?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/crate/napi-derive/3.5.2/3.5.3?slim=true) | workspace.dependencies | patch |

---

### Release Notes

<details>
<summary>napi-rs/napi-rs (@&#8203;napi-rs/cli)</summary>

### [`v3.6.0`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.5.1...@napi-rs/cli@3.6.0)

[Compare Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.5.1...@napi-rs/cli@3.6.0)

</details>

---

### Configuration

📅 **Schedule**: 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.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0My45NC4xIiwidXBkYXRlZEluVmVyIjoiNDMuMTAyLjciLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=-->
2026-04-07 10:48:24 +00:00
overlookmotel b3b2d305f9 perf(parser): introduce ParserConfig (#19637)
### What this PR does

Introduce `ParserConfig` trait (another try at #16785).

The aim is to remove the large performance regression in parser that #19497 created, by making whether the parser generates tokens or not a compile-time option.

`ParserConfig::tokens` method replaces `ParseOptions::collect_tokens` property. The former can be const-folded at compile time, where the latter couldn't.

### 3 options

This PR also introduces 3 different config types that users can pass to the parser:

* `NoTokensParserConfig` (default)
* `TokensParserConfig`
* `RuntimeParserConfig`

The first 2 set whether tokens are collected or not at compile time. The last sets it at runtime.

All 3 implement `ParserConfig`.

`NoTokensParserConfig` is the default, and is what's used in compiler pipeline. It switches tokens off in the parser, and makes all the tokens-related code dead code, which the compiler eliminates. This makes the ability of the parser to generate tokens zero cost when it's not used (in the compiler pipeline).

`TokensParserConfig` is the one to use where you *always* want tokens. This is probably the config that linter will use.

`RuntimeParserConfig` is the one to use when an application decides whether to generate tokens or not at runtime. This config avoids compiling the parser twice, at the cost of runtime checks. This is what NAPI parser package will use.

### Future extension

#### Supporting additional features

In future we intend to build the UTF-8 to UTF-16 offsets conversion table in the parser. This will be more performant than searching through the source text for unicode characters in a 2nd pass later on. But this feature is only required for uses of the parser where we're interacting with JS side (NAPI parser package, linter with JS plugins).

`ParserConfig` can be extended to toggle this feature on/off at compile time or runtime, in the same way as you toggle on/off tokens.

#### Options and configs

This PR introduces `ParserConfig` but leaves `ParseOptions` as it is. So we now have 2 sets of options, passed to `Parser` with `with_options(...)` and `with_config(...)`. This is confusing.

We could merge the 2 by making `ParseOptions` implement `ParserConfig`, so then you'd define all options with one `with_options` call.

This would have the side effect of making all other parser options (e.g. `preserve_parens`) able to be set at either runtime or compile time, depending on the use case.

For users consuming `oxc_parser` as a library, with specific needs, they could also configure `Parser` to their needs e.g. create a parser which only handles plain JS code with all the code paths for JSX and TS shaken out as dead code. This would likely improve parsing speed significantly for these use cases.

### Implementation details

Why a trait instead of a cargo feature?

IMO a trait is preferable for the following reasons:

1. We have 3 different use cases we need to support (the 3 provided configs). 3 different Cargo features would be unwieldy.
2. This situation would become far worse once we introduce more features e.g. UTF-8 -> UTF-16 conversion.
3. No problems around feature unification. We found Cargo features caused headaches when we used them in linter for toggling on/off JS plugins support.
4. No clippy errors which only appear when the feature is/isn't disabled, requiring complex `#[cfg_attr(feature = "whatever", expect(clippy::unused_async))]` etc.

The introduction of a trait does not seem to significantly affect compile time:

```
# Before this PR
cargo build -p oxc_parser                             15.33s user 3.27s system 254% cpu 7.316 total
cargo build -p oxc_parser --release                   17.36s user 2.26s system 231% cpu 8.477 total
cargo build -p oxc_parser --example parser            18.43s user 3.75s system 271% cpu 8.156 total
cargo build -p oxc_parser --example parser --release  32.52s user 2.59s system 180% cpu 19.454 total

# After this PR
cargo build -p oxc_parser                             15.00s user 3.24s system 272% cpu 6.692 total
cargo build -p oxc_parser --release                   16.71s user 2.12s system 287% cpu 6.539 total
cargo build -p oxc_parser --example parser            18.50s user 3.91s system 285% cpu 7.845 total
cargo build -p oxc_parser --example parser --release  33.48s user 2.63s system 169% cpu 21.263 total
```

Measured on Mac Mini M4 Pro, `cargo clean` run before each. The difference appears to be mostly within the noise threshold.
2026-02-24 02:54:46 +00:00
camc314 9e11dc63a0 feat(parser,estree,coverage): collect tokens in parser and convert to ESTree format (#19497)
Implement a `collect_tokens` option for parser. When enabled, the parser stores all tokens produced during parsing in a `Vec`. Implement serializing the tokens to ESTree format.

Currently this is not hooked up to NAPI parser or linter. That happens in a later PR in this stack (#19498).

Serialization of tokens implementation is in new `oxc_estree_tokens` crate. Unfortunately it can't go in existing `oxc_estree` crate because `oxc_ast` depends on `oxc_estree`, and the tokens serialization code requires AST types - which would be a circular dependency. We may be able to move it into another crate (parser?) later on.
2026-02-24 02:35:12 +00:00
leaysgur fc66d6dfd1 refactor(formatter): Remove experimental prefix from sort_imports (#19472)
Internal changes that do not affect Oxfmt users.
2026-02-17 05:03:21 +00:00
leaysgur 1bb0559a32 feat(playground): Support more sort-imports options (#19424)
It hadn't been updated for some time, so I made it compatible with the latest state.

(I also want to stop manual mapping, but we are not yet ready for that...)
2026-02-16 05:05:09 +00:00
leaysgur e48e6b2f77 refactor(formatter,oxfmt): Simplify sort_imports options parsing (#19418)
Introduce `GroupEntry::parse()` to reduce redundants.
2026-02-16 05:05:08 +00:00
leaysgur 9c34f7226b feat(formatter/sort_imports)!: Report invalid group name with renaming side-effect > side_effect (#19416)
- Breaking: Rename ambiguous modifier/selector names
  - `side-effect` → `side_effect`
  - `side-effect-style` → `side_effect_style`
  - Now `-` is used only for joiner (`modifier-modifier-selector`)
    - before: `value-side-effect-style` 🆚 after: `value-side_effect_style`
- Breaking: Add config-time validation for invalid group names
  - previously silently ignored...
  - or wrongly applied (and user didn't notice)
2026-02-16 05:05:08 +00:00
leaysgur 4baebefa81 feat(formatter/sort_imports): Support { newlinesBetween: bool } inside groups (#19358)
Fixes #17830

Introduce new variant for `groups` like:

```json
  // "newlinesBetween": true, // Default
  "groups": [
      ["value-builtin", "value-external"],
      { "newlinesBetween": false }, // 👈🏻
      "value-parent",
      "value-sibling"
  ]
```

This allows you to specify line breaks globally between groups while reverting them at any position.

Conversely, you can also add line breaks at any position while specifying `newlinesBetween: false`.
2026-02-14 14:12:48 +00:00
sapphi-red 2bf7293c69 feat(mangler)!: enable top_level by default for modules and commonjs (#18278)
I think we can enable `top_level` by default.

refs #18233
2026-02-06 12:48:44 +00:00
overlookmotel 005910a084 fix(linter/plugins): support plugins outside of workspace (#18755)
## The problem

### 1. Bug with plugins outside of workspace directory

There was a bug in workspaces implementation. `loadPlugin` was assuming that the plugin file is within the workspace, but that's not necessarily the case. e.g.:

* Workspace 1: `/path/to/project/packages/foo`
* Workspace 2: `/path/to/project/packages/bar`
* JS plugin: `/path/to/project/node_modules/eslint-plugin-whatever`

This problem arises because `loadPlugin` is determining which workspace to load a plugin for by search for the workspace whose root directory contains the plugin file.

### 2. Performance

Every call to `loadPlugin`, `setupRuleConfigs`, or `lintFile` was first searching through all workspaces to find the right one (`getResponsibleWorkspace`).

This is really expensive when there are lots of workspaces, and quite expensive even when there's only 1 because iterating hashsets is relatively slow.

## This PR

This PR separates out CWD and workspace identifiers into 2 different concerns.

* Each workspace has an ID which is unique.
* Each workspace has a CWD (path to root directory as a file path).

The workspace ID is currently the workspace's root directory as a `file://...` URL. Using that as it's guaranteed unique, even for paths which are not valid UTF-8 (`Path::to_string_lossy` can produce the same `&str` for different paths). But ID it doesn't have to be the URI - we could change it later to an integer ID if we want.

Rust-side code passes the workspace ID to every call to `loadPlugin`, `setupRuleConfigs`, or `lintFile`.

In CLI, it just passes `null` as the ID. There's only ever 1 workspace in CLI, so no point in the expense of passing a `String` on every call.

These functions look up the CWD from the workspace ID in the `cwds` map.

Separating these 2 concerns solves both problems listed above:

1. Plugins can be outside of the workspace's CWD (see added test).
2. `getResponsibleWorkspace` is removed, solving the perf issue.

Note: The multiple hashmap lookups on each call to `lintFile` remain a perf drain. This is addressed in a later PR in this stack.
2026-01-31 15:27:30 +00:00
renovate b67ab40408 chore(deps): update dependency rust to v1.93.0 (#18406)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [rust](https://redirect.github.com/rust-lang/rust) | minor | `1.92.0` → `1.93.0` |

---

### Release Notes

<details>
<summary>rust-lang/rust (rust)</summary>

### [`v1.93.0`](https://redirect.github.com/rust-lang/rust/blob/HEAD/RELEASES.md#Version-1930-2026-01-22)

[Compare Source](https://redirect.github.com/rust-lang/rust/compare/1.92.0...1.93.0)

\==========================

<a id="1.93.0-Language"></a>

## Language

- [Stabilize several s390x `vector`-related target features and the `is_s390x_feature_detected!` macro](https://redirect.github.com/rust-lang/rust/pull/145656)
- [Stabilize declaration of C-style variadic functions for the `system` ABI](https://redirect.github.com/rust-lang/rust/pull/145954)
- [Emit error when using some keyword as a `cfg` predicate](https://redirect.github.com/rust-lang/rust/pull/146978)
- [Stabilize `asm_cfg`](https://redirect.github.com/rust-lang/rust/pull/147736)
- [During const-evaluation, support copying pointers byte-by-byte](https://redirect.github.com/rust-lang/rust/pull/148259)
- [LUB coercions now correctly handle function item types, and functions with differing safeties](https://redirect.github.com/rust-lang/rust/pull/148602)
- [Allow `const` items that contain mutable references to `static` (which is *very* unsafe, but not *always* UB)](https://redirect.github.com/rust-lang/rust/pull/148746)
- [Add warn-by-default `const_item_interior_mutations` lint to warn against calls which mutate interior mutable `const` items](https://redirect.github.com/rust-lang/rust/pull/148407)
- [Add warn-by-default `function_casts_as_integer` lint](https://redirect.github.com/rust-lang/rust/pull/141470)

<a id="1.93.0-Compiler"></a>

## Compiler

- [Stabilize `-Cjump-tables=bool`](https://redirect.github.com/rust-lang/rust/pull/145974). The flag was previously called `-Zno-jump-tables`.

<a id="1.93.0-Platform-Support"></a>

## Platform Support

- [Promote `riscv64a23-unknown-linux-gnu` to Tier 2 (without host tools)](https://redirect.github.com/rust-lang/rust/pull/148435)

Refer to Rust's [platform support page][platform-support-doc]
for more information on Rust's tiered platform support.

[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html

<a id="1.93.0-Libraries"></a>

## Libraries

- [Stop internally using `specialization` on the `Copy` trait as it is unsound in the presence of lifetime dependent `Copy` implementations. This may result in some performance regressions as some standard library APIs may now call `Clone::clone` instead of performing bitwise copies](https://redirect.github.com/rust-lang/rust/pull/135634)
- [Allow the global allocator to use thread-local storage and `std::thread::current()`](https://redirect.github.com/rust-lang/rust/pull/144465)
- [Make `BTree::append` not update existing keys when appending an entry which already exists](https://redirect.github.com/rust-lang/rust/pull/145628)
- [Don't require `T: RefUnwindSafe` for `vec::IntoIter<T>: UnwindSafe`](https://redirect.github.com/rust-lang/rust/pull/145665)

<a id="1.93.0-Stabilized-APIs"></a>

## Stabilized APIs

- [`<[MaybeUninit<T>]>::assume_init_drop`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.assume_init_drop)
- [`<[MaybeUninit<T>]>::assume_init_ref`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.assume_init_ref)
- [`<[MaybeUninit<T>]>::assume_init_mut`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.assume_init_mut)
- [`<[MaybeUninit<T>]>::write_copy_of_slice`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.write_copy_of_slice)
- [`<[MaybeUninit<T>]>::write_clone_of_slice`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.write_clone_of_slice)
- [`String::into_raw_parts`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.into_raw_parts)
- [`Vec::into_raw_parts`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.into_raw_parts)
- [`<iN>::unchecked_neg`](https://doc.rust-lang.org/stable/std/primitive.isize.html#method.unchecked_neg)
- [`<iN>::unchecked_shl`](https://doc.rust-lang.org/stable/std/primitive.isize.html#method.unchecked_shl)
- [`<iN>::unchecked_shr`](https://doc.rust-lang.org/stable/std/primitive.isize.html#method.unchecked_shr)
- [`<uN>::unchecked_shl`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.unchecked_shl)
- [`<uN>::unchecked_shr`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.unchecked_shr)
- [`<[T]>::as_array`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_array)
- [`<[T]>::as_array_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_mut_array)
- [`<*const [T]>::as_array`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_array)
- [`<*mut [T]>::as_array_mut`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_mut_array)
- [`VecDeque::pop_front_if`](https://doc.rust-lang.org/stable/std/collections/struct.VecDeque.html#method.pop_front_if)
- [`VecDeque::pop_back_if`](https://doc.rust-lang.org/stable/std/collections/struct.VecDeque.html#method.pop_back_if)
- [`Duration::from_nanos_u128`](https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_nanos_u128)
- [`char::MAX_LEN_UTF8`](https://doc.rust-lang.org/stable/std/primitive.char.html#associatedconstant.MAX_LEN_UTF8)
- [`char::MAX_LEN_UTF16`](https://doc.rust-lang.org/stable/std/primitive.char.html#associatedconstant.MAX_LEN_UTF16)
- [`std::fmt::from_fn`](https://doc.rust-lang.org/stable/std/fmt/fn.from_fn.html)
- [`std::fmt::FromFn`](https://doc.rust-lang.org/stable/std/fmt/struct.FromFn.html)

<a id="1.93.0-Cargo"></a>

## Cargo

- [Enable CARGO\_CFG\_DEBUG\_ASSERTIONS in build scripts based on profile](https://redirect.github.com/rust-lang/cargo/pull/16160/)
- [In `cargo tree`, support long forms for `--format` variables](https://redirect.github.com/rust-lang/cargo/pull/16204/)
- [Add `--workspace` to `cargo clean`](https://redirect.github.com/rust-lang/cargo/pull/16263/)

<a id="1.93.0-Rustdoc"></a>

## Rustdoc

- [Remove `#![doc(document_private_items)]`](https://redirect.github.com/rust-lang/rust/pull/146495)
- [Include attribute and derive macros in search filters for "macros"](https://redirect.github.com/rust-lang/rust/pull/148176)
- [Include extern crates in search filters for `import`](https://redirect.github.com/rust-lang/rust/pull/148301)
- [Validate usage of crate-level doc attributes](https://redirect.github.com/rust-lang/rust/pull/149197).  This means if any of `html_favicon_url`, `html_logo_url`, `html_playground_url`, `issue_tracker_base_url`, or `html_no_source` either has a missing value, an unexpected value, or a value of the wrong type, rustdoc will emit the deny-by-default lint `rustdoc::invalid_doc_attributes`.

<a id="1.93.0-Compatibility-Notes"></a>

## Compatibility Notes

- [Introduce `pin_v2` into the builtin attributes namespace](https://redirect.github.com/rust-lang/rust/pull/139751)
- [Update bundled musl to 1.2.5](https://redirect.github.com/rust-lang/rust/pull/142682)
- [On Emscripten, the unwinding ABI used when compiling with `panic=unwind` was changed from the JS exception handling ABI to the wasm exception handling ABI.](https://redirect.github.com/rust-lang/rust/pull/147224) If linking C/C++ object files with Rust objects, `-fwasm-exceptions` must be passed to the linker now. On nightly Rust, it is possible to get the old behavior with `-Zwasm-emscripten-eh=false -Zbuild-std`, but it will be removed in a future release.
- The `#[test]` attribute, used to define tests, was previously ignored in various places where it had no meaning (e.g on trait methods or types). Putting the `#[test]` attribute in these places is no longer ignored, and will now result in an error; this may also result in errors when generating rustdoc. [Error when `test` attribute is applied to structs](https://redirect.github.com/rust-lang/rust/pull/147841)
- Cargo now sets the `CARGO_CFG_DEBUG_ASSERTIONS` environment variable in more situations. This will cause crates depending on `static-init` versions 1.0.1 to 1.0.3 to fail compilation with "failed to resolve: use of unresolved module or unlinked crate `parking_lot`". See [the linked issue](https://redirect.github.com/rust-lang/rust/issues/150646#issuecomment-3718964342) for details.
- [User written types in the `offset_of!` macro are now checked to be well formed.](https://redirect.github.com/rust-lang/rust/issues/150465/)
- `cargo publish` no longer emits `.crate` files as a final artifact for user access when the `build.build-dir` config is unset
- [Upgrade the `deref_nullptr` lint from warn-by-default to deny-by-default](https://redirect.github.com/rust-lang/rust/pull/148122)
- [Add future-incompatibility warning for `...` function parameters without a pattern outside of `extern` blocks](https://redirect.github.com/rust-lang/rust/pull/143619)
- [Introduce future-compatibility warning for `repr(C)` enums whose discriminant values do not fit into a `c_int` or `c_uint`](https://redirect.github.com/rust-lang/rust/pull/147017)
- [Introduce future-compatibility warning against ignoring `repr(C)` types as part of `repr(transparent)`](https://redirect.github.com/rust-lang/rust/pull/147185)

</details>

---

### Configuration

📅 **Schedule**: 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:eyJjcmVhdGVkSW5WZXIiOiI0Mi44NS4xIiwidXBkYXRlZEluVmVyIjoiNDIuODUuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
2026-01-22 16:21:00 +00:00
nilptr 984d5c1ca6 feat(oxfmt/sort-imports): support options.customGroups (#17576)
#17076 

### Authorship & Use of AI

All code changes in this pull request are completed by a human (myself).
ChatGPT is used **only** to assist with writing comments and this PR
description.

### Commit Structure

The changes are split into small, meaningful commits.
Each commit should be reviewable independently.

## Current State of the Implementation

For easier comparison, I forked
[actual](https://github.com/nilptr/actual) and made a few auxiliary
changes on a separate branch
[nilptr/feat/oxfmt-custom-groups](https://github.com/nilptr/actual/tree/nilptr/feat/oxfmt-custom-groups).

The auxiliary changes include:

- Upgraded `oxfmt` to address the incompatible binding API.
- Updated `eslint.config.mjs` to lint and fix .ts files.
- Updated `.oxfmtrc` to enable `experimentalSortImports`, keeping it
consistent with `eslint.config.mjs`.

### How to Compare? 

1. Clone `git@github.com:nilptr/actual.git` and check out
`nilptr/feat/oxfmt-custom-groups` branch.
2. Run `yarn install`
3. Run `NAPI_RS_NATIVE_LIBRARY_PATH=/path/to/binding.node yarn
fmt:oxfmt`

    The native binding can be obtained in one of the following ways:
- Download the
[oxfmt.darwin-arm64.node.zip](https://github.com/user-attachments/files/24402116/oxfmt.darwin-arm64.node.zip)
built by me (if you trust it, or scan it with
[VirusTotal](https://www.virustotal.com/gui/home/upload)).
- Build it by yourself: clone my repo, run `pnpm build`, and locate the
.node binding file under `apps/oxfmt/src-js`.
    
4. Inspect the results with `git diff` — the diff shows the differences
between perfectionist and oxfmt

### Observed Differences

There are 9 differences in total:

- **4 differences** are unrelevant to `experimentalSortImports`. (be the
way, they are introduced after I rebased main branch)
- **5 differences** are relevant to `experimentalSortImports`, but are
not caused by the group-matching logic, which is the focus of this PR.
  - 4 of them are blank lines after side-effect imports being removed
  - 1 of them is the wrong `ImportLineMetadata.source`

<img width="4064" height="2152" alt="image"
src="https://github.com/user-attachments/assets/8b4cb860-19d6-42b9-a0a4-6db0b3e40194"
/>

<img width="4064" height="2152" alt="image"
src="https://github.com/user-attachments/assets/e2616d7b-b4b2-4dbc-a0d3-ea15d1c0177e"
/>

------

I believe we can start review now.
While the review is ongoing, I will also try to see whether the observed
differences can be fixed in this PR.

---------

Co-authored-by: Yuji Sugiura <y.sugiura.0316@gmail.com>
2026-01-19 10:23:49 +09:00
Dunqing 26ed46bc02 feat(formatter): add TailwindCSS support for JS/TS files (#16990)
Based on https://github.com/oxc-project/oxc/pull/16826

## Summary

Add experimental Tailwind CSS class sorting support to Oxfmt via integration with `prettier-plugin-tailwindcss`.

This implementation uses a patch-based approach to export necessary functions from `prettier-plugin-tailwindcss`. We've requested better integration support from the Tailwind team at https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/430, and can improve the integration when that's available.

## Features

- ✅ Sorts Tailwind CSS classes in `className` and `class` attributes
- ✅ Sorts classes in custom JSX attributes (configurable)
- ✅ Sorts classes in function calls like `clsx()`, `cn()`, `tw()` (configurable)
- ✅ Supports template literals with expressions
- ✅ Supports Tailwind v3 config files and v4 stylesheets
- ✅ Preserves whitespace and duplicates (configurable)
- ✅ **Fixes [prettier-plugin-tailwindcss#426](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/426)** - Correctly handles strings in nested non-Tailwind call expressions
- ⏳ Non-JS file support (HTML, Vue, Svelte, etc.) - Coming in follow-up PR
- ⏳ Tailwind CSS config migration in `oxfmt --migrate=prettier` - Coming in follow-up PR

## Example Usage

### Basic Usage

Enable Tailwind CSS sorting with default options by setting `experimentalTailwindcss` to an empty object in your `.oxfmtrc.json`:

```json
{
  "experimentalTailwindcss": {}
}
```

### With Configuration Options

```json
{
  "experimentalTailwindcss": {
    "config": "./tailwind.config.js",
    "functions": ["clsx", "cn", "cva", "tw"],
    "attributes": ["myCustomClass"],
    "preserveWhitespace": false,
    "preserveDuplicates": false
  }
}
```

### Configuration Options

All options from `prettier-plugin-tailwindcss` are supported (with `tailwind` prefix removed):

| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `config` | `string` | Path to your Tailwind CSS v3 configuration file. Paths are resolved relative to the Oxfmt configuration file. | `"./tailwind.config.js"` |
| `stylesheet` | `string` | Path to your Tailwind CSS v4 stylesheet. Paths are resolved relative to the Oxfmt configuration file. | `undefined` |
| `functions` | `string[]` | List of custom function names that contain Tailwind CSS classes (e.g., `["clsx", "cn", "cva", "tw"]`). | `[]` |
| `attributes` | `string[]` | List of custom JSX attributes that should be sorted (e.g., `["myCustomClass"]`). Note: `class` and `className` are always sorted by default. | `[]` |
| `preserveWhitespace` | `boolean` | Preserve whitespace around classes. | `false` |
| `preserveDuplicates` | `boolean` | Preserve duplicate classes. | `false` |

## Migration from prettier-plugin-tailwindcss

If you're currently using `prettier-plugin-tailwindcss`, you can migrate to Oxfmt's built-in support:

### Manual Migration (Current)

#### Before (Prettier)

```json
{
  "plugins": ["prettier-plugin-tailwindcss"],
  "tailwindConfig": "./tailwind.config.js",
  "tailwindFunctions": ["clsx", "cn"]
}
```

#### After (Oxfmt)

```json
{
  "experimentalTailwindcss": {
    "config": "./tailwind.config.js",
    "functions": ["clsx", "cn"]
  }
}
```

**Key differences:**
- Remove `tailwind` prefix from all option names (e.g., `tailwindConfig` → `config`)
- No need to install `prettier-plugin-tailwindcss` separately, it is bundled within Oxfmt
### Automated Migration (Follow-up PR)

Tailwind CSS configuration migration will be added to the existing `oxfmt --migrate=prettier` command in a follow-up PR. This will automatically convert your `prettier-plugin-tailwindcss` settings from `.prettierrc` to `.oxfmtrc.json`.

## Implementation Details

### Technical Approach

- Uses `pnpm patch` to export necessary functions from `prettier-plugin-tailwindcss`
- Implements Rust-side detection for Tailwind contexts (JSX attributes, function calls, template literals)
- Passes class strings to the JS plugin for sorting via external callback
- Optimized context tracking to avoid repeated AST traversal

### Improvements Over prettier-plugin-tailwindcss

- **Context-based quasi position tracking** ([tailwindcss.rs:273-276](https://github.com/oxc-project/oxc/blob/main/crates/oxc_formatter/src/utils/tailwindcss.rs#L273-L276)) - Stores template literal position in context stack instead of traversing AST, improving performance
- **Fixes [issue #426](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/426)** - Correctly handles strings in nested non-Tailwind call expressions (e.g., `value.includes("\n")` no longer gets corrupted)
- **Comprehensive edge case testing** - Added 58 tests covering edge cases, nested scenarios, error handling, and performance

## Changes

- Changed `experimentalTailwindcss` option from `boolean` to `TailwindcssOptions` object
- Added all configuration options from `prettier-plugin-tailwindcss` (without `tailwind` prefix)
- Exported necessary functions from `prettier-plugin-tailwindcss` via patch
- Added comprehensive test suite with 58 tests
- Improved documentation for all configuration options

## Test Plan

- [x] `pnpm test` passes (58 tests)
- [x] `cargo clippy` passes
- [x] `just fmt` passes
- [x] Basic Tailwind class sorting works with `class` and `className` attributes
- [x] `attributes` option sorts custom JSX attributes
- [x] `functions` option sorts string arguments in function calls (e.g., `clsx()`, `cn()`)
- [x] Template literals with expressions handle whitespace correctly
- [x] Edge cases: empty strings, Unicode, emoji, special characters
- [x] Nested scenarios: deeply nested template literals, function calls
- [x] Strings in nested non-Tailwind calls are preserved (fixes [#426](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/426))
- [x] Test in https://github.com/tailwindlabs/headlessui and https://github.com/dubinc/dub

## Follow-up Work

- **Non-JS file support** - HTML, Vue, Svelte, etc. by enabling original plugin when `experimentalTailwindcss` is set
- **Migration support** - Add Tailwind CSS config migration to existing `oxfmt --migrate=prettier` command
- **Better integration** - Improve when https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/430 is resolved

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-01-05 10:07:07 +00:00
renovate 3741b8171c chore(deps): update napi (#17481)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | Type | Update |
|---|---|---|---|---|---|---|---|
| [@napi-rs/cli](https://napi.rs/) ([source](https://redirect.github.com/napi-rs/napi-rs)) | [`3.5.0` → `3.5.1`](https://renovatebot.com/diffs/npm/@napi-rs%2fcli/3.5.0/3.5.1) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@napi-rs%2fcli/3.5.1?slim=true) | ![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@napi-rs%2fcli/3.5.1?slim=true) | ![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@napi-rs%2fcli/3.5.0/3.5.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@napi-rs%2fcli/3.5.0/3.5.1?slim=true) | pnpm.catalog.default | patch |
| [napi](https://redirect.github.com/napi-rs/napi-rs) | `3.7.1` → `3.8.1` | ![age](https://developer.mend.io/api/mc/badges/age/crate/napi/3.8.1?slim=true) | ![adoption](https://developer.mend.io/api/mc/badges/adoption/crate/napi/3.8.1?slim=true) | ![passing](https://developer.mend.io/api/mc/badges/compatibility/crate/napi/3.7.1/3.8.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/crate/napi/3.7.1/3.8.1?slim=true) | workspace.dependencies | minor |
| [napi-derive](https://redirect.github.com/napi-rs/napi-rs) | `3.4.1` → `3.5.0` | ![age](https://developer.mend.io/api/mc/badges/age/crate/napi-derive/3.5.0?slim=true) | ![adoption](https://developer.mend.io/api/mc/badges/adoption/crate/napi-derive/3.5.0?slim=true) | ![passing](https://developer.mend.io/api/mc/badges/compatibility/crate/napi-derive/3.4.1/3.5.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/crate/napi-derive/3.4.1/3.5.0?slim=true) | workspace.dependencies | minor |

---

### Release Notes

<details>
<summary>napi-rs/napi-rs (@&#8203;napi-rs/cli)</summary>

### [`v3.5.1`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.5.0...@napi-rs/cli@3.5.1)

[Compare Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.5.0...@napi-rs/cli@3.5.1)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0Mi41OS4wIiwidXBkYXRlZEluVmVyIjoiNDIuNjYuMTQiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=-->
2025-12-31 12:52:40 +00:00
renovate 6cf7173c29 chore(deps): update dependency oxfmt to ^0.20.0 (#17263)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|---|---|
| [oxfmt](https://oxc.rs) ([source](https://redirect.github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt)) | [`^0.19.0` -> `^0.20.0`](https://renovatebot.com/diffs/npm/oxfmt/0.19.0/0.20.0) | ![age](https://developer.mend.io/api/mc/badges/age/npm/oxfmt/0.20.0?slim=true) | ![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/oxfmt/0.20.0?slim=true) | ![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/oxfmt/0.19.0/0.20.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/oxfmt/0.19.0/0.20.0?slim=true) |

---

### Release Notes

<details>
<summary>oxc-project/oxc (oxfmt)</summary>

### [`v0.20.0`](https://redirect.github.com/oxc-project/oxc/blob/HEAD/npm/oxfmt/CHANGELOG.md#0200---2025-12-22)

[Compare Source](https://redirect.github.com/oxc-project/oxc/compare/oxfmt_v0.19.0...oxfmt_v0.20.0)

##### 🚀 Features

- [`97a02d1`](https://redirect.github.com/oxc-project/oxc/commit/97a02d1) oxfmt: Add `insertFinalNewline` option ([#&#8203;17251](https://redirect.github.com/oxc-project/oxc/issues/17251)) (leaysgur)

</details>

---

### Configuration

📅 **Schedule**: 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:eyJjcmVhdGVkSW5WZXIiOiI0Mi41OS4wIiwidXBkYXRlZEluVmVyIjoiNDIuNTkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
2025-12-22 12:52:43 +00:00
Boshen c8281dd0bb chore(pnpm): move workspace devDependencies to used packages (#16982)
To ensure monorepo dependencies are only visible to where they are used.
2025-12-17 15:09:08 +08:00
camc314 4cbedc9437 fix(playground): pass transform target to minifier (#16910)
The playground minifier was not respecting the transform target option, causing ES features like optional chaining to be used even when targeting older browsers that do not support them.

Closes #16889
2025-12-16 11:49:07 +00:00
Boshen 6fca2f7f60 chore(infra): use oxfmt for all files, remove dprint (#16599)
Toml formatting is missing but there's only a few toml files, can wait
for oxfmt plugins.

- [x] Something is slow, can't figure out why, need #16607
2025-12-15 12:06:24 +00:00
leaysgur afd6c44cd7 feat(oxfmt): Support quoteProps: consistent in Oxfmtrc (#16721)
Follow up #16677, fixes #16247.
2025-12-11 02:03:49 +00:00
overlookmotel 083fea9fa6 feat(napi/parser)!: represent empty optional fields on JS side as null (#16411)
Follow-on after #16383 and #16403.

While working on those I discovered an option NAPI-RS has to represent `Option::None` as `null`, instead of omitting the field entirely.

Use that option on the structs used for transferring errors and module record over to JS.

I think this is likely more performant because:

1. NAPI-RS can create all properties of the objects using only the faster `node_api_create_object_with_properties` API.
2. It produces consistent object shapes, so JS engine can better optimize code using these objects.

It also brings the shape of the data perfectly into line between standard transfer and raw transfer, and is consistent with how empty fields in the AST are represented as `null`.

I would have used this option before if I'd known it existed.

### Breaking change

I've marked this as a breaking change because code consuming these objects would now need to check for empty fields with `value === null` instead of `value === undefined`.

However in practice, most people probably use `!value` or `value ?? ...`, so it's unlikely to affect many users. This only affects module record and errors anyway, not the AST itself, as we transfer that as JSON, not via NAPI.
2025-12-02 23:30:56 +00:00
leaysgur 8c2d09f39f refactor(formatter/sort-imports): Resolve default options.(internalPattern|groups) in options.rs (#16375)
Yet another pure refactoring.
2025-12-02 07:37:06 +00:00
leaysgur e5a78ed269 refactor(formatter/sort-imports): Move SortImportsOptions to sort_imports/options.rs (#16374)
Pure refactoring.
2025-12-02 07:37:06 +00:00
leaysgur 73748568af feat(formatter/sort-imports): Support options.internalPattern (#16372)
Part of #14253

The original implementation accepts a `RegExp` string, but based on the actual usage, it seems that checking as prefix is sufficient for now.

https://github.com/search?q=%22internalPattern%3A+%5B%22&type=code
2025-12-02 07:37:06 +00:00