mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
codex/parser-grouped-expression-allocation
930 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b4da00b621 |
release(apps): oxlint v1.82.0 && oxfmt v0.67.0 (#26384)
# Oxlint ### 🚀 Features - |
||
|
|
aa38ddfbea |
fix(linter/unicorn/numeric-separators-style): correct schema defaults (#26393)
## 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 |
||
|
|
8dd81f9bb8 |
chore(deps): update dependency @napi-rs/cli to v3.9.0 (#26256)
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---|---|---| | [@napi-rs/cli](https://napi.rs/) ([source](https://redirect.github.com/napi-rs/napi-rs)) | [`3.8.6` → `3.9.0`](https://renovatebot.com/diffs/npm/@napi-rs%2fcli/3.8.6/3.9.0) |  |  |  |  | --- > [!WARNING] > Some dependencies could not be looked up. Check the warning logs for more information. --- ### Release Notes <details> <summary>napi-rs/napi-rs (@​napi-rs/cli)</summary> ### [`v3.9.0`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.6...@napi-rs/cli@3.9.0) [Compare Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.6...@napi-rs/cli@3.9.0) </details> --- ### Configuration 📅 **Schedule**: (in timezone Asia/Shanghai) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/oxc-project/oxc). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC41Ny4zIiwidXBkYXRlZEluVmVyIjoiNDQuNjEuMyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==--> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Cameron <cameron.clark@hey.com> |
||
|
|
6a0e19cfaa |
feat(linter/eslint/no-unmodified-loop-condition): support checkConditionalExpressions option (#26249)
## 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. |
||
|
|
0b4e2e67f4 |
release(apps): oxlint v1.81.0 && oxfmt v0.66.0 (#26199)
# Oxlint ### 🚀 Features - |
||
|
|
cd00a2c635 |
chore(oxlint): regenerate config types by cargo lintgen (#26195)
Follow up on #26193. (Also fixes main CI failures) `json-schema-to-typescript`: `^15.0.4` → `^16.0.0` changed the output. |
||
|
|
d5be0375e6 |
docs(linter/typescript/switch-exhaustiveness-check): clarify default case comment pattern (#26100)
## 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 |
||
|
|
97e99b8548 |
release(apps): oxlint v1.80.0 && oxfmt v0.65.0 (#26045)
# Oxlint ### 🚀 Features - |
||
|
|
8a9bdbd6d8 |
fix(estree): include decorators in FormalParameterRest spans (#26021)
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. |
||
|
|
32c2675084 |
chore(deps): update rust crates (#26024)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [javascript-globals](https://redirect.github.com/oxc-project/javascript-globals) | workspace.dependencies | minor | `1.5.2` → `1.6.0` | | [papaya](https://redirect.github.com/ibraheemdev/papaya) | workspace.dependencies | patch | `0.2.4` → `0.2.5` | | [saphyr](https://redirect.github.com/saphyr-rs/saphyr) | workspace.dependencies | patch | `0.0.11` → `0.0.12` | | [similar](https://redirect.github.com/mitsuhiko/similar) | workspace.dependencies | minor | `3.1.2` → `3.2.0` | --- ### Release Notes <details> <summary>oxc-project/javascript-globals (javascript-globals)</summary> ### [`v1.6.0`](https://redirect.github.com/oxc-project/javascript-globals/blob/HEAD/CHANGELOG.md#160---2026-08-18) [Compare Source](https://redirect.github.com/oxc-project/javascript-globals/compare/javascript-globals-v1.5.2...javascript-globals-v1.6.0) ##### Added - add Bun globals ([#​275](https://redirect.github.com/oxc-project/javascript-globals/pull/275)) ##### Other - reduce Rust dev debug info ([#​271](https://redirect.github.com/oxc-project/javascript-globals/pull/271)) </details> <details> <summary>ibraheemdev/papaya (papaya)</summary> ### [`v0.2.5`](https://redirect.github.com/ibraheemdev/papaya/releases/tag/v0.2.5): 0.2.5 [Compare Source](https://redirect.github.com/ibraheemdev/papaya/compare/v0.2.4...v0.2.5) - Added the `fast-barrier` feature to allow opting out of process wide memory barriers, which can be expensive to initialize ([#​100](https://redirect.github.com/ibraheemdev/papaya/pull/100)). - Fixed a bug where blocking resizes could lead to deadlocks ([#​92](https://redirect.github.com/ibraheemdev/papaya/pull/92)). - `HashSet::retain` no longer takes a mutable reference ([#​101](https://redirect.github.com/ibraheemdev/papaya/pull/101)). </details> <details> <summary>saphyr-rs/saphyr (saphyr)</summary> ### [`v0.0.12`](https://redirect.github.com/saphyr-rs/saphyr/compare/v0.0.11...v0.0.12) [Compare Source](https://redirect.github.com/saphyr-rs/saphyr/compare/v0.0.11...v0.0.12) </details> <details> <summary>mitsuhiko/similar (similar)</summary> ### [`v3.2.0`](https://redirect.github.com/mitsuhiko/similar/blob/HEAD/CHANGELOG.md#320) [Compare Source](https://redirect.github.com/mitsuhiko/similar/compare/3.1.2...3.2.0) - Added structured, line-oriented three-way merging through `TextMerge`, with merge and diff3 conflict output, configurable labels and marker sizes, owned and borrowed inputs, byte output, and whitespace-insensitive comparison. - Added `WhitespaceMode` and `TextDiffConfig::whitespace_mode` for Git-style line comparisons that ignore changes in whitespace (`-b`) or all whitespace (`-w`). - Changed `Algorithm::Myers` to reduce unmatched/confusing records and use Git-style bounded, non-minimal splits on expensive searches, and added `Algorithm::RawMyers` for the previous shortest-edit-script behavior without output-changing heuristics. - Improved heuristics and performance for most algorithms other than LCS. - Improved large-input performance by avoiding quadratic Histogram scans. </details> --- ### Configuration 📅 **Schedule**: (in timezone Asia/Shanghai) - Branch creation - "before 10am on monday" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/oxc-project/oxc). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zOS4wIiwidXBkYXRlZEluVmVyIjoiNDQuMzkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==--> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Cameron Clark <cameron.clark@hey.com> |
||
|
|
1193c9dbaf |
chore(oxfmt): dogfood operator position at line start (#25937)
dogfood |
||
|
|
0db127cc16 |
release(apps): oxlint v1.79.0 && oxfmt v0.64.0 (#25866)
# Oxlint ### 💥 BREAKING CHANGES - |
||
|
|
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. |
||
|
|
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> |
||
|
|
9c6f1912b5 |
chore(deps): update rust crates (#25756)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [futures](https://rust-lang.github.io/futures-rs) ([source](https://redirect.github.com/rust-lang/futures-rs)) | workspace.dependencies | patch | `0.3.33` → `0.3.34` | | [handlebars](https://redirect.github.com/sunng87/handlebars-rust) | workspace.dependencies | patch | `6.4.3` → `6.4.4` | | [javascript-globals](https://redirect.github.com/oxc-project/javascript-globals) | workspace.dependencies | patch | `1.5.1` → `1.5.2` | --- ### Release Notes <details> <summary>rust-lang/futures-rs (futures)</summary> ### [`v0.3.34`](https://redirect.github.com/rust-lang/futures-rs/blob/HEAD/CHANGELOG.md#0334---2026-08-11) [Compare Source](https://redirect.github.com/rust-lang/futures-rs/compare/0.3.33...0.3.34) - Preserve cloned waker identity. ([#​3032](https://redirect.github.com/rust-lang/futures-rs/issues/3032)) - Updato `syn` to 3. ([#​3028](https://redirect.github.com/rust-lang/futures-rs/issues/3028)) </details> <details> <summary>sunng87/handlebars-rust (handlebars)</summary> ### [`v6.4.4`](https://redirect.github.com/sunng87/handlebars-rust/blob/HEAD/CHANGELOG.md#644---2026-08-12) [Compare Source](https://redirect.github.com/sunng87/handlebars-rust/compare/v6.4.3...v6.4.4) - \[Changed] We have to revert `preserve_json_order` as a default feature. Users who need this can still enable it for handlebars-rust, or enable `preserve_order` for you serde\_json. </details> <details> <summary>oxc-project/javascript-globals (javascript-globals)</summary> ### [`v1.5.2`](https://redirect.github.com/oxc-project/javascript-globals/blob/HEAD/CHANGELOG.md#152---2026-08-13) [Compare Source](https://redirect.github.com/oxc-project/javascript-globals/compare/javascript-globals-v1.5.1...javascript-globals-v1.5.2) ##### Other - Auto update globals from upstream ([#​268](https://redirect.github.com/oxc-project/javascript-globals/pull/268)) - Auto update globals from upstream ([#​265](https://redirect.github.com/oxc-project/javascript-globals/pull/265)) - Auto update globals from upstream ([#​261](https://redirect.github.com/oxc-project/javascript-globals/pull/261)) - *(deps)* update rust crate serde\_json to v1.0.151 ([#​259](https://redirect.github.com/oxc-project/javascript-globals/pull/259)) - *(deps)* update rust crate serde to v1.0.229 ([#​256](https://redirect.github.com/oxc-project/javascript-globals/pull/256)) - update sponsor section - *(deps)* update rust crate rustc-hash to v2.1.3 ([#​247](https://redirect.github.com/oxc-project/javascript-globals/pull/247)) - standardize rustfmt config - normalize README sponsor section - add crates environment to release workflow </details> --- ### Configuration 📅 **Schedule**: (in timezone Asia/Shanghai) - Branch creation - "before 10am on monday" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/oxc-project/oxc). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4yOS41IiwidXBkYXRlZEluVmVyIjoiNDQuMjkuNSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==--> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Cameron <cameron.clark@hey.com> |
||
|
|
892238149b |
refactor(linter): remove inactive react config rule (#25740)
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. |
||
|
|
8c4552dfa6 |
feat(linter)!: split react/react-compiler into per-category rules (#25500)
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. |
||
|
|
beb724dce2 |
fix(linter/eslint/no-unused-vars): report bare underscore parameters (#25663)
## 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 |
||
|
|
285820eed6 |
fix(linter/no-large-snapshots): precompile and document allowed snapshot matchers (#25611)
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 |
||
|
|
0c68b7ff8b |
fix(estree): emit decorators on FormalParameterRest (#25582)
This PR adds support for emitting `decorators` when serializing an ast to ESTree (both with/without raw transfer).
Given the following code:
```
class C { method(@dec ...args) {} }
```
Before we emitted the following ESTree shape:
```json
"params": [
{
"type": "RestElement",
"decorators": [],
"argument": {
"type": "Identifier",
"decorators": [],
"name": "args",
"optional": false,
"typeAnnotation": null,
},
"optional": false,
"typeAnnotation": null,
"value": null,
}
],
```
Now we emit:
```json
"params": [
{
"type": "RestElement",
"decorators": [
{
"type": "Decorator",
"expression": { "type": "Identifier", "name": "dec" }
}
],
"argument": {
"type": "Identifier",
"decorators": [],
"name": "args",
"optional": false,
"typeAnnotation": null,
},
"optional": false,
"typeAnnotation": null,
"value": null,
},
],
```
fixes https://github.com/oxc-project/oxc/issues/18981
closes https://github.com/oxc-project/oxc/pull/24736
|
||
|
|
1d22912cb4 |
fix(linter/plugins): parse rule-less directive justifications (#25559)
## Summary - extract directive justifications before parsing directive labels, matching ESLint - return an empty rule value for rule-less directives while preserving their justification - cover block, enable, line, and next-line directives with both eslint and oxlint prefixes Closes #25556 |
||
|
|
4df5835fc3 |
fix(linter): allow capitalized built-in calls (#25516)
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. |
||
|
|
c42d6397ea |
release(apps): oxlint v1.78.0 && oxfmt v0.63.0 (#25473)
# Oxlint ### 💥 BREAKING CHANGES - |
||
|
|
a33788e13e |
feat(ast)!: group class heritage into ClassHeritage (#25360)
## Summary
- Groups `Class::super_class` and `Class::super_type_arguments` into `Class::heritage`.
- Introduces `ClassHeritage` containing the superclass expression and its optional TypeScript type arguments.
- Makes superclass type arguments without a superclass expression unrepresentable.
## Breaking Change
The Rust AST changes from:
```rust
pub struct Class<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub r#type: ClassType,
pub decorators: Vec<'a, Decorator<'a>>,
pub id: Option<BindingIdentifier<'a>>,
#[ts]
pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
pub super_class: Option<Expression<'a>>,
#[ts]
pub super_type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
#[ts]
pub implements: Vec<'a, TSClassImplements<'a>>,
pub body: Box<'a, ClassBody<'a>>,
#[ts]
pub r#abstract: bool,
#[ts]
pub declare: bool,
pub scope_id: Cell<Option<ScopeId>>,
}
```
to:
```rust
pub struct Class<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub r#type: ClassType,
pub decorators: Vec<'a, Decorator<'a>>,
pub id: Option<BindingIdentifier<'a>>,
#[ts]
pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
pub heritage: Option<ClassHeritage<'a>>,
#[ts]
pub implements: Vec<'a, TSClassImplements<'a>>,
pub body: Box<'a, ClassBody<'a>>,
#[ts]
pub r#abstract: bool,
#[ts]
pub declare: bool,
pub scope_id: Cell<Option<ScopeId>>,
}
pub struct ClassHeritage<'a> {
pub expression: Expression<'a>,
#[ts]
pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
```
## Why?
ECMAScript defines the complete `extends` clause as `ClassHeritage`:
```text
ClassHeritage : extends LeftHandSideExpression
```
TypeScript additionally permits type arguments on the heritage expression:
```ts
class Derived extends Base<T> {}
```
Previously, the AST stored the expression and type arguments independently:
```text
Class {
super_class: Some(Base),
super_type_arguments: Some(<T>),
...
}
```
This allowed an invalid state to be constructed:
```text
Class {
super_class: None,
super_type_arguments: Some(<T>),
...
}
```
Superclass type arguments cannot exist without an `extends` expression.
The parser already treated these fields as a pair. It parsed a class heritage entry and then split its expression and type arguments into two independent fields. Builders and transforms therefore had to preserve their relationship manually.
Grouping them into `ClassHeritage` makes the syntactic dependency explicit and prevents consumers from observing or constructing contradictory class heritage.
## Class heritage forms
A class without an `extends` clause has no heritage:
```js
class Base {}
```
```text
Class {
heritage: None,
...
}
```
A class extending an expression has heritage without type arguments:
```js
class Derived extends Base {}
```
```text
Class {
heritage: Some(ClassHeritage {
expression: Base,
type_arguments: None,
}),
...
}
```
TypeScript type arguments belong to the same heritage structure:
```ts
class Derived extends Base<T> {}
```
```text
Class {
heritage: Some(ClassHeritage {
expression: Base,
type_arguments: Some(<T>),
}),
...
}
```
The heritage expression may be any valid class heritage expression:
```js
class Derived extends mixin(Base) {}
class NullDerived extends null {}
```
## How to migrate
Code that previously accessed the superclass expression directly:
```rust
if let Some(super_class) = &class.super_class {
// ...
}
```
can use the compatibility accessor:
```rust
if let Some(super_class) = class.super_class() {
// ...
}
```
Code that needs both parts should access the grouped heritage:
```rust
if let Some(heritage) = &class.heritage {
let expression = &heritage.expression;
let type_arguments = heritage.type_arguments.as_deref();
}
```
Mutating consumers should update the nested fields:
```rust
if let Some(heritage) = &mut class.heritage {
heritage.type_arguments = None;
}
```
Common migrations:
| Previous representation | New representation |
| --- | --- |
| `class.super_class` | `class.heritage.as_ref().map(\|h\| &h.expression)` or `class.super_class()` |
| `class.super_type_arguments` | `class.heritage.as_ref()?.type_arguments` or `class.super_type_arguments()` |
| `class.super_class.is_some()` | `class.heritage.is_some()` |
| Mutate `class.super_class` | Mutate `class.heritage.expression` |
| Clear `class.super_type_arguments` | Clear `class.heritage.type_arguments` |
| Pass superclass and type arguments separately to a `Class` builder | Construct and pass `ClassHeritage` |
| Clone both fields independently | Clone `class.heritage` |
```ts
superClass: Expression | null;
superTypeArguments?: TSTypeParameterInstantiation | null;
```
No `heritage` property or `ClassHeritage` node is added to the ESTree output.
fixes https://github.com/oxc-project/backlog/issues/216
|
||
|
|
5c5cdcd522 |
feat(ast)!: narrow TSInterfaceHeritage::expression to TSTypeName (#24360)
## Summary
Narrow `TSInterfaceHeritage` from a general JavaScript `Expression` to `TSTypeName`.
```rust
pub struct TSInterfaceHeritage<'a> {
pub type_name: TSTypeName<'a>,
pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
```
## Breaking Change
The Rust AST field changes from:
```rust
expression: Expression<'a>
```
to:
```rust
type_name: TSTypeName<'a>
```
## Why?
The current AST can represent shapes such as:
```ts
interface A extends foo() {}
interface A extends A + B {}
interface A extends new Foo() {}
interface A extends true {}
```
Which is an overly wide type since all of the above variants are invalid.
This PR tightens up the AST, making these invalid variants irrepresentable.
## How to migrate?
This should be a trivial migration:
1. instead of accessing `expression` on `TSInterfaceHeritage`, now access `type_name`
2. change any pattern matching on the `type_name` field to use `TypeName` (this is a smaller subset so should allow deleting code!)
closes https://github.com/oxc-project/backlog/issues/215
|
||
|
|
ccb8fe89db |
feat(linter/jsdoc): implement no-blank-blocks rule (#25207)
this PR implements `jsdoc/no-blank-blocks` rule, issue #1170 |
||
|
|
9573937df3 |
fix(linter/typescript): validate ban-ts-comment description_format (#25320)
adds validation for the `descriptionFormat` regex option from #23594 and replaces manual config parsing with `DefaultRuleConfig` |
||
|
|
6be314f226 |
feat(ast)!: remove duplicated VariableDeclarator::kind (#25319)
## Summary
- Removes `VariableDeclarator::kind`.
- Makes `VariableDeclaration::kind` the single source of truth for every declarator in its declaration list.
- Makes contradictory parent and child declaration kinds unrepresentable.
## Breaking Change
The Rust AST changes from:
```rust
pub struct VariableDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub kind: VariableDeclarationKind,
pub declarations: Vec<'a, VariableDeclarator<'a>>,
#[ts]
pub declare: bool,
}
pub struct VariableDeclarator<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
#[estree(skip)]
pub kind: VariableDeclarationKind,
#[estree(via = VariableDeclaratorId)]
pub id: BindingPattern<'a>,
#[ts]
#[estree(skip)]
pub type_annotation: Option<Box<'a, TSTypeAnnotation<'a>>>,
pub init: Option<Expression<'a>>,
#[ts]
pub definite: bool,
}
```
to:
```rust
pub struct VariableDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub kind: VariableDeclarationKind,
pub declarations: Vec<'a, VariableDeclarator<'a>>,
#[ts]
pub declare: bool,
}
pub struct VariableDeclarator<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
#[estree(via = VariableDeclaratorId)]
pub id: BindingPattern<'a>,
#[ts]
#[estree(skip)]
pub type_annotation: Option<Box<'a, TSTypeAnnotation<'a>>>,
pub init: Option<Expression<'a>>,
#[ts]
pub definite: bool,
}
```
## Why?
A variable declaration has one declaration kind shared by every declarator in its declaration list:
```js
const first = 1, second = 2;
```
Previously, the AST stored that kind on both the parent declaration and every child declarator:
```text
VariableDeclaration {
kind: Const,
declarations: [
VariableDeclarator { kind: Const, ... },
VariableDeclarator { kind: Const, ... },
],
}
```
This allowed contradictory ASTs to be constructed:
```text
VariableDeclaration {
kind: Const,
declarations: [
VariableDeclarator { kind: Var, ... },
],
}
```
Different consumers read different copies:
- Code generation used `VariableDeclaration::kind`.
- Semantic binding used `VariableDeclarator::kind`.
- Lint rules, transforms, and minifier passes used a mixture of both.
As a result, one AST could be emitted as `const` while its binding was treated as function-scoped `var`.
Transforms also had to keep the copies synchronized manually. For example, const-to-let normalization updated the parent and then looped through every declarator to update each child. Explicit resource management performed similar paired mutations for `using` declarations.
Keeping the kind only on `VariableDeclaration` removes this synchronization requirement and prevents consumers from observing conflicting declaration semantics.
## Declaration forms
The declaration kind now belongs exclusively to the node that owns the declaration list:
```js
let value;
```
```text
VariableDeclaration {
kind: Let,
declarations: [
VariableDeclarator { id: value, ... },
],
}
```
Multiple declarators share the same parent kind:
```js
const first = 1, second = 2;
```
```text
VariableDeclaration {
kind: Const,
declarations: [
VariableDeclarator { id: first, ... },
VariableDeclarator { id: second, ... },
],
}
```
## How to migrate
Code that previously read the kind from a declarator:
```rust
if declarator.kind.is_const() {
// ...
}
```
should now read it from the containing declaration:
```rust
if declaration.kind.is_const() {
// ...
}
```
Consumers operating through an AST node store should retrieve the parent `VariableDeclaration`:
```rust
let AstKind::VariableDeclaration(declaration) =
nodes.parent_kind(declarator.node_id())
else {
unreachable!();
};
let kind = declaration.kind;
```
Common migrations:
| Previous representation | New representation |
| --- | --- |
| `declarator.kind` | Containing `declaration.kind` |
| Match `VariableDeclarator::kind` during semantic binding | Read the parent `VariableDeclaration` from ancestry |
| Pass `kind` to `VariableDeclarator::new` | Remove the `kind` argument |
| Update parent and every child kind | Update only `VariableDeclaration::kind` |
| Recover kind after detaching a declarator | Capture or pass the parent kind before detaching it |
| Inspect a symbol’s declarator kind in lint rules | Resolve its parent declaration through the node store |
When constructing declarators, use the generated kindless `VariableDeclarator` builders and place them inside a `VariableDeclaration` carrying the required kind.
fixes https://github.com/oxc-project/backlog/issues/237
|
||
|
|
44fd320324 |
feat(ast)!: split TS external modules & Namespace Declarations (#25284)
## Summary
- Splits `TSModuleDeclaration` into two dedicated declaration nodes:
- `TSExternalModuleDeclaration` for string-literal modules and module augmentations.
- `TSNamespaceDeclaration` for identifier-based `module` and `namespace` declarations.
- Removes `TSModuleDeclaration`, `TSModuleDeclarationName`, `TSModuleDeclarationBody`, and `TSModuleDeclarationKind`.
- Makes invalid combinations of names, bodies, and declaration kinds unrepresentable.
- Ensures only namespace declarations introduce a normal binding.
- Updates all AST consumers, generators, traversal, formatting, code generation, semantic analysis, lint rules, transforms, and serialization.
- Keeps normal and eager-raw ESTree output compatible: both declarations remain `TSModuleDeclaration` nodes.
- Allows experimental lazy raw deserialization to expose the two new node types directly.
## Breaking Change
The Rust AST changes from:
```rust
pub enum Declaration<'a> {
VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 32,
FunctionDeclaration(Box<'a, Function<'a>>) = 33,
ClassDeclaration(Box<'a, Class<'a>>) = 34,
TSTypeAliasDeclaration(Box<'a, TSTypeAliasDeclaration<'a>>) = 35,
TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>) = 36,
TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>) = 37,
TSModuleDeclaration(Box<'a, TSModuleDeclaration<'a>>) = 38,
TSGlobalDeclaration(Box<'a, TSGlobalDeclaration<'a>>) = 39,
TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>) = 40,
}
pub struct TSModuleDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub id: TSModuleDeclarationName<'a>,
pub body: Option<TSModuleDeclarationBody<'a>>,
pub kind: TSModuleDeclarationKind,
pub declare: bool,
pub scope_id: Cell<Option<ScopeId>>,
}
```
to:
```rust
pub enum Declaration<'a> {
VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 32,
FunctionDeclaration(Box<'a, Function<'a>>) = 33,
ClassDeclaration(Box<'a, Class<'a>>) = 34,
TSTypeAliasDeclaration(Box<'a, TSTypeAliasDeclaration<'a>>) = 35,
TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>) = 36,
TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>) = 37,
TSExternalModuleDeclaration(Box<'a, TSExternalModuleDeclaration<'a>>) = 38,
TSNamespaceDeclaration(Box<'a, TSNamespaceDeclaration<'a>>) = 39,
TSGlobalDeclaration(Box<'a, TSGlobalDeclaration<'a>>) = 40,
TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>) = 41,
}
pub struct TSExternalModuleDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub id: StringLiteral<'a>,
pub body: Option<Box<'a, TSModuleBlock<'a>>>,
pub declare: bool,
pub scope_id: Cell<Option<ScopeId>>,
}
pub struct TSNamespaceDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub id: BindingIdentifier<'a>,
pub body: TSNamespaceDeclarationBody<'a>,
pub kind: TSNamespaceDeclarationKind,
pub declare: bool,
pub scope_id: Cell<Option<ScopeId>>,
}
pub enum TSNamespaceDeclarationBody<'a> {
TSNamespaceDeclaration(Box<'a, TSNamespaceDeclaration<'a>>) = 0,
TSModuleBlock(Box<'a, TSModuleBlock<'a>>) = 1,
}
pub enum TSNamespaceDeclarationKind {
Module = 0,
Namespace = 1,
}
```
The corresponding inherited `Statement` variants, `AstKind` variants, builders, and visitor methods have also changed.
## Why?
`TSModuleDeclaration` previously represented two declaration families with different syntax and semantics:
```ts
declare module "foo" {}
declare module "*.css";
module Foo {}
namespace Foo {}
namespace Foo.Bar {}
```
The shared representation allowed invalid states such as:
- A namespace with a string-literal name.
- An external module with an identifier name.
- An identifier namespace without a body.
- A dotted external module.
- An external module containing another module declaration as its body.
- A declaration whose `kind` disagreed with its name.
Consumers also had to inspect `id` before determining whether the node introduced a binding or represented an external module.
The two declaration families have stronger independent invariants:
### External modules
- Always have a string-literal name.
- Never introduce a normal binding identifier.
- May omit their body.
- Cannot use dotted names.
- Use module-body import and export rules.
### Namespace declarations
- Always have a binding identifier.
- Always have a body.
- May contain nested namespace declarations for dotted names.
- Explicitly preserve the `module` or `namespace` spelling.
- Introduce a namespace/module symbol.
- Use namespace-body restrictions.
Dedicated nodes encode these differences directly.
## Declaration forms
External modules use `TSExternalModuleDeclaration`:
```ts
declare module "foo" {}
declare module "*.css";
module "foo" {}
```
Identifier-based declarations use `TSNamespaceDeclaration`:
```ts
module Foo {}
namespace Foo {}
```
Dotted namespaces are represented as nested namespace declarations:
```ts
namespace Foo.Bar {}
```
```rust
TSNamespaceDeclaration {
id: Foo,
body: TSNamespaceDeclarationBody::TSNamespaceDeclaration(
TSNamespaceDeclaration {
id: Bar,
body: TSNamespaceDeclarationBody::TSModuleBlock(...),
// ...
}
),
// ...
}
```
Only `TSExternalModuleDeclaration::body` is optional.
## ESTree compatibility
Normal serialization and eager raw deserialization continue to emit the existing ESTree shape:
```ts
interface TSModuleDeclaration {
type: "TSModuleDeclaration";
id: BindingIdentifier | StringLiteral | TSQualifiedName;
body: TSModuleBlock | null;
kind: "module" | "namespace";
declare: boolean;
global: false;
}
```
Dotted namespaces continue to serialize as a single `TSModuleDeclaration` with a `TSQualifiedName`.
Experimental lazy raw deserialization is not compatibility-preserving. It now exposes `TSExternalModuleDeclaration` and `TSNamespaceDeclaration` directly, with separate constructors, layouts, and visitor type IDs.
## How to migrate
Code that previously matched all forms through one variant:
```rust
if let Declaration::TSModuleDeclaration(declaration) = declaration {
// ...
}
```
should now match the required declaration family:
```rust
match declaration {
Declaration::TSExternalModuleDeclaration(declaration) => {
// String-literal external module
}
Declaration::TSNamespaceDeclaration(declaration) => {
// Identifier-based module or namespace
}
_ => {}
}
```
The same applies to `Statement` and `AstKind`:
```rust
Statement::TSExternalModuleDeclaration(declaration)
Statement::TSNamespaceDeclaration(declaration)
AstKind::TSExternalModuleDeclaration(declaration)
AstKind::TSNamespaceDeclaration(declaration)
```
Common field migrations:
| Previous representation | New representation |
| --- | --- |
| `TSModuleDeclarationName::StringLiteral(id)` | `TSExternalModuleDeclaration::id` |
| `TSModuleDeclarationName::Identifier(id)` | `TSNamespaceDeclaration::id` |
| `body: None` | Only valid on `TSExternalModuleDeclaration` |
| `TSModuleDeclarationBody::TSModuleBlock` | External `body` or `TSNamespaceDeclarationBody::TSModuleBlock` |
| `TSModuleDeclarationBody::TSModuleDeclaration` | `TSNamespaceDeclarationBody::TSNamespaceDeclaration` |
| `TSModuleDeclarationKind::Module` | External module, or `TSNamespaceDeclarationKind::Module` |
| `TSModuleDeclarationKind::Namespace` | `TSNamespaceDeclarationKind::Namespace` |
| Inspect `id` to determine binding behavior | Match `TSNamespaceDeclaration` |
| `declaration.id()` for string modules | Now returns `None` |
| `visit_ts_module_declaration` | `visit_ts_external_module_declaration` or `visit_ts_namespace_declaration` |
When constructing nodes, use the generated `TSExternalModuleDeclaration` or `TSNamespaceDeclaration` builders rather than recreating the previous shared representation.
fixes https://github.com/oxc-project/backlog/issues/218
|
||
|
|
d4a897ce22 |
feat(linter/eslint): implement one-var rule (#24470)
## Summary Adds a native Oxlint implementation of [ESLint's `one-var` rule](https://eslint.org/docs/latest/rules/one-var). This implementation is a port of the [eslint rule source](https://github.com/eslint/eslint/blob/main/lib/rules/one-var.js), test suite, and autofix logic. Related #479 ## AI Disclosure GPT 5.6 Sol was used to assist with implementation of this rule. --------- Co-authored-by: Cameron <cameron.clark@hey.com> |
||
|
|
5ab9340637 |
feat(linter/jsx-a11y/anchor-has-content): add options to match eslint (#24571)
## 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> |
||
|
|
9a423f2f48 |
release(apps): oxlint v1.77.0 && oxfmt v0.62.0 (#25251)
# Oxlint ### 💥 BREAKING CHANGES - |
||
|
|
b937e3e2f1 |
chore(deps): update napi (#25019)
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | Type | Update | |---|---|---|---|---|---|---|---| | [@napi-rs/cli](https://napi.rs/) ([source](https://redirect.github.com/napi-rs/napi-rs)) | [`3.7.4` → `3.8.2`](https://renovatebot.com/diffs/npm/@napi-rs%2fcli/3.7.4/3.8.2) |  |  |  |  | pnpm.catalog.default | minor | | [napi](https://redirect.github.com/napi-rs/napi-rs) | `3.11.0` → `3.12.0` |  |  |  |  | workspace.dependencies | minor | | [napi-build](https://redirect.github.com/napi-rs/napi-rs) | `2.3.2` → `2.4.0` |  |  |  |  | workspace.dependencies | minor | | [napi-derive](https://redirect.github.com/napi-rs/napi-rs) | `3.6.0` → `3.6.2` |  |  |  |  | workspace.dependencies | patch | --- ### Release Notes <details> <summary>napi-rs/napi-rs (@​napi-rs/cli)</summary> ### [`v3.8.2`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.1...@napi-rs/cli@3.8.2) [Compare Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.1...@napi-rs/cli@3.8.2) ### [`v3.8.1`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.0...@napi-rs/cli@3.8.1) [Compare Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.0...@napi-rs/cli@3.8.1) ### [`v3.8.0`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.7.4...@napi-rs/cli@3.8.0) [Compare Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.7.4...@napi-rs/cli@3.8.0) </details> --- ### Configuration 📅 **Schedule**: (in timezone Asia/Shanghai) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/oxc-project/oxc). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yODAuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjMuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==--> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Cameron <cameron.clark@hey.com> |
||
|
|
9d4592c72f |
fix(linter): reject duplicate JS plugin names (#25243)
## 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 |
||
|
|
5c0fa61ddb |
fix(linter/eslint/no-warning-comments): unify config structs and remove manual options docs (#25151)
this PR unifies 2 config structs for `eslint/no-warning-comments` and removes the manually written options doc, related issue: #14743. Doc after fix: <img width="919" height="1295" alt="image" src="https://github.com/user-attachments/assets/dab8f999-fffa-4a77-8d6d-39746cfa3860" /> |
||
|
|
067da8c4e7 |
feat(ast)!: store single parameter in TSIndexSignature::parameter (#25154)
|
||
|
|
1bdedd11ca |
feat(ast)!: introduce ExportDeclaration, ExportFromDeclaration (#25095)
## Summary
Split the overloaded Rust `ExportNamedDeclaration` into syntax-specific AST nodes:
- `ExportDeclaration` for exported declarations
- `ExportNamedDeclaration` for local exports
- `ExportFromDeclaration` for re-exports
## Breaking Change
The Rust AST changes from:
```rust
pub struct ExportNamedDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub declaration: Option<Declaration<'a>>,
pub specifiers: Vec<'a, ExportSpecifier<'a>>,
pub source: Option<StringLiteral<'a>>,
pub export_kind: ImportOrExportKind,
pub with_clause: Option<Box<'a, WithClause<'a>>>,
}
pub enum ModuleDeclaration<'a> {
// ...
ExportNamedDeclaration(Box<'a, ExportNamedDeclaration<'a>>),
// ...
}
```
to:
```rust
pub struct ExportDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub declaration: Declaration<'a>,
}
pub struct ExportNamedDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub specifiers: Vec<'a, ExportSpecifier<'a>>,
pub export_kind: ImportOrExportKind,
}
pub struct ExportFromDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub specifiers: Vec<'a, ExportSpecifier<'a>>,
pub source: StringLiteral<'a>,
pub export_kind: ImportOrExportKind,
pub with_clause: Option<Box<'a, WithClause<'a>>>,
}
pub enum ModuleDeclaration<'a> {
// ...
ExportDeclaration(Box<'a, ExportDeclaration<'a>>),
ExportNamedDeclaration(Box<'a, ExportNamedDeclaration<'a>>),
ExportFromDeclaration(Box<'a, ExportFromDeclaration<'a>>),
// ...
}
```
## Why?
The previous node represented three distinct syntax forms through optional and mutually exclusive fields:
```text
export const value = 1; -> ExportDeclaration
export { value }; -> ExportNamedDeclaration
export { value } from "mod"; -> ExportFromDeclaration
```
This allowed invalid combinations such as declarations with specifiers or sources, and import attributes without a source. The new nodes make these states unrepresentable and remove repeated `declaration`/`source` branching from consumers.
## How to migrate
Match the syntax-specific `ModuleDeclaration`, `Statement`, or `AstKind` variant instead of inspecting optional fields. Generated builders, visitors, traversal ancestors, and formatter wrappers now expose corresponding APIs for all three nodes.
Common field migrations:
| Previous representation | New representation |
| --- | --- |
| `declaration: Some(declaration)` | `ExportDeclaration { declaration }` |
| `declaration.as_ref()` | `&export.declaration` |
| `source.is_none()` for local exports | Match `ExportNamedDeclaration` |
| `source: Some(source)` | `ExportFromDeclaration { source }` |
| `source.as_ref().unwrap()` | `&export.source` |
| `with_clause` | Only available on `ExportFromDeclaration` |
| Stored `export_kind` on exported declarations | `ExportDeclaration::export_kind()` |
| `ExportNamedDeclaration::boxed_plain_declaration(...)` | `ExportDeclaration::boxed(...)` |
| `ExportNamedDeclaration::boxed(..., None, specifiers, None, ...)` | `ExportNamedDeclaration::boxed(..., specifiers, ...)` |
| `ExportNamedDeclaration::boxed(..., None, specifiers, Some(source), ...)` | `ExportFromDeclaration::boxed(..., specifiers, source, ...)` |
closes https://github.com/oxc-project/backlog/issues/219
|
||
|
|
9dc7756729 |
docs(linter/typescript/no-unnecessary-condition): clarify options (#25110)
## 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 |
||
|
|
c917f204a7 |
feat(ast)!: introduce ArrowFunctionBody enum (#24987)
## Summary
- Introduces `ArrowFunctionBody` to represent concise expression and block bodies explicitly.
- Removes the `ArrowFunctionExpression::expression` boolean.
- Makes invalid arrow body combinations unrepresentable.
## Breaking Change
The Rust AST changes from:
```rust
pub struct ArrowFunctionExpression<'a> {
pub expression: bool,
pub body: Box<'a, FunctionBody<'a>>,
}
```
to:
```rust
pub enum ArrowFunctionBody<'a> {
FunctionBody(Box<'a, FunctionBody<'a>>) = 64,
INHERIT(Expression<'a>),
}
pub struct ArrowFunctionExpression<'a> {
pub body: ArrowFunctionBody<'a>,
}
```
Concise arrow bodies are now stored directly as expressions instead of synthetic `FunctionBody` and `ExpressionStatement` nodes. Block bodies continue to use `FunctionBody`.
## Why?
The previous representation required consumers to maintain this implicit invariant:
```text
expression == true
→ no directives
→ exactly one statement
→ that statement is an ExpressionStatement
```
Invalid combinations could cause panics, skipped work, or fallback behavior. The enum encodes the grammar directly and makes those invalid states unrepresentable.
Concise expressions also now have `ArrowFunctionExpression` as their direct traversal ancestor, without synthetic `FunctionBody` or `ExpressionStatement` nodes.
## How to migrate
Use the arrow body accessors or match on `ArrowFunctionBody` rather than inspecting `expression` and synthetic statements.
Common field migrations:
| Previous representation | New representation |
| --- | --- |
| `arrow.expression` | `arrow.is_expression()` |
| `arrow.get_expression()` | `arrow.get_expression()` |
| `&arrow.body.statements` | `arrow.get_function_body().map(\|body\| &body.statements)` |
| `&mut arrow.body.statements` | `arrow.get_function_body_mut().map(\|body\| &mut body.statements)` |
| Synthetic expression statement in `arrow.body` | Direct `ArrowFunctionBody` expression variant |
| `arrow.body = function_body` | `arrow.body = ArrowFunctionBody::FunctionBody(function_body)` |
| Expression-to-block conversion | Create a `FunctionBody` containing a return statement |
| Block-to-expression conversion | Assign the expression directly to `arrow.body` |
| Builder `(expression, body)` arguments | Pass a single `ArrowFunctionBody` |
closes https://github.com/oxc-project/backlog/issues/211
|
||
|
|
65fe65d842 |
release(apps): oxlint v1.76.0 && oxfmt v0.61.0 (#24976)
# Oxlint ### 🚀 Features - |
||
|
|
3ff2e0e6fa |
docs(linter): clarify config extends types (#24936)
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. |
||
|
|
8d31dfa393 |
feat(linter): verify eslint/no-restricted-globals config schema (#24598)
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> |
||
|
|
7069621199 |
feat(linter): verify jest/vitest prefer-lowercase-title config schema (#24724)
## 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> |
||
|
|
016cf2acd6 |
feat(linter/oxc): add bad-match-all-arg rule (#24900)
## Summary - add oxc/bad-match-all-arg for non-global regular expressions passed to matchAll - share cycle-safe RegExp flag resolution with bad-replace-all-arg - support callable and global-qualified RegExp constructors - update generated metadata, diagnostics, timings, and CLI rule-count snapshots Closes #24823. |
||
|
|
99d263b123 |
refactor(linter/plugins): simplify getDisableDirectives (#24852)
Pure refactor, except for fixing a TS type, and correcting some comments. Runtime behavior is unaltered. - `LABEL_PATTERN` captures the directive type as well as the label, so the multi-line check is a single comparison, and `type` no longer comes from `label.slice(7)`. - Numbered capture groups instead of named ones, so the match check is just `match === null`. - `match` is no longer reused for a second, unrelated regex. - `justification` is always set, so it's not optional - correct the type def accordingly. - Simplify splitting off `justification`. - Doc comment referenced `match_disable_directive` / `match_enable_directive`. The Rust function is `match_directive` - correct the comment. |
||
|
|
c126eebef6 |
refactor(linter/plugins): remove dead code in getDisableDirectives (#24851)
`LINE_DIRECTIVE_PATTERN` had a stray hyphen (`disable-(?:-next)?-line`), so it only matched `disable--line` / `disable--next-line` - strings `LABEL_PATTERN` can never produce. Therefore it was dead code - remove it. If the regex _was_ correctly written, the behavior would be wrong. Looks like this is a hangover from the code being taken from ESLint. |
||
|
|
3446ee7ebc |
test(linter/plugins): check comments and tokens within bounds of source text (#24832)
Add more debug assertions to debug build-only code which checks the validity of comments and tokens. Make sure all comments and tokens have spans which are wihin bounds of source text. In particular, this will surface any problems where the source text contains a BOM, which causes us to adjust spans on Rust side when trimming off the BOM. |
||
|
|
f5b9e01cf6 |
refactor(linter/plugins): remove unnecessary type asserts (#24831)
Pure refactor. Remove some type assertions which were unnecessary - TS already knows these vars can't be `null`. |
||
|
|
4c25b91c64 |
perf(linter/plugins): test booleans with explicit === false (#24830)
Tiny perf optimization to linter plugins. `if (x === false)` is longer than `if (!x)`, but is more performant. The 1st compiles down to a simple equality check between `<value x>` and `kFalse` constant, whereas the 2nd is more like `coerceToBoolean(<value x>) === kFalse` - a more expensive operation. This follows our convention in all JS plugins code of always using explicit `=== false` / `=== true` comparisons. |
||
|
|
f85e47d7e6 |
refactor(linter/plugins): add debug asserts (#24829)
Pure refactor. Use debug asserts instead of type assertions in code for deserializing comments and tokns. This gives better error messages if we break the invariants in tests. Makes no difference in release build. |