Files
pn-new-crm/eslint.config.mjs

48 lines
1.9 KiB
JavaScript
Raw Normal View History

2026-05-12 22:24:51 +02:00
import nextCoreWebVitals from 'eslint-config-next/core-web-vitals';
import prettier from 'eslint-config-prettier/flat';
const eslintConfig = [
2026-05-12 22:24:51 +02:00
...nextCoreWebVitals,
prettier,
{
rules: {
2026-05-12 22:24:51 +02:00
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
fix(compiler): React Compiler safety triage — 5 categories cleared Cleared 4 rule buckets (37 violations, including 5 real bugs) and silenced 1 informational bucket from the Next 16 / react-hooks v7 upgrade. Cleared rules promoted from `warn` back to `error` so new regressions block CI. Real bug fixes: - `interest-contact-log-tab.tsx`: `useMemo` used for side effects (5 setState calls inside a memo body); converted to `useEffect`. - `PieChart.tsx`: cumulative `let angle` mutation in a render-phase `map`; converted to `reduce` so the slice array is built without re-assignment. - `documents-hub.tsx`: `useMemo(() => ({ count: 0 }))` used as a mutable drag counter; converted to `useRef`. - `notes-list.tsx`: `Date.now()` read during render for note-edit countdown (impure) → pinned to a `now` state ticked every 30s. - `onboarding-checklist.tsx` / `user-profile.tsx` / `user-settings.tsx`: `useEffect(() => void load(), [])` with the `load` function declared AFTER the effect — relied on hoisting, trips Compiler's "access before declared" rule. Declared inside the effect. Pattern fixes (intentional cache-via-ref → state or layout-effect): - 6 `ref.current = x` writes during render moved into layout effects (`use-realtime-invalidation`, `settings-form-card`, `inbox`). - 3 `ref.current` reads during render (search totals cache, scanner file ref) rewritten to backed-by-state. - `use-is-mobile.ts` rewritten on `useSyncExternalStore` to avoid the SSR-then-rehydrate setState dance. - `use-notifications.ts` rewritten to write socket pushes directly into the React Query cache via `setQueryData`, removing a local state mirror. Rule config (`eslint.config.mjs`): - `react-hooks/purity` → error (was warn, cleared) - `react-hooks/set-state-in-render` → error (was warn, cleared) - `react-hooks/immutability` → error (was warn, cleared) - `react-hooks/refs` → error (was warn, cleared) - `react-hooks/incompatible-library` → off (informational only) - `react-hooks/set-state-in-effect` → warn (51 remaining, all the useEffect→fetch→setState data-fetch pattern; migration to useQuery tracked in BACKLOG) Verified: tsc clean, eslint 0 errors / 69 warnings (down from 105), vitest 1315/1315, next build green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 23:14:16 +02:00
// React Compiler safety rules shipped with eslint-config-next@16 /
// react-hooks@7. Triage status (2026-05-12 sweep):
// purity, set-state-in-render, immutability, refs — promoted
// back to error after the existing hits were cleaned up; new
// regressions block CI.
// set-state-in-effect — left as warn. Many hits are the
// useEffect→fetch→setState data-loading pattern that the
// Compiler conservatively flags but can't refactor without
// moving each call site to TanStack Query. ~50 admin-form
// land sites tracked in docs/BACKLOG.md §G.
// incompatible-library — informational only ("Compiler
// skipped this file because of a non-Compiler-safe import").
// No action needed; silenced to keep `pnpm lint` output
// actionable.
'react-hooks/purity': 'error',
'react-hooks/set-state-in-render': 'error',
'react-hooks/immutability': 'error',
'react-hooks/refs': 'error',
2026-05-12 22:24:51 +02:00
'react-hooks/set-state-in-effect': 'warn',
fix(compiler): React Compiler safety triage — 5 categories cleared Cleared 4 rule buckets (37 violations, including 5 real bugs) and silenced 1 informational bucket from the Next 16 / react-hooks v7 upgrade. Cleared rules promoted from `warn` back to `error` so new regressions block CI. Real bug fixes: - `interest-contact-log-tab.tsx`: `useMemo` used for side effects (5 setState calls inside a memo body); converted to `useEffect`. - `PieChart.tsx`: cumulative `let angle` mutation in a render-phase `map`; converted to `reduce` so the slice array is built without re-assignment. - `documents-hub.tsx`: `useMemo(() => ({ count: 0 }))` used as a mutable drag counter; converted to `useRef`. - `notes-list.tsx`: `Date.now()` read during render for note-edit countdown (impure) → pinned to a `now` state ticked every 30s. - `onboarding-checklist.tsx` / `user-profile.tsx` / `user-settings.tsx`: `useEffect(() => void load(), [])` with the `load` function declared AFTER the effect — relied on hoisting, trips Compiler's "access before declared" rule. Declared inside the effect. Pattern fixes (intentional cache-via-ref → state or layout-effect): - 6 `ref.current = x` writes during render moved into layout effects (`use-realtime-invalidation`, `settings-form-card`, `inbox`). - 3 `ref.current` reads during render (search totals cache, scanner file ref) rewritten to backed-by-state. - `use-is-mobile.ts` rewritten on `useSyncExternalStore` to avoid the SSR-then-rehydrate setState dance. - `use-notifications.ts` rewritten to write socket pushes directly into the React Query cache via `setQueryData`, removing a local state mirror. Rule config (`eslint.config.mjs`): - `react-hooks/purity` → error (was warn, cleared) - `react-hooks/set-state-in-render` → error (was warn, cleared) - `react-hooks/immutability` → error (was warn, cleared) - `react-hooks/refs` → error (was warn, cleared) - `react-hooks/incompatible-library` → off (informational only) - `react-hooks/set-state-in-effect` → warn (51 remaining, all the useEffect→fetch→setState data-fetch pattern; migration to useQuery tracked in BACKLOG) Verified: tsc clean, eslint 0 errors / 69 warnings (down from 105), vitest 1315/1315, next build green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 23:14:16 +02:00
'react-hooks/incompatible-library': 'off',
},
},
{
// Tests assert response shape via expect() — narrowing every
// `res.json()` to a structural type adds boilerplate without catching
// bugs. Allow `any` casts at JSON boundaries in test files.
2026-05-12 22:24:51 +02:00
files: ['tests/**/*.ts', 'tests/**/*.tsx'],
rules: {
2026-05-12 22:24:51 +02:00
'@typescript-eslint/no-explicit-any': 'off',
},
},
{
2026-05-12 22:24:51 +02:00
ignores: ['client-portal/**', 'next-env.d.ts'],
},
];
export default eslintConfig;