Tag: npm, pnpm, node, package-manager, javascript
Livello: intermedio
TL;DR: Quick reference comparing npm and pnpm, highlighting pnpm's space-saving symlink approach, strict dependency resolution, and performance benefits over traditional npm.
npm: Default Node.js package manager; uses a flattened node_modules structure which can lead to phantom dependencies.
pnpm: Performant npm; uses a global content-addressable store to save disk space and boost installation speed.
Symlinks & Hard links: pnpm creates hard links from the global store to the project, and uses symlinks to build nested node_modules.
Strictness: pnpm prevents phantom dependencies by ensuring packages can only access modules explicitly declared in their package.json.
Disk Space Efficiency: pnpm shares identical packages and versions across multiple projects on the same machine, drastically reducing disk usage.
Workspaces: Both support monorepos, but pnpm is generally faster and offers more robust workspace filtering out of the box.
Lockfiles: npm uses package-lock.json; pnpm uses pnpm-lock.yaml.
Q: Why should a project migrate from npm to pnpm?R: I would migrate to significantly reduce disk space usage, improve installation times, and enforce a strict node_modules structure that eliminates phantom dependency bugs.
Q: What is a phantom dependency and how does pnpm solve it?R: A phantom dependency is an undeclared package accessible because npm hoists it. pnpm uses a nested symlinked structure that only exposes explicitly declared dependencies.
Q: How do we ensure reproducible builds in CI with pnpm?R: We use pnpm install --frozen-lockfile to ensure deterministic builds based entirely on the existing pnpm-lock.yaml, similar to how npm ci works.
Missing dependencies: Projects migrating from npm to pnpm often fail if they relied on phantom dependencies. The fix is to explicitly install the missing packages.
Symlink issues: Legacy tools or specific bundlers might struggle with symlinks. Configuration tweaks, like enabling resolve.symlinks in Webpack, might be required.
Global installs: Using pnpm add -g behaves differently than npm and requires setting up the global bin directory in the system PATH.
# npm commands
npm install # Install dependencies
npm ci # Clean install for CI (requires lockfile)
npm run <script> # Run a script from package.json
# pnpm equivalents
pnpm install # Install dependencies
pnpm install --frozen-lockfile # Clean install for CI
pnpm <script> # Run a script (no 'run' needed)
# Adding packages
pnpm add <pkg> # npm install <pkg>
pnpm add -D <pkg> # npm install -D <pkg>
# Workspaces (Monorepo)
pnpm --filter <package-name> run build