Aller au contenu

Lab 27 : NX — Monorepo from scratch

Pas de code de départ — vous allez créer un workspace NX from scratch.

Ce lab couvre les mêmes objectifs que le Lab 26 — bibliothèques partagées, alias de chemin, graphe de dépendances et affected — mais démarre depuis un workspace vide plutôt que depuis le preset Angular. Cette approche vous donne un contrôle total sur ce qui est installé et généré.

Fenêtre de terminal
npx create-nx-workspace@latest my-workspace --preset=apps

Quand demandé, choisissez No pour Nx Cloud.

--preset=apps crée un workspace minimal sans code de démo, sans plugin de framework, sans application générée — juste la structure :

my-workspace/
├── packages/ ← placeholder (racine npm workspaces)
├── nx.json
├── tsconfig.base.json
├── tsconfig.json
└── package.json

Les dossiers apps/ et libs/ n’existent pas encore — ils sont créés lors de l’exécution des générateurs dans les étapes suivantes.

Comparez avec le preset Angular (Lab 26), qui pré-génère une application shop et installe @nx/angular pour vous. Ici, rien n’est supposé.

Le workspace vide n’a aucun support de framework. Commencez par naviguer dans le workspace et exporter une variable d’environnement qui supprime un avertissement de compatibilité TypeScript :

Fenêtre de terminal
cd my-workspace
export NX_IGNORE_UNSUPPORTED_TS_SETUP=true

Ajoutez maintenant le plugin Angular :

Fenêtre de terminal
npx nx add @nx/angular

Cela installe @nx/angular et l’enregistre dans nx.json. Cela ne génère pas d’application — c’est votre prochaine étape.

Fenêtre de terminal
nx g @nx/angular:app shop

Quand demandé, choisissez votre format de feuille de style (CSS ou SCSS).

Dans Nx 23, les applications sont générées à la racine du workspace (pas dans un sous-dossier apps/) :

shop/
shop-e2e/

Lancez-la :

Fenêtre de terminal
nx serve shop

Ouvrez http://localhost:4200 — une application Angular vierge sans contenu de démo.

Fenêtre de terminal
nx g @nx/angular:app admin

Vous avez maintenant deux applications indépendantes dans le même dépôt :

shop/
shop-e2e/
admin/
admin-e2e/

Lancez-les indépendamment :

Fenêtre de terminal
nx serve shop # http://localhost:4200
nx serve admin # http://localhost:4201

Étape 5 : Générer une bibliothèque UI partagée

Section intitulée « Étape 5 : Générer une bibliothèque UI partagée »
Fenêtre de terminal
nx g @nx/angular:lib --name=ui --directory=libs/ui

Note (Nx v23+) : Les arguments positionnels ne sont plus supportés par ce générateur — utilisez toujours --name=ui explicitement. --directory=libs/ui place la bibliothèque dans libs/ui/ à la racine du workspace.

La bibliothèque est créée dans libs/ui/ avec son propre project.json. Le générateur crée aussi un composant par défaut dans libs/ui/src/lib/ui/ — vous pouvez le conserver ou le supprimer ; il n’est pas utilisé dans ce lab.

Pourquoi libs/ et non src/ ?

Dans un Integrated Monorepo Nx, la convention est de placer le code partageable dans le dossier libs/ racine afin que plusieurs applications (shop, admin, …) puissent toutes l’importer. Chaque bibliothèque est un projet TypeScript indépendant avec son propre project.json.

libs/ui vs libs/shop/shared-ui

Chemin Portée Quand utiliser…
libs/ui/ Workspace entier Primitives UI génériques utilisées par toute app
libs/shop/shared-ui/ Portée shop uniquement Composants spécifiques au domaine shop

Vérifiez l’alias de chemin généré par Nx

Après la génération, ouvrez tsconfig.base.json :

{
"compilerOptions": {
"paths": {
"@org/ui": ["./libs/ui/src/index.ts"]
}
}
}

