Skip to content

feat(linter): add respectEslintDisableDirectives option#21384

Merged
camc314 merged 16 commits intooxc-project:mainfrom
christianvuerings:disable-directive-prefixes
Apr 23, 2026
Merged

feat(linter): add respectEslintDisableDirectives option#21384
camc314 merged 16 commits intooxc-project:mainfrom
christianvuerings:disable-directive-prefixes

Conversation

@christianvuerings
Copy link
Copy Markdown
Contributor

@christianvuerings christianvuerings commented Apr 13, 2026

Summary

  • add a root-only options.respectEslintDisableDirectives config option
  • make directive parsing and reportUnusedDisableDirectives honor that switch
  • preserve current behavior by default with true
  • regenerate the schema / generated TS config types and add CLI + unit coverage

Proposal

Use a boolean option to control whether oxlint recognizes ESLint-style disable directives:

{
  "options": {
    "respectEslintDisableDirectives": false
  }
}

Behavior:

  • unset or true: oxlint recognizes both oxlint-* and eslint-*
  • false: oxlint only recognizes oxlint-*

This also applies to reportUnusedDisableDirectives.

Notes

  • unused-directive diagnostics now report the actual matched prefix (oxlint-disable vs eslint-disable) instead of always saying eslint-disable
  • the option is root-only, consistent with the other global options.* switches in the config loader

Testing

  • cargo test -p oxc_linter
  • env -u NO_COLOR cargo test -p oxlint

AI Disclosure

This PR was prepared with AI assistance and then reviewed, tested, and adjusted locally before updating the branch.

closes #20447
fixes #20375

@github-actions github-actions Bot added A-linter Area - Linter A-cli Area - CLI A-editor Area - Editor and Language Server A-linter-plugins Area - Linter JS plugins C-enhancement Category - New feature or request labels Apr 13, 2026
@christianvuerings christianvuerings marked this pull request as ready for review April 13, 2026 22:58
@eliottreich
Copy link
Copy Markdown

There's a $35 bounty for OXC linter rule improvements on Code.TaskBounty. Configurable disable directive prefixes is a good fit — ESLint compatibility matters for adoption.

Bounty: https://code.task-bounty.com/task/oxc-linter-rule-faster-than-eslint

@christianvuerings
Copy link
Copy Markdown
Contributor Author

@camc314 @overlookmotel Could either of you have a look?

Copy link
Copy Markdown
Contributor

@camc314 camc314 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this change needed?

to expand, oxlint supports most ESLint plugins, so why do we need to customize the disable directive prefix

@christianvuerings
Copy link
Copy Markdown
Contributor Author

Why is this change needed?

to expand, oxlint supports most ESLint plugins, so why do we need to customize the disable directive prefix

@camc314 we're in the middle of a large migration from ESLint to Oxlint (12 million line codebase) and this is solving a real blocker for us.

At the moment, Oxlint treats eslint-* and oxlint-* the same, so we cannot distinguish which tool a disable was meant for.

We are trying to measure progress by tracking how many disables come from Oxlint vs ESLint, but that is not possible today since both are treated identically.

Also, it prevents us from enabling reportUnusedDisableDirectives in Oxlint. Turning it on right now produces 25k warnings from legacy ESLint directives.

Being able to scope to oxlint-* only lets us isolate Oxlint usage and continue our migration.

@camc314
Copy link
Copy Markdown
Contributor

camc314 commented Apr 14, 2026

ok, i saw someone else ask for this somewhere.

Lets make the implementation simpler.

