// eslint.config.js — root flat ESLint config (ESM) // Covers: apps/api (Node/TS) + apps/pwa (React 19 + TypeScript) // typescript-eslint v8, projectService: true (auto-discovers all tsconfigs) // ESLint pinned to 9.39.4 — do NOT upgrade to ESLint 10 until eslint-plugin-react // resolves the "getFilename is not a function" incompatibility (jsx-eslint#3977). import js from '@eslint/js' import tseslint from 'typescript-eslint' import reactPlugin from 'eslint-plugin-react' import reactHooks from 'eslint-plugin-react-hooks' import prettierConfig from 'eslint-config-prettier/flat' export default tseslint.config( // ── 1. Global ignores (replaces .eslintignore) ──────────────────────────── { ignores: [ '**/dist/**', // build output: apps/api/dist, apps/pwa/dist, generated sw.js '**/node_modules/**', 'apps/api/src/db/migrations/**', // generated Drizzle SQL files 'pnpm-lock.yaml', ], }, // ── 2. Base: all TS/TSX files in both apps ──────────────────────────────── // Uses recommendedTypeChecked (type-aware) per D-13-01. // NOT strict/strictTypeChecked — D-13-03 explicitly rejects the extra churn. { files: ['apps/**/*.{ts,tsx}'], extends: [ js.configs.recommended, tseslint.configs.recommendedTypeChecked, ], languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, rules: { // Allow _-prefixed names as intentionally unused (params + vars) '@typescript-eslint/no-unused-vars': [ 'error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, ], }, }, // ── 3. React + Hooks: PWA only (D-13-02) ──────────────────────────────── // Apps/api is Node/TS only — React config does NOT apply there. { files: ['apps/pwa/**/*.{ts,tsx}'], extends: [ reactPlugin.configs.flat.recommended, reactHooks.configs.flat.recommended, ], settings: { react: { version: 'detect' }, }, rules: { // TypeScript props typing supersedes prop-types in React 19 (RESEARCH Open Question 2) 'react/prop-types': 'off', // React 19 uses the automatic JSX transform (jsx: "react-jsx") — React does not // need to be in scope. The flat.recommended config enables this rule; we disable it. 'react/react-in-jsx-scope': 'off', }, }, // ── 4. disableTypeChecked override (D-13-10) ───────────────────────────── // These files are outside all tsconfig projects (tool configs + API test dir). // projectService: true cannot type-check them → disable type-aware rules only. // Non-type-aware rules (syntax, no-unused-vars) still apply. // Source: typescript-eslint.io/troubleshooting/typed-linting/#i-get-errors-telling-me-the-file-must-be-included { files: [ 'apps/api/drizzle.config.ts', 'apps/api/vitest.config.ts', 'apps/pwa/vite.config.ts', 'apps/pwa/vitest.config.ts', 'apps/pwa/playwright.config.ts', 'apps/api/tests/**/*.ts', // excluded from apps/api/tsconfig.json (Pitfall 2) 'eslint.config.js', // this file itself (not a ts project member) ], extends: [tseslint.configs.disableTypeChecked], }, // ── 5. eslint-config-prettier: MUST BE LAST ────────────────────────────── // Disables all ESLint formatting rules that conflict with Prettier (D-13-07). // Use the /flat import path for ESM flat config (Pitfall 7). // Source: github.com/prettier/eslint-config-prettier prettierConfig, )