Migrate from ESLint to oxlint - #4119
Conversation
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>
| "correctness": "off" | ||
| }, | ||
| "options": { | ||
| "typeAware": true |
There was a problem hiding this comment.
Worth enabling typeCheck? Would that cover all the files checked by the typecheck command?
There was a problem hiding this comment.
Done.
I've kept the typecheck command since it's very useful with --watch.
oxlint does not support --watch
| "@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", |
There was a problem hiding this comment.
Wonder if we should look into unicorn rules. Seems like it can replaces some of the the sonarjs rules
There was a problem hiding this comment.
I wouldn't mind removing the sonarjs rules
There was a problem hiding this comment.
I've removed the sonarjs rules. We can look into enabling unicorn rules in a separate PR
|
This will be a great PR to finish and merge now that oxlint supports react compiler rules. I think we can get rid of eslint completely. For markdown, we can wait for oxlint to support it oxc-project/oxc#18407 |
| // ESLint is only used for markdown; JS/TS is linted by the oxc extension | ||
| "eslint.validate": ["markdown"], | ||
| "eslint.problems.shortenToSingleLine": true, | ||
| "oxc.path.tsgolint": "node_modules/.bin/tsgolint.cmd", |
There was a problem hiding this comment.
need this otherwise type-aware rules don't work, but that path won't work outside windows... should we only set this path in our own individual settings?
| @@ -0,0 +1 @@ | |||
| @AGENTS.md | |||
There was a problem hiding this comment.
|
Ready for review.
I've kept eslint to lint the markdown files. It's fast and shouldn't be much of an issue. |
Replaces ESLint with oxlint for JS/TS linting. ESLint stays only for markdown, since
@eslint/markdownis built on ESLint's language plugin API, which oxlint does not support..oxlintrc.jsonwas generated with@oxlint/migrate --type-aware --with-nurseryand carries over 545 rules at their originalwarnseverity.@eslint-reactandsonarjsload asjsPlugins;typescript,reactandvitestuse oxlint's native implementations, sotypescript-eslint,eslint-plugin-react-hooksand@vitest/eslint-pluginare dropped.Scripts
node --run eslintnode --run lint(oxlint) +node --run lint:md(markdown)node --run eslint:fixnode --run lint:fix/node --run lint:md:fix--report-unused-disable-directivesis passed to oxlint to match ESLint 9's default behaviour. CI,publish.yml,AGENTS.mdand.vscode/settings.jsonare updated to match.Rules that could not be migrated
react-hooks/purity,preserve-manual-memoization,set-state-in-effect, …). oxlint only ships these as a single experimental bundledreact/react-compilerrule, 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-eslintparser 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-eslintrules viaoxlint-tsgolint.Suppressions added
Three, each a behavioural difference rather than a real defect:
sonarjs/no-redundant-optionalinsrc/types.ts— cannot seeexactOptionalPropertyTypeswithout type information, so it flags the intentional?: boolean | undefined.vitest/no-conditional-expectinscrollToCell.test.tsx— oxlint flags a helper function that is not a test block.src/globals.d.tsmoved down one line, because oxlint reports the index signature where ESLint reported the interface.The other 15
eslint-disablecomments 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-factorieswas removed: it does not exist in that plugin and ESLint silently ignored it because it wasoff. It was most likely meant to bereact-hooks/component-hook-factories..agentsis ignored by both linters, mirroring the existing.claudeentry.Known issue:
--type-awareon Windowsoxlint 1.76.0 only looks for
node_modules/.bin/tsgolint.exe, but npm writes.cmd/.ps1shims there, sonode --run lintfails locally on Windows with "Failed to find tsgolint executable".TSGOLINT_PATHdoes not help and 1.76.0 is the latest release. CI onubuntu-latestis unaffected, and oxlint exits1rather than silently skipping the rules. Local workaround:The oxc VS Code extension has an
oxc.path.tsgolintsetting that avoids this for editor diagnostics.Verification
node --run lint,node --run lint:md,node --run typecheck,node --run format:checkall pass, and the full suite is green (353 passed, 2 skipped across 65 files).🤖 Generated with Claude Code