TypeScript résout @org/ui vers libs/ui/src/index.ts. Pas besoin d’alias webpack.

⚠️ Le préfixe dépend de l’organisation npm définie à la création du workspace. Lisez toujours tsconfig.base.json → compilerOptions.paths pour trouver votre alias exact avant de l’utiliser dans les imports.

Tags et frontières de modules

Ouvrez libs/ui/project.json et ajoutez des tags :

{
"tags": ["scope:shared", "type:ui"]
}

Les tags sont appliqués par @nx/enforce-module-boundaries dans eslint.config.mjs. Sans scope:shared, ESLint bloquera les imports depuis les apps une fois les contraintes configurées :

"@nx/enforce-module-boundaries": [
"error",
{
"depConstraints": [
{ "sourceTag": "scope:shop", "onlyDependOnLibsWithTags": ["scope:shop", "scope:shared"] },
{ "sourceTag": "scope:admin", "onlyDependOnLibsWithTags": ["scope:admin", "scope:shared"] }
]
}
]
Fenêtre de terminal
nx g @nx/angular:component libs/ui/src/lib/button/button --export

Note (Nx v23+) : Passez le chemin complet — le flag --project a été supprimé. Le flag --export ajoute automatiquement l’export dans libs/ui/src/index.ts.

Dans Angular 20, les composants générés utilisent la convention de nommage courte : le fichier est button.ts (pas button.component.ts) et la classe est Button (pas ButtonComponent). standalone est la valeur par défaut et n’a pas besoin d’être déclaré.

Remplacez le contenu de libs/ui/src/lib/button/button.ts par :

import { Component, input } from '@angular/core';
@Component({
selector: 'lib-button',
template: `
<button class="btn" [class]="variant()">
<ng-content />
</button>
`,
styles: [`
.btn { padding: 8px 16px; border: none; cursor: pointer; border-radius: 4px; }
.primary { background: #007bff; color: white; }
.secondary { background: #6c757d; color: white; }
`]
})
export class Button {
variant = input<'primary' | 'secondary'>('primary');
}

Le flag --export a déjà ajouté l’export dans libs/ui/src/index.ts. Vérifiez qu’il pointe vers le bon fichier :

export * from './lib/button/button';

Étape 7 : Utiliser la bibliothèque dans l’app shop

Section intitulée « Étape 7 : Utiliser la bibliothèque dans l’app shop »

Dans shop/src/app/app.component.ts :

import { Button } from '@org/ui'; // vérifiez tsconfig.base.json pour votre préfixe exact
@Component({
selector: 'app-root',
imports: [Button],
template: `
<h1>Shop</h1>
<lib-button variant="primary">Ajouter au panier</lib-button>
<lib-button variant="secondary">Voir les détails</lib-button>
`
})
export class AppComponent {}
Fenêtre de terminal
nx serve shop

Le bouton partagé s’affiche dans l’app shop. L’app admin peut importer le même composant depuis le même chemin — une seule source de vérité.

Fenêtre de terminal
nx graph

Vérifiez :

  • shop dépend de ui
  • admin n’a pas encore de dépendances

Faites une modification dans Button, puis :

Fenêtre de terminal
nx affected --target=build
nx affected --target=test

Nx détermine que modifier ui affecte shop — les deux sont reconstruits. admin n’est pas touché. En CI vous ne reconstruisez et retestez que ce qui a vraiment changé.

Lab 26 (preset Angular) Lab 27 (preset apps)
Configuration Prompts interactifs Flag --preset=apps
Plugin Angular Pré-installé npx nx add @nx/angular
Première app Auto-générée (shop) Générée manuellement
Emplacement des apps shop/ à la racine du workspace shop/ à la racine du workspace
Code de démo Inclus Aucun
Idéal pour Démarrage Angular rapide Multi-framework ou setup contrôlé