Skip to content

Migrate from ESLint to oxlint - #4119

Draft
nstepien wants to merge 2 commits into
mainfrom
migrate-to-oxlint
Draft

Migrate from ESLint to oxlint#4119
nstepien wants to merge 2 commits into
mainfrom
migrate-to-oxlint

Conversation

@nstepien

Copy link
Copy Markdown
Collaborator

Replaces ESLint with oxlint for JS/TS linting. ESLint stays only for markdown, since @eslint/markdown is built on ESLint's language plugin API, which oxlint does not support.

.oxlintrc.json was generated with @oxlint/migrate --type-aware --with-nursery and carries over 545 rules at their original warn severity. @eslint-react and sonarjs load as jsPlugins; typescript, react and vitest use oxlint's native implementations, so typescript-eslint, eslint-plugin-react-hooks and @vitest/eslint-plugin are dropped.

Scripts

before after
node --run eslint node --run lint (oxlint) + node --run lint:md (markdown)
node --run eslint:fix node --run lint:fix / node --run lint:md:fix

--report-unused-disable-directives is passed to oxlint to match ESLint 9's default behaviour. CI, publish.yml, AGENTS.md and .vscode/settings.json are updated to match.

Rules that could not be migrated

  • The 13 React Compiler rules (react-hooks/purity, preserve-manual-memoization, set-state-in-effect, …). oxlint only ships these as a single experimental bundled react/react-compiler rule, and will split them once they stabilise. This is the most substantive loss — happy to keep a narrow ESLint config for them alongside markdown if we'd rather not go without.
  • @eslint-react/no-implicit-key, no-leaked-conditional-rendering, no-unused-props. oxlint's JS plugin API cannot supply @typescript-eslint parser services, so these throw on every file rather than running.
  • @typescript-eslint/naming-convention, prefer-destructuring, no-unused-private-class-members, require-atomic-updates, one-var.

Everything else is covered, including all 40 type-aware @typescript-eslint rules via oxlint-tsgolint.

Suppressions added

Three, each a behavioural difference rather than a real defect:

  • sonarjs/no-redundant-optional in src/types.ts — cannot see exactOptionalPropertyTypes without type information, so it flags the intentional ?: boolean | undefined.
  • vitest/no-conditional-expect in scrollToCell.test.tsx — oxlint flags a helper function that is not a test block.
  • The existing directive in src/globals.d.ts moved down one line, because oxlint reports the index signature where ESLint reported the interface.

The other 15 eslint-disable comments work unchanged — oxlint accepts the @typescript-eslint/ prefix, and the unused-directive check confirms all of them are still live.

Incidental

@eslint-react/component-hook-factories was removed: it does not exist in that plugin and ESLint silently ignored it because it was off. It was most likely meant to be react-hooks/component-hook-factories.

.agents is ignored by both linters, mirroring the existing .claude entry.

Known issue: --type-aware on Windows

oxlint 1.76.0 only looks for node_modules/.bin/tsgolint.exe, but npm writes .cmd/.ps1 shims there, so node --run lint fails locally on Windows with "Failed to find tsgolint executable". TSGOLINT_PATH does not help and 1.76.0 is the latest release. CI on ubuntu-latest is unaffected, and oxlint exits 1 rather than silently skipping the rules. Local workaround:

Copy-Item node_modules\@oxlint-tsgolint\win32-x64\tsgolint.exe node_modules\.bin\tsgolint.exe -Force

The oxc VS Code extension has an oxc.path.tsgolint setting that avoids this for editor diagnostics.

Verification

node --run lint, node --run lint:md, node --run typecheck, node --run format:check all pass, and the full suite is green (353 passed, 2 skipped across 65 files).

🤖 Generated with Claude Code

nstepien and others added 2 commits July 27, 2026 20:39
Replace ESLint with oxlint for JS/TS linting, keeping ESLint only for
markdown (`@eslint/markdown` uses ESLint's language plugin API, which
oxlint does not support).

`.oxlintrc.json` was generated by `@oxlint/migrate --type-aware
--with-nursery` and carries over 545 rules at their original `warn`
severity. `@eslint-react` and `sonarjs` are loaded as `jsPlugins`;
`typescript`, `react` and `vitest` use oxlint's native implementations,
so `typescript-eslint`, `eslint-plugin-react-hooks` and
`@vitest/eslint-plugin` are no longer needed.

Rules that could not be migrated:

- the 13 React Compiler rules (`react-hooks/purity`,
  `preserve-manual-memoization`, …) — oxlint only ships these as an
  experimental bundled `react/react-compiler` rule
- `@eslint-react/no-implicit-key`, `no-leaked-conditional-rendering` and
  `no-unused-props` — oxlint's JS plugin API cannot supply parser
  services, so these throw on every file
- `@typescript-eslint/naming-convention`, `prefer-destructuring`,
  `no-unused-private-class-members`, `require-atomic-updates`, `one-var`

Three suppressions were added for behavioural differences rather than
real defects: `sonarjs/no-redundant-optional` cannot see
`exactOptionalPropertyTypes` without type information, and
`vitest/no-conditional-expect` flags a helper that is not a test block.
The existing directive in `globals.d.ts` moved down a line because
oxlint reports the index signature where ESLint reported the interface.

`@eslint-react/component-hook-factories` was dropped: it does not exist
in that plugin and was silently ignored by ESLint because it was `off`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread .oxlintrc.json
"correctness": "off"
},
"options": {
"typeAware": true

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Worth enabling typeCheck? Would that cover all the files checked by the typecheck command?

Comment thread .oxlintrc.json
"@eslint-react/naming-convention-context-name": "warn",
"@eslint-react/naming-convention-id-name": "warn",
"@eslint-react/naming-convention-ref-name": "warn",
"sonarjs/anchor-precedence": "warn",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Wonder if we should look into unicorn rules. Seems like it can replaces some of the the sonarjs rules

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I wouldn't mind removing the sonarjs rules

@nstepien nstepien self-assigned this Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants