chore(lint): swap eslint + prettier for oxlint + oxfmt#109

merged2
develop
chore/lint-oxlint-oxfmt
openedAug 26, 2026byMMihai
View on GitHubTwo-way sync enabledPR last imported 16d ago

replaces eslint + prettier with oxlint + oxfmt. two commits: the toolchain swap, then the mass reformat, so the config diff stays reviewable on its own.

why

eslint is the blocker for typescript 7. typescript-eslint reads the TypeScript JS compiler API, which 7.x drops entirely — require("typescript") under 7.0.2 returns 2 keys and createProgram is undefined, so it hard-crashes:

1TypeError: Cannot read properties of undefined (reading 'Cjs')
2 at @typescript-eslint/typescript-estree/dist/create-program/shared.js:59

it also caps peer typescript at <6.1.0 even on latest. oxlint parses with its own Rust parser and never touches the TS API, so this unblocks #110 by construction rather than by workaround.

what changed

  • added oxlint 1.80.0 + oxfmt 0.65.0; removed eslint, eslint-config-next, @eslint/eslintrc, prettier
  • .oxlintrc.json ports eslint-config-next core-web-vitals + typescript across the eslint, typescript, react, react-hooks, jsx-a11y, nextjs, import, oxc and unicorn plugins
  • .oxfmtrc.json generated by oxfmt --migrate=prettier, printWidth 80 to match prettier defaults (.prettierrc was {}); .prettierignore entries carried over, plus CHANGELOG.md (release-please owns it) and .agents/skills/ (upstream owns it)
  • deleted eslint.config.mjs, .prettierrc, .prettierignore
  • script and mise task NAMES unchanged: lint, lint:fix, format, format:check
  • existing eslint-disable-next-line comments are still honoured by oxlint, so none needed rewriting
  • lint stays non-gating in CI; the trailing comment in ci.yml now names oxlint instead of eslint

the reformat commit

181 files, +4540/-3004. that's not this PR introducing churn — those files were already off-format under prettier, because format:check was never a CI gate. oxfmt also formats toml, hence the wrapped arrays in mise.toml.

rule parity — the honest list

oxlint hard-fails on unknown rule names, so the full 114-rule set from core-web-vitals + typescript was bisected. eight rules had no oxlint equivalent:

no real loss (all were off or no-ops under the modern JSX transform):

  • react/prop-types, react/jsx-uses-react, react/jsx-uses-vars

genuine coverage loss:

  • react/no-deprecated
  • react-hooks/component-hook-factories, react-hooks/config, react-hooks/gating (React Compiler lints)
  • nextjs/no-location-assign-relative-destination

14 of the 17 react-hooks/* rules ported, and all are set explicitly at Next's own severities rather than relying on category defaults. all 21 @typescript-eslint/* rules ported — nothing lost there (no type-aware rules were enabled to begin with).

baseline moved: 64 -> 98 problems

eslint reported 64 (39 errors / 25 warnings). oxlint reports 98 (70 / 28). that's a superset, not a regression, and no new kind of violation:

  • eslint-config-next cherry-picked only 6 jsx-a11y rules; oxlint's correctness category enables all of them (+19: prefer-tag-over-role, html-has-lang, click-events-have-key-events, ...)
  • oxlint's set-state-in-effect flags 22 sites where eslint flagged 7

none of these are fixed here — lint remains non-gating, same as before.

dependency effect

removing eslint-config-next dropped ~80 packages (it was the sole root of the eslint tree: eslint-plugin-import/-jsx-a11y/-react/-react-hooks, both import resolvers, the whole @typescript-eslint scope). install went 592 -> 512 packages. nothing else in the repo depended on it, and no prod dependency pulls eslint transitively.

the three existing eslint-disable-next-line comments still work — oxlint honours ESLint-style disable comments including @next/next/ prefixed rule names, so none needed rewriting. all three verified silent.

prettier@3.9.6 is still on disk as a transitive dep of convex and @react-email/render. no script invokes it; it can't be pruned.

config gotchas worth knowing

  • sortPackageJson defaults to true in oxfmt; --migrate=prettier wrote false explicitly. if that key is ever lost, package.json gets reordered on the next format run
  • printWidth defaults to 100; pinned to 80 to match prettier. same fragility
  • sortImports, sortTailwindcss, jsdoc are oxfmt extensions, all left disabled — enabling any is another repo-wide diff
  • oxfmt formats TOML (prettier doesn't), which is why mise.toml arrays got wrapped

checks: typecheck clean, 203/203 tests, build ok, format:check clean.


RNGO-47 · part of RNGO-40 (dependency upgrade sweep) https://linear.app/mihaicrisan/issue/rngo-47/replace-eslint-prettier-with-oxlint-oxfmt-pr-109

Checks

1 successful
All checks passed
780e535
Synced from GitHub

Too many files changed for review (189 files, 100 file limit).

Bypass the limit by tagging @greptile-apps to review.

Added 2 commits

  • style: reformat repo with oxfmt780e535
  • chore(lint): swap eslint + prettier for oxlint + oxfmtd37335f