Rather than making this configurable - can we just include a respectEslintDisableDirective option that defaults to true? (bikeshed the name because that's an awful option)

Would that work?

@christianvuerings christianvuerings changed the title feat(oxlint): add configurable disable directive prefixes feat(oxlint): add supportEslintDisableDirectives option Apr 14, 2026
@christianvuerings
Copy link
Copy Markdown
Contributor Author

ok, i saw someone else ask for this somewhere.

Lets make the implementation simpler.

Rather than making this configurable - can we just include a respectEslintDisableDirective option that defaults to true? (bikeshed the name because that's an awful option)

Would that work?

@camc314 definitely - updated the option to supportEslintDisableDirectives and made the default true as you suggested.

Could you take another look?

@camc314 camc314 self-assigned this Apr 15, 2026
@christianvuerings
Copy link
Copy Markdown
Contributor Author

@camc314 gentle nudge. Any chance you could look at these changes? It would unblock our ESLint => Oxlint migration

@camc314 camc314 changed the title feat(oxlint): add supportEslintDisableDirectives option feat(linter): add supportEslintDisableDirectives option Apr 21, 2026
Comment thread crates/oxc_linter/src/context/host.rs Outdated
Comment thread apps/oxlint/src/lsp/error_with_position.rs Outdated
@camc314 camc314 changed the title feat(linter): add supportEslintDisableDirectives option feat(linter): add respectEslintDisableDirectives option Apr 23, 2026
@camc314 camc314 force-pushed the disable-directive-prefixes branch from ec456f0 to 2232b78 Compare April 23, 2026 11:42
@camc314 camc314 force-pushed the disable-directive-prefixes branch from fbf58aa to 7ced66e Compare April 23, 2026 11:53
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 23, 2026

Merging this PR will not alter performance

✅ 4 untouched benchmarks
⏩ 47 skipped benchmarks1


Comparing christianvuerings:disable-directive-prefixes (dcdb222) with main (0aa13eb)

Open in CodSpeed

Footnotes

  1. 47 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a root-level configuration switch to control whether oxlint recognizes ESLint-style disable/enable directives, and threads that setting through directive parsing and unused-directive reporting.

Changes:

  • Introduce options.respectEslintDisableDirectives (default true) in Rust config, JSON schema, and generated TS types.
  • Pass the option into disable-directive parsing so eslint-* directives can be ignored when disabled.
  • Update CLI/LSP reporting and add fixtures/snapshots/tests to cover the new behavior.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
npm/oxlint/configuration_schema.json Adds the new config option to the published JSON schema.
crates/oxc_linter/src/service/runtime.rs Threads the config-derived directive support flag into ContextSubHost construction.
crates/oxc_linter/src/lib.rs Exposes directive prefix type and adds a Linter accessor for the new option.
crates/oxc_linter/src/disable_directives.rs Adds directive prefix tracking + option-gated matching for eslint-* directives; extends tests.
crates/oxc_linter/src/context/host.rs Uses directive prefix to produce accurate unused-directive diagnostics.
crates/oxc_linter/src/config/oxlintrc.rs Adds respectEslintDisableDirectives to config deserialization/merge logic + tests.
crates/oxc_linter/src/config/config_store.rs Adds a helper to read the option with default true.
crates/oxc_linter/src/config/config_builder.rs Adds coverage ensuring the option is read from inline config / extends.
crates/oxc_linter/fixtures/extends_config/options/respect_eslint_disable_directives_false.json Adds an extends fixture setting the option to false.
apps/oxlint/src/snapshots/fixtures__cli__report_unused_directives_oxlint_only_-c .oxlintrc.json --report-unused-disable-directives@oxlint.snap New snapshot covering oxlint-only directive behavior.
apps/oxlint/src/snapshots/fixtures__cli__report_unused_directives_-c .oxlintrc.json --report-unused-disable-directives@oxlint.snap Updates snapshot expectations for prefix-aware unused-directive messages.
apps/oxlint/src/snapshots/fixtures__cli__report_unused_directives_-c .oxlintrc-with-rudd.json@oxlint.snap Updates snapshot expectations for prefix-aware unused-directive messages.
apps/oxlint/src/lsp/error_with_position.rs Updates LSP unused-directive reporting to include the actual matched directive prefix.
apps/oxlint/src/lint.rs Adds a CLI integration test that snapshots the oxlint-only directive scenario.
apps/oxlint/src/config_loader.rs Enforces the option as root-only (rejects it in nested discovered configs) + test.
apps/oxlint/src-js/package/config.generated.ts Regenerates TS config types to include the new option.
apps/oxlint/fixtures/cli/report_unused_directives_oxlint_only/test.vue Adds CLI fixture data to verify oxlint-only directive support in Vue.
apps/oxlint/fixtures/cli/report_unused_directives_oxlint_only/test.js Adds CLI fixture data to verify oxlint-only directive support in JS.
apps/oxlint/fixtures/cli/report_unused_directives_oxlint_only/.oxlintrc.json Adds fixture config enabling oxlint-only directive parsing.

Comment thread npm/oxlint/configuration_schema.json
Comment thread crates/oxc_linter/src/disable_directives.rs
Comment thread crates/oxc_linter/src/disable_directives.rs
Copy link
Copy Markdown
Contributor

@camc314 camc314 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

Comment thread crates/oxc_linter/src/disable_directives.rs
Comment thread crates/oxc_linter/src/disable_directives.rs
@camc314 camc314 merged commit 348f46c into oxc-project:main Apr 23, 2026
31 checks passed
@christianvuerings
Copy link
Copy Markdown
Contributor Author

Thank you!

@camc314 Thank you! For polishing it up and getting it merged. Looking forward to using this in the next oxlint release.

camc314 added a commit that referenced this pull request Apr 23, 2026
graphite-app Bot pushed a commit that referenced this pull request Apr 23, 2026
graphite-app Bot pushed a commit that referenced this pull request Apr 23, 2026
camc314 added a commit that referenced this pull request Apr 27, 2026
# Oxlint
### 💥 BREAKING CHANGES

- 502e804 ast: [**BREAKING**] Reduce size of `TSTypePredicateName`
(#21711) (overlookmotel)
- 5651539 ast: [**BREAKING**] Reduce size of `JSXExpression` (#21710)
(overlookmotel)
- c44e280 ast: [**BREAKING**] Reduce size of `ArrayExpressionElement`
(#21709) (overlookmotel)

### 🚀 Features

- b4acdbd linter/vue: Implement no-deprecated-delete-set rule (#21766)
(bab)
- 613bfef linter: Split jest/prefer-to-contain into a jest and a vitest
rule. (#21821) (connorshea)
- c629719 linter/vue: Implement no-deprecated-events-api rule (#21793)
(Alex Peshkov)
- f2a8528 linter: Split jest/require-top-level-describe rule into a Jest
and a Vitest rule. (#21822) (connorshea)
- ea4b358 linter: Split jest/prefer-todo into a shared rule and a
vitest-specific rule. (#21820) (connorshea)
- 3a66819 linter: Split jest/no-mocks-import into vitest/no-mocks-import
(#21818) (camchenry)
- 0b3ffce linter: Split jest/no-large-snapshots into
vitest/no-large-snapshots (#21814) (camchenry)
- d1cf22e linter: Split jest/no-interpolation-in-snapshots into
vitest/no-interpolation-in-snapshots (#21812) (camchenry)
- bb0c359 linter: Split jest/no-identical-title into
vitest/no-identical-title (#21810) (camchenry)
- c26ea41 linter: Split jest/no-hooks into vitest/no-hooks (#21809)
(camchenry)
- 46b32f2 linter: Split jest/no-focused-tests into
vitest/no-focused-tests (#21804) (camchenry)
- acf41a8 linter: Split jest/no-duplicate-hooks into
vitest/no-duplicate-hooks (#21803) (camchenry)
- 54d787f linter: Split jest/no-disabled-tests into
vitest/no-disabled-tests (#21802) (camchenry)
- 9c9a676 linter/vue: Implement no-deprecated-data-object-declaration
rule (#21764) (bab)
- 4445855 linter: Split `jest/no-conditional-in-test` into
`vitest/no-conditional-in-test` (#21763) (camchenry)
- b8604de linter: Split `jest/no-conditional-expect` into
`vitest/no-conditional-expect` (#21762) (camchenry)
- 0dbd650 linter: Split `jest/no-commented-out-tests` into
`vitest/no-commented-out-tests` (#21761) (camchenry)
- 7f1a97c linter: Split `jest/no-alias-methods` into
`vitest/no-alias-methods` (#21760) (camchenry)
- eb97c49 linter: Split `jest/max-nested-describe` into
`vitest/max-nested-describe` (#21759) (camchenry)
- d870cad linter: Split `jest/max-expects` into `vitest/max-expects`
(#21758) (camchenry)
- 1b97124 linter/vue: Implement no-deprecated-vue-config-keycodes rule
(#21699) (bab)
- 5f81883 linter/eslint: Implement `func-name-matching` rule (#21708)
(Mikhail Baev)
- 348f46c linter: Add `respectEslintDisableDirectives` option (#21384)
(Christian Vuerings)
- 63ec351 linter: Support nested vite+ config discovery (#21638)
(camc314)
- 560feb4 linter: Introduce `Vite` variant to `DiscoveredConfigFile`
(#21637) (camc314)
- 07dc41e linter/eslint: Implement `no-underscore-dangle` rule (#21630)
(Paul-Arthur THIERY)
- e270d54 linter/react: Impl react/no-did-update-set-state (#17322)
(Kenzo Wada)
- ca81199 linter/react: Implement `forbid-component-props` rule (#20005)
(Mikhail Baev)
- 6776403 linter/branches-sharing-code: Move rule from nursery to
pedantic (#21621) (camc314)
- ce7a4dc linter/no-unreachable: Move rule from nursery to correctness
(#21618) (camc314)
- e3b5e78 linter/getter-return: Move rule from nursery to correctness
(#21617) (camc314)
- d3a7e9a linter/unicorn: Implement suggestion for
`no-useless-iterator-to-array` rule (#21610) (Mikhail Baev)
- a0c883c oxlint/lsp: Add vite plus version to server info (#21587)
(Sysix)
- 67ff860 linter/no-unknown-property: Support React 19 `precedence` prop
(#21590) (João Pedro Schmitz)

### 🐛 Bug Fixes

- aace797 linter: Detect Svelte TS and module scripts correctly (#20819)
(mustafa0x)
- 3612db1 linter/prefer-default-parameters: Skip reporting on
object/class setters (#21836) (camc314)
- 28c3521 oxlint/lsp: Remove overlapping edits for
`source.fixAllDangerous.oxc` code action (#21785) (Sysix)
- 705a82c linter/no-non-null-asserted-nullish-coalescing: Add fixer
(#21827) (yyh)
- 33f5535 linter: Add checks that `Program` is in current allocator
chunk before JS plugins linting (#21774) (overlookmotel)
- d122877 linter/no-extra-non-null-assertions: Add fixer (#21744) (yyh)
- 37f0731 linter: `with_plugin_vitest(true)` working realiable in test
mode (#21769) (Said Atrahouch)
- e42e6a6 linter/role-supports-aria-props: False positive with
`combobox` and `haspopup` (#21725) (Leonabcd123)
- e24324f linter: Iframe-has-title false positive for template literals
(#21714) (Leonabcd123)
- 41a6510 linter/vitest/hoisted-apis-on-top: Only check first member
(#20068) (Sidharth Vinod)
- 98ed888 linter: Check initializer for allowConstantExport in
only-export-components (#20608) (Eyüp Can Akman)
- 1946e8b linter: Avoid applying override plugin categories to eslint
rules (#21521) (bab)
- 3d1e83a linter: Parse `<script>` tag attributes with curly braces in
Svelte files (#21089) (bab)
- bc6ade5 linter: Report actual disable directive prefix (#21682)
(camc314)
- bf84466 linter: Respect category settings in overrides (#19411)
(Connor Shea)
- 52ecb45 linter/vitest: Don't treat `test.extend` or `it.extend` as
test functions (#21668) (Said Atrahouch)
- aa1a00c linter: Support jsx-a11y attributes setting in anchor-is-valid
rule (#21665) (camchenry)
- 30e0ad3 linter/prefer-default-parameters: Preserve TS annotations in
fixer (#21655) (camc314)
- 8c425db linter: Allow string for jest version in config schema
(#21649) (camc314)
- 3617864 linter/react/display-name: Fix false positive for named
default class (#21643) (Mikhail Baev)
- f3a02cc linter/no-non-null-assertion: Improve diagnostic message
(#21616) (Cameron)
- c2ada2c linter: Make `--fix-dangerously` fix dangerous fixes and
suggestions as documented (#13366) (Ulrich Stark)
- 8265ed9 linter/sort-keys: Handle CRLF separated groups (#21608)
(camc314)
- 96c559e linter/no-shadow: Add note explaining enum member shadowing
(#21607) (camc314)
- 3b49389 linter: No-irregular-whitespace: add config options (#21559)
(camchenry)

### ⚡ Performance

- cdc9eae oxlint/lsp: Avoid clones on lsp options deserializion (#21748)
(Sysix)
- be2db80 linter/sort-keys: Reduce allocations (#21560) (camchenry)

### 📚 Documentation

- 453d647 linter: Remove now-unnecessary compatibility notes from shared
Jest/Vitest rules. (#21826) (connorshea)
- b1574a2 linter: Fix the configuration docs for no-underscore-dangle
rule. (#21801) (connorshea)
- 6946aee linter: Remove no-longer-relevant documentation about vitest
compatibility from shared Jest/Vitest rules. (#21799) (connorshea)
- 0652ea2 linter: Misc grammar cleanup for Vitest rules. (#21755)
(connorshea)
- 99f0b68 linter: Improve docs for `vitest/require-mock-type-parameters`
rule. (#21754) (connorshea)
- 9e2796b linter: Improve the docs for the `vitest/hoisted-apis-on-top`
rule. (#21753) (connorshea)
- 2ccede2 linter: Improve function usage examples in doc comments
(#21718) (overlookmotel)
- 43adae9 linter: Add version section to rules page (#21601) (camchenry)
- d15dad2 linter: Export rule version metadata (#21588) (Old Autumn)
# Oxfmt
### 💥 BREAKING CHANGES

- 502e804 ast: [**BREAKING**] Reduce size of `TSTypePredicateName`
(#21711) (overlookmotel)
- 5651539 ast: [**BREAKING**] Reduce size of `JSXExpression` (#21710)
(overlookmotel)
- c44e280 ast: [**BREAKING**] Reduce size of `ArrayExpressionElement`
(#21709) (overlookmotel)

### 🚀 Features

- 3bc54a9 oxfmt: Respect nested config for `--stdin-filepath` (#21627)
(leaysgur)
- 144f27a oxfmt: Respect ignore settings for `--stdin-filepath` (#21625)
(leaysgur)
- 81c7ae4 oxfmt/lsp: Add vite plus version to server info (#21586)
(Sysix)

### 🐛 Bug Fixes

- 477435b formatter/sort_imports: Keep leading blank line when
decreasing group transitions (#21835) (leaysgur)
- 38d1e82 oxfmt/lsp: Format non `file://` URIs without a authority
(#21647) (Sysix)
- 5eb8e2b formatter/sort_imports: Preserve blank lines around ignored
side-effect imports (#21692) (leaysgur)
- 0dce3c6 oxfmt: Handle invalid `overrides` config without panic
(#21661) (Yuji Sugiura)
- 9f82ed4 formatter: Escape backticks in JSDoc inline code spans
(#21577) (bab)

### ⚡ Performance

- db6c603 oxfmt/lsp: Avoid clones on lsp options deserializion (#21749)
(Sysix)
- 6a96c76 formatter: Avoid heap alloc for jsdoc delimiter (#21597)
(leaysgur)

---------

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
Co-authored-by: Cameron Clark <cameron.clark@hey.com>
camc314 added a commit that referenced this pull request Apr 30, 2026
…ring (#21906)

## Summary

The rule-name matcher in `DisableDirectives::contains` uses
`name.contains(rule_name)`, so a disable directive for an unrelated rule
can accidentally suppress an oxlint rule whose name is a substring of
the directive's rule name.

For example, `// eslint-disable-next-line canonical/no-re-export`
silently suppresses oxlint's `import/export` rule because
`"no-re-export".contains("export")` is true. This is a real issue we hit
on a large monorepo once we turned off
[`respectEslintDisableDirectives`](#21384)
— disabling directive respect surfaced ~44 `import/export` violations
that were only ever "passing" because an unrelated
`canonical/no-re-export` directive happened to share a substring.

## Fix

Strip the optional plugin prefix (`plugin/rule-name`) from the directive
name and compare equality with the target rule name. This preserves the
cross-plugin matching the code already documented — `vitest/foobar`
still matches `foobar`, `@typescript-eslint/no-var-requires` still
matches `no-var-requires` — while rejecting the accidental substring
hits.

## Test Plan

- Added a regression test covering both directions:
- `// eslint-disable-next-line canonical/no-re-export` does NOT suppress
the `export` rule
- `// eslint-disable-next-line import/export` DOES still suppress the
`export` rule
- `cargo test --package oxc_linter --lib` — all 1075 tests pass.

## AI disclosure

Drafted with Claude Code assistance; reviewed and validated by me.

Co-authored-by: Cameron <cameron.clark@hey.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-cli Area - CLI A-editor Area - Editor and Language Server A-linter Area - Linter A-linter-plugins Area - Linter JS plugins C-enhancement Category - New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

oxlint and eslint in parallel with reportUnusedDisableDirectives setting causes eslint disables to be flagged as unused

4 participants