Aller au contenu

Monorepo Conventions

Monorepo TypeScript : React Native (Expo) pour le front, NestJS pour l'API, pnpm + Turborepo.

Regle absolue

Ne JAMAIS refactoriser du code existant sauf demande explicite. Les conventions s'appliquent au nouveau code uniquement. Si un refactoring semble benefique, demander confirmation d'abord.

DO NOT

  • DO NOT ajouter de commentaires sur du code explicite. Comments uniquement pour regles metier non-evidentes ou contexte historique
  • DO NOT paraphraser le code dans les commentaires ("// Fetch data" avant un fetch)
  • DO NOT ecrire les commentaires de code en francais → tous les commentaires inline / JSDoc des fonctions, methodes et types restent en anglais (convention du codebase, comme les noms de tests describe()/it() — exception : les tests d'integration API suivent le naming FR Quand …/Alors …, cf. integration-test-conventions.mdc)
  • Exceptions ou le francais est autorise : (1) les scripts de migration Flyway (migration/flyway/*.sql), (2) la JSDoc descriptive d'un cron task (le bloc qui explique a quoi sert le cron, sa frequence, son cap journalier, etc.), (3) le bloc d'explication d'un script one-shot (projects/api/scripts/**). Les commentaires explicatifs DANS le code du cron / du script restent en anglais

DO

  • Utiliser _ comme separateur de milliers pour les literaux numeriques >= 1_000
  • Literaux en centimes — Cents(...) mais aussi toute valeur cents nue (assertion/payload de test, seed) : separer euros et centimes avec _ sur les 2 derniers chiffres — Cents(1_000_00) = 1 000 EUR, total: 550_00 = 550 EUR, Cents(1_99) = 1,99 EUR. Le separateur milliers s'applique a la partie entiere en euros uniquement
  • Toujours verifier les composants/hooks/helpers existants avant d'en creer de nouveaux
  • Code auto-documentant > commentaires
  • Preferer Err('error-code' as const) pour preserver les unions litterales d'erreur avant d'introduire des alias de type Result dedies uniquement a l'inference
  • TS6: compilerOptions.types defaults to [] (no auto @types/*). Globals need an explicit types entry. Isomorphic UUID: import { v4 } from 'uuid', not global crypto, node:crypto, or a sibling workspace package
  • tsconfig.base.json uses moduleResolution: bundler. Do not re-declare it in extenders. API keeps customConditions: ["node"] for TypeORM 0.3.5. front-app stays on TypeScript 4.9 and does not extend this base.
  • TS6: noUncheckedSideEffectImports defaults true. Side-effect CSS/polyfill imports need declare module '*.css' (or equivalent), not flipping the flag.
  • Docker build after turbo prune: do not call tsc. The cmd-shim targets a nested node_modules/typescript/bin/tsc that prune does not install once that version is the hoisted one. Commons: tsdown emits JS+dts. Vite BO apps (noEmit): vite build only; typecheck stays in type-check.

DO NOT (cross-cutting)

  • DO NOT anticiper des besoins futurs — coder pour le sprint actuel. La complexite est une dette (YAGNI/KISS)
  • DO NOT extraire un query builder partagé entre getMany et getOne — chaque méthode écrit sa query
  • DO NOT re-déclarer une union déjà portée par un validator (typeof x.T)
  • DO NOT rendre optionnel un champ du payload Graphile si tous les enqueue le posent — le validator = le contrat d'enqueue
  • DO NOT rendre required un champ GET nouveau dans la même PR que l'API si Maestro tape une API stale — PR 1 : API + validator client optional. PR stackée : required, une fois l'API sur develop / e2e
  • DO NOT creer d'abstractions inutiles — pas de hook wrapper pour un seul appel, pas de type intermediaire utilise une seule fois, pas de const intermediaire sans valeur ajoutee
  • DO NOT inliner un appel de fonction multi-lignes dans lift().add(...) — extraire un const en amont, le chain reste plat. Un objet literal ou une expression courte peut rester inline
  • DO NOT utiliser as unknown as X pour contourner le typage — trouver une solution typee (className, .web.tsx, type guard) ou corriger le type source
  • DO NOT ecrire les literaux Cents(...) avec un _ sur la valeur cent totale (Cents(100_000)) — illisible ; utiliser Cents(1_000_00) (cf. DO ci-dessus)
  • DO NOT extraire des helper functions one-liner ou single-use quand un const local ou du code inline reste plus lisible. Exception : ternaires imbriqués (3+ branches) → early returns dans une fonction locale
  • DO NOT utiliser void pour ignorer une Promise — await (dans une fonction async) ou gestion explicite de l'erreur
  • DO NOT await une Promise dans la condition d'un if (if (!(await x()))) — extraire dans une const d'abord, la condition lit un booléen
  • DO NOT ajouter un alias npm dans package.json pour un CLI ops (scripts/*.script.ts) — l'invoquer directement avec pnpm exec tsx
  • DO NOT spread conditionnel pour props optionnelles — ...(x ? { key: x } : {}) ajoute du bruit sans gain ; assigner key: x ? x : undefined (ou undefined seul) dans l'objet literal — les libs/API ignorent undefined, pas besoin de mini-objet spreadé
  • DO NOT spread le retour d'un helper dans un payload / params object (...computeX()). Nommer les champs au call-site : un spread cache le contrat et casse si le helper gagne un champ
  • DO prefer expliciter les merges de domain objects fermes avec produce() + ?? plutot qu'un deep merge generique ou une cascade de if
  • DO NOT hesiter a challenger un choix qui casse l'architecture — expliquer l'impact et proposer l'alternative
  • DO NOT dupliquer les libellés d'un enum admin entre le BO et l'API (Slack, mails) — ils divergent. Colocaliser as const satisfies Record<Enum, string> à côté de l'enum dans @bricks-common/api-communication-bricksoffice
  • DO NOT utiliser satisfies quand const x: T = { ... } vérifie la contrainte sans cast. satisfies seulement si annoter élargirait un littéral dont un caller a encore besoin
  • DO NOT appeler .toISOString() pour sérialiser une Date en JSON — Date#toJSON() appelle déjà toISOString()
  • DO NOT traiter un changement de libellé (export XLSX, i18n) comme un touch du legacy — intouchable = tables, kinds WT, routes et logique d'investissement 2022
  • DO NOT ouvrir une PR ready-for-review — toujours draft (gh pr create --draft, draft: true). Jamais gh pr ready / draft: false ; un humain un-draft
  • DO NOT citer un ticket Linear (BRI-xxxx) dans un .mdc — les rules décrivent le contrat actuel, pas l'historique de livraison
  • DO NOT utiliser .default() sur un champ de body d'endpoint que le client doit envoyer — le rendre obligatoire. Une feature pas encore en prod n'est pas une raison de default silencieux

Cross-cutting

  • match().exhaustive() de ts-pattern pour les branchements complexes (3+ cas). Simple if/else ou early return reste prefere quand c'est plus lisible. Un Result en erreur : if (!result.ok) return match(error) — succès en dernier. Pas de if imbriqués sur le code d'erreur
  • Data transforms : space-lift lift() pour 2+ operations. filter + map → collect() (undefined droppe l'élément)
  • Validation : zod pour les nouveaux validateurs, idonttrustlikethat pour le code existant (migration progressive)
  • Branded types depuis @bricks-common/api-communication pour IDs, montants, dates
  • dayjs pour les dates (pas moment). Délai en jours : dayjs(now).diff(at, 'day') >= n, pas de constante MS_PER_DAY
  • produce() d'immer pour les mutations immutables d'objets

Validation TypeScript & commit

Apres chaque serie de modifications, executer tsc sur le projet concerne et corriger jusqu'a zero erreur. Ne pas considerer une tache terminee tant que tsc echoue sur les fichiers touches.

Avant de committer, re-executer les checks sur les fichiers modifies :

  1. TypeScript — zero erreur toleree
  2. Biome : npx @biomejs/biome check <fichiers> — formatting + lint

Si les checks echouent, corriger et re-verifier. Ne jamais push du code qui ne passe pas tsc ou biome.

Commandes par projet : - api : cd projects/api && npx tsc -p tsconfig.build.json --noEmit + npx @biomejs/biome check <fichiers> - front-mobile-app : cd projects/front-mobile-app && npx tsc --noEmit + npx @biomejs/biome check <fichiers> - app-pdp-financement : cd projects/app-pdp-financement && npx tsc --noEmit + npx @biomejs/biome check <fichiers>

Note : les dependances communes (@bricks-common/*, @bricks-common-front/*) doivent etre buildees avant tsc. Si tsc echoue sur des modules manquants, lancer npx turbo run build --filter=<package> sur les packages concernes.

Testing

See Test Conventions — Vitest (API + packages), Jest (front apps), fake timers, mocking, config pattern, tsconfig structure

Auto-apprentissage des rules

Quand le dev te corrige sur un pattern, une convention ou un choix technique : 1. Appliquer la correction immediatement 2. Ajouter une ligne DO/DO NOT dans la section appropriee de front-conventions.mdc ou generic-monorepo-rules.mdc 3. Ne pas demander — le faire directement. Une correction du dev = une nouvelle rule partagee

  • DO NOT utiliser le non-null assertion operator sur une valeur (value!) → guard / ?. / ??. Exception : definite assignment sur champs de classe ORM/DTO (prop!: Type) requis par strictPropertyInitialization (TypeORM hydrate hors constructeur)
  • DO NOT utiliser le type assertion (as SomeType) → utiliser un type guard ou restructurer le code pour que TypeScript infere correctement. Pour itérer les clés d'un objet connu : keysOf(obj) (projects/api/src/__new/lib/object/keysOf.ts) — encapsule le cast Object.keys → keyof T. Pour NonEmptyArray : type guard (arr is NonEmptyArray<T>), pas [arr[0], ...arr.slice(1)] (identique à arr)