Skip to content

build: #119 add Biome formatter and improve ESLint#132

Open
ryancraigdavis wants to merge 1 commit into
ReduxAPI_GUIfrom
feature/119-hardened-eslint-rules-and-biome-for-formatting
Open

build: #119 add Biome formatter and improve ESLint#132
ryancraigdavis wants to merge 1 commit into
ReduxAPI_GUIfrom
feature/119-hardened-eslint-rules-and-biome-for-formatting

Conversation

@ryancraigdavis

Copy link
Copy Markdown

Harden ESLint + add Biome (formatter-only)

Branch: feature/119-hardened-eslint-rules-and-biome-for-formattingdevelop
Repo: Redux_GUI


⚠️ Read this first — scope guardrails

This PR is plumbing to support future quality gates. It does not turn any gate on, and it does
not reformat any code.

  • No blocking. CI's ESLint job stays report-only (continue-on-error: true). Nothing in this
    PR can fail a build or block a merge. Flipping to blocking is a deliberate, separate decision.
  • The formatter has NOT been run. biome.json is added, but npm run format was not
    executed — zero source files are reformatted in this PR. The repo-wide format sweep is a
    separate, isolated commit for later, kept out of here on purpose so this change stays reviewable.
  • No source / runtime changes. The only files touched are config, CI, scripts, and the
    lockfile. App behavior is unchanged (verified — see Verification).

In short: this PR makes the linter useful and honest, and stages the formatter, without
enforcing anything yet.
It lets us see what the tools would catch before we commit to gates.


Why

The GUI's ESLint setup was a two-line passthrough of eslint-config-next/core-web-vitals, run in
CI with continue-on-error: true. Two problems:

  1. A whole layer of coverage was missing. next/core-web-vitals layers React/Next/a11y rules
    on top of nothing — it does not include ESLint's base recommended set. So every core
    JavaScript correctness rule was off: no-undef, no-unused-vars, no-dupe-keys,
    no-unreachable, use-isnan, etc. The plain-JS bug class went completely unchecked. This
    matters more here than in most repos because the GUI has no test suite at all — ESLint is
    the only automated quality gate.
  2. The gate was theater. With continue-on-error, the linter could report errors and CI
    stayed green regardless.

This PR fixes (1) and stages the tooling to fix (2) later.


What changed

File Change
eslint.config.js Two-line passthrough → explicit, hardened flat config (details below)
biome.json New. Biome as formatter only (linter.enabled: false)
package.json Added lint:fix, format, format:check scripts + 3 devDependencies
package-lock.json Dependency resolutions for the new devDeps
.github/workflows/code-analysis.yml ESLint job Node 22 → 26 (aligns with build/engines)

New devDependencies (all dev-only): @biomejs/biome, eslint-config-prettier,
eslint-plugin-import-x.

