diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8157385..c8d7e5b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -180,6 +180,22 @@ jobs: working-directory: frontend run: npm audit --audit-level=high --omit=dev + # ESLint. This step did not exist until 2026-09-11, which made + # `eslint.config.js` decorative: the config was added specifically + # to fix `npm run lint` silently erroring with "couldn't find a + # config", and then nothing ever ran it. Thirteen errors + # accumulated in master unnoticed, and the eslint 9 -> 10 bump was + # reviewed by running the linter by hand because CI could not say + # anything about it. + # + # eslint exits non-zero on errors only, so the ~34 deliberate + # `warn`-level findings (React Compiler advisories, unused-vars) + # stay visible in the log without blocking a deploy. That split is + # the whole point — see the rule notes in eslint.config.js. + - name: Lint (eslint) + working-directory: frontend + run: npm run lint + # Vitest component tests — run BEFORE the build so a regression # caught by tests doesn't get the chance to ship via a successful # build. We have ~50 tests today (HelpTooltip, InstallCameraNodeCard, diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index 4796d2e..83e1f7a 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -90,6 +90,25 @@ export default [ "react-hooks/purity": "warn", "react-hooks/refs": "warn", "react-hooks/preserve-manual-memoization": "warn", + // Same family, same reasoning — these two were missed when the + // list above was written, and they were the only thing keeping + // `npm run lint` at a non-zero exit: + // + // - `immutability` fires 12 times on the shape + // `useEffect(() => { loadThings() }, [...])` where `loadThings` + // is a `const` arrow function declared further down the + // component. It is correct at runtime — effects run after + // render, by which point the binding is initialized — so this + // is the compiler saying it cannot track the value, not a live + // bug. The fix is to hoist each loader above its effect (or + // wrap in useCallback), which is a real change to six files and + // belongs with the next edit to each. + // - `use-memo` fires once, on a non-inline first argument. + // + // Kept at `warn` rather than disabled: the diagnostic stays in + // the CI log, and lint can now be enforcing for everything else. + "react-hooks/immutability": "warn", + "react-hooks/use-memo": "warn", }, },