Was ist pnpm?
pnpm ist ein Package Manager, der jede Version jedes Packages nur einmal in einem globalen, content-addressable store speichert und diese per Hardlink in jedes Projekt einbindet, das sie benötigt. Das Ergebnis ist ein drastisch geringerer Speicherverbrauch und wesentlich schnellere Installationen, insbesondere bei vielen Projekten oder in einem Monorepo.
Zudem ist pnpm strikt. Anstatt jede Dependency in einem einzigen node_modules zu flachen, nutzt pnpm Symlinks, welche den tatsächlichen Dependency-Graph widerspiegeln. Ein Package kann nur das importieren, was es auch explizit deklariert. Dadurch schlägt die versehentliche Abhängigkeit von einer transitiven Dependency sofort fehl, anstatt lokal zu funktionieren und erst in der Production zu crashen.
Der Store und node_modules
Der Store befindet sich in einem globalen Verzeichnis und enthält die Inhalte jeder Paketversion, die Sie jemals installiert haben. Wenn ein Projekt ein Paket benötigt, erstellt pnpm einen Hardlink aus dem Store, anstatt es zu kopieren.
Das node_modules-Layout verwendet dann Symlinks:
node_modules/.pnpmenthält die eigentlichen Pakete.node_modules/<name>verlinkt auf die Version, die Ihr Projekt deklariert hat.- Die
node_modulesjedes einzelnen Pakets verlinkt nur auf dessen deklarierte Abhängigkeiten.
Aus diesem Grund erkennt pnpm Phantom-Abhängigkeiten: Wenn Ihr Code ein Paket importiert, das Sie vergessen haben, in package.json hinzuzufügen, schlägt dies fehl, da das Paket auf der obersten Ebene nicht verlinkt ist. Das flache Layout von npm lässt diesen Fehler oft unbemerkt passieren.
Befehle
Die Befehle ähneln denen von npm, was die Migration erleichtert.
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 nutzt den Store, wodurch wiederholte Installationen schnell gehen. pnpm-lock.yaml übernimmt die gleiche Rolle wie package-lock.json und sollte in das Repository eingecheckt werden.
Workspaces
Workspaces sind das herausragende Feature von pnpm. Die Paketstandorte werden in pnpm-workspace.yaml definiert.
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
Ein Workspace kann dann über das Workspace-Protokoll anstatt über einen relativen Dateipfad von einem benachbarten Paket abhängen.
{
"name": "@repo/web",
"dependencies": {
"@repo/ui": "workspace:*"
}
}
pnpm verlinkt das lokale Paket, und workspace:* wird beim Veröffentlichen durch die tatsächliche Version ersetzt. Dadurch bleiben interne Abhängigkeiten explizit und fehleranfällige file:../..-Pfade werden vermieden.
Kataloge und Versionskonsistenz
In einem großen Monorepo passiert es leicht, dass verschiedene Packages von unterschiedlichen Versionen derselben Library abhängen. Kataloge zentralisieren diese Entscheidung.
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
catalog:
react: ^19.0.0
typescript: ^5.6.0
{
"dependencies": {
"react": "catalog:"
}
}
Jedes Package, das catalog: verwendet, löst auf die einmal im Root definierte Version auf. Dadurch wird ein Upgrade zu einer einzigen Änderung und ein Auseinanderdriften der Versionen wird verhindert.
Tasks filtern und ausführen
pnpm kann eine Teilmenge eines Workspaces ansteuern, was in einem Monorepo essenziell ist.
# 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
Filter unterstützen Paketnamen, Directory Globs und Abhängigkeitsbeziehungen, sodass Sie einen Befehl nur dort ausführen können, wo er relevant ist. Für das Caching und die Orchestrierung über Pakete hinweg kombinieren Sie pnpm am besten mit Turborepo, das genau für dieses Setup entwickelt wurde.
CI und Reproduzierbarkeit
Verwenden Sie in der CI eine eingefrorene Lockfile, damit die Installation fehlschlägt, wenn die Lockfile und die Manifeste nicht übereinstimmen.
pnpm install --frozen-lockfile
pnpm run build
Dies ist das pnpm-Äquivalent zu npm ci und garantiert einen reproduzierbaren Build. Da Installationen schnell sind und der Store in der CI gecached werden kann, trägt pnpm zudem dazu bei, die Pipeline-Zeit zu reduzieren.
pnpm im Vergleich zu npm
- Festplatte und Geschwindigkeit: pnpm teilt Pakete über einen Store und verlinkt diese; npm kopiert pro Projekt einen flachen Baum.
- Strenge: pnpm stellt nur deklarierte Abhängigkeiten bereit; das flache Layout von npm ermöglicht sogenannte Phantom-Imports.
- Workspaces: Beide unterstützen diese, aber das Workspace-Protokoll und das Filtern von pnpm sind für große Monorepos ergonomischer.
- Kompatibilität: Beide lesen
package.jsonund unterstützen dieselbe Registry, sodass ein Wechsel normalerweise nur das Löschen vonnode_modulesund der alten Lockfile erfordert.
Wählen Sie npm für Einfachheit und maximale Verbreitung und pnpm, wenn Festplattenplatz, Geschwindigkeit oder die Ergonomie von Monorepos eine Rolle spielen. Siehe den npm-Guide für den Standard-Workflow.
Best Practices
- Commit
pnpm-lock.yaml. - Nutze
pnpm install --frozen-lockfilein der CI. - Verwende
workspace:*für interne Abhängigkeiten. - Zentralisiere gemeinsam genutzte Versionen mithilfe von Catalogs.
- Nutze die Strictness aus: Füge fehlende Abhängigkeiten hinzu, anstatt die Prüfung zu deaktivieren.
- Nutze
--filter, um Tasks nur dort auszuführen, wo sie relevant sind. - Kombiniere pnpm mit einem Task-Runner für das Caching in großen Repositories.
Häufige Fehler
- Hinzufügen von Dependencies auf der falschen Workspace-Ebene mit
-w. - Verwendung von
file:-Pfaden anstelle des Workspace-Protokolls. - Ignorieren von „not declared in package.json“-Fehlern, anstatt das Manifest zu korrigieren.
- Vergessen von
--frozen-lockfilein der CI, was zu nicht reproduzierbaren Installationen führt. - Jedes Paket eine eigene Version einer gemeinsam genutzten Library pinnen lassen.
- Mischen von Package Managern in einem Repository, wodurch sich widersprüchliche Lockfiles erstellen.
Wie geht es weiter?
pnpm ist die effiziente Wahl für moderne JavaScript-Projekte, insbesondere für Monorepos. Vergleiche es mit npm, ergänze Turborepo für das Task-Caching und verschaffe dir ein Verständnis für die zugrunde liegende Node.js-Runtime. Probiere es anschließend an einem bestehenden Projekt aus, indem du node_modules entfernst und pnpm den Dependency-Tree aus dem Store neu aufbauen lässt.