


Travailler dans un monorepo frontend TypeScript sans franchir les limites entre paquets
Pour une IA qui modifie un frontend TypeScript modulaire. Couvre les limites entre paquets et un cœur sans framework, l'état modifié seulement via son action propriétaire, où ranger fichiers et tests, les contrôles avant commit, l'ordre de release et les couches d'UI.
Pour utiliser ces règles d'IA
## Ne franchissez les limites entre paquets que par les exports publics
- Dans un espace de travail multi-paquets, donnez à chaque paquet une seule responsabilité et ne franchissez les limites qu'via les exports publics du paquet ; n'importez jamais les éléments internes d'un autre paquet depuis le code applicatif, car les imports internes couplent les appelants à une structure privée et se cassent dès qu'elle change. Faites respecter ces limites par du lint automatisé et des contrôles d'architecture plutôt que par la revue de code, et lorsqu'aucun emplacement correct n'existe, créez-en un délibérément et mettez à jour les conventions documentées.
- Gardez le cœur partagé agnostique du framework et exempt de DOM navigateur : placez les liaisons avec le framework UI et la projection DOM/CSS dans des couches dédiées, et chargez les dépendances d'exécution lourdes telles que les modules WASM ou les gros SDK depuis un module central unique qui fournit aux appelants une instance injectée, plutôt que de les importer au cas par cas à chaque point d'appel.
- Ne créez pas de fichiers de redirection (« shims ») dont le seul but est de réexporter l'API d'un autre paquet ; importez directement le paquet propriétaire aux points d'appel. Un barrel de compatibilité peut réexporter un propriétaire uniquement pour préserver une API publique déjà établie.
- Préférez des exports de sous-chemins précis et ciblés à un barrel fourre-tout, lorsque des imports plus restreints clarifient l'intention des dépendances.
## Ne modifiez l'état partagé que via l'action qui en est propriétaire
- Construisez les grands sous-systèmes à partir d'un contexte partagé et de modules d'action spécifiques à chaque domaine (zone d'affichage, sélection, historique, structure, etc.) plutôt qu'à partir d'un seul fichier qui grossit sans cesse. Avant d'ajouter un comportement, examinez le dossier existant et les motifs voisins, puis placez le changement dans le module de domaine le plus proche ; n'entassez pas de logique sans rapport dans le fichier qui se trouve déjà ouvert.
- Faites transiter chaque mutation d'état par le setter ou l'action qui en est responsable, afin que les effets de bord — événements, regroupement des annulations, mises à jour dérivées — se déclenchent systématiquement. N'assignez jamais directement les champs d'état partagé, sinon ces effets ne s'exécutent pas silencieusement, et ne conservez aucun état mutable au niveau du module à l'intérieur des composants ; conservez l'état partagé dans le store.
- Exposez les changements via un système d'événements typé et faites réagir les abonnés aux événements plutôt que par sondage (polling). Faites en sorte que les modules de domaine partagent l'état via l'objet de contexte, et non en important du code applicatif ou du framework.
- Les wrappers applicatifs peuvent ajouter de l'état réactif, de la persistance et des entrées-sorties spécifiques à la plateforme autour du cœur, mais celui-ci doit rester agnostique du framework ; protégez toute API navigateur dans le code partagé par une vérification explicite à l'exécution avant utilisation.
## Interdisez any et les assertions non-null en TypeScript
- Interdisez `any` : utilisez des types précis, des génériques et la fusion de déclarations. Interdisez les assertions non-null : utilisez des gardes de type, le chaînage optionnel et la coalescence des valeurs nulles. Ces deux règles permettent au vérificateur de types de continuer à détecter les vraies erreurs au lieu de les masquer.
- Préférez des types union précis à des primitives trop larges — une petite union de littéraux de chaînes plutôt que `number | string | null` — et n'écrivez pas de définition de type en ligne lorsqu'un type partagé nommé existe déjà ; importez-le et réutilisez-le.
- Nommez les constantes répétées ou utilisées entre plusieurs fonctionnalités dans un module de constantes partagé, au lieu de dupliquer des littéraux à travers la base de code.
- N'utilisez jamais une source aléatoire non cryptographique pour des identifiants ou tout élément lié à la sécurité ; utilisez partout un générateur aléatoire cryptographique. Effectuez une copie profonde des objets imbriqués avec un clonage structuré, jamais avec un simple spread superficiel, lorsque vous modifiez des données imbriquées.
## Rangez les dossiers par domaine et mettez chaque type de test à son emplacement
- Organisez le code par espace de noms de domaine plutôt que par un formalisme structurel excessif, et gardez les services applicatifs, les vues de routes ou de mise en page, et l'UI dans leurs propres zones de premier niveau.
- Utilisez un sous-dossier pour un domaine à plusieurs fichiers plutôt que des fichiers frères partageant un préfixe répété. Dès qu'un domaine acquiert son deuxième fichier, créez immédiatement le dossier plutôt que de préfixer les noms — préférez `selection/hit-test` à `selection-hit-test`.
- Gardez en minuscules les noms de fichiers de point d'entrée conventionnels tels que `index`, `types` et `context`, et suivez la casse propre à l'écosystème pour les fichiers de composants. Ne placez pas le code ou les tests au hasard ; si aucun emplacement approprié n'existe, créez-en un délibérément et documentez-le.
- Gardez l'emplacement des tests strict et basé sur la localisation : placez chaque type de test dans son répertoire établi (bout en bout, intégration, unitaire, utilitaires partagés, par paquet). Le comportement visible par l'utilisateur relève des tests bout en bout ; les assertions sur l'état interne et les données relèvent des tests unitaires. Ne commitez jamais de fichiers de test temporaires ou de profilage.
## Utilisez une bibliothèque existante au lieu de réécrire le même code à la main
- Ne réimplémentez pas à la main ce qu'une dépendance fait déjà. Vérifiez d'abord les dépendances existantes ; si aucune ne convient, adoptez une bibliothèque de qualité plutôt que d'écrire une implémentation en ligne — utilisez par exemple une bibliothèque de diff pour les diffs unifiés et une bibliothèque de couleurs pour les calculs colorimétriques, pas une boucle écrite à la main.
- Avant de construire une UI, des contrôles ou des composables sur mesure, lisez la documentation officielle de la dépendance au lieu de deviner à partir de l'usage local, en privilégiant les points d'entrée de documentation lisibles par machine lorsqu'ils sont proposés. Si la documentation officielle contredit les motifs locaux, suivez les API officielles actuelles et mettez à jour les wrappers locaux de façon délibérée.
- Placez les programmes et utilitaires maintenables dans une zone d'outils dédiée organisée par domaine, chacun avec des tests ciblés, plutôt qu'en scripts improvisés à la racine. Réservez une zone de scripts uniquement à de minuscules shims de point d'entrée assurant la compatibilité, qui importent depuis la zone d'outils, en gardant la logique d'implémentation hors des shims.
- Incluez les tests de l'outillage ainsi que la couverture de lint et de formatage dans les contrôles standard, afin que la zone d'outils soit tenue au même niveau d'exigence que le code produit.
## Passez tous les contrôles qualité avant le commit et suivez Conventional Commits
- Exécutez le portail qualité complet avant de commiter, puis à nouveau avant d'ouvrir une modification en revue : lint sensible aux types et vérification de types sans aucune erreur, formateur avec tri des imports, détection de copier-coller et de duplication sans aucun clone, ainsi que les suites de tests unitaires et bout en bout concernées.
- Relisez-vous vous-même avant de soumettre : si la duplication augmente, extrayez des utilitaires partagés ou réutilisez des types existants ; supprimez les définitions de type en ligne qui dupliquent des types nommés ainsi que toute logique copiée-collée ; gardez les fichiers en dessous de quelques centaines de lignes et découpez-les par domaine lorsqu'ils grossissent.
- Rédigez les messages de commit selon le format Conventional Commits (`feat`, `fix`, `refactor`, `perf`, `docs`, `test`, `build`, `ci`, `chore`) : une première ligne courte, à l'impératif, avec un type en minuscules et un scope optionnel, puis la justification dans le corps, chaque ligne du corps commençant par un mot en majuscule.
- Conservez la casse des noms de produits et des acronymes dans les sujets et les corps de message, plutôt que de les aplatir en minuscules, et utilisez le scope le plus étroit et le plus honnête — ou omettez le scope lorsqu'un changement touche des zones sans rapport entre elles.
## Publiez les releases dans un ordre fixe et tenez le changelog à jour
- Suivez une procédure de release fixe et ordonnée : incrémentez la version dans chaque fichier qui la porte, déplacez les entrées du changelog depuis une section « Unreleased » vers un nouvel en-tête de version daté, commitez avec un message de release, puis créez et poussez le tag. Vérifiez que les secrets de signature ou de release existent bien avant de créer le tag.
- Traitez le workflow de build et de release comme la source de vérité unique pour l'empaquetage et la publication, et gardez toute liste de paquets dupliquée synchronisée avec lui. Conditionnez les déploiements en production à des tags de release ou à un déclenchement manuel explicite, jamais à de simples push sur la branche par défaut, afin que chaque mise en production reste intentionnelle.
- Maintenez la documentation à jour : mettez à jour la section « Unreleased » du changelog et la documentation destinée aux utilisateurs dès qu'un comportement change, et mettez à jour la référence pour contributeurs ou agents lorsque l'architecture ou les conventions évoluent.
- Gardez la documentation publiée exempte de plans spéculatifs ou de plans d'implémentation internes — placez les plans temporaires dans une zone de brouillon ignorée et distillez l'orientation durable dans les documents canoniques — et ne créez pas de copies localisées provisoires ; faites pointer la navigation localisée vers la page canonique tant qu'une véritable traduction n'existe pas.
## Empêchez les composants UI de base partagés d'importer du code de l'application
- Séparez une couche de primitives UI partagées et réutilisables des couches de fonctionnalités et de coquille applicative (shell). Les primitives partagées et les contrôles stylés de bas niveau ne doivent pas importer de services applicatifs, de stores ou de panneaux de fonctionnalités ; les composants de fonctionnalité possèdent leurs propres contrôles spécifiques.
- Résolvez le style via des variantes de thème statiques et typées, définies à un seul endroit et fusionnées par instance, plutôt qu'en dispersant des branches conditionnelles de classes utilitaires ou des props de classe ponctuelles par slot. Liez l'état visuel via des attributs de données sémantiques et résolvez les variantes typées dans le script.
- Pour les localisateurs de test, privilégiez le rôle, le label et le texte visibles par l'utilisateur ; exposez des attributs sémantiques scopés pour l'identité, et réservez des ids de test dédiés aux rares frontières d'intégration plutôt que de fabriquer des ids globalement uniques à l'intérieur des composants partagés.
- Ne faites pas passer de setters ou d'actions impératifs à travers des slots, sauf si un composant est explicitement une primitive sans rendu propre (renderless) ; privilégiez les liaisons de modèle, les événements émis et les props normales. Utilisez une bibliothèque d'icônes plutôt que du SVG en ligne brut ou des symboles Unicode, et gardez les labels sémantiques en générant les raccourcis clavier à partir des métadonnées de commande plutôt qu'en les codant en dur dans les labels.
