From a9b68a5712ff3f7b8209dc1f9842d40d43a0dbb8 Mon Sep 17 00:00:00 2001 From: Shinuwa Date: Sat, 12 Sep 2026 12:53:08 +0200 Subject: [PATCH] Enable app-wide StrictMode and restore React effect checks --- .beads/interactions.jsonl | 1 + README.md | 6 ++++++ eslint.config.js | 1 - tests/react-lint.test.mjs | 12 ++++++++++++ website/src/main.jsx | 4 ++-- 5 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 tests/react-lint.test.mjs diff --git a/.beads/interactions.jsonl b/.beads/interactions.jsonl index 84e4d35..0259c83 100644 --- a/.beads/interactions.jsonl +++ b/.beads/interactions.jsonl @@ -36,3 +36,4 @@ {"id":"int-9cb5a663c6e65593625066b25976439c","kind":"field_change","created_at":"2026-09-11T19:15:51.529868089Z","actor":"Shinuwa","issue_id":"sokko-g-5i3","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Library category now derives from anchor navigation; same-page hash/history navigation works; malformed anchors handled and obsolete RAF/scroll timers cleaned up. Existing library coverage and two new behavioral E2E pass. npm run check:full passes with 32 E2E; targeted set-state-in-effect lint passes."}} {"id":"int-4ece504edad6e21c4829485c33215835","kind":"field_change","created_at":"2026-09-12T07:06:41.579702638Z","actor":"Shinuwa","issue_id":"sokko-g-739","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Tutorial visibility now follows toolbox identity and request transitions before commit; tour keyed by toolbox resets step and start latch. Two StrictMode behavioral tests cover identity changes, one-time flag consumption, dismissal and pending requests. check:full passes with 34 E2E; targeted set-state-in-effect lint passes."}} {"id":"int-c7fa4c94461f676392625658508ae231","kind":"field_change","created_at":"2026-09-12T10:15:48.480122568Z","actor":"Shinuwa","issue_id":"sokko-g-5rq","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Notepad now separates page identity from focused editing, replaces the editor DOM on page/module changes, preserves selection during local echoes and applies external updates after blur without stealing focus. Removed obsolete selection helpers and timestamp-only echo detection. check:full passes with 36 E2E; global set-state-in-effect diagnostic passes."}} +{"id":"int-e79db6c1131fb3c5c57dde09917ef735","kind":"field_change","created_at":"2026-09-12T10:50:44.387707762Z","actor":"Shinuwa","issue_id":"sokko-g-8po","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Enabled app-wide StrictMode, restored recommended react-hooks/set-state-in-effect rule, documented development checks and added an effective ESLint configuration regression test. npm run check:full passed: lint, 101 Node tests, build and 36 E2E. All C10 subsets complete."}} diff --git a/README.md b/README.md index 77f805c..cce76d1 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,12 @@ chargement initial des outils par Vite. Le parallélisme reste ajustable avec `npm run test:e2e -- --workers=4`. Les traces des tests en échec sont conservées dans `test-results` et consultables depuis le rapport Playwright. +L'application est enveloppée dans `StrictMode` : en développement (y compris les +E2E sur Vite), React rejoue certains rendus et cycles d'effets pour détecter les +effets de bord et les nettoyages manquants. Ces contrôles supplémentaires ne +s'exécutent pas en production. Les règles recommandées React Hooks, dont +`react-hooks/set-state-in-effect`, sont actives dans ESLint. + ## Contenu éditable Les contenus maintenus à la main sont regroupés dans `website/public/data`. diff --git a/eslint.config.js b/eslint.config.js index 08dc48a..34297b6 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -34,7 +34,6 @@ export default [ }, rules: { ...reactHooks.configs.recommended.rules, - "react-hooks/set-state-in-effect": "off", "no-unused-vars": ["error", { argsIgnorePattern: "^_", varsIgnorePattern: "^_", diff --git a/tests/react-lint.test.mjs b/tests/react-lint.test.mjs new file mode 100644 index 0000000..830bad2 --- /dev/null +++ b/tests/react-lint.test.mjs @@ -0,0 +1,12 @@ +// Rôle : vérifie que les contrôles React critiques restent actifs dans la configuration ESLint effective. +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { ESLint } from "eslint"; + +test("React effect and purity checks are enabled for application sources", async () => { + const eslint = new ESLint(); + const config = await eslint.calculateConfigForFile("website/src/main.jsx"); + for (const rule of ["react-hooks/set-state-in-effect", "react-hooks/purity", "react-hooks/rules-of-hooks", "react-hooks/exhaustive-deps"]) { + assert.ok(config.rules[rule][0] > 0, `${rule} must remain enabled`); + } +}); diff --git a/website/src/main.jsx b/website/src/main.jsx index 052e8af..7637db6 100644 --- a/website/src/main.jsx +++ b/website/src/main.jsx @@ -1,6 +1,6 @@ // Rôle : point d'entrée React, assemble données, routes, shell et overlays. /* eslint-disable react-refresh/only-export-components -- point d'entrée Vite sans export de composant. */ -import { lazy, Suspense, useCallback, useEffect, useState } from "react"; +import { lazy, StrictMode, Suspense, useCallback, useEffect, useState } from "react"; import { createRoot } from "react-dom/client"; import "./styles/main.scss"; import { AppOverlays } from "./components/AppOverlays.jsx"; @@ -175,4 +175,4 @@ function App() { ); } -createRoot(document.querySelector("#app")).render(); +createRoot(document.querySelector("#app")).render();