Passe de performance guidée par la mesure, pas par l'intuition. Porte de qualité (`npm run check`) — la commande qui tourne des dizaines de fois par jour : - Les 5 vérifications sont indépendantes : exécutées en parallèle via scripts/check.mjs au lieu d'un enchaînement `&&` qui imposait la somme des durées ET 7 démarrages npm imbriqués. 14 s → 6 s - Cache ESLint (--cache) et compilation incrémentale tsc. 16 s → 14 s - Chemin critique restant : vitest 4,9 s, dont ~2 s de mise en place jsdom. Tests de mutation : - Config Vitest dédiée en pool `threads` sans ré-isolation. Mesuré sur le dry-run Stryker : overhead d'amorçage 12 844 ms → 1 592 ms, soit ~90 % du cycle par mutant. 5 min 02 → 3 min 40 - Le mode incrémental ramène le profil complet à 12 s après une édition. Pistes mesurées puis REJETÉES (documentées dans README-CI.md pour éviter qu'on les retente) : - Découper la mutation en 5 processus Stryker parallèles : 262 s contre 235 s. Chaque processus repaie son bac à sable et son dry-run. - Monter `concurrency` de 6 à 16 : aucun effet (~8,5 mutants/s partout), le débit est borné par l'orchestration mono-processus de Stryker. - Séparer vitest API/frontend en deux processus : aucun gain, jsdom domine. - Pool `threads` sur la suite complète : 18,8 s contre 3,5 s. Le bon réglage dépend du jeu de fichiers (jsdom ou non) — d'où deux configs distinctes. Le périmètre muté est inchangé (1511 mutants) : un score obtenu en retirant les mutants gênants vaudrait moins que pas de score. Corrections au passage : - scripts/*.mjs n'était couvert par aucune section ESLint - Les deux suites pytest ont chacune un paquet `src` : réunies dans un seul processus, le premier importé masquait l'autre. Séparées (et parallèles). Co-Authored-By: Claude <noreply@anthropic.com>
107 lines
3.1 KiB
JavaScript
107 lines
3.1 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 et scripts d'outillage (racine + scripts/) ----
|
|
{
|
|
files: ["*.js", "*.config.js", "scripts/**/*.{js,mjs}"],
|
|
languageOptions: {
|
|
ecmaVersion: 2023,
|
|
sourceType: "module",
|
|
globals: { ...globals.node },
|
|
},
|
|
rules: {
|
|
"no-unused-vars": ["error", { argsIgnorePattern: "^_", caughtErrors: "none" }],
|
|
"no-console": "off",
|
|
},
|
|
},
|
|
];
|