metalfrom.eu/eslint.config.js
Nicolas Fryder 60074fb015
Some checks are pending
CI / javascript (push) Waiting to run
CI / python (push) Waiting to run
CI / mutation (push) Waiting to run
feat(qualité): outillage de test complet, CI locale, annulation réelle des runs
Le dépôt n'avait aucun test, aucun linter, aucune vérification de types.

Outillage
- ESLint 9 (flat config) sur api + les deux frontends, Ruff sur le Python
- tsc --checkJs sur l'API (pas de TypeScript, juste la vérification)
- Vitest : 401 tests JS ; pytest : 43 tests Python
- Tests de mutation (Stryker), deux profils : logique pure et API complète
- Hook pre-push `npm run check` (~17 s) — le déploiement Coolify est sur webhook,
  c'est donc la seule porte de qualité avant la mise en ligne
- Workflow Forgejo Actions prêt (inerte tant qu'aucun runner n'est enregistré)

Sécurité
- Injection SQL authentifiée dans resolve-conflict : `field` était interpolé
  dans le SET sans allowlist
- timingSafeEqual levait sur un jeton multi-octets (500 au lieu de 401)
- setErrorHandler écrasait tous les 4xx en 500
- .env.example : ADMIN_JWT_SECRET et ADMIN_SEED_* n'étaient documentés nulle part
  alors que leur absence casse toute connexion admin

Annulation réelle des crawl_run (migration 014)
- L'API posait status='error' sans que le crawler en sache rien : le process
  continuait, et son UPDATE final ne matchait plus (run réussi affiché en erreur)
- Protocole coopératif : drapeau cancel_requested lu à chaque lot, le crawler
  écrit lui-même status='cancelled'

Cohérence géographique (migration 014)
- Le trigger 013 supprimait les band_locations sans purger le point dénormalisé
- L'édition admin de lat/lon n'atteignait jamais band_locations : la carte
  ignorait la correction. Override step_order = -1, dans une transaction

Corrections
- limit/offset NaN → 500 au lieu de 400
- OPTIONS sans `return reply` (Fastify poursuivait le cycle de vie)
- listen() sans catch, cast ::text en dur sur les colonnes numériques
- /admin/api/logs ne renvoyait pas sa pagination
- a11y : sélecteur de langue annoncé comme liste vide (role=option manquant)

Nettoyage
- apps/web/quizz-site supprimé (sans rapport avec le projet)
- Code mort : openModal(), LANG_NAMES, double import, variables inutilisées
- .dockerignore ajoutés ; node_modules racine n'était pas gitignoré

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-18 10:05:40 +02:00

103 lines
2.9 KiB
JavaScript

import js from "@eslint/js";
import globals from "globals";
/**
* Config ESLint « flat » (ESLint 9).
*
* Trois environnements coexistent dans ce repo et n'ont pas les mêmes globales :
* - apps/api : Node ESM
* - apps/web/site : scripts classiques navigateur (Leaflet en global)
* - apps/admin/site : scripts classiques navigateur
*/
export default [
{
ignores: [
"**/node_modules/**",
"apps/web/quizz-site/**",
"reports/**",
".stryker-tmp/**",
"coverage/**",
],
},
js.configs.recommended,
// ---- API : Node, ESM ----
{
files: ["apps/api/src/**/*.js"],
languageOptions: {
ecmaVersion: 2023,
sourceType: "module",
globals: { ...globals.node },
},
rules: {
"no-unused-vars": ["error", { argsIgnorePattern: "^_", caughtErrors: "none" }],
"no-console": "off",
eqeqeq: ["error", "smart"],
"prefer-const": "error",
"no-var": "error",
"no-throw-literal": "error",
"require-await": "warn",
},
},
// ---- Tests (vitest). Certains tournent sous jsdom → globales navigateur aussi. ----
{
files: ["apps/*/test/**/*.js"],
languageOptions: {
ecmaVersion: 2023,
sourceType: "module",
globals: { ...globals.node, ...globals.browser },
},
rules: {
"no-unused-vars": ["error", { argsIgnorePattern: "^_", caughtErrors: "none" }],
},
},
// ---- Frontends : scripts classiques navigateur ----
{
files: ["apps/web/site/**/*.js", "apps/admin/site/**/*.js"],
languageOptions: {
ecmaVersion: 2023,
sourceType: "script",
globals: {
...globals.browser,
// Chargés via <script> (CDN ou fichier local) avant app.js
L: "readonly",
LOCALES: "readonly",
noUiSlider: "readonly",
BMPure: "readonly",
// Footer CJS des modules « purs » partagés navigateur/test
module: "writable",
},
},
rules: {
"no-unused-vars": ["error", { argsIgnorePattern: "^_", caughtErrors: "none" }],
// `try { … } catch {}` volontaire sur les appels best-effort (logout, télémétrie)
"no-empty": ["error", { allowEmptyCatch: true }],
"no-console": ["warn", { allow: ["warn", "error"] }],
eqeqeq: ["error", "smart"],
"prefer-const": "error",
"no-var": "error",
// Les frontends déclarent volontairement des globales partagées entre fichiers
"no-redeclare": ["error", { builtinGlobals: false }],
},
},
// ---- Traductions : les espaces insécables fines sont de la typographie
// volontaire (français, tchèque…), pas des caractères parasites. ----
{
files: ["apps/web/site/locales.js"],
rules: { "no-irregular-whitespace": "off" },
},
// ---- Config outillage à la racine ----
{
files: ["*.js", "*.config.js"],
languageOptions: {
ecmaVersion: 2023,
sourceType: "module",
globals: { ...globals.node },
},
},
];