Monorepo avec TypeScript : organisation efficace du code pour des projets évolutifs
Le développement d'applications modernes nécessite souvent de diviser le code en plusieurs modules ou packages indépendants. Cela soulève un dilemme : stocker chaque package dans un dépôt séparé (polyrepo) ou tout regrouper dans un seul (monorepo). Pour les projets TypeScript, le monorepo devient une solution particulièrement attrayante, assurant l'uniformité des types, simplifiant le refactoring et centralisant la gestion des dépendances. Dans cet article, nous allons voir comment organiser un monorepo avec TypeScript, le configurer et l'utiliser efficacement en développement.
Qu'est-ce qu'un monorepo et pourquoi est-il utile pour TypeScript ?
Un monorepo est une approche où le code de plusieurs projets (packages, bibliothèques, microservices) est stocké dans un seul dépôt. Dans le contexte de TypeScript, cela est particulièrement utile car :
- Types et interfaces communs : Vous pouvez définir des types communs dans un package et les réutiliser dans tous les autres, évitant ainsi la duplication et la désynchronisation.
- Configuration unique :
tsconfig.json, les linters et les formateurs sont configurés une fois pour l'ensemble du projet. - Refactoring simplifié : La modification du code ou des interfaces communs se répercute immédiatement sur tous les packages dépendants, et TypeScript vous signalera les incohérences.
- Commits atomiques : Les modifications affectant plusieurs packages peuvent être commitées et poussées en une seule opération.
Cependant, un monorepo nécessite une organisation correcte, sinon il se transforme en chaos. Les outils clés pour travailler avec les monorepos en TypeScript sont npm/yarn/pnpm workspaces et TypeScript Project References.
Structure d'un monorepo avec TypeScript
Considérons une structure typique de monorepo pour une application web composée d'un frontend, d'un backend et d'une bibliothèque d'utilitaires partagés :
my-monorepo/├── packages/│ ├── shared/ # Types, utilitaires, constantes partagés│ │ ├── src/│ │ │ └── index.ts│ │ ├── tsconfig.json│ │ └── package.json│ ├── backend/ # Application serveur (ex: Express)│ │ ├── src/│ │ │ └── app.ts│ │ ├── tsconfig.json│ │ └── package.json│ └── frontend/ # Application cliente (ex: React)│ ├── src/│ │ └── App.tsx│ ├── tsconfig.json│ └── package.json├── tsconfig.base.json # Fichier de configuration TypeScript de base├── package.json # package.json racine (workspaces)└── .gitignoreDans cette structure :
packages/*— répertoire contenant tous les packages du projet.tsconfig.base.json— configuration TypeScript commune dont héritent tous les packages.package.jsonà la racine déclare les workspaces.
Configuration du monorepo : guide étape par étape
1. Initialisation du projet racine et configuration des Workspaces
Créez le répertoire racine et le fichier package.json :
{ "name": "my-monorepo", "private": true, "workspaces": [ "packages/*" ], "scripts": { "build": "npm run build --workspaces", "lint": "eslint packages/*/src --ext .ts,.tsx", "test": "jest" }}Le paramètre "private": true empêche la publication accidentelle du package racine. "workspaces": ["packages/*"] indique à npm où trouver les packages enfants.
2. Création de la configuration TypeScript de base
Fichier tsconfig.base.json à la racine du projet :
{ "compilerOptions": { "target": "ES2020", "module": "ESNext", "moduleResolution": "node", "strict": true, "esModuleInterop": true, "ski