Qu’est-ce que pnpm ?
pnpm est un gestionnaire de paquets qui stocke chaque version de chaque paquet une seule fois dans un stock adressable par le contenu (content-addressable store) global, puis crée des liens physiques (hard-links) vers chaque projet qui en a besoin. Le résultat est une utilisation du disque considérablement réduite et des installations beaucoup plus rapides, particulièrement lorsque l’on gère de nombreux projets ou un monorepo.
Il est également strict. Au lieu d’aplatir toutes les dépendances dans un seul node_modules, pnpm utilise des liens symboliques (symlinks) qui reflètent le véritable graphe de dépendances. Un paquet ne peut importer que ce qu’il déclare explicitement ; ainsi, toute dépendance accidentelle à une dépendance transitive échoue immédiatement, au lieu de fonctionner localement et de planter en production.
Le store et node_modules
Le store réside dans un répertoire global et contient toutes les versions de chaque package que vous avez installées. Lorsqu’un projet a besoin d’un package, pnpm crée un lien physique (hard-link) depuis le store plutôt que de le copier.
La structure node_modules utilise ensuite des liens symboliques (symlinks) :
node_modules/.pnpmcontient les packages réels.node_modules/<name>pointe vers la version déclarée par votre projet.- Le
node_modulesde chaque package ne pointe que vers ses propres dépendances déclarées.
C’est pourquoi pnpm détecte les dépendances fantômes : si votre code importe un package que vous avez oublié d’ajouter au package.json, l’opération échoue, car le package n’est pas lié au niveau supérieur. La structure plate de npm laisse souvent passer cette erreur inaperçue.
Commandes
Les commandes sont proches de celles de npm, ce qui facilite la migration.
pnpm install # install from the lockfile
pnpm add zod # add a dependency
pnpm add -D vitest # add a dev dependency
pnpm remove zod # remove a dependency
pnpm run build # run a script
pnpm dlx create-vite # run a package without installing
pnpm install utilise le store, rendant ainsi les installations répétées très rapides. pnpm-lock.yaml joue le même rôle que package-lock.json et doit être versionné dans Git.
Workspaces
Les Workspaces sont la fonctionnalité phare de pnpm. Vous déclarez l’emplacement des packages dans pnpm-workspace.yaml.
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
Ensuite, un workspace peut dépendre d’un package frère en utilisant le workspace protocol au lieu d’un chemin de fichier relatif.
{
"name": "@repo/web",
"dependencies": {
"@repo/ui": "workspace:*"
}
}
pnpm lie le package local, et workspace:* est remplacé par la version réelle lors de la publication. Cela permet de garder les dépendances internes explicites et d’éviter les chemins file:../.. fragiles.
Catalogues et cohérence des versions
Dans un monorepo de grande taille, il est fréquent que différents packages dépendent de versions différentes d’une même bibliothèque. Les Catalogues permettent de centraliser cette décision.
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
catalog:
react: ^19.0.0
typescript: ^5.6.0
{
"dependencies": {
"react": "catalog:"
}
}
Chaque package utilisant catalog: se réfère à la version définie une seule fois à la racine, ce qui transforme les mises à jour en une modification unique et évite la divergence des versions.
Filtrer et exécuter des tâches
pnpm peut cibler un sous-ensemble d’un workspace, ce qui est essentiel dans un monorepo.
# run tests only in packages that changed since main
pnpm --filter "...[origin/main]" test
# run a script in one package
pnpm --filter @repo/web dev
# run a script in every package
pnpm -r build
Les filtres prennent en charge les noms de packages, les globs de répertoires et les relations de dépendance, vous permettant ainsi d’exécuter une commande uniquement là où elle est pertinente. Pour le caching et l’orchestration entre les packages, associez pnpm à Turborepo, qui est conçu précisément pour ce type de configuration.
CI et reproductibilité
Utilisez un lockfile figé (frozen lockfile) en CI afin que l’installation échoue si le lockfile et les manifestes ne sont pas synchronisés.
pnpm install --frozen-lockfile
pnpm run build
C’est l’équivalent pnpm de npm ci et c’est ce qui garantit un build reproductible. Comme les installations sont rapides et que le store peut être mis en cache en CI, pnpm a également tendance à réduire le temps d’exécution des pipelines.
pnpm comparé à npm
- Disque et vitesse : pnpm partage les packages via un store et utilise des liens ; npm copie un arbre aplati pour chaque projet.
- Rigueur : pnpm n’expose que les dépendances déclarées ; la structure plate de npm permet les imports fantômes (phantom imports).
- Workspaces : les deux les supportent, mais le protocole de workspace et le filtrage de pnpm sont plus ergonomiques pour les monorepos de grande taille.
- Compatibilité : les deux lisent
package.jsonet supportent le même registre, donc passer de l’un à l’autre consiste généralement à supprimernode_moduleset l’ancien lockfile.
Choisissez npm pour sa simplicité et son ubiquité, et pnpm lorsque l’espace disque, la vitesse ou l’ergonomie des monorepos sont prioritaires. Consultez le guide npm pour le workflow par défaut.
Bonnes pratiques
- Effectuez des commits
pnpm-lock.yaml. - Utilisez
pnpm install --frozen-lockfiledans votre CI. - Utilisez
workspace:*pour les dépendances internes. - Centralisez les versions partagées avec des catalogues.
- Tirez profit de la rigueur : ajoutez les dépendances manquantes plutôt que de désactiver les alertes.
- Utilisez
--filterpour exécuter des tâches uniquement là où elles sont nécessaires. - Couplez pnpm avec un task runner pour optimiser le cache dans les gros dépôts.
Erreurs courantes
- Ajouter des dépendances au mauvais niveau de workspace avec
-w. - Utiliser des chemins
file:au lieu du protocole workspace. - Ignorer les erreurs “not declared in package.json” au lieu de corriger le manifest.
- Oublier
--frozen-lockfiledans la CI, ce qui entraîne des installations non reproductibles. - Laisser chaque package fixer sa propre version d’une bibliothèque partagée.
- Mélanger les gestionnaires de paquets dans un même dépôt et créer des lockfiles conflictuels.
Et après ?
pnpm est le choix le plus efficace pour les projets JavaScript modernes, particulièrement pour les monorepos. Comparez-le avec npm, ajoutez Turborepo pour la mise en cache des tâches, et apprenez comment fonctionne le runtime Node.js sous-jacent. Ensuite, essayez-le sur un projet existant en supprimant node_modules et en laissant pnpm reconstruire l’arborescence à partir du store.