AI Disclosure: Generated with Claude Code, Fable 5. Reviewed by me. This sets up the short_description to be added to the various rules shared between jest and vitest.
`declare_oxc_lint!` now accepts a path to a shared constant for `short_description`, mirroring how `docs = SHARED_DOCUMENTATION` works. Like `documentation`, the handling is gated behind the `ruledocs` feature.
A shared short description is added to `expect-expect`, `no-disabled-tests`, and `valid-expect`.
Rules where the wording legitimately differs per framework (e.g. `no-alias-methods`) can keep per-plugin string literals, which remain supported. Or we can opt to add the raw strings on every one of these rules and drop the shared short desc idea, I don't have a strong preference either way.
Once we decide and merge this, we can add the short descriptions for all the other remaining shared rules. We'll also need to split up the two remaining rules that are problematic (https://github.com/oxc-project/oxc/issues/23186), then we can expose the `short_description` field in the JSON rules formatter for use on the website's rules list view.
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.
Simplified redo of #22295. Calling this a chore because I don't think it
warrants mention in the changelog for now as this is just basic
scaffolding changes, but we can mark it as a feature if preferred.
AI Disclosure: generated with Claude Code, reviewed by me.
The `short_description` field will be a one-sentence summary of the rule
that can be used in documentation, and especially the rules list on the
website. The idea would be to enable the rules list to include the short
description as a subtitle under each rule, similar to this:
<img width="1120" height="618" alt="image"
src="https://github.com/user-attachments/assets/ff9e4d63-b61f-474e-9455-d3567c96d51c"
/>
The rule macros will now look like this:
```rs
declare_oxc_lint!(
/// Longer rule description here.
MyRule,
eslint,
nursery,
pending,
config = MyRule,
version = "next",
info {
short_description = "FIXME: One-sentence description of the rule.",
},
);
```
We could do `info = {` here instead of `info {`, up to the reviewers.
I'll move `version` into the `info` field in a subsequent PR. I didn't
do it here because I wanted to keep this simple and avoid problems of
merge conflicts with the release tomorrow. The `info` field was
suggested by camchenry to avoid continuing to bloat the size of the rule
meta struct / rules enum file.
In a subsequent PR I'll add the short_description to most rules using
the same script as I used in #22295. Again, just keeping this simple /
avoiding merge conflicts.
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Cameron Clark <cameron.clark@hey.com>
In order to support shared config structs like in https://github.com/oxc-project/oxc/pull/18467, we need to allow defining `config` as a path to a struct in a different module, like `config = shared::SomeConfig`
> Updates the `declare_oxc_lint!` proc-macro parser/codegen to support sourcing rule documentation from a shared constant (via a `docs = path::TO_CONST` option) in addition to inline doc comments.
## Summary
This PR covers the first two follow-up tasks from #19890:
- backfill missing `version` metadata for the remaining rules
- add a test to prevent future regressions
- expose rule version metadata through `RuleEnum` so the new test can
validate it programmatically
The following rules now declare `version = "1.60.0"`:
- `jest/valid-expect-in-promise`
- `jest/prefer-importing-jest-globals`
- `unicorn/consistent-template-literal-escape`
## Why
Part of #19890 tracks adding per-rule introduction versions for oxlint
rules. This PR focuses on finishing the metadata backfill and adding a
guardrail so future rules cannot regress by omitting `version = ...`.
## Testing
```bash
cargo test -p oxc_linter --test rule_version_metadata_test --features ruledocs
cargo test -p oxc_linter --test rule_configuration_documentation_test --features ruledocs
cargo test -p oxc_linter --features ruledocs
cargo fmt
```
## Notes
This PR intentionally does not include:
- release automation for rewriting `version = "next"`
- website/docs rendering changes
Those are better handled in follow-up PRs.
## AI Usage Disclosure
I used AI assistance to help analyze the issue, inspect the relevant
code paths, and draft the implementation plan and PR text. I manually
reviewed the proposed changes and remain responsible for the final code
and submission.
## Summary
- Bump `cargo-shear` from 1.10.0 to 1.11.2 in justfile and CI workflows
- Apply `cargo shear --fix`: set `doctest = false` on 6 crates with no
doc tests
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Enforce that rules with no config struct/enum raise an error when provided with config options.
For example, the `no-debugger` rule does not take any configuration options. It would silently allow them, but that was an invalid behavior.
```json
{
"rules": {
"eslint/no-debugger": ["error", { "some": "option" }]
}
}
```
With this PR, we get this error:
```
Failed to parse oxlint configuration file.
x Invalid configuration for rule `no-debugger`:
| This rule does not accept configuration options.
```
This prevents misconfiguration from causing problems / confusing behaviors if you try to define a config option on a rule which supports no config options. This is another part of the work for #17854.
There were still a handful of rules not yet migrated over to the `config` defined inside the lint macro. I've worked around that problem by giving them dummy configs for now, and added a test accordingly.
AI Disclosure: Built with help from Raptor Mini + Copilot / Claude Code + Opus. Reviewed and verified by me.
AI Disclosure: Used GitHub Copilot + Raptor mini for help with this, have code reviewed and confirmed the behavior is correct after adding an invalid codeblock. The implementation is a bit naive (markdown technically allows 4 backticks in some situations, for example, and this doesn't technically catch whether that is closed properly), but I think it should be fine if it works fine with all 650 rules we already have.
This will prevent a somewhat-common mistake in the rule documentation where we unintentionally have invalid markdown. Now, the rule will fail if the docs are invalid in this way. e.g. see #17888.
We should maybe consider updating the macro to also error on certain missing headers, but that's a future concern.
Built on top of #17478.
Based on the changes I experimented with in #17199, and the suggestion
given by camc in that PR.
AI Disclosure: This was done almost entirely via find+replace and manual
updates to fix individual rules. I did use AI for the change in
0abc9814d3b9e44c59dcd042ef65f446a556d1a3 since I had trouble figuring
out a solution for that.
It may be easier to review this by going through it commit-by-commit.
Ran ecosystem CI on this branch and it looks like it works fine, no new
regressions
https://github.com/oxc-project/oxc-ecosystem-ci/actions/runs/20590908980
---------
Co-authored-by: Cameron Clark <cameron.clark@hey.com>
Pure refactor. `RuleEnum::read_json` delegates to the rule's `from_configuration` method. So it makes more sense IMO for this method to be called `from_configuration` too. This follows the same pattern as the rest of `RuleEnum`'s methods.
This depends on the next version of `tsgolint` (after 0.6.0) to be released in order to work, otherwise not all `options` will not be accepted.
This implements rule configuration for rules that run in tsgolint. This is achieved by serializing the rule configuration as JSON and sending it as part of the payload, which is now supported by tsgolint. This is implemented by adding a new `to_configuration` method, which does the opposite of `from_configuration`. This is only implemented and used for tsgolint rules, so that there's no additional performance cost unless you are using type-aware linting.
Unfortunately, this does mean that we sort of have to maintain config parsing in two separate codebases, but I think we can improve this in the future with more tooling.
---
Generated rule documentation:
````
## Configuration
This rule accepts a configuration object with the following properties:
### allowForKnownSafeCalls
type: `array`
default: `[]`
Allows specific calls to be ignored, specified as type or value specifiers.
#### allowForKnownSafeCalls[n]
Type or value specifier for matching specific declarations
Supports four types of specifiers:
1. **String specifier** (deprecated): Universal match by name
```json
"Promise"
```
2. **File specifier**: Match types/values declared in local files
```json
{ "from": "file", "name": "MyType" }
{ "from": "file", "name": ["Type1", "Type2"] }
{ "from": "file", "name": "MyType", "path": "./types.ts" }
```
3. **Lib specifier**: Match TypeScript built-in lib types
```json
{ "from": "lib", "name": "Promise" }
{ "from": "lib", "name": ["Promise", "PromiseLike"] }
```
4. **Package specifier**: Match types/values from npm packages
```json
{ "from": "package", "name": "Observable", "package": "rxjs" }
{ "from": "package", "name": ["Observable", "Subject"], "package": "rxjs" }
```
### allowForKnownSafePromises
type: `array`
default: `[]`
Allows specific Promise types to be ignored, specified as type or value specifiers.
#### allowForKnownSafePromises[n]
Type or value specifier for matching specific declarations
Supports four types of specifiers:
1. **String specifier** (deprecated): Universal match by name
```json
"Promise"
```
2. **File specifier**: Match types/values declared in local files
```json
{ "from": "file", "name": "MyType" }
{ "from": "file", "name": ["Type1", "Type2"] }
{ "from": "file", "name": "MyType", "path": "./types.ts" }
```
3. **Lib specifier**: Match TypeScript built-in lib types
```json
{ "from": "lib", "name": "Promise" }
{ "from": "lib", "name": ["Promise", "PromiseLike"] }
```
4. **Package specifier**: Match types/values from npm packages
```json
{ "from": "package", "name": "Observable", "package": "rxjs" }
{ "from": "package", "name": ["Observable", "Subject"], "package": "rxjs" }
```
### checkThenables
type: `boolean`
default: `false`
Check for thenable objects that are not necessarily Promises.
### ignoreIIFE
type: `boolean`
default: `false`
Ignore immediately invoked function expressions (IIFEs).
### ignoreVoid
type: `boolean`
default: `true`
Ignore Promises that are void expressions.
````
- closes https://github.com/oxc-project/backlog/issues/191
Previous PRs removed most usage of this run function, and this migrates the rest to use `run_once`. The motivation for this is not performance (this is likely a slight regression, actually), but should be a slight benefit to binary size and general maintenance. We now only have 3 ways of running lint rules, which makes the linter runtime code simpler.
Adds linter codegen to specify run information about what functions a given rule implements. Looks for the `impl` block for the given rule and looks at all of the rules and checks what functions are implemented. multiple `impl` blocks cannot be specified for the same struct, so we are guaranteed to be able to know this just from the one block.
On its own, this information doesn't do much for us, but it will allow us to improve the linter runtime performance by optimizing how rules are run based on which functions are implemented and which are no-ops.
Suggestion from @overlookmotel. Simplifies the `RuleRunner` trait down to a single `Option` constant. `None` indicates we should run the lint rule on any node type, and `Some(AstTypesBitset)` means we should use the specific node types given for filtering lint rules.
- part of https://github.com/oxc-project/oxc/issues/12223
- supersedes stack: https://github.com/oxc-project/oxc/pull/12228
This PR adds the ability to add node type information to indicate what node types a lint rule acts on. The lint rule runner can use this information to optimize running lint rules by skipping nodes, files, or rules that don't apply.
This is a simple version that is focused on creating the framework for automatically generating the rule runner code. This is not the end of optimizations. Currently only 102 rules have node type information, while 471 rules have no node type information and will not be skipped. Adding node type information for more rules will improve performance. We may also be able to skip more nodes and files by being clever about how we use this information. For example, filtering out any rules which do not run on any node type, if the file contains no relevant node types for the rule.
- Adds a new `RuleRunner` trait which includes information needed for indicating whether a rule has node type info or not
- Adds a `oxc_linter_codegen` task which is used to generate the implementation of `RuleRunner` trait for all lint rules. This is mostly written by AI, so worth a bit of skepticism, but I've tried to manually verify the results and it looks good (just lacking more complex detection).
- Currently, only rules that contain a single top-level `if` statement (or chain of `else if`), or a single `match` statement will have their node type info generated. This should give us a high level of confidence, since these patterns are relatively easy to verify and identify, but it's only a subset of all rules. Next steps will be to add support for `let...else`
- Adjusts the main linting loop to use the node type information to skip rules for node types that don't apply
- Updates CI to include a check for any uncommitted linter codegen changes
---
## Benchmarks
In the latest version of this PR, CodSpeed shows an up to +13% increase in linter performance across large and small files. In the real-world, I've seen a ~5-15% improvement in performance, but in some cases close to zero for very small files. In either case, there appears to be little downside and a lot of potential upside here.
<img width="769" height="242" alt="Screenshot 2025-08-24 at 9 29 30 PM" src="https://github.com/user-attachments/assets/d2c430c4-17a9-4606-85d5-c66c734975d9" />
<img width="757" height="308" alt="Screenshot 2025-08-24 at 9 30 24 PM" src="https://github.com/user-attachments/assets/5508464f-de55-4f4e-9ddd-58f215c4ce85" />
<img width="976" height="368" alt="Screenshot 2025-08-24 at 9 34 52 PM" src="https://github.com/user-attachments/assets/203f83c7-46f3-49f1-a1a8-e18f7ec8483b" />
Making it easier to add new `tsgolint` rules, which will make this more
favorable for implementing new rules with AI to speed up importing all
the type-aware rules. Adds the ability to add a `(tsgolint)` marker in
the `declare_oxc_lint!` macro which generates the necessary code.
## What This PR Does
Adds auto-fixing capabilities for some scenarios in
`react-hooks/exhaustive-deps`.
This is a poor man's stacked PR. #12337 must be merged first.
1. Add missing plugin to example declaration
2. Remove double empty line in example declaration
3. Normalize way of using `declare_oxc_lint!` to round braces
Replace `#[allow]` with `#[expect]` through the whole codebase.
This also surfaced many places where the `#[allow]` attributes were outdated/pointless and could be removed entirely.
Only places `#[allow]` remains are:
1. In generated code and macros, where lint errors may or may not be triggered depending on the generated code.
2. A few places where clippy malfunctions. These use e.g. `#[allow(unused, clippy::allow_attributes)]`.
Apologies @Boshen for the stupidly large diff.
I've done the following:
- `RuleEnum`'s members are now prefixed with the plugin name. e.g. `NoDebugger` has become `EslintNoDebugger`
- updated tester.rs to accept the rule's NAME, CATEGORY to allow us to test rules with the same name (diff category)
- updates `declare_all_lint_rules` satisfy the first above change.
- part of https://github.com/oxc-project/oxc/issues/6038
Adds a new `run_on_jest_node` method for running lint rules which allows only Jest/Vitest nodes to be linted. This simplifies a number of Jest rules by removing the need to iterate/collect Jest nodes inside of them. Now, they can simply run on the Jest nodes that are passed to them directly. This also saves time by skipping the running of the rule if it is not a test file or the Jest/Vitest plugins are not enabled.
## What This PR does
Adds a new `oxc-security/api-key` rule that scans for hard-coded API keys.
It is broken up into "secret rules", where each one is responsible for finding a different kind of key. It is architecturally identical to how lint rules themselves. This PR also includes the first of these rules, for AWS access key IDs.
Logic and rules are based on [keyhunter](https://github.com/Donisaac/keyhunter). I've licensed that repo under GNU GPLv3, but it's my code and I can do what I want with it 😈 (read: I'm fine with it being MIT for oxc).
This PR is a complete feature in its own right, but does not represent the end of this work. See https://github.com/oxc-project/backlog/issues/116 to track overall progress.
> Related to #5770
## What This PR Does
Moves state that is constant over a linted file out of `LintContext` and into a shared `ContextHost` struct, turning `LintContext` into a [flyweight](https://en.wikipedia.org/wiki/Flyweight_pattern).
This brings `LintContext` from 144 bytes down to 96. `Linter::run` iterates over `(rule, ctx)` pairs in a very tight loop, and each rule instance gets its own clone of `ctx`. A smaller `LintContext` means better cache locality and a smaller heap allocation for this vec. I'm hoping to eventually get it small enough to fit in a single cache line.
I made a PR a while ago that did something similar to this one, but instead of using an `Rc`, each `LintContext` stored a read-only reference. This added an extra lifetime parameter, making it slightly unwieldy, but I saw up to 15%/25% perf improvements on local benchmarks. I'll dig around for it and add a link shortly.
### Architecture

----
