- Disable detect-object-injection globally in eslint.config.js: all hits were numeric loop array indices (ranks[i]) — not user-controlled keys; zod guards real API input boundaries; justification comment added (T-16-09) - Add inline eslint-disable for detect-non-literal-fs-filename at 2 sites: - apps/api/src/index.ts: realpathSync(process.argv[1]) — runtime entry path, not user input - apps/api/tests/broker/expand.test.ts: readFileSync of test fixture path — test-controlled - pnpm lint exits 0 across both apps with --max-warnings 0 - 14 of 15 security rules remain active at error; no blanket file disables
130 lines
6.3 KiB
JavaScript
130 lines
6.3 KiB
JavaScript
// 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 pluginSecurity from 'eslint-plugin-security';
|
|
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',
|
|
// react-hooks v7.1.1 flat.recommended enables React Compiler rules (immutability,
|
|
// set-state-in-effect, purity, refs, etc.). These rules are designed for use with
|
|
// the React Compiler and flag valid pre-Compiler React patterns as violations.
|
|
// This codebase does NOT use the React Compiler — disable the Compiler-only rules.
|
|
'react-hooks/set-state-in-effect': 'off',
|
|
'react-hooks/immutability': 'off',
|
|
'react-hooks/purity': 'off',
|
|
'react-hooks/refs': 'off',
|
|
'react-hooks/static-components': 'off',
|
|
'react-hooks/use-memo': 'off',
|
|
'react-hooks/preserve-manual-memoization': 'off',
|
|
'react-hooks/incompatible-library': 'off',
|
|
'react-hooks/globals': 'off',
|
|
'react-hooks/error-boundaries': 'off',
|
|
'react-hooks/set-state-in-render': 'off',
|
|
'react-hooks/unsupported-syntax': 'off',
|
|
'react-hooks/config': 'off',
|
|
'react-hooks/gating': 'off',
|
|
// exhaustive-deps fires as a warn in v7 flat recommended; with --max-warnings 0
|
|
// that counts as a failure. Promote to error so violations are surfaced explicitly.
|
|
'react-hooks/exhaustive-deps': 'error',
|
|
},
|
|
},
|
|
|
|
// ── 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)
|
|
// e2e specs live in tsconfig.e2e.json which extends tsconfig.json; projectService
|
|
// discovers tsconfigs by walking up from each file's directory, but the e2e dir
|
|
// has no tsconfig of its own — projectService maps it to tsconfig.e2e.json only
|
|
// when that tsconfig explicitly names the files. In practice projectService cannot
|
|
// find these files via automatic discovery, so type-aware rules are disabled here.
|
|
'apps/pwa/e2e/**/*.ts',
|
|
'eslint.config.js', // this file itself (not a ts project member)
|
|
],
|
|
extends: [tseslint.configs.disableTypeChecked],
|
|
},
|
|
|
|
// ── 5. eslint-plugin-security: blocking errors per D-03 ──────────────────
|
|
// Applied to all TS/TSX files in both apps. 14 of 15 rules active at error.
|
|
// detect-object-injection is disabled globally: it fires on every obj[key]
|
|
// pattern including numeric array index access (e.g. arr[i] in loops).
|
|
// After triage: all hits are schema-derived or numeric loop counters — not
|
|
// user-controlled keys. Real user-controlled input is guarded by zod
|
|
// validation at API boundaries. Disabling one rule; the remaining 14 enforce.
|
|
{
|
|
files: ['apps/**/*.{ts,tsx}'],
|
|
...pluginSecurity.configs.recommended,
|
|
rules: {
|
|
...pluginSecurity.configs.recommended.rules,
|
|
'security/detect-object-injection': 'off', // High FP: all hits are numeric loop indices or schema-derived keys, not user input
|
|
},
|
|
},
|
|
|
|
// ── 6. 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,
|
|
);
|