Skip to content

refactor: migrate from ESLint to oxlint#716

Merged
yoannfleurydev merged 2 commits into
mainfrom
ntatoud/eslint-to-oxlint
Mar 19, 2026
Merged

refactor: migrate from ESLint to oxlint#716
yoannfleurydev merged 2 commits into
mainfrom
ntatoud/eslint-to-oxlint

Conversation

@ntatoud

@ntatoud ntatoud commented Mar 12, 2026

Copy link
Copy Markdown
Member

Summary

  • Replace eslint.config.ts with .oxlintrc.json using oxlint's native react, unicorn, typescript, and node plugins, plus jsPlugins for ESLint plugins without native oxlint equivalents
  • Remove ESLint-only packages (@eslint-react/eslint-plugin and sub-plugins, eslint-plugin-unicorn)
  • Add --fix flag to lint:oxlint script and lefthook pre-commit oxlint command

jsPlugins used

Plugin Reason
@tanstack/eslint-plugin-query No native oxlint equivalent
@tanstack/eslint-plugin-router No native oxlint equivalent
eslint-plugin-simple-import-sort No native oxlint equivalent
eslint-plugin-sonarjs No native oxlint equivalent (only 3 custom rules kept)
eslint-plugin-storybook No native oxlint equivalent

Notes

  • sonarjs.configs.recommended is not replicated — only the 3 explicitly configured rules are kept (cognitive-complexity, prefer-immediate-return, todo-tag). Broad correctness coverage is handled by oxlint's native correctness category.
  • no-restricted-syntax (blocking direct import.meta.env access) has no oxlint equivalent and was dropped.

Summary by CodeRabbit

  • Chores
    • Replaced ESLint tooling with oxlint across dev dependencies and scripts.
    • Updated pre-commit and pre-push hooks to use the new lint/format commands.
    • Switched CI lint step to run oxlint with stricter failure behavior.
    • Added a centralized oxlint configuration to enforce project-wide linting rules and file-specific overrides.
    • Minor formatting cleanup in a seed data file.

@vercel

vercel Bot commented Mar 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
start-ui-web-v3 Ready Ready Preview, Comment Mar 19, 2026 8:34am

Request Review

@coderabbitai

coderabbitai Bot commented Mar 12, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 33d451cd-8da1-4b26-9926-a0ed8b22f47a

📥 Commits

Reviewing files that changed from the base of the PR and between 246f6f6 and 33ee5e5.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • .github/workflows/code-quality.yml
  • .oxlintrc.json
  • eslint.config.ts
  • lefthook.yml
  • package.json
  • prisma/seed/book.ts
💤 Files with no reviewable changes (1)
  • eslint.config.ts
✅ Files skipped from review due to trivial changes (3)
  • prisma/seed/book.ts
  • .github/workflows/code-quality.yml
  • .oxlintrc.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • lefthook.yml
  • package.json

Walkthrough

Replaces ESLint with Oxlint across the repo: adds .oxlintrc.json, removes eslint.config.ts and ESLint dev-deps, updates package scripts, lefthook pre-commit hook, and CI linter step to run oxlint; plus a single-line whitespace edit in a seed file.

Changes

Cohort / File(s) Summary
Oxlint config & ESLint removal
/.oxlintrc.json, eslint.config.ts
Adds new Oxlint configuration with plugins (react, typescript, unicorn, node, TanStack, SonarJS, Storybook), rules, and overrides; deletes the previous flat eslint.config.ts.
Package, hooks & CI
package.json, .github/workflows/code-quality.yml, lefthook.yml
Replaces ESLint tooling with oxlint in devDependencies and scripts (lint:oxlint); updates CI linter command to pnpm oxlint --deny-warnings .; adds oxlint pre-commit command in lefthook.
Minor formatting
prisma/seed/book.ts
Whitespace-only change: added a blank line between imports for formatting consistency.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • yoannfleurydev
  • ivan-dalmet
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor: migrate from ESLint to oxlint' accurately and clearly summarizes the main objective of the changeset, which is replacing the ESLint linting tool and configuration with oxlint throughout the project.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ntatoud/eslint-to-oxlint
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.oxlintrc.json:
- Around line 3-15: The plugins array in .oxlintrc.json currently overwrites
Oxlint's defaults and omits the built-in ESLint and Oxc plugins so core ESLint
rules like "no-unreachable" are effectively disabled; update the "plugins" field
to include the default entries ("eslint" and "oxc") alongside the existing ones
(e.g., "react", "unicorn", "typescript", "node") so Oxlint will load ESLint core
rules and the explicit "no-unreachable" rule will take effect.

