## Summary
Fix the generated defaults for `unicorn/numeric-separators-style`. The rule already uses `number.minimumDigits: 5`, matching Unicorn, but the generated documentation and JSON schema advertise `0`. The same schema issue also advertises `groupLength: 0` for every numeric base.
## Why the defaults are wrong
`NumericBaseConfig` is shared by binary, hexadecimal, octal, and decimal configuration. Its derived `Default` initializes both numeric fields to zero. The rule supplies the actual defaults in `NumericSeparatorsStyleConfig::default()` and `NumericNumberConfig::default()`.
Schema generation uses the shared type's defaults for its individual properties. In particular, flattening `NumericBaseConfig` into `NumericNumberConfig` preserves the zero-valued property defaults instead of the decimal defaults. This makes the documented threshold disagree with the rule's existing behavior:
```js
1234; // Allowed with the default configuration.
12345; // Reported; the fix is 12_345.
```
## Change
Add a single parameterized schema helper for `NumericBaseConfig`, then select the appropriate defaults with `schemars(schema_with = ...)` on each field. The helper retains the derived schema's property descriptions, types, and constraints, and overrides only the `groupLength` and `minimumDigits` default metadata.
The resulting defaults are:
| Configuration | `groupLength` | `minimumDigits` |
| --- | --- | --- |
| `binary` | 4 | 0 |
| `hexadecimal` | 2 | 0 |
| `octal` | 4 | 0 |
| `number` | 3 | 5 |
The schema annotation on the flattened decimal base also corrects `number.minimumDigits`. `number.fractionGroupLength` remains unchanged, and the documentation continues to display its default as `Infinity`.
Regenerate the JSON configuration schema and the TypeScript configuration types from it. The generated TypeScript types inline the binary, hexadecimal, and octal option objects because those properties now have individual schemas; their accepted configuration shapes are unchanged.
This changes schema and documentation metadata only. Runtime defaults, option parsing, diagnostics, and autofixes are unchanged.
Fixes#26391
`ParserReturn` contains a property `panicked`.
The name `panicked` is misleading. The parser did _not_ panic. When `true`, it means that the parser encountered a fatal error and is unable to parse the source, because it contains an unrecoverable syntax error.
Rename this property to `fatal_error`, to better describe what it means.
This is a breaking change, but one that's trivial to fix in downstream code - the property doesn't change its meaning or behavior, just its name.
All the changes in this PR in other crates are just reacting to this change and making everywhere use the term "fatal error" instead of "panicked", so the codebase is consistent.
`run_file` is an internal function, which does not get called by the language server.
Instead, it calls the parent function `run_diagnostic`. Refactor the code, so internal implication is kept secret in tests.
Refactors the `oxc_language_server::Tool` trait and associated LSP plumbing to pass `TextDocument` and `CodeActionParams` by value, aligning tool APIs with “consume” semantics and simplifying call sites that previously had to keep borrowed values alive.
With https://github.com/oxc-project/oxc/pull/23329 the conversion from offset to line/column position is now done by a helper function in `oxc_language_server`.
There was a helper function inside `oxlint` to call the function. Instead, call it directly now.
Moved the tests to the correct place, and removed duplicated ones.
The `Option<>` wrap for `Vec<LinterCodeAction>` is not needed, after each lint-run we save an entry (even if empty) to the map.
There is no different code branch for `None` as a value.
## Summary
- update workspace test dependencies to Vitest 5.0.0
- pin Vitest and related tooling to Vite 8.2.2
- migrate removed sequential and benchmark APIs
- update the pnpm lockfile
## Summary
- keep UTF-8 translation offsets absolute when the converter is built
from source with a trimmed BOM or partial-source prefix
- extend the Oxlint BOM fixture with the reported comment-before-Unicode
case
- assert the JS plugin API returns the complete comment range and source
slice
Without the fix, the integration snapshot reports an end offset of 4 and
slices `"/*x*"`. With the fix, it reports an end offset of 5 and slices
`"/*x*/"`.
Fixes#26162
*Update*: #26173 fixed the same bug in the same way, and has been
merged. This PR adds more tests, so merging it on top.
Co-authored-by: camc314 <18101008+camc314@users.noreply.github.com>
Aligned the handling of line comments immediately following an opening `{`.
(For GraphQL only,) previously kept their own line, now matches other languages by inserting a line break and treating them as leading comments for the next element.
This brings it in line with Prettier's style. The only drawback is that the behavior of `-line` style comments. However, since most of these can be replaced with `-next-line` style comments, we are going to standardize on this consistent behavior for now.
prettier/prettier#1267 (won't fix)
`Lexer::finalize_tokens` discards the last collected token - the `Eof` that terminates every token stream - and debug asserted that it was `Eof`.
Turns out that assumption does not always hold. Where the lexer cannot lex any further it emits `Kind::Undetermined`, and no `Eof` ever follows, so the last token is `Undetermined` and the debug assertion fails. This can be triggered by an unterminated string, for example:
```js
foo();
'unterminated
```
Like `Eof`, `Undetermined` is not a "real" token, so either way it's fine to remove it from the token stream. The fix is just to relax the debug assertion to accept either.
Release behaviour is unchanged - it was only a debug assert. Only came across this while running the conformance tests which compare current lexer to the new one (`oxc_lexer`).
We had at the beginning some rules' diagnostics like `<rule-name>: diagnostic message`, from that message we did create a code action `Fix this <rule-name> problem`, but this is buggy.
I searched in `crates/oxc_linter` for `OxcDiagnostic::\w+\(".*:` and found only `no-invalid-regexp` to be a useful case.
For other rules this could be wrong like:
- Unexpected `javascript:` url
- `<Document />` from `next/document` should not be imported outside of `pages/_document.js`. See: https:/..`
- Always prefer `const x: T = { ... }`.
## Summary
- preserve plugin names when tracking default and enabled rules
- prevent same-named rules from inheriting another plugin’s state in Markdown and JSON output
- add regression coverage for all known rule-name collisions
Fixes#26209.
## 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.
# Oxlint
### 🚀 Features
- 60b945d linter/nextjs/no-typos: Implement suggestion (#26091) (Mikhail Baev)
### 🐛 Bug Fixes
- 33ac4b0 linter/lsp: Prevent tsgolint from holding onto processes (#25570) (Adrian Schaedle)
- baf4b1e linter/import/no-empty-named-blocks: Make empty value import removal a suggestion (#26155) (camc314)
- 77cfaec linter/eslint/object-shorthand: Preserve `__proto__` semantics (#26154) (camc314)
- fa3c082 linter/unicorn/prefer-set-size: Ignore shadowed `Set` constructors (#26153) (camc314)
- e412cf2 linter: Clamp invalid JS plugin locations (#26144) (camc314)
- d86c113 linter: Normalize reversed JS plugin locations (#26138) (camc314)
- 73c09b2 linter/eslint/no-use-before-define: Run on JS, JSX files (#26114) (camc314)
- 03ef0f2 linter/unicorn/no-useless-spread: Treat typed arrays as a distinct value hint (#26067) (Aadharsh Pannirselvam)
- bd15905 linter/react/no-unstable-nested-components: Check nested component object property names (#26101) (camc314)
- 3910e2b linter/eslint/no-unassigned-vars: Skip Svelte and Vue files (#26042) (Hamody We)
- 047f7ca linter/plugins: Fix interaction between JS plugins and Vue rules (#26080) (overlookmotel)
- 8531b9b linter/plugins: Alter method for obtaining mutable `Program` when sending AST to JS plugins (#26077) (overlookmotel)
- dc464ff linter/unicorn/prefer-math-min-max: Avoid unsafe autofix (#26060) (camc314)
### 📚 Documentation
- 464ddd1 linter: Support a shared short description for jest/vitest rules (#26186) (connorshea)
- 9db5ad3 linter: Add short description to `vue/no-dupe-keys` (#26183) (connorshea)
- db66f58 linter: Correct export/import mismatch in bar and foo example (#25927) (billychannnnnn)
- d5be037 linter/typescript/switch-exhaustiveness-check: Clarify default case comment pattern (#26100) (camc314)
# Oxfmt
### 🚀 Features
- 1fb37b6 formatter/css: Format declaration-shaped raw-prelude rules (postcss nested config blocks) (#26194) (leaysgur)
### 🐛 Bug Fixes
- 57e8e22 formatter_css: Space a folded sign after a call in Less operations (#26134) (leaysgur)
- e4298fb formatter/jsdoc: Follow CommonMark for interrupting lists and guard wrapping from creating them (#26098) (leaysgur)
- a1e21c2 formatter: Apply head body policy everywhere (#26074) (leaysgur)
- 676b7e1 formatter: Keep comments in their for-head slot, before an empty-statement body and do-while (#26073) (leaysgur)
- 3bbcea6 formatter: Keep comments between a head and its body out of the braces across all constructs (#26071) (leaysgur)
- 57537a2 formatter: Place comments between a head and open paren (#26058) (leaysgur)
- e2d53e7 formatter: Place comments between a statement head and its body (#26043) (leaysgur)
- 662556c formatter: Print the idempotent placement for comments in dropped parens (#26041) (leaysgur)
- faf11d9 formatter_yaml: Keep trailing whitespace in block scalars (#26072) (leaysgur)
### ⚡ Performance
- ac4785a diagnostics: Use fixed ANSI styles (#26130) (Boshen)
### 📚 Documentation
- b879608 formatter,formatter_graphql,formatter_yaml: Annotate own-line comment inlining as known policy violation (#26131) (leaysgur)
- 181953b oxfmt,formatter_core,formatter,formatter_yaml,formatter_css,formatter_graphql: Extract `DIVERGENCES.md` out from `AGENTS.md` (#26121) (leaysgur)
- a67cb9d formatter,formatter_core: Document comment moving policy (#26075) (leaysgur)
- fc175b0 formatter_core: Clarify idempotency test infra (#26069) (leaysgur)
**Before:**
Converting `Fix` to `FixContent` did convert `Cow` into `String` and stored into the `ServerLinter.code_action` Map, to get requested later by the client, where we convert `FixContent` to `CodeAction` (where we need the `String` format)
**After:**
Consume directly `Fix` `Cow` fields, and delay to conversion to `String` until the `CodeAction` is requested to be converted.
So only downside I see, is that `textDocument/codeAction` is requested more times than a `textDocument/diagnostic` request. So the conversion does happen more times, but get released quickly.
Fixes#26165
- `side_effect` is predefined selector, it cannot be used as custom group name
- respect custom group which uses `side_effect` as side-effect-import correctly
- fix metadata detection bug
Follow-up to #26138, closes#25867.
External plugins can report a node range past the source text. The default formatter rejects that invalid span and previously panicked. Clamp external diagnostic offsets to the source's UTF-16 length before normalizing reversed ranges.
The end-to-end fixture now covers:
- reversed locations
- negative loc values
- negative node ranges
- an out-of-range node range in Unicode source text
- Extract "known divergences" section from `AGENTS.md` to explicit `DIVERGENCES.md` with code example
- Update all anchors
- Add missing pinned fixtures
Replace dynamic diagnostic styles with fixed ANSI prefixes, reducing `oxfmt` and `oxlint` release text size.
AI assistance: Codex was used. The contributor remains responsible for this change.
## Summary
- Add an end-to-end regression fixture for unused disable directives in Vue files when a JavaScript plugin is enabled.
- Exercise the original #25892 source and snapshot the unused rule-name diagnostics instead of panicking.
## Testing
- Confirmed the same fixture aborts with the reported out-of-bounds panic on detached pre-fix commit 9d97da14c8.
Closes#25892Closes#25922
Originally fixed by #25280.
## 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
Closes#26038.
`no-unassigned-vars` is a `correctness` rule, so this fires by default
with no config:
```svelte
<script lang="ts">
let dialog: HTMLDialogElement;
function openDialog() {
dialog.showModal();
}
</script>
<dialog bind:this={dialog}>Hello</dialog>
```
```text
repro.svelte:2:7: warning eslint(no-unassigned-vars): 'dialog' is always 'undefined' because it's never assigned.
```
## Why the rule cannot get this right today
The rule's decision is `has_read && no reference is_write()`, taken over
the symbol's references in the current script block. Oxlint reaches
`.svelte`/`.vue` through `PartialLoader`, which extracts the `<script>`
blocks and throws the markup away before parsing — so the template's
write is not in the AST the rule sees, and no reference on `dialog` is
ever a write. Every ingredient of the diagnostic is satisfied, and the
diagnostic is still wrong: Svelte assigns the element to `dialog` on
mount.
The same shape exists in Vue, where a `<script setup>` `let` is a
`setup-let` binding that `v-model="x"` and inline handlers such as
`@click="x = 1"` compile to direct assignments against.
This is not a matter of tightening the check — the write is in a part of
the file the linter never parses.
## The change
`prefer-const` hit exactly this and already skips both extensions
([#25148](https://github.com/oxc-project/oxc/pull/25148)):
```rust
fn should_run(&self, ctx: &crate::context::ContextHost) -> bool {
// ignore svelte/vue: their templates can reassign a binding, which we can't see.
!ctx.file_extension().is_some_and(|ext| ext == "svelte" || ext == "vue")
}
```
This applies the sibling rule's guard rather than inventing one, and
documents it in the rule docs under an `#### Ignored Files` heading,
matching `prefer-const`'s wording.
`.astro` is deliberately **not** included: an Astro template renders a
frontmatter binding but has no construct that writes back to it, so the
false positive does not arise there. `prefer-const` drew the same line.
## Scope, stated honestly
This trades a false positive for lost coverage: a genuinely unassigned
variable inside a `.svelte`/`.vue` `<script>` block is no longer
reported. That matches the precedent and the reporter's own request, and
a `correctness` false positive that fires on the documented Svelte idiom
costs more than the missed report. Restoring coverage needs
template-aware analysis, which is the language-support effort tracked
separately (#13017, #17991).
## Verification
- `cargo test -p oxc_linter --lib` — **1254 passed, 0 failed**.
- Two new tests, `test_svelte` and `test_vue`, cover the reported repro,
`bind:value`, a two-`<script>` Svelte component, `v-model`, and an
inline `@click` handler.
- **Revert-checked:** with `should_run` removed and the tests kept,
exactly those two tests fail and the rest pass. The failure output
reproduces the reported diagnostic verbatim, including the `let shared`
case from the two-script component — so the tests fail for the right
reason, not because the fixture path changed.
- `cargo clippy -p oxc_linter --lib --all-features` — no new findings
(the two `FromIterator::from_iter` warnings are pre-existing, in
`tsgolint.rs` and `lib.rs`).
- `cargo fmt --check` clean.
- The rule is not part of `docs_rule_pages.snap`, so the doc addition
needs no snapshot regeneration.
Built and tested on Windows with the `1.97.1-x86_64-pc-windows-gnu`
toolchain rather than the pinned `1.98.0`, and with `RUSTFLAGS`
overridden to drop the MSVC-only link args that `.cargo/config.toml`
applies to every `target_os = "windows"` target. Nothing in the
repository was changed for that.
---
Written with AI assistance (Claude Code), reviewed and tested by me
before submitting, per AGENTS.md.
---------
Signed-off-by: hamodywe <iosapk.org@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
#26076 updated Oxlint JS plugin conformance snapshots, revealing that one ESLint Stylistic test was failing.
Turns out the cause was #25154 which (correctly) made it illegal for a `TSIndexSignature` to contain more than one parameter. The code in this test case is invalid. So just skip the test.
While working on #26077, Claude spotted another related bug.
JS plugins have to run last because they need to mutate the AST, and in the process they destroy the stored AST.
For files with multiple sections (Vue, Astro, Svelte), we ran native rules then JS rules for each section in turn. That appeared fine - JS rules do run last. But the problem is that some rules e.g. `vue/valid-define-emits` also access _earlier_ sections. Because JS plugins already ran on those previous sections, their ASTs have been discarded already, leading to incorrect results.
Instead, lint in 3 separate passes which each run on _all_ sections before going on to the next pass:
1. Native rules.
2. JS rules.
3. Unused directives.
i.e.:
- Before: section 1 native, section 1 JS, section 2 native, section 2 JS.
- After: section 1 native, section 2 native, section 1 JS, section 2 JS.
Unused directives check has to be in a separate final pass, as it requires both native rules and JS rules to have run before it.
Looping over sections is cheap compared to the work that happens in each turn of the loops, so I very much doubt this has a measurable impact on perf.
This change does have one visible effect: The order of diagnostics will change for multi-section files. Previously diagnostics would be output in section order, now they're in rule type (native/JS) order. I don't _think_ that matters.
Conformance snapshots had got out of sync. At some point 1 test for ESLint Stylistic's `no-comma` rule has got broken (unclear when or how).
This PR doesn't attempt to fix the bug, but just makes sure the snapshot matches reality.
`eslint(no-control-regex)` correctly flags this regex
```ts
const fnKeyRe = /^(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/
```
`'U+001B' is a control character.` and shows in help text, `help: Avoid
matching control characters in regular expressions. If intentional,
consider using a Unicode escape instead.`
but the help text is misleading, all forms of unicode escapes are also
flagged.
```ts
const a = /\x1b/;
const b = /\u001b/;
const c = /\u{1b}/u;
const d = /\u{001b}/u;
```
This behaviour is consistent with upstream eslint rule, just the help
text is misleading, so I have changed the help text to say to disable
this rule when its intentional
Serialize `FormalParameterRest` with its outer span so its range includes parameter decorators. Regenerate the raw-transfer deserializers and remove `sourceMapValidationDecorators.ts` from the ESTree mismatch baseline.
Fixes#26011.