はじめに
「npm install遅いな...」「node_modulesがまたディスク食ってる...」
Node.js開発をしていると、こういうモヤモヤ、ありませんか。個人的には、同じパッケージが複数プロジェクトに重複して入っているのがずっと気になっていました。
pnpmは、そんな悩みを解決してくれるパッケージマネージャーです。GitHubで33,000スター以上を獲得していて、2016年から本番運用されている実績があります。
名前の「p」は「performant」の略で、その名の通り速くて効率的。一度使うと、正直npmには戻れなくなります。
pnpmとは
pnpmは「Fast, disk space efficient package manager」を謳うNode.js向けパッケージマネージャーです。
最大の特徴はcontent-addressable storageという仕組み。同じパッケージは一度だけダウンロードして、各プロジェクトからはハードリンクで参照します。これによってディスク容量を大幅に節約できます。
公式サイト: https://pnpm.io GitHub: https://github.com/pnpm/pnpm
特徴・メリット
1. npmより最大2倍高速
これ、体感できるレベルで速いんですよ。
# 大規模プロジェクトでの比較(目安)
npm install # 60秒
pnpm install # 30秒以下
特に2回目以降のインストールが爆速です。一度ダウンロードしたパッケージはグローバルストアにキャッシュされるので、別プロジェクトでも即座に使えます。
2. ディスク容量を大幅節約
30代になって思うのは、SSDの容量って意外と大事だということ。
node_modulesのサイズ、気にしたことありますか。複数プロジェクトを抱えていると、同じパッケージが何度も重複保存されてギガ単位で消費していたりします。
pnpmはシンボリックリンク方式で、これを解決します。
~/.pnpm-store/ # グローバルストア(実体はここに1つだけ)
├── react@18.2.0/
├── typescript@5.3.0/
└── ...
project-a/node_modules/
└── .pnpm/
└── react@18.2.0 → ~/.pnpm-store/react@18.2.0 (リンク)
project-b/node_modules/
└── .pnpm/
└── react@18.2.0 → ~/.pnpm-store/react@18.2.0 (同じ実体を参照)
つまり、10個のプロジェクトでReactを使っていても、実体は1つだけ。コスパ的にこれは大きいです。
3. 厳格な依存関係管理
これ、意外と重要なんですよ。
npmやyarnは「フラットなnode_modules」を作成します。これの問題は、package.jsonに書いていないパッケージも使えてしまうこと。
// npmの場合、lodashを直接インストールしていなくても
// 他の依存関係がlodashを使っていれば動いてしまう
import _ from 'lodash'; // これが通ってしまう
pnpmは非フラットな構造を採用していて、package.jsonで明示的に宣言したパッケージしかアクセスできません。
// pnpmの場合
import _ from 'lodash';
// Error: Cannot find module 'lodash'
// package.jsonに追加が必要
最初は「面倒だな」と思うかもしれませんが、依存関係が明確になるのでデプロイ時のトラブルが減ります。
4. モノレポに最適
複数パッケージを1つのリポジトリで管理する「モノレポ」構成。pnpmはこれに対する組み込みサポートがあります。
# pnpm-workspace.yaml
packages:
- 'packages/*'
- 'apps/*'
MicrosoftのRushチームが「hundreds of projects and hundreds of PRs per day」という規模で採用しているのは、信頼の証ですね。
5. Node.jsバージョン管理もできる
これは知らない人も多いかもしれません。
# Node.js v20をインストールして使用
pnpm env use --global 20
# LTS版を使用
pnpm env use --global lts
nvmやfnmの代わりとしても使えます。ツールを減らせるのはありがたい。
インストール方法
Node.js v18.12以上が必要です(スタンドアロンスクリプト以外)。
npm経由(一番シンプル)
npm install -g pnpm
Corepack経由(Node.js v16.13以降推奨)
corepack enable pnpm
Corepackを使うと、プロジェクトごとにpnpmのバージョンを固定できます。
スタンドアロンスクリプト(Node.jsなしでOK)
# Linux/macOS
curl -fsSL https://get.pnpm.io/install.sh | sh -
# Windows PowerShell
iwr https://get.pnpm.io/install.ps1 -useb | iex
Homebrew(macOS)
brew install pnpm
その他
# Windows (winget)
winget install pnpm
# Windows (Scoop)
scoop install pnpm
# Windows (Chocolatey)
choco install pnpm
# Volta
volta install pnpm
バージョン確認
pnpm --version
# 10.x.x
基本的な使い方
npmを使ったことがあれば、すぐに使えます。
プロジェクトの初期化
pnpm init
パッケージのインストール
# 全依存関係をインストール
pnpm install
# パッケージを追加
pnpm add express
# devDependenciesに追加
pnpm add -D typescript
# グローバルインストール
pnpm add -g typescript
スクリプトの実行
# package.jsonのscriptsを実行
pnpm run dev
# 省略形
pnpm dev
パッケージの更新
# 対話的に更新
pnpm update --interactive
# 最新版に更新
pnpm update --latest
パッケージの削除
pnpm remove express
npmとのコマンド比較
| npm | pnpm | 説明 |
|---|---|---|
npm install |
pnpm install |
依存関係のインストール |
npm install pkg |
pnpm add pkg |
パッケージの追加 |
npm uninstall pkg |
pnpm remove pkg |
パッケージの削除 |
npm run script |
pnpm script |
スクリプト実行 |
npm update |
pnpm update |
更新 |
実践的なユースケース
1. 既存プロジェクトへの移行
cd existing-project
# 既存のlockファイルを削除(任意)
rm package-lock.json yarn.lock
# pnpmでインストール
pnpm import # 既存のlockファイルから変換も可能
pnpm install
npmやyarnのlockファイルからの変換に対応しているので、移行は簡単です。
2. モノレポのセットアップ
# ルートディレクトリ
mkdir my-monorepo && cd my-monorepo
pnpm init
# ワークスペース設定
cat > pnpm-workspace.yaml << EOF
packages:
- 'packages/*'
- 'apps/*'
EOF
# パッケージを作成
mkdir -p packages/shared apps/web
# ルートから特定パッケージにインストール
pnpm add lodash --filter @myorg/shared
# 全パッケージで共通の依存関係をインストール
pnpm add -w typescript
3. CIでのキャッシュ活用
GitHub Actionsの例:
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm test
- run: pnpm build
--frozen-lockfileオプションで、lockファイルと差分があればエラーにできます。
4. ディスク使用量の確認
# なぜこのパッケージがインストールされているか確認
pnpm why lodash
# ストアの状態確認
pnpm store status
# 使われていないパッケージを削除
pnpm store prune
定期的にpnpm store pruneを実行すると、ディスク容量をさらに節約できます。
npmやyarnとの比較
| 項目 | npm | yarn | pnpm |
|---|---|---|---|
| 速度 | 普通 | 速い | 最速 |
| ディスク効率 | 低い | 低い | 高い |
| 依存関係の厳格さ | 低い | 低い | 高い |
| モノレポ対応 | △ | workspaces | ネイティブ |
| 学習コスト | - | 低い | 低い |
個人的には、新規プロジェクトならpnpm一択ですね。
注意点
1. シンボリックリンクの制約
一部の古いツールやエディタで、シンボリックリンクがうまく解決されない場合があります。その場合はnode-linker設定で対応できます。
# .npmrc
node-linker=hoisted
2. チーム全体での導入
pnpmを使うなら、チーム全員がpnpmを使う必要があります。npmやyarnと混在すると、lockファイルが競合します。
// package.json
{
"packageManager": "pnpm@10.0.0",
"scripts": {
"preinstall": "npx only-allow pnpm"
}
}
こうしておけば、npm installを実行するとエラーになります。
3. 一部パッケージの互換性
99%のパッケージは問題なく動きますが、node_modulesのフラットな構造を前提としたパッケージは動かない場合があります。
まとめ
正直なところ、2025年にnpmをそのまま使い続ける理由は少なくなってきていると思います。
pnpmを導入するメリット:
- インストールが2倍速くなる
- ディスク容量を大幅に節約できる
- 依存関係が明確になる
- モノレポ構成が簡単
- npmからの移行が簡単
30代になって思うのは、開発環境への投資は惜しまない方がいいということ。pnpmへの移行にかかる時間なんてせいぜい5分です。それで日々のnpm installのストレスから解放されるなら、QOL上がること間違いなしです。
まずは個人プロジェクトで試してみて、問題なければチームにも展開していくのがおすすめです。
公式サイト: https://pnpm.io GitHub: https://github.com/pnpm/pnpm ドキュメント: https://pnpm.io/motivation