Repository navigation
Description
Audit complet du projet (2026-07-29). Cette issue liste les améliorations identifiées, hors travaux déjà couverts par les PR/issues en cours : #54 (CI GitHub Actions, résout #40), #55 (fix Field non-DOM + types polymorphes), #56 (CHANGELOG), #6 (valeur courante dans les messages d'erreur).
Vue d'ensemble
La lib elle-même est petite, bien documentée (JSDoc typé, tests runtime + matrice de types @types/react 16/17/18/19) et saine. Les vrais problèmes sont ailleurs : un socle d'outillage de 2020 en fin de vie (CRA 3, Enzyme, Jest 24, AJV 6) qui génère ~700 vulnérabilités d'audit, quelques bugs de robustesse dans les helpers, et aucune accessibilité.
1. 🔴 Accessibilité — priorité haute
Rien n'existe aujourd'hui. C'est le différenciateur le plus visible face aux libs concurrentes, pour un coût faible :
-
<FieldError>: ajouterrole="alert"(ouaria-live="polite") pour que les lecteurs d'écran annoncent l'erreur. -
<Field>: câbleraria-invalidquand le champ est en erreur. - Lier
aria-describedbydu champ vers l'id du<FieldError>associé (génération d'id à partir duname). - Vérifier le comportement clavier/focus de
scrollToError(déplacer le focus sur le premier champ en erreur, pas seulement le scroll).
2. Bugs à corriger (quick wins)
- Chemins de tableaux imbriqués cassés —
src/lib/Form/helpers.js(formatErrors) :.replace(/\[([0-9]+)\]/, '.$1')sans flagg. Une erreur AJV suritems[0].tags[1]donnefield = "items.0.tags[1]": le 2ᵉ index n'est pas converti, donc<FieldError name="items.0.tags.1">ne matche jamais. Test existant uniquement sur un seul niveau. -
isFieldInvalidignore tous les noms sauf le premier —src/lib/Form/Form.js:getFieldErrors(...names)ne lit que le premier argument, doncisFieldInvalid(['a', 'b'])ne vérifie que'a'. Passernamesdirectement (l'API accepte déjà un tableau). -
scrollToFirstErrorpeut planter — si aucun élément DOM ne porte lenamede l'erreur (champ custom, erreurrequiredsur un objet imbriqué),document.getElementsByName(...)[0]estundefinedet part tel quel dansscrollToElement. Ajouter un garde. - Wildcard non échappé —
filterByFieldNameWithWildcard:user.*devient la regex/^user./où le.matche n'importe quel caractère (userXmatche). Échapper le préfixe.
3. Dépendances runtime
- AJV 6 (2019, EOL) → AJV 8 : pas de draft 2019-09/2020-12, l'option
v5est morte, et les consommateurs avec AJV 8 dans leur arbre embarquent deux versions. Breaking (dataPath→instancePath,ajv-formatsséparé) — chantier le plus structurant, candidat 0.7.0/1.0.0. -
peerDependencies: react >=15est faux : le code utilisecreateContext(16.3+) etuseContext(16.8+). Corriger en>=16.8. -
scroll-to-element(non maintenu depuis ~2018) : remplacer par le natifelement.scrollIntoView({ behavior: 'smooth', block: 'center' })— zéro dépendance,scrollOptionspeut mapper dessus. -
lodash.throttle: paquets per-method lodash dépréciés ; une implémentation locale de ~15 lignes suffit. -
dot-prop-immutable: plus maintenu depuis 2020. À surveiller ou internaliser (seule fonction utilisée :set).
4. Outillage — le gros morceau
- Sortir de react-scripts 3 / CRA (déprécié, archivé) : porte l'essentiel des ~700 vulnérabilités d'audit et le bruit dependabot. Migrer la démo/docs vers Vite, les tests vers Vitest (ou Jest 29 standalone). Supprime ~1000 paquets transitifs.
- Remplacer Enzyme (mort, pas d'adaptateur officiel > React 16) par des tests Vitest + requêtes Testing Library, exécutés en Vitest Browser Mode (provider Playwright) plutôt que jsdom — en cohérence avec la direction DOM-first de la RFC RFC: DOM-first form API (v1.0 architecture) #58 : association native par attribut
form, focus au submit, champs portalés et (à terme) projection Constraint Validation sont précisément ce que jsdom simule mal ou pas du tout. Un environnement jsdom peut subsister pour les tests de logique pure (helpers). Tant que les tests tournent avecenzyme-adapter-react-16, la lib n'est réellement testée que sur React 16 — la matrice de types vérifie la compilation, pas le runtime. (Édit 2026-08-07 : précision Browser Mode suite à la discussion RFC RFC: DOM-first form API (v1.0 architecture) #58 — RTL seul reste valable mais jsdom testerait mal le DOM-first.) - Tester le runtime sur plusieurs versions de React dans la CI (alias d'installation par version, comme la matrice
@types/reactexistante). Périmètre simplifié par la RFC RFC: DOM-first form API (v1.0 architecture) #58 : React 18 + 19 pour la v1 ; 16/17 restent servis par la branche 0.x. - Packaging npm :
mainetmodulepointent vers le mêmedist/index.js(un seul build Babel) — l'un des deux ment. Ajouter un champexports(avectypes),sideEffects: false, etengines.
5. API / DX
-
reset()appelé avantonSubmitdanshandleSubmitSuccess: les champs touchés etisSubmittedsont effacés même si le submit côté serveur échoue. ProposerresetOnSubmit={false}ou exposerresetvia le contexte/ref. - Mettre en avant
useFormContextcomme API publique de premier rang dans le README (boutons "disabled tant qu'invalide", résumés d'erreurs…). Il est exporté mais peu documenté. - Validation à chaque
componentDidUpdatemême quand nidatanischeman'ont changé (le memoize ne sauve que pour la même référencedata). À revisiter lors du chantier outillage.
6. Gouvernance / publication
- Automatiser la release (après ci: migrate from CircleCI to GitHub Actions #54) : workflow
npm publishavec provenance sur tag, au lieu duprepublishOnlymanuel. - Arrêter de committer
docs/sur master (preversion) : déployer GitHub Pages depuis Actions pour ne plus versionner le build. - Nettoyer les badges README : CircleCI mort, Code Climate stale depuis 2020 (la ci: migrate from CircleCI to GitHub Actions #54 pose déjà la question Code Climate — trancher là-bas).
- Ajouter
SECURITY.mdetCONTRIBUTING.md(conventions de PR, lancement de la matrice de types en local).
Ordre suggéré
- Merger le train en cours : ci: migrate from CircleCI to GitHub Actions #54 (CI) → Fix/field non dom components #55 (fix Field) → Docs/changelog #56 (CHANGELOG).
- Accessibilité (§1) + quick wins bugs (§2) + peerDeps : petites PR, publiables en 0.6.x/0.7.0.
- Chantier outillage (§4) : Vite + Vitest (Browser Mode) + Testing Library — éteint le bruit dependabot.
- AJV 8 + packaging
exports: la vraie 1.0.0.
Activity
fix(Form): check every name in isFieldInvalid; guard and focus scroll…
hugomallet commentedon Aug 6, 2026
Progress update (2026-08-06)
Done — merged to master:
gflag) + wildcard prefix escaped (also fixes a latentSyntaxErrorcrash on unclosed brackets in prefixes). Heads-up: bracket-notation names in<FieldError name>(e.g.items.0.tags[1]) no longer match.isFieldInvalidnow checks every name of the list;scrollToFirstErrorguarded (no error / no matching DOM element) and moves keyboard focus to the first invalid field (§1 last item).<FieldError>rendersrole="alert"by default (overridable) + §3: react peerDependency corrected to>=16.8.In progress (next batch, 3 PRs):
aria-invalid+aria-describedbywiring on<Field>(was blocked by Fix/field non dom components #55, now merged).scroll-to-elementwith nativescrollIntoView+ inline the throttle helper (dropslodash.throttle).SECURITY.md+CONTRIBUTING.md+ README badge cleanup.Follow-ups noted during review:
role="alert"announcement with a real screen reader: visibility is CSS-gated by.isSubmitted, not mount-gated — a JS-gated render would be more robust.Remaining: AJV 8, tooling migration (Vite/Vitest/RTL), npm packaging (
exports), release automation,reset()-before-onSubmitDX,useFormContextdocs, per-React-version runtime tests in CI.fix(a11y): gate Field aria attributes on the same reveal condition as…
feat(a11y)!: form-scoped FieldError id registry with merged aria-desc…
fix(a11y): reset unmounting flag on remount; match wildcard FieldErro…
hugomallet commentedon Aug 7, 2026
Progress update: #65 merged —
aria-invalidgated on touched/submitted, form-scopedFieldErrorid registry (no cross-form collisions, custom ids followed automatically, wildcard names wired), and user-suppliedaria-describedbymerged with error ids instead of overridden. §1 accessibility is now fully done except the real-screen-reader verification follow-up. Batch 2 complete: #64, #65, #66 all merged. Next release must be 0.7.0 (peer range narrowing, scroll options semantics, FieldError internals).Merge pull request #68 from 53js/build/npm-packaging-exports
hugomallet commentedon Aug 7, 2026
Progress update — batch 3 merged (#67, #68, #69):
resetOnSubmitopt-out prop +reset()exposed through the form context; flaky Form snapshot fixed (no longer serializes the AJV instance).exportsmap (root,./package.json, both CSS files),sideEffects: ["*.css"](CSS pruning proven by webpack A/B test),engines.node >= 20. Finding:dist/index.jsis ESM —mainwas the lying field, package is de facto bundler-only (a real dual CJS/ESM build is a possible follow-up).Remaining big items: AJV 8, tooling migration (Vite/Vitest/RTL),
dot-prop-immutable, release automation, multi-React runtime tests in CI. Next release: 0.7.0.hugomallet commentedon Aug 8, 2026
Tooling migration (§4) complete — merged as a 4-PR train:
yarn lintnow enforced in CI — it never was before)@babel/runtimefrom published dependencies;npm-run-alldropped; published JS −56%; package now loadable by modern Node import/require)Net effect: 1125 → 418 top-level packages, audit advisories ~725 → ~119 (remaining are dev-only), no 2020-era toolchain left.
Deferred with rationale: CI runtime job for React 19 (isolated fixture, optional), ESLint 9/flat config migration.
Remaining §3: AJV 8 (the 1.0 core),
dot-prop-immutable. Next release must be 0.7.0.hugomallet commentedon Aug 8, 2026
AJV 6 → 8 migration (§3) landed in two steps:
formatErrorsis now dual-shape — reads AJV 8instancePath(RFC 6901 JSON Pointer, incl.~0/~1escapes) with a fallback to AJV 6dataPath. Groundwork reusable by the future Standard Schema adapter (RFC RFC: DOM-first form API (v1.0 architecture) #58). Ships with the 0.7.0 line.ajv@^8.17+ajv-formatsas published dependencies,createAjv={ allErrors, $data, strict: false }+ formats, duck-typedajvprop (accepts Ajv2019/Ajv2020), deadv5option removed. Gate held: all 127 pre-existing assertions unchanged; demo verified in-browser; migration notes in README/CHANGELOG (incl. the lost unknown-format compile crash understrict: false).Branch strategy: master stays the v0 line (AJV 6 + dual-shape); v1 accumulates validated breaking work toward 1.0.
docs(form): address review — pre-existing mutation limitation, defaul…
hugomallet commentedon Aug 9, 2026
🎉 Audit complete — every item on this issue is now done
Final batch (#80, #81, #82, #83, #84):
dot-prop-immutablereplaced by a localsetInwith proven-identical semantics (differential-tested against the real package, prototype-pollution hardened).v*tag (any commit, HEAD or not) → full verification (lint, types matrix, runtime matrix, dist) →npm publish --provenance. GitHub Pages deploys from Actions;docs/is no longer committed.One-time maintainer setup required (documented in CONTRIBUTING): Pages source → GitHub Actions; npm auth (trusted publisher on npmjs.com, or
NPM_TOKENsecret); recommended: a tag ruleset protectingv*.Where the project stands: master = v0 line, ready for 0.7.0 (after a local build test in a real project).
v1branch carries AJV 8 and awaits the RFC #58 rewrite (useForm, hooks, prop-types removal there). Suite: 63 tests at audit start → 159 on three React versions today.