In `@package.json`:
- Around line 23-24: The workflow still calls the old script name "lint:eslint"
while package.json now defines "lint:oxlint"; either restore a compatibility
alias or update the workflow: add a new npm script "lint:eslint" that simply
runs the existing "lint:oxlint" (e.g., make "lint:eslint" invoke "npm run
lint:oxlint"), or edit the .github/workflows code-quality job to call "npm run
lint:oxlint" (replace any "lint:eslint" references) so both the CI and local
scripts stay in sync.
- Around line 23-24: The package.json script "lint:oxlint" currently runs
"oxlint --fix .", which causes the top-level "lint" entrypoint to auto-fix
files; change "lint:oxlint" to a read-only verification (e.g., "oxlint .") so
pnpm lint remains non-mutating, and add a separate fixer script (e.g.,
"lint:oxlint:fix" or "lint:fix") that runs "oxlint --fix ." for local developer
use; update the scripts keys "lint" and "lint:oxlint" (and add the fixer script
name you choose) accordingly so pre-push CI uses the read-only check and
developers can run the explicit fix script when needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7ad4e1d7-aac4-4a65-a0c0-0c4c372f815f

📥 Commits

Reviewing files that changed from the base of the PR and between 821d8f7 and 3b6c205.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • .oxlintrc.json
  • eslint.config.ts
  • lefthook.yml
  • package.json
  • prisma/seed/book.ts
💤 Files with no reviewable changes (1)
  • eslint.config.ts

Comment thread .oxlintrc.json
Comment on lines +3 to +15
"plugins": ["react", "unicorn", "typescript", "node"],
"jsPlugins": [
"@tanstack/eslint-plugin-query",
"@tanstack/eslint-plugin-router",
"eslint-plugin-simple-import-sort",
"eslint-plugin-sonarjs",
"eslint-plugin-storybook"
],
"categories": {
"correctness": "error"
},
"rules": {
"no-unreachable": "error",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and inspect the .oxlintrc.json file
find . -name ".oxlintrc.json" -type f

Repository: BearStudio/start-ui-web

Length of output: 83


🏁 Script executed:

# Check if there's any documentation or config files related to oxlint
git ls-files | grep -i oxlint

Repository: BearStudio/start-ui-web

Length of output: 81


🌐 Web query:

oxlint default plugins configuration eslint oxc

💡 Result:

Oxlint has built-in “plugins” (named rule groups, mostly ports of popular ESLint plugin rule sets). By default, these built-in plugins are enabled: eslint, typescript, unicorn, and oxc. Others (e.g. react, import, jsx-a11y, jest, nextjs, etc.) are off by default. [1]

Configure default vs extra plugins

  • Config file (.oxlintrc.json or oxlint.config.ts)

    • Using the plugins field overwrites the default plugin set, so you must list everything you want enabled (including defaults you want to keep). [2][1]
    • Example: enable react and import while keeping defaults:
      {
        "plugins": ["eslint", "typescript", "unicorn", "oxc", "react", "import"]
      }
    • Disable all default plugins:
      { "plugins": [] }
      [1]
  • CLI

    • Enable a plugin: oxlint --import-plugin (similar flags exist per plugin). [1]
    • Disable a default plugin: oxlint --disable-unicorn-plugin (only works for plugins that are on by default). [1]

ESLint naming note

When configuring rules, Oxlint uses ESLint-like namespacing (e.g. eslint/no-unused-vars, oxc/...), and if an ESLint core rule name is unique you can omit the eslint/ prefix (e.g. no-console == eslint/no-console). [2]

Sources: Oxlint built-in plugins + defaults [1], Oxlint config behavior (plugins overwrites defaults) and rule naming [2].

[1] https://oxc.rs/docs/guide/usage/linter/plugins
[2] https://oxc.rs/docs/guide/usage/linter/config.html


🌐 Web query:

oxlint plugins config override defaults no-unreachable rule

💡 Result:

  • plugins does not merge with Oxlint’s defaults — it overwrites the default plugin set. So if you add plugins, you must list every plugin you want enabled (including the default ones). (oxc.rs)
    {
      "plugins": ["eslint", "typescript", "unicorn", "oxc", "import"]
    }
    To disable all default plugins:
    { "plugins": [] }
    ``` ([oxc.rs](https://oxc.rs/docs/guide/usage/linter/plugins.html))
    
  • Override the no-unreachable rule explicitly in rules (this is independent of plugins, since it’s an ESLint-core rule in Oxlint’s eslint plugin). (oxc.rs)
    Enable:
    {
      "rules": {
        "no-unreachable": "error"
      }
    }
    Disable/override:
    {
      "rules": {
        "no-unreachable": "off"
      }
    }
    ``` ([oxc.rs](https://oxc.rs/docs/guide/usage/linter/config))
    

Citations:


🏁 Script executed:

cat .oxlintrc.json

Repository: BearStudio/start-ui-web

Length of output: 3111


🌐 Web query:

oxlint rule disabled plugin configuration no-unreachable not in plugins list

💡 Result:

That error happens when your config overrides plugins and the rule you’re trying to disable belongs to a plugin you didn’t include.

In Oxlint, no-unreachable is an ESLint core rule, provided by the built-in eslint plugin. The eslint plugin is default = yes, but as soon as you set plugins, you overwrite the defaults—so eslint may no longer be enabled. [1], [2]

Fix options

Option A (recommended): include eslint in your plugins list

{
  "plugins": ["eslint", "typescript", "unicorn", "oxc"],
  "rules": {
    "no-unreachable": "off"
  }
}

(Include whatever other plugins you actually use.) [1], [2]

Option B: don’t set plugins at all (keep default plugins), and just disable the rule:

{
  "rules": {
    "no-unreachable": "off"
  }
}

[1], [2]

Option C: explicitly namespace it

{
  "plugins": ["eslint"],
  "rules": {
    "eslint/no-unreachable": "off"
  }
}

[1], [2]

Sources: Oxlint configuration docs [1] and built-in plugins docs (shows eslint is a default plugin and that plugins overwrites defaults) [2].


🌐 Web query:

oxlint how to use eslint plugin rules explicit rule configuration

💡 Result:

To use Oxlint with ESLint-style plugin rules and explicitly configure which rules are on, you do two things in your Oxlint config (.oxlintrc.json or oxlint.config.ts):

  1. Load the plugin (native Rust plugin via plugins, or JS/ESLint plugin via jsPlugins)
  2. Explicitly enable rules under rules using the pluginName/ruleName key format

A) Native (Rust) plugins: plugins + explicit rules

// .oxlintrc.json
{
  "plugins": ["typescript", "unicorn", "oxc"],
  "rules": {
    "typescript/no-explicit-any": "error",
    "unicorn/filename-case": "warn"
  }
}

Oxlint’s plugins list controls which built-in rule namespaces are available; you still turn rules on/off explicitly in rules. [1]

B) Use an actual ESLint plugin: jsPlugins + explicit rules

// .oxlintrc.json
{
  "jsPlugins": ["eslint-plugin-playwright"],
  "rules": {
    "playwright/no-focused-test": "error"
  }
}

JS plugins are declared in jsPlugins, and then you enable their rules in rules using pluginName/ruleName. [2]

C) If the plugin name is “reserved”, alias it (avoids collisions with native plugins)

Some plugin names can’t be used directly for JS plugins because Oxlint implements those natively; in that case, give the JS plugin a custom name and reference rules via that alias: [1][3]

{
  "plugins": ["import"],
  "jsPlugins": [{ "name": "import-js", "specifier": "eslint-plugin-import" }],
  "rules": {
    "import/no-cycle": "error",
    "import-js/no-unresolved": "warn"
  }
}

Sources: Oxlint configuration (plugins/jsPlugins/overrides) [1], Oxlint JS plugins usage [2], Oxlint config file reference (reserved names + aliasing example) [3].


Restore the default eslint and oxc plugins to enable built-in ESLint rules.

The plugins field overwrites Oxlint's default plugins (eslint, typescript, unicorn, oxc), and the current config omits eslint and oxc. This disables all ESLint core rules, including no-unreachable—the explicit configuration of that rule in the rules section will not take effect without the eslint plugin enabled. Restore the missing defaults to restore ESLint rule coverage:

Suggested change
-  "plugins": ["react", "unicorn", "typescript", "node"],
+  "plugins": ["eslint", "oxc", "react", "unicorn", "typescript", "node"],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.oxlintrc.json around lines 3 - 15, The plugins array in .oxlintrc.json
currently overwrites Oxlint's defaults and omits the built-in ESLint and Oxc
plugins so core ESLint rules like "no-unreachable" are effectively disabled;
update the "plugins" field to include the default entries ("eslint" and "oxc")
alongside the existing ones (e.g., "react", "unicorn", "typescript", "node") so
Oxlint will load ESLint core rules and the explicit "no-unreachable" rule will
take effect.

Comment thread package.json
@ntatoud ntatoud marked this pull request as ready for review March 12, 2026 11:43
ntatoud added 2 commits March 19, 2026 09:32
- Replace eslint.config.ts with .oxlintrc.json using oxlint's native react/unicorn/typescript/node plugins and jsPlugins for @tanstack/eslint-plugin-query, @tanstack/eslint-plugin-router, eslint-plugin-simple-import-sort, eslint-plugin-sonarjs, and eslint-plugin-storybook
- Remove ESLint-only packages: @eslint-react/eslint-plugin and its sub-plugins, eslint-plugin-unicorn
- Add --fix flag to lint:oxlint script and lefthook pre-commit oxlint command
@ntatoud ntatoud force-pushed the ntatoud/eslint-to-oxlint branch from 246f6f6 to 33ee5e5 Compare March 19, 2026 08:32
@sonarqubecloud

Copy link
Copy Markdown

@yoannfleurydev yoannfleurydev merged commit a7e64cd into main Mar 19, 2026
15 checks passed
@yoannfleurydev yoannfleurydev deleted the ntatoud/eslint-to-oxlint branch March 19, 2026 09:04
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