Skip to content

Audit 2026-07 : améliorations (accessibilité, bugs helpers, dépendances, outillage) #57

Open

Description

@hugomallet

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> : ajouter role="alert" (ou aria-live="polite") pour que les lecteurs d'écran annoncent l'erreur.
  • <Field> : câbler aria-invalid quand le champ est en erreur.
  • Lier aria-describedby du champ vers l'id du <FieldError> associé (génération d'id à partir du name).
  • 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 flag g. Une erreur AJV sur items[0].tags[1] donne field = "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.
  • isFieldInvalid ignore tous les noms sauf le premier — src/lib/Form/Form.js : getFieldErrors(...names) ne lit que le premier argument, donc isFieldInvalid(['a', 'b']) ne vérifie que 'a'. Passer names directement (l'API accepte déjà un tableau).
  • scrollToFirstError peut planter — si aucun élément DOM ne porte le name de l'erreur (champ custom, erreur required sur un objet imbriqué), document.getElementsByName(...)[0] est undefined et part tel quel dans scrollToElement. Ajouter un garde.
  • Wildcard non échappé — filterByFieldNameWithWildcard : user.* devient la regex /^user./ où le . matche n'importe quel caractère (userX matche). Échapper le préfixe.

3. Dépendances runtime

  • AJV 6 (2019, EOL) → AJV 8 : pas de draft 2019-09/2020-12, l'option v5 est morte, et les consommateurs avec AJV 8 dans leur arbre embarquent deux versions. Breaking (dataPath → instancePath, ajv-formats séparé) — chantier le plus structurant, candidat 0.7.0/1.0.0.
  • peerDependencies: react >=15 est faux : le code utilise createContext (16.3+) et useContext (16.8+). Corriger en >=16.8.
  • scroll-to-element (non maintenu depuis ~2018) : remplacer par le natif element.scrollIntoView({ behavior: 'smooth', block: 'center' }) — zéro dépendance, scrollOptions peut 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 avec enzyme-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/react existante). 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 : main et module pointent vers le même dist/index.js (un seul build Babel) — l'un des deux ment. Ajouter un champ exports (avec types), sideEffects: false, et engines.

5. API / DX

  • reset() appelé avant onSubmit dans handleSubmitSuccess : les champs touchés et isSubmitted sont effacés même si le submit côté serveur échoue. Proposer resetOnSubmit={false} ou exposer reset via le contexte/ref.
  • Mettre en avant useFormContext comme 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 componentDidUpdate même quand ni data ni schema n'ont changé (le memoize ne sauve que pour la même référence data). À revisiter lors du chantier outillage.

6. Gouvernance / publication

  • Automatiser la release (après ci: migrate from CircleCI to GitHub Actions #54) : workflow npm publish avec provenance sur tag, au lieu du prepublishOnly manuel.
  • 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.md et CONTRIBUTING.md (conventions de PR, lancement de la matrice de types en local).

Ordre suggéré

  1. 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).
  2. Accessibilité (§1) + quick wins bugs (§2) + peerDeps : petites PR, publiables en 0.6.x/0.7.0.
  3. Chantier outillage (§4) : Vite + Vitest (Browser Mode) + Testing Library — éteint le bruit dependabot.
  4. AJV 8 + packaging exports : la vraie 1.0.0.

Activity

  1. hugomallet commented on Aug 6, 2026

    @hugomallet
    CollaboratorAuthor

    Progress update (2026-08-06)

    Done — merged to master:

    In progress (next batch, 3 PRs):

    • §1: aria-invalid + aria-describedby wiring on <Field> (was blocked by Fix/field non dom components #55, now merged).
    • §3: replace scroll-to-element with native scrollIntoView + inline the throttle helper (drops lodash.throttle).
    • §6: SECURITY.md + CONTRIBUTING.md + README badge cleanup.

    Follow-ups noted during review:

    • Verify 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.
    • Pre-existing flake: the Form snapshot test serializes the AJV instance and fails in single-suite runs.

    Remaining: AJV 8, tooling migration (Vite/Vitest/RTL), npm packaging (exports), release automation, reset()-before-onSubmit DX, useFormContext docs, per-React-version runtime tests in CI.

  2. hugomallet commented on Aug 7, 2026

    @hugomallet
    CollaboratorAuthor

    Progress update: #65 merged — aria-invalid gated on touched/submitted, form-scoped FieldError id registry (no cross-form collisions, custom ids followed automatically, wildcard names wired), and user-supplied aria-describedby merged 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).

  3. added a commit that references this issue on Aug 7, 2026
  4. hugomallet commented on Aug 7, 2026

    @hugomallet
    CollaboratorAuthor

    Progress update — batch 3 merged (#67, #68, #69):

    • §5: resetOnSubmit opt-out prop + reset() exposed through the form context; flaky Form snapshot fixed (no longer serializes the AJV instance).
    • §4 (packaging): exports map (root, ./package.json, both CSS files), sideEffects: ["*.css"] (CSS pruning proven by webpack A/B test), engines.node >= 20. Finding: dist/index.js is ESM — main was the lying field, package is de facto bundler-only (a real dual CJS/ESM build is a possible follow-up).
    • §5/§6 docs: README gains Accessibility and useFormContext sections (examples verified executable against the lib), packaged CSS import documented, stale CircleCI references purged from test-types.

    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.

  5. hugomallet commented on Aug 8, 2026

    @hugomallet
    CollaboratorAuthor

    Tooling migration (§4) complete — merged as a 4-PR train:

    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.

  6. hugomallet commented on Aug 8, 2026

    @hugomallet
    CollaboratorAuthor

    AJV 6 → 8 migration (§3) landed in two steps:

    Branch strategy: master stays the v0 line (AJV 6 + dual-shape); v1 accumulates validated breaking work toward 1.0.

  7. hugomallet commented on Aug 9, 2026

    @hugomallet
    CollaboratorAuthor

    🎉 Audit complete — every item on this issue is now done

    Final batch (#80, #81, #82, #83, #84):

    • §3: dot-prop-immutable replaced by a local setIn with proven-identical semantics (differential-tested against the real package, prototype-pollution hardened).
    • §5: no more validation dispatch on updates with unchanged inputs (guard on data/schema/ajv/throttleDuration references).
    • §4: runtime test matrix on React 16/18/19, green and blocking in CI — landing it surfaced and fixed 4 act/batching test defects (test: wrap instance-method state updates in act() #83) before the v1 work starts.
    • §6: release is now a deliberate act — push a 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_TOKEN secret); recommended: a tag ruleset protecting v*.

    Where the project stands: master = v0 line, ready for 0.7.0 (after a local build test in a real project). v1 branch 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions