ESLint was misconfigured (only globals.browser → 47 false no-undef) and run NOWHERE in CI, so 259 errors had accumulated unnoticed. Replace it with oxlint (Rust, ~50-100x faster) as the primary linter AND a real CI gate so lint debt can't silently pile up again. Tooling: - frontend/.oxlintrc.json — correctness=error; no-unused-vars with the existing ^[A-Z_] convention; node/vitest env overrides + AudioWorklet/__APP_VERSION__ globals (kills the false no-undef class); max-lines:500 (warn). - package.json: `lint` → oxlint, `lint:fix`, `lint:hooks` (advisory eslint). - eslint.config.js stripped to ONLY the React-Compiler rule family oxlint can't do yet (set-state-in-effect etc.), run via `lint:hooks`, NOT gated. Drop once oxlint's JS-plugin support leaves alpha. - ci.yml: new "Frontend lint (oxlint)" step in the Tests job — the gate. Real bugs oxlint caught (were buried in ESLint's noise): - GlossaryPanel: <X/> close-icon used but never imported → the edit-row cancel button threw ReferenceError at render. Imported X. - Two use*-named NON-hooks (useEngine action, useArchetypeAsProfile API call) tripped rules-of-hooks; suppressed with documented disables (renaming these misleading names is a worthwhile follow-up). Cleanup to reach a 0-error gate: removed 52 genuinely-dead vars/imports across 18 files (heavy in App.jsx — stale useState left over from prior refactors) and 4 behavior-preserving autofixes (no-useless-fallback-in-spread / no-useless-escape). Verified: oxlint 0 errors; bun install --frozen-lockfile in sync (Docker rule); vite build passes; full suite 638/638. Co-authored-by: mergetest <test@local> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
39 lines
1.2 KiB
JSON
39 lines
1.2 KiB
JSON
{
|
|
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
|
"env": {
|
|
"browser": true,
|
|
"es2024": true
|
|
},
|
|
"globals": {
|
|
"__APP_VERSION__": "readonly",
|
|
"AudioWorklet": "readonly",
|
|
"AudioWorkletNode": "readonly",
|
|
"AudioWorkletProcessor": "readonly",
|
|
"registerProcessor": "readonly"
|
|
},
|
|
"plugins": ["react", "import", "unicorn"],
|
|
"ignorePatterns": ["dist/", "src-tauri/", "node_modules/"],
|
|
"categories": {
|
|
"correctness": "error"
|
|
},
|
|
"rules": {
|
|
"no-unused-vars": ["error", { "varsIgnorePattern": "^[A-Z_]", "argsIgnorePattern": "^_", "caughtErrors": "none" }],
|
|
"no-empty": ["warn", { "allowEmptyCatch": true }],
|
|
"no-unused-expressions": ["error", { "allowShortCircuit": true, "allowTernary": true }],
|
|
"react/exhaustive-deps": "warn",
|
|
"react/rules-of-hooks": "error",
|
|
"react/only-export-components": ["warn", { "allowConstantExport": true }],
|
|
"max-lines": ["warn", { "max": 500, "skipBlankLines": true, "skipComments": true }]
|
|
},
|
|
"overrides": [
|
|
{
|
|
"files": ["vite.config.js", "*.config.js", "eslint.config.js"],
|
|
"env": { "node": true }
|
|
},
|
|
{
|
|
"files": ["**/*.test.{js,jsx}", "**/*.spec.{js,jsx}", "src/test/**"],
|
|
"env": { "node": true, "vitest": true }
|
|
}
|
|
]
|
|
}
|