import { dirname } from "node:path"; import { fileURLToPath } from "node:url"; import { FlatCompat } from "@eslint/eslintrc"; /** * ESLint-Konfiguration. * * Bis Phase 8 gab es ein Skript `npm run lint`, aber keine Konfiguration — * der Aufruf tat also nichts. Ein Werkzeug, das aussieht, als prüfe es etwas, * und nichts prüft, ist schlechter als gar keines: es steht in der * Befehlsliste, jemand ruft es auf, bekommt keine Beanstandung und hält das * für ein Ergebnis. * * Zwei Festlegungen bestimmen, was hier drinsteht: * * **Keine Formatierungsregeln.** Die macht Prettier. Zwei Werkzeuge, die sich * über Anführungszeichen und Zeilenlängen streiten, sind eines zu viel — und * die Streitpunkte lenken von den Regeln ab, auf die es ankommt. * * **Nur Regeln, die einen echten Fehler anzeigen.** Der Maßstab sind die * Fehler, die in diesem Projekt tatsächlich passiert sind: * * - `react-hooks/exhaustive-deps` hätte den Kiosk-Fehler aus Phase 4 * gemeldet: eine Mutation von React Query in den Abhängigkeiten eines * useCallback, wodurch der Wiederholungszeitgeber der Warteschlange nie * ansprang. Gefunden hat ihn damals ein E2E-Test — teuer. * - unbenutzte Importe blieben in Phase 7 zweimal liegen und fielen erst * beim Lesen auf. * - `react/jsx-key` und `react/no-unescaped-entities` kommen aus den * Next-Regeln und fangen genau die Sorte Flüchtigkeitsfehler, die man * beim Lesen überliest. * * Bewusst **nicht** eingeschaltet sind die typgestützten Regeln von * typescript-eslint (`no-floating-promises` und Verwandte). Sie brauchen den * Compiler bei jedem Lauf, und der Lauf dauert dann Minuten statt Sekunden — * bei einem Werkzeug, das vor jedem Commit laufen soll, ist das der falsche * Tausch. `npm run typecheck` deckt den größten Teil davon ohnehin ab. */ const __dirname = dirname(fileURLToPath(import.meta.url)); const compat = new FlatCompat({ baseDirectory: __dirname }); /** Die Konfiguration selbst — benannt, damit sie kein anonymer Export ist. */ const konfiguration = [ { ignores: [ ".next/**", "node_modules/**", "playwright-report/**", "test-results/**", "coverage/**", "docs/**", // Von Prisma erzeugt, nicht von Hand geschrieben. "src/generated/**", "next-env.d.ts", // Ein gewoehnliches Node-Skript fuer den Healthcheck des Containers. // Es laeuft ausserhalb des Bundles, ist CommonJS und hat mit den // Next-Regeln nichts zu tun. "docker/healthcheck.js", ], }, // Die Next-Regeln über die alte Schreibweise einbinden: eslint-config-next // liefert noch keine Flat Config. ...compat.extends("next/core-web-vitals", "next/typescript"), { rules: { /** * Unbenutztes ist ein Fehler, kein Schönheitsmakel: ein Import, den * niemand braucht, ist meistens der Rest einer halb zurückgebauten * Änderung. * * Ausnahme sind Bezeichner mit führendem Unterstrich — so wird in * diesem Projekt seit Phase 0 gekennzeichnet, dass ein Parameter * absichtlich ungenutzt bleibt (`_anfrage`, `_abo`). */ "@typescript-eslint/no-unused-vars": [ "error", { argsIgnorePattern: "^_", varsIgnorePattern: "^_", caughtErrorsIgnorePattern: "^_", }, ], /** * Die Regel, die den teuersten Fehler des Projekts gemeldet hätte. * Bewusst als Fehler und nicht als Warnung: Warnungen liest nach zwei * Wochen niemand mehr. */ "react-hooks/exhaustive-deps": "error", /** * `any` schaltet den Compiler ab. Wo es unvermeidlich ist — bei * Roh-SQL und beim Auswerten fremder Antworten —, steht es mit einer * Ausnahmezeile und einer Begründung da. */ "@typescript-eslint/no-explicit-any": "error", /** Ein leeres catch verschluckt genau den Fehler, den man sucht. */ "no-empty": ["error", { allowEmptyCatch: false }], /** * `console.log` gehört nicht in den Bestand. `console.info`, `.warn` * und `.error` schon — der Worker und die Integrationen schreiben ihre * Meldungen so, und die will man im Betrieb sehen. */ "no-console": ["error", { allow: ["info", "warn", "error"] }], }, }, { // In Tests ist `console.log` beim Fehlersuchen legitim, und die // Abhängigkeitsregel für Hooks greift dort ohnehin nicht. files: ["tests/**/*.ts", "tests/**/*.tsx", "scripts/**/*.ts", "prisma/**/*.ts"], rules: { "no-console": "off", }, }, { // Der Service Worker läuft außerhalb des Bundles und kennt weder Node // noch die Browser-Globals in der Fassung, die ESLint hier annimmt. files: ["public/sw.js"], languageOptions: { globals: { self: "readonly", caches: "readonly", clients: "readonly" }, }, rules: { "no-undef": "off", }, }, ]; export default konfiguration;