Commit Graph

14287 Commits

Author SHA1 Message Date
autofix-ci[bot] 452a3edcd7 [autofix.ci] apply automated fixes 2026-08-19 13:41:26 +00:00
Cameron 10077eb4d5 u 2026-08-19 14:40:01 +01:00
Cameron 687c06e219 u 2026-08-19 13:36:54 +01:00
Cameron 3b6ee86b1b u 2026-08-19 11:40:22 +01:00
Cameron bc305f7c32 perf test 2026-08-19 11:24:54 +01:00
oxc-guard 0389c4010e release(crates): oxc v0.146.0 (#25912)
### 🚀 Features

- 83cb516 minifier: Improve negation of expressions in boolean context (#25755) (Armano)

### 🐛 Bug Fixes

- 134fb22 isolated-declarations: Preserve unresolved parameter types (#25909) (Dunqing)
- 526c2b3 codegen: Remove deprecated test APIs (#25889) (camc314)
- 344a9b2 codegen: Align enum template sourcemaps (#25872) (camc314)

### ⚡ Performance

- 5ef0c93 minifier: Unwrap all unary exprs without creating dummy expressions (#25891) (Armano)
- 8783524 packages/codegen: Remove loop from `growMappingBuffer` (#25886) (overlookmotel)
- b6b14c0 packages/codegen: Remove redundant branch (#25885) (overlookmotel)
- be6adf0 packages/sourcemap: Make `location` passed to `recordSourceMapping` always a number (#25884) (overlookmotel)
- f720f15 packages/codegen: Take named-mapping only code off hot path in `recordSourceMapping` (#25883) (overlookmotel)
- a734af5 packages/codegen: Remove redundant setting of `last` in `printImportAttributes` and `printIf` (#25882) (overlookmotel)
- c6e44c6 packages/codegen: Combine 2 writes in `printJSXElement` (#25881) (overlookmotel)
- 8801662 packages/codegen: Remove redundant mapping from `printParams` (#25880) (overlookmotel)

### 📚 Documentation

- 5ad330e packages/codegen: Reformat comments (#25876) (overlookmotel)
2026-08-19 08:29:51 +00:00
Connor Shea 51d36d7228 fix(linter/vue): resolve vue imports via shared import helpers (#25903)
AI Disclosure: Generated with Claude Code, Opus 5. Tested and reviewed
by me.

Why do many code when few do trick?

Follow-up to #25901. Hand-rolled import checks can simply be replaced
with the shared `is_import_symbol` helper, this also helps improve the
accuracy of the code to catch aliased imports for one of them.

### Bug fixed

`is_vue_define_component_reference` (used by
`vue/no-deprecated-delete-set` to decide whether a `this.$set` /
`this.$delete` call sits inside a Vue component) gated on the **local**
name being `defineComponent` before walking up to the
`ImportDeclaration`, so an aliased import was never recognised:

```vue
<script>
import { defineComponent as dc } from 'vue'
dc({
  mounted () {
    this.$set(obj, key, value)
    this.$delete(obj, key)
  }
})
</script>
```

Neither call was reported before; both are now. Covered by a new test
case.

### Pure simplifications

- `is_imported_set_or_del_from_vue` (`no-deprecated-delete-set`) — 20
lines → two `is_import_symbol` calls.
- `is_vue_next_tick_import` (`utils/vue.rs`, used by
`vue/next-tick-style` and `vue/valid-next-tick`) — 18 lines → one call.

Both already matched by symbol id, so behavior is unchanged.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 09:05:56 +01:00
Connor Shea 83a68d2296 fix(linter/react/no-react-children): resolve react imports by symbol (#25901)
AI Disclosure: Generated with Claude Code, Opus 5. Tested and reviewed
by me.

`no-react-children` decided whether an identifier came from `react` with
a hand-rolled helper that matched by **name**:

```rust
fn is_imported_from_react(local_name: &str, ctx: &LintContext) -> bool {
    ctx.module_record().import_entries.iter().any(|entry| {
        entry.module_request.name() == "react" && entry.local_name.name() == local_name
    })
}
```

This replaces it with the existing shared helpers, `is_import_symbol`
and `is_import_from_module`.

### Bugs fixed

Four false positives, where a local binding shadowed the import:

```js
import { Children } from 'react';
function f(x) { const Children = { count: () => 0 }; return Children.count(x); }

import React from 'react';
function f(x) { const React = { Children: { map: () => {} } }; return React.Children.map(x); }

import { Children } from 'react';
export function f(Children) { return Children.count(1); }

import { count as Children } from 'react'; Children.toArray(x)
```

One false negative, an aliased import the local-name check couldn't see:

```js
import { Children as C } from 'react'; C.toArray(x)
```

All five are covered by new test cases.

### Performance

This has a minor downside in perf, but it's significantly less than a
millisecond difference and is probably worth the downside. We had
originally decided not to care about aliases when implementing this
rule, but this change allows us to reuse the existing util code for
checking where the import comes from, and fixes the aforementioned edge
cases, so I think this is a worthwhile change.

Measured with two `--features allocator` builds, paired and interleaved,
using `--debug=timings --threads=1` to isolate the rule's own time from
parse/semantic noise.

| corpus | base | new | change |
| --- | --- | --- | --- |
| sentry | 4.416ms | 4.589ms | +3.9% slower |
| material-ui | 3.111ms | 3.217ms | +3.4% slower |
| bluesky | 0.503ms | 0.540ms | +7.2% slower |
| actualbudget | 2.012ms | 2.105ms | +4.6% slower |
| synthetic, `Children` used heavily | 19.112ms | 15.443ms | 19.2%
faster |

There is a small, consistent regression on real code, and it's
deliberate. Dropping the `ident.name == "Children"` gate means every
`obj.method()` call in a file importing `react` now does two array
lookups instead of one string compare; removing the `O(imports)` scan
only pays off where `Children` is actually used, which is 27 files out
of 8.8K in sentry. The absolute cost is ≤0.17ms, and `no-react-children`
isn't a default rule anyway.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Cameron <cameron.clark@hey.com>
2026-08-19 08:05:03 +00:00
Connor Shea 124e196551 fix(linter): resolve globals by reference, not by name (#25905)
AI Disclosure: Generated with Claude Code, Opus 5. Tested and reviewed
by me.

Ten call sites across nine rules checked whether an identifier is a
specific global with:

```rust
ctx.scoping().root_unresolved_references().contains_key(&ident.name)
```

That asks whether the **name** is unresolved **somewhere in the file**,
not whether **this reference** is global. Any file that shadows the
global in one scope and uses the real global in another gets a false
positive on the shadowed use:

```js
function f(myLogger) {
  const console = myLogger;
  console.log(1);        // reported by `no-console` — this is the bug
}
console.log(2);          // the real global, elsewhere in the file
```

`ctx.is_reference_to_global_variable(ident)` already exists for this and
is used at ~50 other call sites. It is `reference.symbol_id().is_none()`
— one field read instead of hashing the name into the
unresolved-references map — and it honours the user's `globals: { X:
"off" }` config.

### Rules fixed

| rule | shadowed code that was wrongly reported |
| --- | --- |
| `eslint/no-console` | `const console = myLogger; console.log(foo)` |
| `eslint/no-constant-binary-expression` | `const String = MyString;
String(x) ?? y` |
| `eslint/no-new-native-nonconstructor` | `const Symbol = FakeSymbol;
new Symbol()` |
| `eslint/prefer-object-spread` | `const Object = MyObject;
Object.assign({}, a)` |
| `eslint/symbol-description` | `const Symbol = FakeSymbol; Symbol()` |
| `eslint/valid-typeof` | `const undefined = u; typeof x === undefined`
|
| `promise/avoid-new` | `const Promise = FakePromise; new Promise(fn)` |
| `unicorn/new-for-builtins` | `const String = MyString; new
String('x')` |
| `unicorn/prefer-global-this` | `const window = fake; window.foo` |

Each rule gets a pass test in that shape. Both halves matter — without a
second, genuine use of the global the name lookup already returns false,
so a single-scope test would pass before the fix too.

Three of these ship a fix or suggestion (`prefer-global-this`,
`valid-typeof`, `no-console`), so the false positive was rewriting or
deleting code the user owns.

### Real-world impact

Base vs. branch over four corpora (vscode, sentry, material-ui, actual),
each rule isolated with `-A all -A nursery -D <rule> --format=json
--no-ignore --disable-nested-config`, diagnostics normalised to `(file,
span, code, message)`:

**0 added, 23 removed.** Every removal is `prefer-global-this` on a
genuinely shadowed `window`:

- `vscode/src/vs/base/browser/dompurify/dompurify.js` (16) — `let window
= arguments.length > 0 ? arguments[0] : getGlobal()`
- `material-ui/packages-internal/core-docs/src/Demo/DemoSandbox.tsx` (5)
— `function IsolatedDemo({ children, cssVarPrefix, colorSchemeNode,
window })`, a destructured prop
- `sentry/src/sentry/templates/sentry/error-page-embed.js` (2) —
`(function (window, document, JSON) { ... })(window, document, JSON)`,
the classic IIFE parameter

In each file the real global references outside the shadowing scope are
still reported.

### Notes

- `no-console` uses `ident.is_global_reference(ctx.scoping())` rather
than the context helper. An existing fail case pins that `globals: {
console: "off" }` must still report, and the context helper treats
`"off"` as "not a global". The other eight sites use the context helper,
so they now respect that config, but this one doesn't.
- Five upstream ESLint pass cases that were commented out in
`no-constant-binary-expression` now pass and are re-enabled (`function
Boolean(n) { return n; }; Boolean(x) ?? foo` and friends). Two others in
that list stay commented — they are `function foo(undefined) { ... }`,
which is a separate problem: the rule matches `undefined` by name via
`Expression::is_undefined()`. Left a TODO pointing at it.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Cameron <cameron.clark@hey.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Cameron <cameron.clark@hey.com>
2026-08-19 08:03:44 +00:00
Dunqing 134fb2228d fix(isolated-declarations): preserve unresolved parameter types (#25909)
#25292 made isolated declaration emit report a false TS9025 for a defaulted parameter whose annotation is an indexed access type. This breaks Vite's `UserConfig["base"]` case when Rolldown upgrades Oxc, even though the indexed property already includes `undefined`.

Oxc's isolated declaration emitter has no type checker, so it cannot safely resolve indexed accesses, type references, conditional types, or similar annotations. This change uses a syntax-only classification: widen or diagnose types whose syntax is conclusive, and preserve checker-dependent types rather than emit an unstable error. The union/intersection rules retain the distinct behavior of `any`, `never`, `unknown`, and `void`, and an explicit `undefined` union member is never duplicated.

Regression coverage includes the Rolldown/Vite case and a compact matrix of the relevant type algebra. The implementation and tests were reduced to one commit after review.

Validated with the isolated-declarations tests, strict Clippy, the all-features workspace suite, lint, docs, AST generation, and TypeScript 5.6.1-rc/6.0.3 type-algebra checks. The full local `just ready` run stopped only at a runner-dependent source-map snapshot because the repository expects Node 26.5.0 and this machine has 25.8.1; the remaining suite passed with that and the known terminal-color snapshot group skipped.

Follow-up to #25292. Related Rolldown failure: https://github.com/rolldown/rolldown/actions/runs/32158377691/job/95782148042?pr=10707

Disclosure: this change was prepared and verified with AI assistance (Codex).
2026-08-19 07:36:24 +00:00
Armano 83cb5163c0 feat(minifier): improve negation of expressions in boolean context (#25755)
add `boolean_context` to `minimize_not` so negation folding can happen
in-place when possible, reducing extra AST rebuilds and allocations and
`!0` / `!1` can now collapse directly to `1` / `0` in boolean context
2026-08-19 13:21:35 +08:00
connorshea 9b7e15362d docs(linter): set version to 1.79.0 for rules shipped in 1.79.0 (#25902)
The 22 React Compiler rules split out by #25500, plus `eslint/no-unreachable-loop`, all first shipped in oxlint 1.79.0 but were still marked `version = "next"`. I think the release PR must've failed to run this part of the release task?

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-19 02:52:17 +00:00
Boshen a701bccac9 fix(linter): remove invalid React compiler doc links (#25900)
The shared React Compiler rule macro generated `react.dev` links for internal categories without public lint pages. Render those attributions without links and verify linked versus unlinked rule documentation.

Validated with `just ready`.

AI-assisted.
2026-08-19 02:19:06 +00:00
Armano 5ef0c93295 perf(minifier): unwrap all unary exprs without creating dummy expressions (#25891)
This is a follow up for changes introduced in #25836 by applying
`replace_expression_with` helper to unused expressions and to substitute
2026-08-19 10:04:50 +08:00
oxc-guard[bot] 0db127cc16 release(apps): oxlint v1.79.0 && oxfmt v0.64.0 (#25866)
# Oxlint
### 💥 BREAKING CHANGES

- 8c4552d linter: [**BREAKING**] Split react/react-compiler into
per-category rules (#25500) (Boshen)

### 🚀 Features

- 9b7394e linter/typescript/no-empty-object-type: Implement suggestion
(#25833) (Mikhail Baev)

### 🐛 Bug Fixes

- 345f981 react_compiler: Skip node_modules by default (#25859) (Boshen)
- 2f40afa linter/unicorn/no-array-callback-reference: Ignore effect
library imports (#25857) (Michaël Bitard)
- 7b565a0 linter: Align React Compiler rule categories (#25840) (Boshen)
- 228e8e0 linter: Resolve inactive React compiler rules (#25830)
(Boshen)
- f8e4884 linter/eslint/no-useless-constructor: Allow parameter
properties (#25811) (camc314)
- e4b6993 linter: Report exhaustive effect dependencies (#25829)
(Boshen)
- 742e25f linter/eslint/no-return-assign: Anchor diagnostic on return
stmt (#25803) (camc314)
- b831ca8 react-compiler: Improve derived state diagnostic (#25804)
(Boshen)
- 5908c87 linter/jest/prefer-mock-return-shorthand: Preserve
implementations using this (#25802) (camc314)
- f6b2265 linter/eslint/no-redeclare: Run in ES modules, skipping only
the globals check (#25691) (Hamody We)
- ca9f553 linter/unicorn/prefer-default-parameters: Mark fixer as a
suggestion (#25801) (camc314)
- aa49d86 linter: Allow spread rule options in config types (#25675)
(ch3rry)
- d5483f8 linter/typescript/no-useless-empty-export: Skip TS declaration
files (#25789) (Evyatar Daud)
- 57f2199 linter/typescript/no-useless-empty-export: Report after
imports (#25786) (camc314)
- 633bc19 linter/typescript/no-unnecessary-type-conversion: Mark fixer
as `suggestion` (#25787) (camc314)
- dea3339 linter/typescript/explicit-module-boundary-types: Anchor
diagnostics on arrows (#25779) (camc314)
- 5cd6f02 linter/eslint/array-callback-return: Anchor diagnostic on
callback (#25780) (camc314)
- ef8cebf linter/eslint/preserve-caught-error: Handle AggregateError
options (#25775) (camc314)
- c108d5c linter: Improve React Compiler diagnostics (#25742) (Boshen)
- c9ec55e react-compiler: Improve diagnostic spans (#25731) (Boshen)
- 1cbbcb7 linter/unicorn/prefer-string-replace-all: Apply fixes in one
pass (#25709) (Mikhail Baev)
- dec0a86 react-compiler: Standardize diagnostics (#25702) (Boshen)
- 5fd77f7 linter/react/display-name: Report curried HOC inner component
(#25662) (camc314)
- 4dbb80e linter/import/no-named-as-default: Report local export
conflicts (#25658) (camc314)
- e8a7214 linter/react/rules-of-hooks: Report hooks inside try blocks
(#25670) (camc314)
- 36f8451 linter/eslint/no-eval: Align indirect default with ESLint
(#25656) (camc314)
- d901d24 linter/react/exhaustive-deps: Handle destructured object
dependencies (#25669) (camc314)
- beb724d linter/eslint/no-unused-vars: Report bare underscore
parameters (#25663) (camc314)
- fa36f26 linter/react/no-this-in-sfc: Detect this in nested arrow
callbacks (#25653) (camc314)
- 44d1be3 linter/typescript/no-var-requires: Run rule on JavaScript
(#25664) (camc314)
- 4004c10 linter/eslint/no-irregular-whitespace: Check comments by
default (#25660) (camc314)
- 625401d linter/import/no-named-default: Flag type-only named defaults
(#25661) (camc314)
- 8eacb2d linter/typescript/no-non-null-asserted-optional-chain: Detect
assertions after continued optional chains (#25659) (camc314)
- d55b76c linter/react/no-direct-mutation-state: Detect computed state
mutations (#25654) (camc314)
- f92c651 linter/unicorn/no-abusive-eslint-disable: Use directive prefix
in diagnostic (#25657) (camc314)
- ee44996 linter/eslint/max-classes-per-file: Anchor diagnostic at
program start (#25652) (camc314)
- 403ec40 linter/eslint/no-multi-assign: Handle parenthesized declarator
initializers (#25651) (camc314)
- 6c9ba37 linter/jest/expect-expect: Detect assertions in nested
function declarations (#25650) (camc314)
- 285820e linter/no-large-snapshots: Precompile and document allowed
snapshot matchers (#25611) (Mikhail Baev)
- 1be1e20 linter/eslint/prefer-promise-reject-errors: Report on spread
arguments (#25648) (Mikhail Baev)
- a38f7b3 oxlint,oxfmt: Apply .gitignore only to walk targets, not
explicitly named files (#25531) (leaysgur)
- 509931b semantic: Classify global references per identifier (#25608)
(camc314)
- 0c68b7f estree: Emit `decorators` on `FormalParameterRest` (#25582)
(camc314)
- 1d22912 linter/plugins: Parse rule-less directive justifications
(#25559) (camc314)
- eaf80d8 linter/unicorn/prefer-node-protocol: Validate exact Node.js
built-in specifiers (#25558) (camc314)
- 57b94af linter/promise/no-multiple-resolved: Ignore unresolved globals
(#25530) (Tushar Singh)
- 80e64c2 linter/expect-expect: Validate assertion regex (#25504)
(Mikhail Baev)
- 7830604 linter/react/exhaustive-deps: Treat binary expressions as a
stable dependency (#25508) (Hamody We)
- 4df5835 linter: Allow capitalized built-in calls (#25516) (Boshen)
- 9d49d27 react_compiler: Bail out instead of panicking on "Expected a
node for all scopes" (#25506) (Boshen)
- d43b562 linter/vitest/prefer-expect-assertions: Handle parameterized
Vitest assertion suggestions (#25476) (camc314)

### ⚡ Performance

- e8303d6 linter: Shrink rule serialization dispatch (#25818) (Boshen)
- c84ede3 estree_tokens: Share JS token update entry point (#25826)
(Boshen)
- 35c8eb2 linter: Compute sort keys once with sort_by_cached_key
(#25821) (Boshen)
- f0121ed linter: Share config deserialization entry point (#25820)
(Boshen)
- 2dacf7a oxlint: Reduce debug formatting code size (#25797) (Boshen)
- 9ed10b7 linter: Use unstable sorts for unique keys (#25778) (Boshen)
- 63ff8ef linter: Outline diagnostic construction (#25762) (Boshen)
- 2dd7446 diagnostics: Preallocate graphical output (#25721) (Boshen)
- 9c8abab diagnostics: Batch graphical reports (#25710) (Boshen)
- 8e9b95f linter: Use table for rule names (#25458) (Boshen)
# Oxfmt
### 🚀 Features

- c07fe7c oxfmt: Support `experimentalOperatorPosition` (#25643)
(leaysgur)
- 2adb8fb formatter: Implement `operatorPosition` option (#25581)
(leaysgur)

### 🐛 Bug Fixes

- 44231fb formatter: Fix comment and indent issues around unions
(#25665) (leaysgur)
- 2465e87 formatter: Keep comments in place when dropping
`EmptyStatement` (#25730) (leaysgur)
- 7e4f3b7 formatter: Print `JSDocUnknownType` (#25729) (leaysgur)
- 5238d9e formatter: Keep nested awaited-paren member chain grouped in
call arguments (#25646) (leaysgur)
- a38f7b3 oxlint,oxfmt: Apply .gitignore only to walk targets, not
explicitly named files (#25531) (leaysgur)
- 5c0a5ff formatter_css: Fill own-line block comments inside
space-separated values (#25578) (leaysgur)
- 53debfa formatter_css: Keep trailing comma and indent after
comment-preceded last map value (#25577) (leaysgur)
- 1a83a8d oxfmt: Mirror printer line suppression and restore
dedent-to-root (#25573) (leaysgur)
- 7f350d1 formatter: Keep unary comment and print only needed parens
(#25526) (leaysgur)
- f405789 formatter_yaml: Consistent chomped eos behavior (#25523)
(leaysgur)
- a243311 formatter_yaml: Bump oxc-yaml-parser for contentless block
scalar (#25519) (leaysgur)
- 40d38a0 formatter_css: Keep multi value function args intact after
leading comment (#25518) (leaysgur)

### 📚 Documentation

- 223e785 oxfmt/conformance: Update notes on conformance (#25757) (Yuji
Sugiura)
- 673dc6d oxfmt: Move some docs from AGENTS.md to code comment (#25649)
(leaysgur)
- fed6681 oxfmt: Skip expanding overrides options (#25572) (leaysgur)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
2026-08-18 22:27:08 +08:00
oxc-guard[bot] 4a4a1fb2e7 release(crates): oxc v0.145.0 (#25865)
### 💥 BREAKING CHANGES

- 365274e packages/codegen: [**BREAKING**] `printSync` return an object
(#25720) (overlookmotel)

### 🚀 Features

- 6a7eb60 packages/codegen: Alter capitalization of `sourceFilename`
option (#25854) (overlookmotel)
- 300763f packages/codegen: Add full source map support (#25585)
(camc314)
- a169e4a napi/minify: Expose property name mangling options (#24741)
(Dunqing)
- 2f5cdb1 minifier: Add property name mangling (#24740) (Dunqing)
- 4922141 mangler: Deduplicate private accessor names (#25601) (camc314)
- 1c4f519 napi: Add Relay transform plugin (#25503) (Boshen)
- a4478e9 codegen: Add `oxc-codegen` package (#25488) (overlookmotel)

### 🐛 Bug Fixes

- 0fbcf64 codegen: Validate sourcemap options (#25860) (camc314)
- 345f981 react_compiler: Skip node_modules by default (#25859) (Boshen)
- ab81f3f minifier: Drop side-effect-free additions (#25639) (Dunqing)
- 241c559 napi/minify: Accept RegExp for property filters (#25827)
(Dunqing)
- 4cc7ea4 codegen: Reject invalid indent options (#25807) (camc314)
- 53f9295 isolated-declarations: Preserve undefined for defaulted any
(#25292) (camc314)
- 8ab883a codegen: Preserve property key annotations (#25766) (Dunqing)
- b13fd48 minifier: Model uninitialized module vars as undefined
(#25497) (Dunqing)
- f6000ac ecmascript: Fold `**` with `Number::exponentiate`, not IEEE
`pow` (#25644) (Kotaro Chikuba)
- b846abc isolated-declarations: Handle ambient expando properties
(#25655) (camc314)
- dec0a86 react-compiler: Standardize diagnostics (#25702) (Boshen)
- fca2e0c parser: Reject initialized lexical declarations in for-in
(#25700) (Boshen)
- ae9be8f parser: Forbid type parameters on quoted constructors (#25696)
(Boshen)
- c5f188a react-compiler: Use Babel v1 validation defaults (#25676)
(Boshen)
- db44651 napi: Disable reuseWorker in browser bindings (#25640)
(leaysgur)
- ce35d47 react_compiler: Preserve JSX import source pragmas (#25592)
(Boshen)
- 059784d semantic: Respect shadowed `Infinity` and `NaN` in enum
evaluation (#25604) (camc314)
- bb5a232 minifier: Keep variable declaration initilized with class when
keepNames is enabled (#25584) (sapphi-red)
- f49229d minifier: Keep side effects when rotating bitwise operands
(#25596) (Kotaro Chikuba)
- e82495b ecmascript: Derive `ToNumber` of `!x` from `ToBoolean`
(#25595) (Kotaro Chikuba)
- 509931b semantic: Classify global references per identifier (#25608)
(camc314)
- 80484ce mangler: Correct base54 safety comment (#25606) (camc314)
- c1369a7 codegen: Resolve private names in class heritage (#25588)
(camc314)
- c002f29 codegen: Escape sources for empty import specifiers (#25586)
(camc314)
- 0c68b7f estree: Emit `decorators` on `FormalParameterRest` (#25582)
(camc314)
- cdf1846 semantic: Allow legacy escapes in JSX attributes (#25576)
(Boshen)
- e75e102 minifier: Preserve block statement in labeled iteration
statements (#25162) (Armano)
- 771d79a mangler: Exclude non-manglable symbols from slot assignment
(#25539) (sapphi-red)
- 76b19f5 minifier: Avoid duplicating large folded strings (#25532)
(Dunqing)
- 59e8895 codegen: Validate starting indent level (#25550) (camc314)
- 88b34f2 clippy: Remove unneeded `unsafe` (#25551) (camc314)
- 23a7ad0 parser: Stop delimited lists at end of file (#25542) (Boshen)
- 73acba9 parser: Preserve fatal errors during await reparse (#25541)
(Boshen)
- c3e99d1 minifier: Avoid invalid octal escapes in template folds
(#25495) (Dunqing)
- b4e6a9e codegen: Output newline after `export default interface`
(#25487) (overlookmotel)
- 5fcf683 minifier: Correct issue with try finally termination (#25185)
(Armano)
- 1645d93 react_compiler: Preserve source spans (#25462) (Boshen)
- 8d7f9cf react_compiler: Honor eslint suppressions (#25394) (Boshen)

### ⚡ Performance

- 568203e ecmascript: Use binary search for known globals (#25817)
(Boshen)
- c84ede3 estree_tokens: Share JS token update entry point (#25826)
(Boshen)
- 9a6e862 minifier: Move owned statements directly (#25835) (Dunqing)
- 61b2aef minifier: Move owned AST nodes directly (#25837) (Dunqing)
- 673b04b minifier: Replace expressions without take_in dummies (#25836)
(Dunqing)
- 63ff8ef linter: Outline diagnostic construction (#25762) (Boshen)
- 621808e diagnostics: Measure graphemes lazily (#25723) (Boshen)
- f62ed0e diagnostics: Optimize graphical number rendering (#25715)
(Boshen)
- 7152834 diagnostics: Reduce graphical formatting overhead (#25714)
(Boshen)
- 10fc4b7 diagnostics: Reduce graphical rendering allocations (#25711)
(Boshen)
- 9c8abab diagnostics: Batch graphical reports (#25710) (Boshen)
- 757f3d4 mangler: Share allocated names across reused slots (#25605)
(camc314)
- 5444cbf codegen: Skip escaping harmless `<` tokens (#25564) (camc314)

### 📚 Documentation

- 58f7ab9 packages/codegen: Reformat docs and comments (#25848)
(overlookmotel)
- 627466e transform-react: Document all options (#25800) (Boshen)
- ce03ac1 packages/codegen: Fix JSDoc comments for `printSync` (#25722)
(overlookmotel)
- 386a699 packages/codegen: Correct JSDoc comment (#25716)
(overlookmotel)
- 31e571d mangler: Update code example (#25599) (camc314)
- fd62354 codegen: Revamp package readme (#25560) (camc314)
- ffa3153 codegen: Clarify raw transfer Node requirement (#25565)
(camc314)
- c5d4063 codegen: Clarify binary walk allocations (#25562) (camc314)
- 5703d7a codegen: Correct stale printer comments (#25561) (camc314)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
2026-08-18 22:26:40 +08:00
Boshen 345f9818c5 fix(react_compiler): skip node_modules by default (#25859)
## Summary
- skip React Compiler transform and lint analysis for `node_modules` by default
- let explicit transform `sources` allowlists opt dependencies back in
- document the behavior and add regression coverage

## Validation
- focused Oxlint and transform-react checks
- Clippy with warnings denied
- `just ready` reached the all-features suite; three Oxlint ANSI-color snapshots differed in this environment

AI assistance: Codex was used to implement and verify this change.
2026-08-18 13:50:31 +00:00
Michaël Bitard 2f40afa45b fix(linter/unicorn/no-array-callback-reference): ignore effect library imports (#25857)
Fix #25856

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-18 14:49:54 +01:00
Boshen 7b565a0344 fix(linter): align React Compiler rule categories (#25840)
Align React Compiler rule categories with the ESLint presets in `babel-plugin-react-compiler@0.0.0-experimental-a1856f3-20260507`. Rules that are off upstream no longer use Oxlint's default correctness category, while `incompatible-library` uses correctness because it is recommended upstream.

| Rule name | ESLint preset | Oxlint category | Note |
| --- | --- | --- | --- |
| `error-boundaries` | `recommended` | `correctness` |  |
| `globals` | `recommended` | `correctness` |  |
| `immutability` | `recommended` | `correctness` |  |
| `incompatible-library` | `recommended` | `correctness` |  |
| `preserve-manual-memoization` | `recommended` | `correctness` |  |
| `purity` | `recommended` | `correctness` |  |
| `refs` | `recommended` | `correctness` |  |
| `set-state-in-effect` | `recommended` | `correctness` |  |
| `set-state-in-render` | `recommended` | `correctness` |  |
| `static-components` | `recommended` | `correctness` |  |
| `use-memo` | `recommended` | `correctness` |  |
| `unsupported-syntax` | `recommended` | `restriction` | Compiler support boundary. |
| `config` | `recommended` | Not implemented | Oxlint uses fixed, valid compiler options. |
| `gating` | `recommended` | Not implemented | Oxlint does not expose compiler gating options. |
| `void-use-memo` | `recommended-latest` | `correctness` | Enabled by the newer upstream preset. |
| `no-deriving-state-in-effects` | `off` | `perf` | Performance and derived-state guidance. |
| `invariant` | `off` | `restriction` | Internal compiler invariant. |
| `rule-suppression` | `off` | `restriction` | Compiler policy restriction. |
| `syntax` | `off` | `restriction` | Compiler syntax restriction. |
| `todo` | `off` | `restriction` | Unimplemented-feature diagnostic; a hint upstream. |
| `capitalized-calls` | `off` | `suspicious` | Kept out of default correctness. |
| `exhaustive-effect-dependencies` | `off` | `suspicious` | Dependency diagnostic, not default correctness. |
| `hooks` | `off` | `suspicious` | Overlaps the non-compiler `rules-of-hooks` rule. |
| `memo-dependencies` | `off` | `suspicious` | Overlaps the non-compiler `exhaustive-deps` rule. |
| `fbt` | `off` | Not implemented | This is a Meta-internal FBT category. |
| `memoized-effect-dependencies` | `off` | Not implemented | Upstream's `EffectDependencies` category is absent from the Rust compiler port. |

Validated with `just ready` and the category integration test after rebasing onto main.

AI-assisted: Codex was used to implement and validate this change.
2026-08-18 12:57:01 +00:00
Boshen 228e8e0f85 fix(linter): resolve inactive React compiler rules (#25830)
Enable React Compiler memo dependency validation so `react/memo-dependencies` reports missing and extra values. Remove `react/gating` until Oxlint exposes compiler gating options.

Validated with `just ready`.

AI-assisted.
2026-08-18 16:16:24 +08:00
Boshen 568203e24f perf(ecmascript): use binary search for known globals (#25817)
Replaces the 639-arm known-global string match with a sorted static table and binary search. The table is guarded for sortedness, uniqueness, and representative lookups.

Release `oxlint`: 12,491,264 → 12,474,736 bytes (−16,528 bytes, −0.13%); `__text` is reduced by 29,252 bytes.

Prepared with AI assistance.
2026-08-18 08:04:34 +00:00
Dunqing ab81f3f7f8 fix(minifier): drop side-effect-free additions (#25639)
An unused addition can be side-effect-free even when constant folding keeps it. This happens when replacing the expression with its exact constant value would make the output longer.

For example, constant folding correctly keeps `0.1 + 0.2` instead of expanding it to `0.30000000000000004`. But when the result is unused, the whole expression can be removed.

This PR checks the existing side-effect analysis before running the string-addition cleanup. It removes the whole addition when it is safe. Additions that can run code or throw still use the existing cleanup and are preserved when needed.

Tests cover both the removable numeric addition and an addition containing `foo()` that must stay.

Verified with the full `oxc_minifier` test suite (652 passed, 34 ignored), Clippy, formatting, minsize, and allocation checks. Minsize and allocation snapshots are unchanged.

AI-assisted by OpenAI Codex.
2026-08-18 07:59:10 +00:00
Boshen 691a1f0135 refactor(react_compiler): remove unreachable JSX colon check (#25810)
Remove an unreachable React Compiler diagnostic for colons in JSX attribute identifiers. Oxc's parser represents namespaced attributes with `JSXNamespacedName`, so parser-produced identifiers cannot contain colons.

Validated with `cargo test -p oxc_react_compiler`. `just ready` passed formatting and workspace checks, but its full suite hit environment-sensitive ANSI snapshot mismatches in unrelated Oxlint CLI tests.

AI-assisted; reviewed and validated by the contributor.
2026-08-18 07:48:12 +00:00
camc314 f8e488496f fix(linter/eslint/no-useless-constructor): allow parameter properties (#25811)
## Summary
 - avoid reporting forwarding constructors that declare TypeScript parameter properties
 - share the modifier and decorator check across base and derived constructors
 - cover private, public, protected, readonly, and combined parameter-property variants

Closes #25806
2026-08-18 07:44:01 +00:00
Boshen e8303d6fc8 perf(linter): shrink rule serialization dispatch (#25818)
Generate `RuleEnum::to_configuration` arms only for the 33 rules that override the trait default, returning `None` for all other rules.

Release `oxlint`: 12,491,264 → 12,491,200 bytes (-64 bytes). `__text`: 10,589,512 → 10,585,376 bytes (-4,136 bytes).

AI-assisted; reviewed and validated by the contributor.
2026-08-18 07:38:14 +00:00
Dunqing 65da89795a refactor(napi): share JavaScript RegExp conversion (#25834)
Oxc and Rolldown currently duplicate the N-API conversion between a JavaScript `RegExp` and owned Rust data.

This moves that conversion to `oxc_napi::JsRegExp`. It copies `source`, `flags`, and `lastIndex`, supports converting back to a JavaScript `RegExp`, and keeps regex compilation in each consumer. `oxc_minify_napi::JsRegExp` remains available as a re-export.

This is a good place to put since Rolldown already used `oxc_napi` and crate is exactly for napi related struct
2026-08-18 07:25:04 +00:00
Boshen c84ede371b perf(estree_tokens): share JS token update entry point (#25826)
Add a shared non-generic JS-style token update entry point so `oxc_linter` and `oxlint` do not each instantiate the ESTree token visitor.

On `aarch64-apple-darwin`, `cargo build -p oxlint --release --features allocator` reduces the `oxlint` binary by 49,616 bytes (48.5 KiB, 0.39%), from 12,756,960 to 12,707,344 bytes.

Validated with `just ready`.

AI assistance was used for implementation and measurement.
2026-08-18 07:19:31 +00:00
Mikhail Baev 9b7394ed8b feat(linter/typescript/no-empty-object-type): implement suggestion (#25833)
Implement suggestion for `typescript/no-empty-object-type` rule, issue
#2180. Tests ported from
[upstream](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/tests/rules/no-empty-object-type.test.ts)
2026-08-18 08:18:48 +01:00
Dunqing 9a6e86226e perf(minifier): move owned statements directly (#25835)
Statement transforms still use `take_in` after popping or consuming their parent nodes. Removing those dummies is less mechanical because references from discarded branches and pass mutations must remain accounted for.

Move the owned statement fields directly, centralize the shared return/throw conditional rewrite, and use explicit dropped-subtree or mutation reporting at each ownership boundary. The adjacent-`if` rewrite also uses the closure-based replacement API introduced at the bottom of this stack.

Relative to its parent, the tracked allocation fixtures record 2,039 fewer arena allocations and 31,552 fewer arena bytes. Across the complete stack, the improvement is 25,252 arena allocations and 404,352 arena bytes. Minsize output is unchanged.

Validated with `cargo test -p oxc_minifier`, `just minsize`, `cargo clippy -p oxc_minifier -- -D warnings`, `just fmt`, and `git diff --check`.

AI assistance was used for research, implementation, review, and testing. I reviewed and take responsibility for the changes.
2026-08-18 06:57:09 +00:00
Dunqing 61b2aefa94 perf(minifier): move owned AST nodes directly (#25837)
Several peephole transforms call `take_in` after removing or consuming the parent AST node. At that point there is no borrowed slot to preserve, so allocating a dummy replacement is unnecessary.

Move those already-owned child nodes directly in the straightforward expression and declaration cases. This second branch stays intentionally mechanical and leaves the statement/reference-bookkeeping cases to the final PR in the stack.

Relative to its parent, the tracked allocation fixtures record 22,957 fewer arena allocations and 370,544 fewer arena bytes. Minsize output is unchanged.

Validated with `cargo test -p oxc_minifier`, `just minsize`, `cargo clippy -p oxc_minifier -- -D warnings`, `just fmt`, and `git diff --check`.

AI assistance was used for research, implementation, review, and testing. I reviewed and take responsibility for the changes.
2026-08-18 06:57:08 +00:00
Dunqing 673b04bc46 perf(minifier): replace expressions without take_in dummies (#25836)
`replace_expression` currently makes callers extract the old value before constructing its replacement, leaving an arena-backed dummy node behind.

Add a closure-based replacement path that owns the old expression and records the AST mutation. Its dropped-subtree contract remains explicit: callers report discarded subtrees, while values moved into the replacement stay live. Convert the logical-not unwrapping sites whose discarded wrappers contain no references.

This is the foundation of a three-PR Graphite stack. The tracked allocation fixtures record 256 fewer arena allocations and 2,256 fewer arena bytes. Minsize output is unchanged.

Validated with `cargo test -p oxc_minifier`, `just minsize`, `cargo clippy -p oxc_minifier -- -D warnings`, `just fmt`, and `git diff --check`.

AI assistance was used for research, implementation, review, and testing. I reviewed and take responsibility for the changes.
2026-08-18 06:57:08 +00:00
Armano bfb0832d24 refactor(minifier): simplify if-statement minimization (#25178)
Refactors `try_minimize_if` to improve clarity:

- restructures the function around `alternate` presence rather than
`consequent` type
- removes a recursive `Self::try_minimize_if` call — the fallthrough
after branch-flip achieves the same result
- this also removes need for code duplication for inlining expressions
to `||` or `&&`
  - `if (!a) {} else b();` → `a && b();`
  - `if (!a) b();` → `a || b();`
  - `if (a) {} else b();` → `a || b();`
  - `if (a) b();` → `a && b();`
- we also do not have to always call for `wrap_to_avoid_ambiguous_else`
and we can do that after we update code (eg, after rotation, and never
when we convert it to expression)

----------------------

#### changes in stack:
- https://github.com/oxc-project/oxc/pull/25267 
- https://github.com/oxc-project/oxc/pull/25178 👈
- `main`
2026-08-18 13:37:07 +08:00
Boshen 35c8eb2b0f perf(linter): compute sort keys once with sort_by_cached_key (#25821)
Three sorts pass a comparator that recomputes a derived key on every comparison. A sort instantiation expands to about eight functions — `quicksort`, `heapsort`, `small_sort_general`, `median3_rec`, `bidirectional_merge`, … — and the comparator is inlined into each of them, so an expensive key is duplicated eight times over and costs several KiB per call site:

- `OxlintRules::override_rules` sorted errors by `ToString::to_string`, which allocates a `String` and pulls the whole `Display` impl into the sort.
- `LintIgnoreMatcher::new` sorted nested configs by `Path::components().count()`.
- The `sort_imports` member fixer sorted specifiers by `cow_to_ascii_lowercase()`.

`sort_by_cached_key` computes each key once, up front, and sorts those.

For `sort_imports` the resulting order is identical: itertools' `sorted_by` and `sorted_by_cached_key` are both stable sorts (`Vec::sort_by` / `Vec::sort_by_cached_key`). The other two go from an unstable sort to a stable one, so entries with an equal key keep their input order instead of an arbitrary one. Both of those sort for deterministic output in the first place, so that is a small improvement rather than a change in behavior.

### Size

Release `oxlint`, `__text`: 10,582,632 → 10,553,076 bytes — **-29,556 bytes (-0.28%)**; stripped binary -33,024 bytes.

Where it comes from:

| call site | before | after | delta |
| --- | ---: | ---: | ---: |
| `override_rules` (`OverrideRulesError`) | 14,656 B / 11 symbols | 656 B / 4 symbols | -14,000 |
| `sort_imports` member fixer | 28,896 B / 18 symbols | 9,108 B / 11 symbols | -19,788 |
| `LintIgnoreMatcher::new` | 11,648 B / 10 symbols | 2,700 B / 3 symbols | -8,948 |

(The per-call-site figures cover each site's whole symbol group, so they overlap where a sort shares a monomorphization; `core::slice::sort::*` overall goes from 379,100 to 348,136 bytes.)

Validated with `cargo test -p oxc_linter -p oxlint` and `cargo clippy` with warnings denied.

AI assistance was used for implementation and measurement.
2026-08-18 04:53:31 +00:00
Boshen f0121edb71 perf(linter): share config deserialization entry point (#25820)
Share a non-generic JSON deserialization entry point across `oxc_linter` and `oxlint`, avoiding duplicate monomorphizations. Also deserialize well-known settings without deep-cloning the raw JSON value.

Validated with `just ready`.

AI assistance was used for implementation and measurement.
2026-08-18 04:40:16 +00:00
Boshen e4b69935fe fix(linter): report exhaustive effect dependencies (#25829)
Enable React Compiler exhaustive effect dependency validation in Oxlint so the rule reports missing and extra dependencies instead of remaining inert.

Validated with `just ready`.

AI-assisted.
2026-08-18 04:35:11 +00:00
Boshen d0d41099be refactor(linter): route two config parses through the shared path (#25823)
#25765 routed rule configuration parsing through `DefaultRuleConfig::from_value`, which deserializes from `&serde_json::Value`. Two call sites were missed and still went through `serde_json::from_value`, which instantiates the owned-`Value` deserializer for those configuration types on top of the borrowed-`Value` one every other rule uses:

- `react_perf_from_configuration`, shared by the `react_perf` rules.
- `ConsistentTestItConfig::from_configuration`, shared by the jest and vitest `consistent-test-it` rules. This one also cloned the configuration value before parsing it; deserializing by reference drops the clone.

### Size

Release `oxlint`, `__text`: **-2,072 bytes** — `consistent_test_it` 5,784 → 3,760 bytes (its `TestCaseName` owned-`Value` deserializer goes away entirely) and `react_perf` 10,004 → 9,956 bytes. The stripped binary comes out the same size, as a saving this small falls inside existing padding.

Mostly a consistency fix, then, rather than a size win on its own.

Validated with `cargo test -p oxc_linter` and `cargo clippy` with warnings denied.

AI assistance was used for implementation and measurement.
2026-08-18 01:51:24 +00:00
camc314 742e25f42b fix(linter/eslint/no-return-assign): anchor diagnostic on return stmt (#25803)
## Summary

 - align with ESLint and anchor the diagnostic on the full return stmt

closes #25628
2026-08-17 16:00:27 +00:00
Boshen b831ca8af9 fix(react-compiler): improve derived state diagnostic (#25804)
Improve `react/no-deriving-state-in-effects` diagnostics with labeled state updates, named reactive dependencies, and actionable render guidance. Adds fixture coverage for single and multiple dependencies.

AI assistance was used for this change.
2026-08-17 14:33:28 +00:00
camc314 5908c87e0b fix(linter/jest/prefer-mock-return-shorthand): preserve implementations using this (#25802)
## Summary

- skip prefer-mock-return-shorthand when the returned expression references this
- reuse the existing return-expression traversal without adding another AST pass
- cover the reported typed this parameter and mockImplementationOnce arrow callback cases

Closes #25796
2026-08-17 14:28:35 +00:00
Hamody We f6b22654d2 fix(linter/eslint/no-redeclare): run in ES modules, skipping only the globals check (#25691)
Closes #25339.

## The problem

`no-redeclare` gated the entire rule on the file not being an ES module:

```rust
fn should_run(&self, ctx: &ContextHost) -> bool {
    // ES modules run in their own scope, and don't conflict with existing globals
    !ctx.source_type().is_module()
}
```

The comment justifies the globals half of the rule, and it is right
about that: an ES module has its own top-level scope, so declaring
`Object` or `self` there shadows nothing, which is why ESLint reports
`builtinGlobals` redeclarations against the global scope rather than
inside a module. But the gate took the rest with it — plain `var a = 3;
var a = 10;`, redeclarations in nested scopes, and the TS type/value
collision check added for #10396 all stopped running too.

**One correction to the issue's framing, from testing it rather than
reading it.** `.ts` is parsed as `ModuleKind::Unambiguous`, which
resolves to `Module` only when ESM syntax is actually present. So a
`.ts` file with no `import`/`export` is a script, and the rule *did* run
there — `var a = 3; var a = 10;` on its own is reported today. What was
dead is every `.ts` file that contains an import or an export, which in
practice is nearly all of them. The effect the issue describes is real;
the boundary is ESM syntax, not the extension.

The rule's own tests hid this either way. Every failing case runs under
`.change_rule_path_extension(".cts")` — CommonJS, never a module — and
the single `.ts` block asserts a *pass*, which it would do whether or
not the rule executed.

## The change

Drop `should_run`, and narrow the exemption to the thing the comment
actually describes:

```rust
// ES modules run in their own scope, so a top-level `Object` or `self` shadows nothing.
// Only the globals half of the rule is skipped for them -- redeclaring a local binding is
// still a redeclaration.
let builtin_globals = if self.builtin_globals && !ctx.source_type().is_module() {
    Some(&GLOBALS_BUILTIN)
} else {
    None
};
```

`builtin_globals` is already the single switch for that half — it drives
`is_builtin`, which chooses between
`no_redeclare_as_builtin_in_diagnostic` and the plain redeclaration
diagnostic — so `None` in modules disables exactly the globals
reporting, for the builtin table and user-configured `globals` alike,
and leaves both redeclaration loops running.

## Tests

A new `.ts` block under the `es-modules` snapshot suffix. Every case
carries ESM syntax so it resolves as a module — otherwise it would test
the script path by accident, which is the trap above:

- **pass** — `export {}; var Object = 0;` and `export {}; var globalThis
= 0;` (a module shadows nothing, so these must stay quiet), plus a
redeclaration-free case and a TS overload pair.
- **fail** — `export {}; var a = 3; var a = 10;`; `export {}; function
f() { var a; var a; }` (nested scope — nothing to do with global scope
at all, and previously silent); the #10396 `type foo = 1; export
function foo(): void; …` case; and `export {}; var Object = 0; var
Object = 0;`.

That last one is the seam this change creates, so it is worth pinning:
inside a module the first `var Object` is no longer reported as
shadowing a builtin, but the second is still reported as a redeclaration
of the first. Without it, narrowing the gate could quietly have turned
builtin-named symbols into a blind spot.

The existing `.cts` blocks and their snapshot are untouched — `.cts` is
CommonJS, so `is_module()` was already false there.

## Verification

Ran locally with `cargo test -p oxc_linter --lib no_redeclare`: passes,
the existing `eslint_no_redeclare.snap` is unchanged, and the new
`eslint_no_redeclare@es-modules.snap` is included. `cargo clippy -p
oxc_linter --lib` is clean.

> AI disclosure, per the contributing policy: I used an AI assistant
while investigating and writing this. I have read and understand the
change, ran the tests myself, and stand behind it.

---------

Co-authored-by: Cameron <cameron.clark@hey.com>
2026-08-17 13:20:33 +00:00
camc314 ca9f5534a5 fix(linter/unicorn/prefer-default-parameters): mark fixer as a suggestion (#25801)
## Summary

- mark `unicorn/prefer-default-parameters` as suggestion-only
- prevent `--fix` from changing behavior for `null` and other falsy values

Closes #25799
2026-08-17 13:13:58 +00:00
ch3rry aa49d86046 fix(linter): allow spread rule options in config types (#25675)
## Summary

- represent variadic rule options as a severity followed by zero or more
option values
- regenerate the Oxlint configuration schema and TypeScript declarations
- add a type-level regression for mapped options spread into
`no-restricted-globals`

## Root cause

The schema wrapper materialized the first `additionalItems` entry as a
required tuple element and set `minItems` to 2. A dynamic TypeScript
spread has type `T[]` and may be empty, so it cannot satisfy that
required position even though the severity-only form is already valid
through `RuleNoConfig`.

Keeping severity as the only fixed tuple item and leaving the rule
options in `additionalItems` generates `[severity, ...options]`, which
accepts both an empty spread and populated spreads without changing
runtime rule behavior.

Closes #25666


## AI assistance

Implemented with assistance from OpenAI Codex. The contributor remains
responsible for reviewing, understanding, and submitting the change
under the repository AI usage policy.

---------

Co-authored-by: Cameron <cameron.clark@hey.com>
2026-08-17 14:11:07 +01:00
Boshen 2dacf7a9b6 perf(oxlint): reduce debug formatting code size (#25797)
Use compact `Display` formatting for React Compiler terminals and reuse `ConfigBuilderError`'s existing `Display` implementation for `Debug`.

This reduces the release `oxlint` binary from 12,773,552 to 12,740,432 bytes: **-33,120 bytes (-0.26%)**, with `__text` reduced by 24,452 bytes.

Validated with the affected crate tests, workspace checks, and oxlint snapshot tests.

AI assistance was used for implementation and measurement.
2026-08-17 13:04:51 +00:00
Evyatar Daud d5483f829b fix(linter/typescript/no-useless-empty-export): skip TS declaration files (#25789)
Closes #25788 

Ports https://github.com/typescript-eslint/typescript-eslint/pull/7718

Co-authored-by: Cameron <cameron.clark@hey.com>
2026-08-17 11:28:36 +00:00
camc314 57f2199017 fix(linter/typescript/no-useless-empty-export): report after imports (#25786)
## Summary:
- consider static requested modules when deciding whether an empty export is redundant
- add regression coverage for default, side-effect, and namespace imports
- verify autofixes remove the empty export

Closes #25785.
2026-08-17 11:16:43 +00:00
camc314 633bc19c0f fix(linter/typescript/no-unnecessary-type-conversion): mark fixer as suggestion (#25787)
## Summary

- classify typescript/no-unnecessary-type-conversion as suggestion-only
- prevent its runtime-changing removal from being advertised as a normal autofix
- keep TSGolint RuleSuggestion edits available through suggestion handling

Related to #25549.
2026-08-17 11:04:32 +00:00
camc314 dea3339abf fix(linter/typescript/explicit-module-boundary-types): anchor diagnostics on arrows (#25779)
## Summary

- anchor exported arrow return-type diagnostics on the arrow token, matching typescript-eslint
- preserve existing property and nested-function diagnostic anchors
- add focused multiline and disable-directive regression coverage

Closes #25627.
2026-08-17 10:17:00 +00:00
camc314 5cd6f028cd fix(linter/eslint/array-callback-return): anchor diagnostic on callback (#25780)
## Summary

- anchor switch/if missing-return diagnostics on the array callback function head, matching ESLint
- preserve the inner control-flow statement as a secondary diagnostic label
- add regression coverage for diagnostic location and eslint-disable-next-line behavior

Fixes #25632
2026-08-17 10:11:04 +00:00
camc314 53f9295ac5 fix(isolated-declarations): preserve undefined for defaulted any (#25292)
## Summary

- emit `any | undefined` for default-initialized `any` parameters followed by required parameters
- add a regression fixture for #25290 and update the existing parameter snapshot
- preserve the existing handling of `unknown`

Closes #25290
2026-08-17 09:43:42 +00:00
Boshen 9ed10b728d perf(linter): use unstable sorts for unique keys (#25778)
Switches order-independent sorts to unstable sorting and avoids cloning rule-table plugin names.

Reduces the stripped `oxlint` release binary from 12,773,568 to 12,757,040 bytes: 16,528 bytes (0.13%).

Validated with `cargo test -p oxc_linter` and focused oxlint JSON checks.

AI assistance was used for analysis and implementation.
2026-08-17 09:34:39 +00:00