## Summary
- add the ESLint-compatible `checkConditionalExpressions` option to `no-unmodified-loop-condition`, defaulting to `false`
- port tests added by eslint/eslint#21175 and add regression coverage for repeated references within and across binary groups
Closes#26182.
## Summary
- clarify that `defaultCaseCommentPattern` recognizes a trailing comment in place of an omitted `default` clause
- document the required location, absence of a live `default`, and the `considerDefaultExhaustiveForUnions` requirement for union switches
- regenerate the JSON schema and TypeScript configuration types
Fixes#26087
## 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>
Remove the inactive `react/config` rule and its generated metadata. Oxlint uses fixed, valid React Compiler options, so the rule cannot produce actionable diagnostics.
AI-assisted: Codex was used to implement and validate this change.
Splits the experimental `react/react-compiler` rule into 24 category-specific rules matching `eslint-plugin-react-hooks` v7.
- Runs React Compiler once per file and routes structured diagnostics to each enabled rule.
- Removes `react/react-compiler` and `reportAllBailouts`, with rules categorized by user-facing lint intent.
- Adds per-rule documentation and deduplicates shared React lint URLs.
- Preserves standardized compiler diagnostics while reporting each finding under its Oxlint rule name.
Breaking change: configurations using `react/react-compiler` must migrate to the category-specific rules.
Written with Claude Code and Codex.
## Summary
- report a bare underscore parameter when no args ignore pattern is configured
- preserve the optimized underscore-prefix behavior for explicit argsIgnorePattern configuration
- update focused rule coverage and generated documentation
Closes#25626
issue #23594
- Move regex compilation to configuration parsing to avoid recompiling
patterns for every snapshot entry
- Document the matching behavior of allowedSnapshots (first interpret as
regex, if not valid match as string)
- Fix a small typo where both example sections were labeled “incorrect"
- Replace manual config parsing with DefaultRuleConfig
Fixes#24405.
The React Compiler global registry omitted `BigInt` and `Symbol`, causing `CapitalizedCalls` false positives. Register both primitives and expose the upstream-compatible `environment.validateNoCapitalizedCalls` option.
Validated with `just ready`.
AI-assisted: Codex was used to investigate and implement this change.
## Description
Eslint supports [a `components`
option](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/anchor-has-content.md)
for `jsx-a11y/anchor-has-content` that specified additional components
to treat as anchor elements. This adds equivalent support for that
option.
Note: I am not certain if this is something that should instead be read
off of
[`settings.jsx-a11y.components`](https://oxc.rs/docs/guide/usage/linter/config-file-reference.html#settings-jsx-a11y-components),
as that could list `{ MyAnchor: 'a' }`. Doing that would enable implicit
application of rules to custom components based on rendered entity, but
that may be the wrong use case, as the custom component definition
itself could be responsible for rendering required children.
## AI disclosure
I used GPT 5.6 Sol to assist in implementation of this fix. I've
reviewed all generated code and test coverage.
---------
Signed-off-by: Cameron <cameron.clark@hey.com>
Co-authored-by: Cameron <cameron.clark@hey.com>
## Summary
- reject JS plugins whose normalized names collide
- keep registered plugin names isolated per LSP workspace
- add a CLI regression fixture covering two plugins that expose the same rule name
Fixes#25164
## Summary
- document the exact allowConstantLoopConditions behavior, including the four directly allowed literals
- explain how always differs from only-allowed-literals
- add a checkTypePredicates example and regenerate the published configuration schema
Closes#25098
The generated config reference only described the JSON schema's `string[]` form. Clarify that `oxlint.config.ts` accepts imported `OxlintConfig[]`, add an example and public JSDoc, and regenerate the derived artifacts.
Closes#20238.
Validated with the website generator suite, targeted TypeScript checks, and the relevant Oxlint tests. The full `just ready` run reached an unrelated parallel suppression-fixture cleanup race; the failing test passes in isolation.
AI-assisted.
Part of #22955. Follow-up to the resolved#23617.
## What
`eslint/no-restricted-globals` stayed in `NO_VERIFIED_VALID_SCHEMA`
after #23617 was resolved, because the derived schema described the
internal `NoRestrictedGlobalsConfig` struct (a `globals` hash map)
rather than the user-facing positional-array format the rule actually
parses.
This PR adds a hand-written `JsonSchema` following the existing
`no-restricted-imports` pattern, describing the three accepted
positional item forms:
1. `"event"` — a global name string
2. `{ "name": "event", "message": "…" }` — a name/message object
3. `{ "globals": [ … ], "checkGlobalObject": true, "globalObjects": [ …
] }` — the object form added in #23617
Generated TS declaration:
```ts
"no-restricted-globals"?:
RuleNoConfig | [AllowWarnDeny, NoRestrictedGlobalsConfigEnum, ...NoRestrictedGlobalsConfigEnum[]];
export type NoRestrictedGlobalsConfigEnum = string | RestrictedGlobal | NoRestrictedGlobalsObjectConfig;
```
Verified against the #22955 checklist: reflects all accepted user
configuration forms, denies unknown fields on object forms, and produces
no `[k: string]: unknown` in the TS declaration. `from_configuration`
and rule behavior are unchanged; rule tests pass.
Note: this branch removes only the `no-restricted-globals` entry from
`NO_VERIFIED_VALID_SCHEMA`; #24586 (separate PR) removes the
`filename-case` entry, so whichever merges second will have a trivial
conflict in that array.
## Disclosure
This contribution was developed with assistance from Claude Code
(Anthropic); I have reviewed and tested all changes.
---------
Signed-off-by: Alexander S. <sysix@sysix-coding.de>
Co-authored-by: Alexander S. <sysix@sysix-coding.de>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## Summary
- Unskip `jest/prefer-lowercase-title` and
`vitest/prefer-lowercase-title` from `NO_VERIFIED_VALID_SCHEMA`.
- PreferLowercaseTitleConfig already derives `Deserialize` +
`JsonSchema` with `deny_unknown_fields` and docs for all public options,
so the generated schema/types can describe the real config.
- Regenerates `npm/oxlint/configuration_schema.json` and
`apps/oxlint/src-js/package/config.generated.ts`.
## Motivation
Part of #22955. These two plugin names shared a clean config struct
already used at runtime; they were still emit-as-`DummyRule` only
because of the broader #23186 split note (mainly about `valid-title`
option divergence). Leaving `valid-title` skipped.
Generated config now exposes:
- `allowedPrefixes: string[]` (default `[]`)
- `ignore: string[]` (default `[]`)
- `ignoreTopLevelDescribe: boolean` (default `false`)
- `lowercaseFirstCharacterOnly: boolean` (default `true`)
- `additionalProperties: false` / no `[k: string]: unknown`
## Verification
- `cargo test -p website_linter` — 8 passed
- `cargo test -p oxc_linter --features ruledocs
test_rules_with_custom_configuration_have_schema` — passed
- `cargo test -p oxc_linter prefer_lowercase_title --lib` — 2 passed
(jest + vitest)
- Inspected generated schema/TS: not DummyRule; no index signature
## AI disclosure
Assisted by AI (Hermes/Sera). I reviewed the existing
PreferLowercaseTitleConfig against the GFI checklist, regenerated
schema/types locally, and ran the tests above.
---
**Agent-Owner:** `sera` · **Platform:** hermes · **Claim:** `sera`
---
<!-- fleet-pr-claim: agent_id=sera platform=hermes -->
**Agent-Owner:** `sera` · **Platform:** hermes · **Claim-TTL:** 24h
---------
Co-authored-by: Bartok9 <259807879+Bartok9@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## Summary
- contextually type oxlint and oxfmt `defineConfig` object arguments as
their config interfaces
- keep the identity-helper return type so literal inference is preserved
- restore field-level JSDoc hover for config object properties in
editors
## Validation
Manual expansion of `just ready` because `just` is not installed in my
local shell:
- `pnpm install`
- `typos`
- `cargo lintgen`
- `cargo shear --fix --check-test-targets` (returned 0; emitted an
unrelated existing redundant-ignore warning for `oxc_lexer` in workspace
metadata)
- `cargo fmt`
- `node --run fmt`
- `cargo ck`
- `env -u NO_COLOR cargo test --all-features`
- `cargo lint -- --deny warnings`
- `RUSTDOCFLAGS='-D warnings' cargo doc --no-deps
--document-private-items --all-features`
- `cargo run -p oxc_ast_tools`
TypeScript language-service probe using VS Code's bundled TypeScript
SDK:
- old `defineConfig<T extends Config>(config: T): T` returned no docs
for object fields
- new `defineConfig<T extends Config>(config: T & Config): T` returned
JSDoc for `oxlint.ignorePatterns` and `oxfmt.semi`
- invalid property/type probes still reported TS2561 and TS2322
- return literal inference stayed unchanged for representative
oxlint/oxfmt config objects
## AI Assistance
This PR was prepared with AI assistance. I reviewed the change,
validation approach, and PR contents before submission.
Co-authored-by: cixiangtao <cixiangtao@kanyun.com>
Co-authored-by: Cameron <cameron.clark@hey.com>
This implements
[`no-top-level-await`](https://github.com/eslint-community/eslint-plugin-n/blob/master/docs/rules/no-top-level-await.md)
from the Node ESLint plugin (#493).
AI Disclosure: Generated with Claude Code. Reviewed and tested by me.
I have an alternative implementation using a Program visitor but it made
no difference performance-wise on vscode or in benchmarking outside
extreme cases, so I don't think it's really worth the trouble vs the
simpler implementation here. It's still in the commit history if that
implementation is preferred.
There is one notable difference between this and the original
implementation, which is that the `convertPath` option is not
implemented, and the checking of `package.json`/`.npmignore` is not
implemented (it was used for checking if the given module was actually
exposed in the library) as there's no way to check against those in
oxlint. There's an argument to be made that this makes the rule not
worth implementing, and I'm open to that argument if we'd prefer to mark
this rule as unsupported instead.
All tests were ported from the original rule (excluding those reliant on
the mentioned config options/checks that were skipped). I've also had
Claude add a few additional test cases (see commit
40418f7e63) on top to cover behavior that
wasn't tested previously. I have confirmed that all of these tests are
identical to the behavior of the upstream rule, but I can remove them if
we don't want to have any divergence from the upstream test suite.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Summary
- skip prefer-destructuring diagnostics for variable declarations with type annotations by default
- add enforceForDeclarationWithTypeAnnotation for typescript-eslint compatibility
- report without an autofix when annotated declarations are explicitly enforced, preserving the declared type
- cover object, computed-property, array, renamed-property, and reporter configuration cases
This matches the current `@typescript-eslint/prefer-`destructuring behavior.
Fixes#24615.
this PR moves `jest/padding-around-test-blocks` to the shared
jest_vitest rules, so it can be reused by both the jest and vitest
rules.
issue #4656
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
`is_ignored` in `vue/prop-name-casing` recompiled each slash-wrapped `ignoreProps` pattern with `Regex::new` for every prop it checked. This compiles the patterns once in `from_configuration` into an `Exact`/`Pattern(Regex)` matcher enum stored on the options (`serde`/`schemars`-skipped field), and matches against that.
Invalid regex patterns are dropped at compile time, which preserves the previous behavior (they could never match before either, and were not compared as literals).
### Benchmark
Interleaved paired-sample benchmark (N=40, alternating order, `--silent`), release binaries built from this branch and its merge-base.
Fixture: 220 Vue SFCs / 35,200 props / 36.5K lines, single-rule oxlintrc with `ignoreProps: ["ignored_prop", "/^legacy/", "/_deprecated$/", "/^(alpha|beta)_/", "/[/"]` (one exact pattern, three regexes, one invalid regex).
| | baseline | this branch |
|---|---|---|
| rule on | 66.87 ms | 16.69 ms |
| parse only | 8.47 ms | 8.41 ms |
Paired Δ of **+50.17 ms** (sd 2.18, t=146, faster in 40/40 pairs). The parse-only differential is null (+0.06 ms, t=0.2), so the saving is attributable to the rule and not to binary layout.
That works out to 4.0× on this fixture, but the multiplier is fixture-dependent — the work removed scales with `regex_pattern_count × prop_count`, so denser configs show a larger ratio. The ~50 ms saved across 35K props is the more transferable number.
### Correctness
Verified with `--format=json` (**without** `--silent`, which empties the JSON reporter's `diagnostics` array): both binaries emit the same 10,560 diagnostics, identical after sorting — the array order varies between runs because files are linted in parallel. The fixture includes canary props that are only ignored via the regex patterns, plus an invalid regex; the ignore semantics are unchanged.
### Disclosure
This change was implemented and benchmarked with AI assistance (Claude Code), and has been reviewed by the submitter.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
AI Disclosure: Generated with Claude Code, reviewed and tested by me. I don't think there's a particularly good reason to use a RwLock like this?
This can be split into separate PRs if preferred, as it also adds the type for this rule to the generated definitions and JSON Schema.
The rule kept compiled `checkTypesPattern` regexes in a global `OnceLock<RwLock<FxHashMap<String, Regex>>>`. Every `run()` on a JSDoc-documented function:
- cloned the pattern `String` to use as the map key,
- took the RwLock **write** lock even on cache hits, serializing all linter threads through a single lock, and
- held the guard across the entire parameter check.
The pattern is fixed at configuration time, so the cache is unnecessary: this PR deserializes the option directly into a `Regex` field on the config (same approach as #24412 / #24417) and deletes the cache entirely.
## Benchmark
Paired interleaved samples (N=40), release builds, fixture of 64 files × 400 fully-documented JSDoc functions (25,600 `run()` calls, all hitting the regex path, zero violations) with only `jsdoc/require-param` enabled:
| Run | Baseline (8de6fca) | This PR | Result |
|---|---|---|---|
| Rule enabled | 56.95 ms | 18.25 ms | **3.12×**, t=132.8, 40/0 wins |
| Parse-only null control (same fixture) | 11.05 ms | 10.75 ms | 1.03×, t=1.5 — noise |
Subtracting the ~11 ms parse floor, rule execution drops from ~46 ms to ~7 ms (~6.4×). The win is mostly the removed cross-thread lock contention, so it scales with file count on multi-core machines.
## Correctness
- Diagnostics are byte-identical between baseline and this PR on a mixed pass/fail canary, under both the default config and a custom `checkTypesPattern` config (which changes reporting, confirming the option deserializes and takes effect).
- `oxc_linter` lib tests (1,169), `oxlint` tests (396), and clippy pass; the generated website rule-doc page is byte-identical (`schemars(with = "String", default)` preserves the documented default).
## Invalid patterns are now a config error, not a panic
An invalid `checkTypesPattern` previously panicked lazily, on the first checked function. It now fails config loading with a proper diagnostic:
```
Failed to parse oxlint configuration file.
x Invalid configuration for rule `jsdoc/require-param`:
| regex parse error:
| ^(unclosed
| ^
| error: unclosed group
```
To get there, two things changed:
- The regex deserializer lives in `utils/config.rs` alongside the existing `deserialize_regex_option` / `deserialize_regex_vec` / `deserialize_required_regex_option` family, and builds via `RegexBuilder` with `D::Error::custom` like they all do.
- `from_configuration` propagates the serde error instead of dropping it with `.ok()`. The linter already has the plumbing for this — `config/rules.rs` turns an `Err` into `OverrideRulesError::RuleConfiguration` — the `.ok()` was just discarding it.
### Behavior change worth flagging
Now that `from_configuration` no longer swallows errors, this config's `deny_unknown_fields` is load-bearing for the first time. A config carrying an `eslint-plugin-jsdoc` option that oxc doesn't implement (e.g. `unnamedRootBase`) previously fell back to defaults silently; it now fails to load with an "unknown field" error. No lint *results* change, but a user porting an eslint-plugin-jsdoc config that oxc previously tolerated will now see a config error.
That surfaced in the ported test suite: eight cases configure `unnamedRootBase` (×5), `enableFixer` (×2), `enableRootFixer`, and `autoIncrementBase`, none of which this rule implements. They're commented out, and the snapshot is regenerated accordingly.
If maintainers would rather keep the lenient behavior, the alternative is to validate `checkTypesPattern` up front and keep the `.ok()` fallback for everything else — that removes the panic without touching any test or changing config tolerance. Happy to switch.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Adds a `namespaces` option to `eslint/no-inner-declarations`:
```json
["error", "both", { "namespaces": "allow" }]
```
With `namespaces: "allow"`, `function`/`var` declarations placed directly inside a TypeScript `namespace`/`module` body are no longer reported. Declarations nested inside blocks within a namespace (e.g. `namespace N { if (x) { function f() {} } }`) are still reported. The default (`disallow`) is unchanged.
Closes#21143
Fixes#24310
Root config's `ignorePatterns` are now resolved relative to the directory containing the config file, instead of CWD.
Also fixes a latent issue: a `--config <path>` containing `..` silently disabled all `ignorePatterns` (the unnormalized parent dir never prefix-matches lint target paths). Explicit config paths are now lexically normalized, same as Oxfmt.
### Behavior change
The new anchoring applies in all cases, including `--config` / LSP `configPath`. This intentionally differs from ESLint, which resolves alternate-config ignore patterns relative to CWD:
- `extends`, `overrides[].files`, and JS plugin specifiers are already config-file-relative, including via `--config`
- rooting only `ignorePatterns` at CWD would give one file two coordinate systems
- Wrappers like Vite+ pass `--config <workspace-root config>` as an implementation detail while CWD stays at the invocation directory, so "`--config` means CWD-relative" does not hold in practice
- #24276
Impact should be small: bare names (`dist`, `*.min.js`) and `**/`-prefixed patterns are unaffected by the anchor.
The updated `config_ignore_patterns/with_oxlintrc` snapshot shows the `--config` case.
Implements unicorn/no-confusing-array-with, which flags two ways
Array#with() calls can silently misbehave:
A literal negative index (array.with(-1, value)). Unlike
slice()/splice(), with() treats a negative index as an offset from the
end of the array, which is easy to assume incorrectly.
.length used as the index (array.with(array.length, value)), which is
always out of bounds and always produces a RangeError / unintended
result, since valid indices run 0 .. length - 1.
---------
Co-authored-by: Cameron <cameron.clark@hey.com>
## Summary
- add native `eslint/no-unreachable-loop` support
- support the ESLint `ignore` option for loop statement types
- reuse `no-unreachable`'s effective CFG reachability helper
(`effective_unreachable_blocks`)
- generate oxlint config schema/types and rule metadata
## Why
`no-unreachable-loop` is currently missing from Oxlint's ESLint rule
coverage. The rule catches loops whose body cannot reach a second
iteration, while preserving ESLint-compatible behavior for unreachable
loops and configured ignored loop types.
## Performance
The effective-reachability helper is a whole-CFG traversal. It is only
needed to recognize a loop that is dead code *after* an infinite loop;
the next-iteration search prunes infinite loops on its own. So the
analysis runs on the CFG's base reachability first and builds the
corrected map only when a loop would otherwise be reported *and* is a
static infinite loop — a rare path the common case never hits. This
keeps the rule self-contained and brings the `linter[binder.ts]` and
`linter[kitchen-sink.tsx]` benches from a ~4-5% regression down to
noise.
## AI Assistance
This PR was implemented with AI assistance.
---------
Co-authored-by: Cameron <cameron.clark@hey.com>
## Summary
- add `checkRelationalComparisons` support to `no-constant-binary-expression`
- default the Oxc option to `true` and allow explicit opt-out with `false`
- port the ESLint relational comparison test cases into the existing rule fixture
Adds an `allowAfterSpread` option (default `false`) to `unicorn/no-array-sort`. When enabled, sorting a freshly-spread array (`[...iterable].sort()`) is allowed — it doesn't mutate an existing array and avoids the extra allocation `toSorted()` would incur when sorting a non-array iterable such as a `Set`.
`array.sort()` is still reported (not a spread), and `[...array].sort()` is still reported when the option is off.
Closes#20544
`ForbidItemObject` derives `JsonSchema`, but its manual `Deserialize` impl already defaults the four allow/disallow `Vec` fields. Without `#[serde(default)]`, schemars marked them as `required` in the generated configuration schema, so editors/tooling wrongly required all four (`allowedFor`, `allowedForPatterns`, `disallowedFor`, `disallowedForPatterns`) whenever the object form of `react/forbid-component-props` was used.
Add `#[serde(default)]` to the four fields and regenerate the schema — the `required` array is dropped and each field gets `"default": []`.
Closes#23732
Reject invalid caret-prefixed `node/handle-callback-err` patterns during configuration parsing instead of falling back to literal string matching.
`^`-prefixed options are documented as regex patterns, so malformed regex input should surface as a config error.
Part of #23594
- Avoided looping over `array` of `array`, we only want to know the first level of structure for detecting a tuple config
- refactored some code and introduced a `.clone()` at the beginning. Hope this is fine for the rule doc code path. Feeling sometimes stupid without AI quick fixes :')
- enabled `node-callback-return` schema, which is the only rule that accepts a array as a config
- https://github.com/eslint-community/eslint-plugin-n/blob/master/docs/rules/callback-return.md