pnpmは「performant npm」を名前の由来に持つNode.js向けのパッケージマネージャです。依存パッケージの実体をディスク上の1か所にまとめ、各プロジェクトのnode_modulesからハードリンクで参照します。2026年8月26日にはRustで書き直したpnpm 12が安定版になり、ピア依存の解決速度が2〜3倍、メモリ使用量が約25%削減されました。この記事では共有ストアと非フラットなnode_modulesの構造、11.0で変わった設定ファイルの役割分担、workspacesとcatalogによるモノレポの実装、npmからの移行手順を扱い、採用しない方がよい規模条件にも線を引きます。
まとめ:pnpmを選ぶ条件と2026年9月時点で入れるべき版の結論
パッケージ数が3つ以上のモノレポ、または同種のプロジェクトを1台のマシンで多数抱える環境では、pnpmへの切り替えが投資として回収できます。共有ストアの効果はプロジェクト数に比例するため、単一パッケージのリポジトリを1つだけ持つ開発者にとって体感できる差はほとんど出ません。
版の選択は2026年9月時点で二段構えです。CIとローカルの両方にNode.js 22以上を用意できるなら、Rust実装のpnpm 12を`pnpm self-update latest-12`で入れます。ロックファイル形式とコマンド体系は11系と互換なので、移行に伴う書き換えは発生しません。Node.js 20が残っている環境では11系に留め、packageManagerフィールドで版を固定します。10系以前から持ち上げる場合は、11.0で.npmrcの役割が認証とレジストリだけに縮んだ点が実作業です。
pnpmとは何か|共有ストアとハードリンクでnpmと分かれる仕組み
pnpmとnpmの違いは機能の多寡ではなく、node_modulesの作り方にあります。同じpackage.jsonを読み、同じレジストリから取得しても、ディスクに残る形が違います。npm側の構成要素とコマンドの役割はnpmとは?CLIとレジストリの役割・package.jsonとv12の変更点を解説で整理しています。
コンテンツアドレス可能ストアとハードリンクで実体を1つに畳む構造
pnpmは取得したパッケージのファイルを、内容のハッシュで管理する共有ストアに保存します。各プロジェクトのnode_modulesに置かれるのは、そのストアへのハードリンクです。公式のmotivationページは、100個のプロジェクトが同じ依存を使うとき、npmではコピーが100個できるのに対しpnpmでは1つで済むと説明しています。
効き方はバージョン更新時にも現れます。100ファイルのうち1ファイルだけが変わった版に上げるなら、追加されるのは新しいファイル1つだけです。差分しかディスクを増やしません。
非フラットなnode_modulesが未宣言の依存の読み込みを止める理屈
npmとYarn Classicは、依存の依存まで含めた全パッケージをnode_modulesのルートへホイストします。この構造だと、package.jsonに書いていないパッケージもimportできてしまいます。宣言していない依存にコードがぶら下がった状態です。
pnpmはシンボリックリンクを使い、プロジェクトの直接依存だけをnode_modulesのルートに置きます。実体はnode_modules/.pnpm配下に展開され、そこから相互に参照される形です。Windowsでのシンボリックリンクとジャンクションの違いや、作成に必要な権限はWindowsでシンボリックリンクを作る手順の解説にまとめています。公式ドキュメントはこの差を「npmやYarn Classicではソースコードが依存として追加していない依存にアクセスできる」と表現しており、pnpmでは既定で直接依存だけが利用可能になります。
Resolving・Fetching・Linkingの3段に分かれるインストール処理
pnpmのインストールは3つの段階に分かれます。依存グラフを確定するResolving、ストアへパッケージを取得するFetching、node_modulesへリンクを張るLinkingです。実行するとこの3段が進捗に出ます。
$ pnpm install
Lockfile is up to date, resolution step is skipped
Packages: +412
Progress: resolved 412, reused 412, downloaded 0, added 412, done
dependencies:
+ react 18.3.1
+ react-dom 18.3.1
出力のreusedは共有ストアから再利用した数、downloadedはネットワークから取得した数です。2回目以降で`downloaded 0`になっていれば、ネットワークへのアクセスは発生していません。CIでインストール時間が縮まらないときは、まずこの行でストアが残っているかを確かめます。
pnpm 12のRust実装と11系の使い分け|self-updateでの切替手順
pnpmは2026年に2回、大きな更新を通過しました。4月28日の11.0でNode.js要件とセキュリティ既定が変わり、8月26日の12.0で実装言語がRustへ移りました。
pnpm 12のRust実装で変わったピア解決の速度とメモリ使用量
pnpm 12.0のリリースノートは、コマンド・フラグ・設定・ロックファイル形式のいずれもpnpm 11から引き継いだと明記しています。実装言語がJavaScriptからRustへ入れ替わった一方で、利用者から見えるインターフェースは変わっていません。
公式が挙げる改善は、循環参照を多く含む大規模ワークスペースでのピア依存解決が2〜3倍速くなった点と、メモリ使用量が約25%減った点です。ただしリモートの副作用キャッシュはLinux/glibcのx64とarm64のみの対応で、macOSやWindowsを開発機に使う構成ではまだ手に入りません。実装はpnpm本体のリポジトリで追跡できます。
npmのlatestが11系を指す状況での版の確認とself-updateの切替
2026年9月時点でnpmレジストリのlatestタグは11系を指しています。`npm install -g pnpm`で入れると11系が入るということです。12系へ移るには、11.10.0以降から明示的に切り替えます。
$ pnpm -v
11.25.0
$ pnpm self-update latest-12
$ pnpm -v
12.0.0
Node.jsの要件に注意が必要です。11.0でNode.js 22以上が必須になり、18から21のサポートが打ち切られました。pnpm自身が純粋なESMになったためです。ランタイム側の版の選び方はNode.jsとは?仕組み・版の選び方・標準機能で動かす手順で整理しています。チーム全体で揃えるなら、package.jsonのpackageManagerフィールドに`[email protected]`のように書いておくのが確実です。CIとローカルで別の版が動いてロックファイルの差分が出る事故は、この1行で止まります。
導入から基本コマンドまで|install・add・updateの動かし方と落とし穴
pnpmの入れ方は2系統あります。Node.jsに同梱されるCorepackを使う方法と、pnpm自身を独立して入れる方法です。どちらを選ぶかで、後の版管理の手間が変わります。
Corepack経由と独立インストールのどちらで入れるかの判断基準
Corepackはpackage.jsonのpackageManagerフィールドを読んで、指定された版のpnpmを自動で用意します。リポジトリ側で版を強制できるため、複数人での開発では取り違えが起きません。半面、Node.js 25以降はCorepackが同梱から外れ、有効化の手順が版によって変わります。`corepack enable`や`corepack use`の書き方はcorepackでpnpmを管理する方法|corepack enable・prepareの使い方とNode.js 25の変更で手順ごとに扱っています。
独立インストールを選ぶ基準は、Node.jsの版を頻繁に切り替えるかどうかです。ランタイムを入れ替えるとCorepack経由で有効化したpnpmは版ごとに設定し直しになります。行き来する環境では、pnpmをスタンドアロン実行ファイルで入れて独立させた方が壊れません。
install・add・updateの対応とCIでのfrozen-lockfileの既定
コマンド体系はnpmとほぼ対応します。移行時に覚え直す量は多くありません。
$ pnpm install # 依存をすべて導入
$ pnpm add zod # dependencies へ追加
$ pnpm add -D vitest # devDependencies へ追加
$ pnpm remove zod # 削除
$ pnpm update --latest # 最新版へ更新
$ pnpm dlx create-vite my-app # 一時実行(npx 相当)
公式のpnpm installリファレンスで確認しておきたいのは–frozen-lockfileの既定値です。非CI環境ではfalse、CI環境ではロックファイルが存在する場合にtrueが既定になります。CIでは何も指定しなくても、pnpm-lock.yamlとpackage.jsonが噛み合っていないインストールは失敗します。ほかに使用頻度が高いのは、ロックファイルだけ更新する–lockfile-only、ストア内のパッケージだけで解決する–offline、ディスクへ書かずに解決結果を報告する–dry-run(11.8.0以降)です。
pnpm 11.0で縮んだ.npmrcの役割とallowBuildsへの設定統一
pnpm 11.0のリリースノートが挙げる破壊的変更のうち、既存リポジトリの作業量に直結するのは設定ファイルの分割です。.npmrcは認証とレジストリの設定だけを持つファイルになり、pnpm固有の設定はpnpm-workspace.yamlへ移りました。
| 変更点 | 10系まで | 11.0以降 |
|---|---|---|
| 設定の置き場所 | .npmrc | pnpm-workspace.yaml |
| ビルド許可の指定 | 旧5設定に分散 | allowBuilds に統一 |
| 公開直後の版 | 制限なし | minimumReleaseAge=1440 |
| 特殊な下位依存 | 許可 | blockExoticSubdeps=true |
| 依存のビルド失敗 | 警告で継続 | strictDepBuilds=true |
| 環境変数の接頭辞 | npm_config_* | pnpm_config_* |
実際に引っかかりやすいのはminimumReleaseAgeの1440分です。公開から1日経っていない版は既定で採用されないため、自社パッケージを公開してすぐCIで取り込む運用は止まります。公開直後の悪性版を掴まないための既定なので、緩めるなら対象パッケージを絞ってください。strictDepBuildsも、警告で流していたネイティブモジュールのビルド失敗をエラーへ変えます。
モノレポでのpnpm運用|workspacesとfilter・catalogsの実装例
pnpmが最も差を出すのは、複数パッケージを1リポジトリに収めた構成です。モノレポそのものの利点と向き不向きはモノレポ(Monorepo)とは?マルチリポジトリとの違い・メリットとツールの選び方で整理しているため、ここではpnpm側の実装に絞ります。
pnpm-workspace.yamlとworkspace:プロトコルでの内部参照
公式のworkspacesドキュメントは、ワークスペースのルートにpnpm-workspace.yamlを置くことを条件として挙げています。対象ディレクトリをpackagesフィールドに列挙します。
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
ワークスペース内のパッケージを相互に参照するときは、workspace:プロトコルを使います。`”@app/utils”: “workspace:*”`と書けばローカルの最新版へリンクされ、レジストリを見に行きません。`workspace:^`は同一メジャー内、`workspace:~`は互換範囲に絞る指定です。別名で参照する`workspace:foo@*`や、相対パスで指す`workspace:../foo`も使えます。公開時にはこの記述が実際のバージョン範囲へ置き換わるため、リポジトリ内ではローカル参照のままにしておけます。
catalogでバージョン指定を1か所に集めて衝突を減らす設定
パッケージが増えると、同じ依存の版が各package.jsonに散らばります。catalogsは版の範囲をワークスペース共通の定数として定義する機能です。
# pnpm-workspace.yaml
catalog:
react: ^18.2.0
react-dom: ^18.2.0
catalogs:
react17:
react: ^17.0.2
react-dom: ^17.0.2
参照側のpackage.jsonでは版の代わりに`”react”: “catalog:”`と書きます。`catalog:`は`catalog:default`の省略形で、名前付きカタログなら`”react”: “catalog:react17″`という指定です。公式のcatalogsドキュメントは利点として、版の統一、更新時の編集箇所がpnpm-workspace.yamlの1か所に収まること、package.jsonの変更が減るためgitのマージ衝突が減ることを挙げています。10パッケージが同じUIライブラリに依存する構成なら、版上げで触るファイルは1個です。
-rと–filterで実行対象のパッケージを絞るタスクの指定方法
ワークスペース全体へ同じスクリプトを流すには-r(–recursive)を付けます。対象を絞る場合は–filterでパッケージ名やパスを指定します。
$ pnpm -r build # 全パッケージのbuild
$ pnpm --filter @app/web build # 単一パッケージ
$ pnpm --filter @app/web... build # 依存先も含める
$ pnpm --filter ...@app/utils test # 依存元も含める
`…`を後ろに付けると依存しているパッケージ群、前に付けると依存されているパッケージ群まで対象が広がります。共通ライブラリを直したときに影響範囲のテストだけを流す指定が、この記法で書けます。pnpm 11系ではワークスペースのタスク実行にスケジューラが入り、タスクごとの並行度と依存関係の設定、中断した再帰実行の再開が加わりました。ビルドが10分を超えるモノレポでは、この再開の有無が待ち時間を分けます。
npm・Yarn・Bunとの比較|ディスク量とCI時間で見る選定の基準
選定で見る軸は、ディスク使用量・インストール時間・未宣言依存への厳しさの3つです。機能の網羅性で並べても差は出ません。
npm・Yarn・pnpm・Bunをディスク量と依存の厳しさで並べた比較
| 観点 | npm | Yarn Berry | pnpm | Bun |
|---|---|---|---|---|
| node_modulesの形 | 平坦化 | 作らない(PnP) | 非フラット+リンク | 平坦化 |
| 実体の持ち方 | 案件ごとコピー | zipを共有 | 共有ストアに1つ | 共通キャッシュ |
| 未宣言依存の読込 | 通る | 止まる | 止まる | 通る |
| 既存ツール相性 | 良い | 非対応で詰まる | 良い | 要検証 |
| モノレポ機能 | workspaces | workspaces+制約 | workspaces+catalog | workspaces |
Yarn BerryのPlug’n’Playはnode_modulesを作らないため理論上は最も速いのですが、その実体を前提にしたツールで詰まります。pnpmはnode_modulesを作る側に留まり、既存のバンドラやテストランナーをそのまま動かせます。一方でBunランタイムは実行環境ごと置き換わり、Node.js互換の検証範囲が広くなるため、パッケージ管理だけを直したい場合には過剰でしょう。
CIのキャッシュ設計で効くストアの置き場所とfetch回数の削減
CIでpnpmの利点を回収するには、キャッシュ対象をnode_modulesではなく共有ストアに向けます。ストアのパスは`pnpm store path`で取得できます。
$ pnpm store path
/home/runner/setup-pnpm/node_modules/.bin/store/v11
$ pnpm store prune # 参照されていないパッケージを削除
ストアをキャッシュしておけば、次のジョブではFetchingが飛んでLinkingだけになります。node_modulesをそのままキャッシュする設計と比べ、復元するアーカイブが小さくなるのが利点です。キャッシュキーはpnpm-lock.yamlのハッシュにしてください。package.jsonのハッシュだと、解決結果が変わらない編集でもキャッシュが捨てられます。
既存プロジェクトからの移行手順とpnpmを採用しない条件の見極め
移行そのものは半日で終わります。時間を取られるのは、未宣言依存の洗い出しとCI設定の書き換えです。
npmプロジェクトからの移行手順とロックファイルの入れ替え手順
package-lock.jsonからpnpm-lock.yamlへの変換は、pnpm import が引き受けます。手順は次の順に踏みます。
- 作業ブランチを切り、package-lock.jsonを残した状態でpnpm importを実行してpnpm-lock.yamlを生成する
- node_modulesとpackage-lock.jsonを削除し、pnpm installで作り直す
- ビルドとテストを流し、解決できないimportをpackage.jsonのdependenciesへ追記する
- CIのインストールコマンドをpnpm installへ差し替え、キャッシュ対象を共有ストアに変える
- package.jsonのpackageManagerフィールドに使用する版を書き、.npmrcからpnpm固有設定をpnpm-workspace.yamlへ移す
3番目で出るエラーの件数が、そのプロジェクトが抱えていた未宣言依存の量です。ここで数十件出ても、記載を足せば解消します。逆に–shamefully-hoistでホイストを再現して黙らせると、pnpmを入れた意味が薄れます。移行の目的が依存の宣言を正すことにあるなら、エラーは潰す対象です。
単一パッケージ・小規模構成でpnpmを採用しない条件の線引き
次の3条件がすべて当てはまる場合、pnpmへの移行は見送ってよいと判断できます。パッケージが1つだけのリポジトリであること、扱う開発者が2〜3人で全員が同じCIを使っていること、依存が数十個で新規インストールが1分以内に終わること。この条件では共有ストアの効果が出る母数がなく、未宣言依存のエラー潰しとCI設定の書き換えだけが残ります。
逆に、パッケージが3つ以上のモノレポ、同種の案件を複数抱えて1台のマシンで行き来する受託開発の環境、依存が1,000個を超えてCIのインストールが3分以上かかっている状況では、移行の効果が測れる形で出ます。判断の材料はプロジェクト数と依存数であり、pnpmの機能一覧ではありません。CIとロックファイル運用ごと見直す規模になるなら、保守運用・内製化支援のように移行作業と運用ルールの整備をまとめて外部に持たせる手もあります。
よくある質問|pnpmのバージョン確認・容量・CI設定でつまずく点
導入と移行の現場で実際に検索されている質問を5つ取り上げます。
pnpmのバージョンを確認する方法は?
`pnpm -v`または`pnpm –version`で確認します。Corepack経由で入れている場合はpackage.jsonのpackageManagerフィールドの記述が優先されるため、コマンドの出力とグローバルに入れた版が食い違うことがあります。どの経路で解決されているか確かめるには、`pnpm root -g`でグローバルの場所を見るか、packageManagerフィールドの値を直接読んでください。CIとローカルで版が違う疑いがあるときは、両方で実行して突き合わせます。
pnpm 12へ上げるとロックファイルは作り直しになりますか?
なりません。pnpm 12.0のリリースノートは、ロックファイル形式・コマンド・フラグ・設定のいずれも11と互換だと明記しています。ロックファイルのサイズが縮む方向の変更は入っているため、初回のインストールでpnpm-lock.yamlに差分が出ることはあります。その差分はコミットして構いません。11系から12系への移行で、依存の再解決やpackage.jsonの書き換えは発生しません。
pnpmはnpmのpackage-lock.jsonをそのまま読めますか?
直接は読みません。`pnpm import`を実行すると、package-lock.jsonの解決結果からpnpm-lock.yamlを生成します。この変換を通せば、npm側で固定していた版をそのまま引き継げます。変換後はpackage-lock.jsonを削除し、pnpm-lock.yamlをコミット対象にしてください。両方を残すと、どちらが正なのか分からない状態になります。
node_modulesの容量はどれくらい減りますか?
減り方はプロジェクト数に依存します。1プロジェクトだけを置く環境では、共有ストアに実体が1つとハードリンクが並ぶ形になるため、ディスク上の合計は大きく変わりません。差が出るのは同じ依存を使うプロジェクトを複数抱える環境です。公式のmotivationページが挙げる例では、100プロジェクトが同じ依存を使う場合にnpmでは100コピー、pnpmでは1つです。測るならストアと各node_modulesの実サイズを分けて集計してください。
CIでpnpm installが失敗するのはどんなときですか?
最も多いのは–frozen-lockfileの既定によるものです。CI環境ではロックファイルが存在する場合にこの指定が既定でtrueになるため、package.jsonとpnpm-lock.yamlが噛み合っていないと失敗します。package.jsonだけを編集してコミットした状態が典型例です。ローカルでpnpm installを実行し、更新されたロックファイルを一緒にコミットすれば解消します。11以降では、公開から1日経っていない版をminimumReleaseAgeが弾く挙動も原因になります。
関連記事
- corepackでpnpmを管理する方法|corepack enable・prepareの使い方とNode.js 25の変更:Corepack経由でpnpmの版を固定・更新する具体的な手順を扱っています
- モノレポ(Monorepo)とは?マルチリポジトリとの違い・メリットとツールの選び方:pnpm workspacesを載せる土台としてのモノレポ構成の判断材料になります
- Node.jsとは?仕組み・版の選び方・標準機能で動かす手順を実装目線で解説:pnpm 11以降が要求するNode.js 22以上の位置づけを確認できます
- Volta(ヴォルタ)とは?Node.jsバージョン管理ツールの使い方と現状:ランタイムの版を切り替える環境でpnpmをどう固定するかの検討に使えます
- Bunとは?インストールから使い方・コマンドまで解説:パッケージ管理とランタイムを一体で置き換える選択肢との比較ができます