import js from '@eslint/js'; import { defineConfig, globalIgnores } from 'eslint/config'; import prettierAus from 'eslint-config-prettier'; import jsxA11y from 'eslint-plugin-jsx-a11y'; import reactHooks from 'eslint-plugin-react-hooks'; import reactRefresh from 'eslint-plugin-react-refresh'; import globals from 'globals'; import tseslint from 'typescript-eslint'; export default defineConfig([ globalIgnores([ 'out/**', 'dist/**', 'release/**', 'build/**', 'node_modules/**', 'coverage/**', 'playwright-report/**', 'test-results/**', ]), js.configs.recommended, // ── Typbewusste Regeln (nur für TypeScript-Dateien) ────────────────── { files: ['**/*.{ts,tsx}'], extends: [tseslint.configs.strictTypeChecked, tseslint.configs.stylisticTypeChecked], languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, }, /* Diese Konfigurationsdatei selbst wird ohne Typinformationen geprüft: mehrere ESLint-Plugins liefern keine Typdeklarationen mit, was sonst nur Rauschen aus `no-unsafe-*` erzeugen würde. */ { files: ['**/*.{js,mjs,cjs}'], languageOptions: { globals: { ...globals.node }, }, }, // ── Main- und Preload-Prozess (Node-Umgebung) ──────────────────────── { files: ['src/main/**/*.ts', 'src/preload/**/*.ts', 'e2e/**/*.ts', '*.config.ts'], languageOptions: { globals: { ...globals.node }, }, }, // ── Renderer (Browser-Umgebung, React) ─────────────────────────────── { files: ['src/renderer/**/*.{ts,tsx}', 'tests/**/*.{ts,tsx}'], languageOptions: { globals: { ...globals.browser }, }, plugins: { 'react-hooks': reactHooks, 'react-refresh': reactRefresh, }, rules: { ...reactHooks.configs.recommended.rules, 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], }, }, /* Der Renderer läuft in der Sandbox und hat keinerlei Node-Zugriff. tsconfig.web.json lädt die Node-Typen trotzdem mit – `AnwendungsInfo` in src/shared/ipc.ts benennt die Plattform als `NodeJS.Platform`. Diese Regel verhindert, dass dadurch versehentlich Node-APIs in Renderer-Code landen, die erst zur Laufzeit knallen würden. */ { files: ['src/renderer/**/*.{ts,tsx}'], rules: { 'no-restricted-imports': [ 'error', { patterns: [ { group: ['node:*', 'fs', 'path', 'child_process', 'electron'], message: 'Der Renderer hat keinen Node-Zugriff. Bitte einen typisierten IPC-Kanal in src/shared/ipc.ts ergänzen.', }, ], }, ], }, }, /* ── Barrierefreiheit ──────────────────────────────────────────────── `jsx-a11y` ist hier keine Kür: Barrierefreiheit ist Definition of Done. Deshalb der strikte Regelsatz – Verstöße sind Fehler, keine Warnungen. */ { files: ['src/renderer/**/*.tsx', 'tests/**/*.tsx'], plugins: { 'jsx-a11y': jsxA11y }, rules: jsxA11y.flatConfigs.strict.rules, }, // ── Tests ──────────────────────────────────────────────────────────── { files: ['tests/**/*.{ts,tsx}', 'e2e/**/*.ts'], rules: { // In Tests sind bewusste Nicht-Null-Zusicherungen praktikabel. '@typescript-eslint/no-non-null-assertion': 'off', }, }, /* ── Die Konsolenwache darf niemand versehentlich umgehen ──────────── `e2e/konsolenwache.ts` hängt an `test` zwei automatische Fixtures, die jede Konsolenausgabe der laufenden Anwendung auffangen. Wer `test` wieder direkt von `@playwright/test` bezieht, bekommt sie nicht – und zwar lautlos: Die Suite läuft grün weiter und hört nur eben nicht mehr zu. Genau diese Art von stiller Lücke war der Grund, warum der CSP-Konsolenfehler jahrelang unbemerkt bei jedem Start auflief. */ { files: ['e2e/**/*.spec.ts'], rules: { 'no-restricted-imports': [ 'error', { paths: [ { name: '@playwright/test', importNames: ['test'], message: '`test` bitte aus ./konsolenwache beziehen – nur darüber hört jemand die Konsole der Anwendung ab.', }, ], }, ], }, }, // ── Projektweite Verschärfungen ────────────────────────────────────── { files: ['**/*.{ts,tsx}'], rules: { '@typescript-eslint/consistent-type-imports': [ 'error', { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, ], '@typescript-eslint/no-unused-vars': [ 'error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrors: 'all', caughtErrorsIgnorePattern: '^_', }, ], 'no-console': ['warn', { allow: ['warn', 'error', 'info'] }], eqeqeq: ['error', 'always', { null: 'ignore' }], }, }, // Muss zuletzt stehen: schaltet alle Regeln ab, die mit Prettier kollidieren. prettierAus, ]);