pnpm

パッケージマネージャーNode.jsJavaScriptTypeScriptディスク効率モノレポハードリンク

パッケージマネージャー

pnpm

概要

pnpm(performant npm)は、npmとYarnの代替として開発された高速でディスク効率の良いJavaScript/TypeScriptパッケージマネージャーです。ハードリンクとシンボリックリンクを活用してパッケージを1つのグローバルストアに保存し、プロジェクト間でディスク容量を共有することで、従来の3倍高速で最大50%のディスク容量を節約できます。npm/Yarnとの完全な互換性を保ちながら、厳密な依存関係管理、効率的なモノレポサポート、セキュリティの向上を実現した次世代パッケージマネージャーです。

詳細

pnpmは2017年にZoltan Kochanによって開発され、既存のパッケージマネージャーの問題を解決するために設計されました。最大の特徴は、Content-Addressable Store(CAS)というアプローチで、すべてのパッケージバージョンをグローバルストアに一度だけ保存し、プロジェクト間でハードリンクによって共有することです。これにより、同じパッケージを複数のプロジェクトで使用してもディスク上には1つのコピーしか存在しません。また、node_modules/.pnpmディレクトリにフラット構造でパッケージを配置し、シンボリックリンクで適切な依存関係ツリーを構築します。これにより、phantom dependencies(幻の依存関係)やnpm doppelgängers問題を解決し、より安全で一貫性のある依存関係管理を実現しています。

メリット・デメリット

メリット

  • ディスク効率: ハードリンクによる容量節約(最大50%削減)
  • 高速インストール: 既存パッケージの再利用により3倍高速
  • 厳密な依存関係: phantom dependenciesを防ぐ安全な構造
  • モノレポサポート: 効率的なワークスペース機能
  • npm互換性: 既存のnpmプロジェクトで即座に使用可能
  • セキュリティ: パッケージの分離とアクセス制御の向上

デメリット

  • シンボリックリンク: 一部のツールがシンボリックリンクに対応していない場合がある
  • Windows制限: 古いWindowsバージョンでのハードリンク制限
  • 学習コスト: .pnpmディレクトリ構造の理解が必要
  • デバッグ複雑さ: 複雑なリンク構造によるデバッグの困難さ

参考ページ

書き方の例

基本的なパッケージ管理

# pnpmのインストール
npm install -g pnpm
# または
curl -fsSL https://get.pnpm.io/install.sh | sh -

# プロジェクトの初期化
pnpm init

# パッケージのインストール
pnpm install

# パッケージの追加
pnpm add react react-dom
pnpm add -D typescript @types/react
pnpm add -g nodemon

# パッケージの削除
pnpm remove lodash
pnpm remove -D webpack

ワークスペース(モノレポ)設定

# pnpm-workspace.yaml
packages:
  - 'packages/*'
  - 'apps/*'
  - '!**/test/**'
// ルートのpackage.json
{
  "name": "my-monorepo",
  "private": true,
  "scripts": {
    "build": "pnpm -r run build",
    "test": "pnpm -r run test"
  }
}
# ワークスペース全体でのコマンド実行
pnpm -r run build
pnpm -r run test

# 特定のワークスペースでの実行
pnpm --filter @myorg/app run dev
pnpm --filter "./packages/utils" run build

# 依存関係が変更されたパッケージのみ実行
pnpm --filter "...[HEAD~1]" run test

# ワークスペース間の依存関係追加
pnpm --filter @myorg/app add @myorg/utils@workspace:*

高度なフィルタリングとタスク管理

# パターンベースフィルタリング
pnpm --filter "*app*" run build
pnpm --filter "@myorg/*" run lint

# 依存関係ベースフィルタリング
pnpm --filter ...@myorg/core run build  # coreに依存するすべて
pnpm --filter @myorg/core^... run test   # coreが依存するすべて

# 並列実行制御
pnpm -r --parallel run dev
pnpm -r run build --workspace-concurrency=2

# 特定の条件でのスキップ
pnpm --filter "!@myorg/legacy" -r run test

パッケージ情報とストア管理

# パッケージ情報の表示
pnpm list
pnpm list --depth=0
pnpm list --global

# 依存関係の理由確認
pnpm why lodash
pnpm audit

# ストア管理
pnpm store status
pnpm store prune  # 使用されていないパッケージを削除
pnpm store path   # ストアパスの表示

.npmrc設定とカスタマイズ

# .npmrc
# ストア場所の指定
store-dir=~/.pnpm-store

# ハードリンクを無効化(必要な場合)
package-import-method=copy

# ワークスペース設定
link-workspace-packages=true
prefer-workspace-packages=true

# ログレベル設定
loglevel=warn

# 自動インストール有効化
auto-install-peers=true

# 依存関係解決の厳密化
strict-peer-dependencies=true

CI/CD環境での最適化

# CI環境での高速インストール
pnpm install --frozen-lockfile
pnpm install --prefer-offline

# キャッシュ活用
pnpm install --ignore-scripts
pnpm rebuild

# プロダクションビルド
pnpm install --prod --frozen-lockfile
pnpm prune --prod

# Docker環境
# package.jsonとpnpm-lock.yamlのみコピーしてインストール
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

高度な機能とトラブルシューティング

# パッチ管理
pnpm patch [email protected]
# パッチファイル編集後
pnpm patch-commit /tmp/patch-dir

# リンク管理
pnpm link --global
pnpm link ../local-package

# 重複排除と最適化
pnpm dedupe
pnpm install --fix-lockfile

# 環境変数とスクリプト
PNPM_HOME=/custom/path pnpm install
pnpm exec -- jest --coverage
pnpm dlx create-react-app my-app  # npx相当