eslint.config.js — the layering

  1. ignoresnode_modules, .next, out, build, public (the vendored Q.js library +
    static assets — must not be linted), next-env.d.ts.
  2. @eslint/js recommended — the previously-missing base-correctness floor.
  3. next/core-web-vitals — the existing React / hooks / a11y / @next coverage + globals.
  4. eslint-plugin-import-x — import correctness (import-x/named, import-x/no-unresolved)
    with a node resolver (correct for a pure-JS repo). See the note below on why import-x and
    not Next's bundled import plugin.
  5. project tuningno-unused-vars set to warn (there's a backlog; keep it visible, not a
    wall of blocking errors while we're non-blocking anyway).
  6. eslint-config-prettier (last) — turns off stylistic rules so ESLint and the Biome
    formatter never fight. ESLint owns correctness; Biome owns formatting. No overlap.

Why import-x instead of Next's bundled eslint-plugin-import: Next ships a vendored
eslint-plugin-import whose default resolver is a TypeScript resolver. Under ESLint 9 flat config
that resolver loads with an "invalid interface" and crashes the entire lint run the moment a
resolver-dependent rule hits a file that imports a package. Overriding the resolver setting doesn't
help (ESLint deep-merges settings, so the broken typescript resolver can't be removed by
omission). eslint-plugin-import-x is the maintained, flat-config-native fork; we register it under
its own import-x/* namespace with a node-only resolver and leave Next's rules untouched. This is
dev-time only — Next 16's next build does not run ESLint, so none of it can affect the build
or deploy.

biome.json — formatter only

  • formatter.enabled: true (2-space, double quotes, semicolons, lineWidth 100) — matched to the
    existing code style.
  • linter.enabled: false — Biome does not lint. ESLint remains the linter. The
    recommended ruleset is pre-wired but disabled, so adopting Biome's linter later (a separate,
    much-later decision) is a one-key flip.
  • Import-organizing enabled (assist.actions.source.organizeImports).
  • Excludes public/** and package-lock.json so a future sweep can never touch vendored or
    generated files.

What this fixes / enables

Real bugs surfaced immediately (reported, not enforced — nothing blocks):

Finding Rule Location
Duplicate object key border no-dupe-keys components/Visualization/svgs/SSSPTableSvgReact.js:96
Dead import — requestIsCertificateValid doesn't exist in ../redux import-x/named components/pageblocks/VerifyRowReact.js:17
Dead import — Typograph doesn't exist in @mui/material import-x/named pages/index.js:25
Empty block no-empty (surfaced by base ruleset)

The two dead imports are notable: they're imported and referenced, so no-unused-vars cannot
catch them — only import resolution does. These are exactly the class of latent bug the old config
was blind to.

Also fixed / aligned:

  • The ESLint-9 import-plugin crash (via import-x) — the linter now runs cleanly to completion.
  • CI Node inconsistency: the ESLint job was pinned to Node 22 while build + engines require 26.

Foundation laid for future gates (explicitly out of scope for this PR):

  • Making lint blocking: flip warn → error on chosen rules + drop continue-on-error.
  • The Biome format sweep + format:check in CI.

Findings overview

npm run lint now reports 106 findings — 37 errors, 69 warnings. None block (CI is
report-only). Breakdown:

Count Sev Rule New in this PR?
66 warn no-unused-vars Yes (base floor)
24 error react-hooks/set-state-in-effect No — already flagged by old config
10 error react-hooks/immutability No — already flagged by old config
2 warn import-x/named Yes (import-x)
1 error no-dupe-keys Yes (base floor)
1 error no-empty Yes (base floor)
1 warn react-hooks/exhaustive-deps No
1 error react/jsx-no-duplicate-props No — already flagged by old config

The 34 react-hooks/* errors are pre-existing (they come from next/core-web-vitals and were
already reported by the old config — just ignored by continue-on-error). This PR neither adds nor
fixes them; it leaves severity as-is. Whether/when to fix or downgrade them is a follow-up.


Verification

The GUI has no automated tests (no test files, framework, config, or script — confirmed). So
verification was: dependency integrity, a production build, and a live end-to-end smoke test.

  • Dependencies resolve cleanly; lockfile in sync (npm install --dry-run → "up to date").
  • Production build passes on the final config (next build → exit 0, all 7 routes prerendered).
  • ESLint is decoupled from the build — proven by building successfully with a deliberately
    crashing ESLint config
    on disk. Next 16 removed lint-during-build.
  • Live smoke test (GUI dev server → proxy → backend), all green:
    • / renders (HTTP 200) with all five rows; /aboutus renders (200).
    • info(SAT3)3SAT; solve(SAT3)(x1:True,x2:True,x3:False);
      reduce(SAT3→CLIQUE) → real CLIQUE instance; visualize(CLIQUE) → frames.

Conclusion: the tooling changes are inert to the running app; Redux behaves identically.


How to try it (Node 26)

npm run lint          # 106 findings, no crash, nothing blocks
npm run lint:fix      # auto-fix the auto-fixable subset
npm run format:check  # DRY — shows what a format sweep would change (writes nothing)
# npm run format      # the sweep itself — NOT run in this PR; separate future commit

Follow-ups (out of scope — do not expect them here)

  1. Run the Biome format sweep as its own isolated commit once these tools are signed off.
  2. Make the gate blocking (promote selected rules to error, remove continue-on-error,
    add format:check to CI) — pending sign-off on gates.
  3. Triage the surfaced findings — fix the real bugs above; decide fix-vs-downgrade for the
    pre-existing react-hooks/* errors.

Add Biome for consistent code formatting, decoupled from ESLint so
correctness and style concerns don't overlap. Add lint:fix, format,
and format:check npm scripts.

Rework eslint.config.js to layer:
- js.recommended for base JS correctness (previously missing, so
  bugs like no-undef/no-unused-vars in plain JS went unchecked)
- eslint-config-next for React/hooks/a11y coverage
- eslint-plugin-import-x for import correctness, using a node
  resolver instead of Next's bundled import plugin, which crashes
  under ESLint 9 flat config due to a broken TypeScript resolver
- eslint-config-prettier last, to disable stylistic rules now
  owned by Biome

Bump CI Node.js version to 26 to match engines requirement.
@ryancraigdavis ryancraigdavis requested a review from wrigjl July 14, 2026 05:29
@ryancraigdavis ryancraigdavis linked an issue Jul 14, 2026 that may be closed by this pull request
9 tasks
@ReduxISU ReduxISU deleted a comment from CLAassistant Jul 14, 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.

Hardened ESLint rules and Biome for Formatting

1 participant