pnpm とは、依存のファイルを共有の保存庫に置いてリンクで配り、ディスクと時間を節約する Node.js のパッケージ管理の道具です。
- pnpm の仕組みと、pnpm install・pnpm update・
--lockfile-only・--filterが公式の文書でどう定義されているか - pnpm workspace(モノレポ)と pnpm-workspace.yaml、そこに書く
overridesの扱い - 当社が pnpm で実際に踏んだ落とし穴(無視された overrides・宣言まで書き替えた update・CI の公開の段の失敗・新しい worktree の全件赤)と、確かめ方
当社は、コードの変更を AIエージェント の席に書かせ、モノレポの依存を pnpm で管理しています。席は命令を撃って終了コードと差分を見て先へ進むので、「エラーも出ず、差分も無い」形の失敗がそのまま通り抜けやすい。この頁は、pnpm の公式の文書の定義と、その形で起きた当社の記録を並べたものです。
pnpm の仕組み:保存庫とリンクで node_modules を組む
公式の文書によると、pnpm は依存のファイルを内容で番地を決める保存庫(content-addressable store)にまとめて置き、各プロジェクトにはハードリンクか copy-on-write の複製で配ります。同じ版を使うプロジェクトが増えても、ファイルは増えません。版を上げたときは、変わったファイルだけが保存庫に足されます。
node_modules の形にも特徴があります。プロジェクトの直下に見えるのは package.json に宣言した直接の依存だけで、それらは node_modules/.pnpm の中の実体を指すシンボリックリンクです。宣言していない依存を、コードがたまたま読めてしまうことを防ぐ形です。シンボリックリンクを扱えない道具のためには、nodeLinker を hoisted にする逃げ道が用意されています。
出典: pnpm.io「Motivation」https://pnpm.io/motivation (2026-11-07 に確認)。
pnpm の使い方:install・update と、よく使う印
日々の作業で撃つのは、ほぼ下の表の命令です。とくに pnpm update と pnpm install --lockfile-only は、どちらもロックファイルを動かしますが、package.json を書き替えるかどうかが違います。後の節の記録はこの違いで起きました。
| 命令 | 何をするか(公式の文書) | package.json を書き替えるか |
|---|---|---|
pnpm install | プロジェクトの依存をすべて入れる。workspace の中では、既定で全プロジェクトの依存を入れる | 書き替えない |
pnpm install --frozen-lockfile | ロックファイルが package.json とずれていたら、更新せずに失敗する。ロックファイルが在る CI では既定で有効 | 書き替えない |
pnpm install --lockfile-only | pnpm-lock.yaml と package.json だけを更新し、node_modules には何も書かない | 文書上は更新の対象に入る |
pnpm update | 依存を更新し、package.json の範囲を解決した版へ移す。^ などの演算子はそのまま残す | 書き替える(既定) |
pnpm update --no-save | pnpm-lock.yaml だけを更新し、package.json の範囲は動かさない | 書き替えない |
pnpm update --latest | 最新の安定版へ上げる。メジャー版をまたぐこともある | 書き替える |
出典: pnpm.io「pnpm install」https://pnpm.io/cli/install ・「pnpm update」https://pnpm.io/cli/update (どちらも 2026-11-07 に確認)。
pnpm workspace と pnpm-workspace.yaml(モノレポ)
pnpm workspace は、ひとつのリポジトリに複数のプロジェクトをまとめる、pnpm に組み込みのモノレポの仕組みです。workspace のルートには pnpm-workspace.yaml が必要で、その packages に、workspace に含めるディレクトリを glob で並べます。! で始まる行は除外です。pnpm はルートの package.json の workspaces ではなく、このファイルから workspace を読みます。
pnpm-workspace.yaml は workspace の範囲だけでなく、pnpm の設定の大半を書く場所でもあります。overrides(依存の版の上書き)、catalog、node_modules の組み方、ビルドの設定などがここに入ります。overrides はプロジェクトのルートにしか書けません。
workspace の中の別のプロジェクトを依存にするときは "foo": "workspace:*" のような workspace: の書き方を使います。これを使うと、pnpm は workspace の中の物にしか解決せず、合わなければレジストリから取らずに失敗します。
出典: pnpm.io「Workspace」https://pnpm.io/workspaces ・「pnpm-workspace.yaml」https://pnpm.io/pnpm-workspace_yaml ・「Dependency resolution settings」https://pnpm.io/settings/dependency-resolution (いずれも 2026-11-07 に確認)。
pnpm filter:命令を一部のプロジェクトに絞る
--filter(短く -F)は、命令を workspace の一部のプロジェクトに絞る印です。書き方で、選ぶ範囲が変わります。
| 書き方 | 選ばれる物 |
|---|---|
--filter <名前> | そのプロジェクトだけ(@scope/* のような型も使える) |
--filter <名前>... | そのプロジェクトと、それが依存する物 |
--filter ...<名前> | そのプロジェクトと、それに依存する物 |
--filter "./<glob>" | ディレクトリで選ぶ |
--filter "[<起点>]" | 指定した commit や branch から変わったプロジェクト |
--filter=!<名前> | そのプロジェクトを除く |
出典: pnpm.io「Filtering」https://pnpm.io/filtering (2026-11-07 に確認)。
絞って入れると速い反面、node_modules が「一部だけ入った途中の状態」になります。後の CI の記録は、この途中の状態の上で起きました。
pnpm overrides が無言で無視された記録
依存の版を固定しようとして、ルートの package.json に pnpm.overrides を書いて pnpm install を撃ちました。結果は終了コード 0、警告 0、ロックファイルの差分 0 行でした。pnpm 10 以降では、この場所の pnpm.overrides は読まれず、何も言わずに無視されていました(社の実測 2026-09-18)。pnpm 10 以降で効くのは、pnpm-workspace.yaml に書いた overrides: です(社の実測 2026-09-18)。
危ないのは、差分 0 行が「もう最新だから何も変わらない」と同じ見た目だという点です。設定が読まれていないときも、効いていて既に揃っているときも、git diff は空です。そこで当社は、効いているかを差分の有無ではなく、陽性対照で確かめています。いま効いている上書きは pnpm-lock.yaml の先頭の overrides: の欄に載るので、書いた上書きがそこに在るかを見ます(社の実測 2026-09-18)。
| 見る所 | 読み方 |
|---|---|
| git diff が 0 行 | 「もう最新」とは読まない。設定が読まれていないときも同じ形になる |
pnpm-lock.yaml の先頭の overrides: | 書いた上書きが載っていれば効いている。載っていなければ読まれていない |
package.json に残った pnpm.overrides | 効かないので消す。効く場所と効かない場所の 2 つに情報源を残さない |
出典: 社の実測 2026-09-18。
pnpm update が package.json の宣言まで書き替えた記録
ある脆弱性の修正版は、package.json の宣言(caret の範囲)の中に収まっていました。範囲の中で当てれば済むので pnpm update を使ったところ、ロックファイルだけでなく package.json の宣言まで、最新の caret へ書き替わりました(社の実測 2026-09-18)。公式の文書の通り、pnpm update は既定で範囲を解決した版へ移す命令です。
宣言は動かさず、範囲の中のまま新しい版を当てるなら pnpm install --lockfile-only を使います(社の実測 2026-09-18)。
もうひとつ分かった事があります。再解決をかけると、狙った脆弱性とは別に、sharp の 0.35.3 から 0.35.4 への移動で別の high の脆弱性も同じ 1 手で閉じました(社の実測 2026-09-18)。今回は良い方向でしたが、裏返せば、意図しない版の移動も混ざるということです。当てた後は、差分の行数と、版が動いたパッケージを全部並べて、狙った物以外が何か動いていないかを確かめます。
| やりたい事 | 使う命令 | 当てた後に見る物 |
|---|---|---|
| 宣言の範囲の中で、修正版を当てる | pnpm install --lockfile-only | 差分の行数と、版が動いたパッケージの全部 |
| 宣言ごと新しい版へ上げる | pnpm update | package.json の宣言の変化と、ロックファイルの差分 |
出典: 社の実測 2026-09-18 と、pnpm.io「pnpm update」https://pnpm.io/cli/update (2026-11-07 に確認)。
CI の公開の段が落ちた記録:pnpm run の前の依存検査
pnpm 11.1.2 で、CI の公開の段が exit 254 で落ちました(社の実測 2026-10-29)。起きていた順は次の通りです。
- その job は、先に
--filterで絞って依存を入れていた(社の実測 2026-10-29)。 - 続けて
pnpm run buildを撃つと、pnpm 11 は実行の前に依存を検査し、ずれていると全体のpnpm installを自動で撃った(社の実測 2026-10-29)。 - その install が、絞って入れた途中の状態の上で、hoist(シンボリックリンクを張る段)で ENOENT(unlink)を出して落ちた(社の実測 2026-10-29)。
この自動の検査は、公式の文書では verifyDepsBeforeRun という設定です。pnpm run と pnpm exec の前に node_modules が古くないかを確かめ、値は install(既定・古ければ install を撃つ)、warn、prompt、error、false(検査しない)から選びます。なお、2026-11-07 に確認した文書には「install が失敗したら警告を出して命令を続ける」と書かれていますが、当社の pnpm 11.1.2 の CI では job が exit 254 で終わりました(社の実測 2026-10-29)。版による違いかどうかは確かめていません。
直しは、その job の環境変数に次の行を足すことでした。同じ状態で終了コード 0 になりました(社の実測 2026-10-29)。
env:
pnpm_config_verify_deps_before_run: 'false'
環境変数の頭は pnpm_config_ です。pnpm 11 は npm_config_* の環境変数を読まないので、npm の流儀で書くと効きません(社の実測 2026-10-29)。
「揺れ」に見えて、毎回落ちていた
手こずった理由は、同じ機で install を撃ち直すと通ってしまったことです。途中まで張られた状態の上で撃ち直すので通り、揺れる試験のように見えました。一方、CI の撃ち直しは毎回まっさらな機で始まるので、毎回落ちていました(社の実測 2026-10-29)。手元でもまっさらな node_modules から再現させ、二分探索で、依存の版を 1 つ上げたコミットから落ち始めると突き止めました(社の実測 2026-10-29)。
同じ機で撃ち直して通ったことを「揺れ」の証拠にしない、というのが教訓です。揺れと、揺れではない赤の見分け方は flaky test の頁に、job ごとにまっさらな機を使う形は self-hosted runner の頁に書いています。
出典: 社の実測 2026-10-29 と、pnpm.io「Build settings」https://pnpm.io/settings/build ・「Settings」https://pnpm.io/settings (どちらも 2026-11-07 に確認)。
新しい worktree で pnpm install の後に全件赤:生成物が無い
モノレポの新しい worktree を作り、pnpm install の後に型検査とテストを撃つと、どちらも全件が赤になりました。自分が触っていない行で Cannot find module が大量に出ていました(社の実測 2026-09-21)。
原因は、pnpm install が生成物を作らないことでした。Prisma Client のように生成の手順で作る物は、install だけでは在りません(社の実測 2026-09-21)。生成のスクリプトと、テストが頼る内部のパッケージのビルドを先に撃ってから測ります。この形の赤を、自分の変更の赤と読みません(社の実測 2026-09-21)。生成の命令の注意は prisma migrate の頁にもまとめています。
pnpm で設定を変えた後に確かめる順
上の記録は、どれも「エラーが出ない」「差分が無い」「撃ち直すと通る」のどれかの形をしていました。当社は設定や依存を変えた後、次の順で確かめています(社の実測 2026-09-18・2026-09-21・2026-10-29)。
| 確かめる事 | 見る物 | 元の記録 |
|---|---|---|
| 書いた設定が読まれているか | pnpm-lock.yaml の先頭の overrides: など、効いたら必ず出る所(陽性対照) | overrides の無視 |
| 狙った物だけが動いたか | 差分の行数と、版が動いたパッケージの全部。package.json の宣言の変化 | pnpm update の書き替え |
| 赤が本当に揺れか | まっさらな node_modules から再現するか。落ち始めたコミットを二分探索で探す | CI の公開の段の失敗 |
| 赤が自分の変更か | 生成と内部のパッケージのビルドを済ませた後でも赤か | 新しい worktree の全件赤 |
出典: 社の実測 2026-09-18・2026-09-21・2026-10-29。
よくある質問
pnpm とは何ですか?
Node.js のパッケージ管理の道具です。依存のファイルを共有の保存庫に置き、各プロジェクトへはハードリンクなどで配るので、同じ版を何度も複製しません。node_modules の直下には宣言した直接の依存だけが、シンボリックリンクとして並びます(pnpm.io「Motivation」)。
pnpm の overrides はどこに書けば効きますか?
pnpm-workspace.yaml の overrides: に、プロジェクトのルートで書きます(pnpm.io の設定の文書)。当社では pnpm 10 以降、ルートの package.json の pnpm.overrides がエラーも警告も無く無視されました(社の実測 2026-09-18)。効いているかは、pnpm-lock.yaml の先頭の overrides: に載っているかで確かめます。
pnpm update と pnpm install --lockfile-only はどう使い分けますか?
pnpm update は既定で package.json の範囲まで書き替えます(pnpm.io「pnpm update」)。宣言の範囲の中のまま新しい版を当てたいときは、当社は pnpm install --lockfile-only を使います(社の実測 2026-09-18)。どちらでも意図しない版の移動が混ざり得るので、動いたパッケージを全部並べて確かめます。
pnpm run の前に勝手に install が走るのを止めるには?
設定 verifyDepsBeforeRun を false にします(pnpm.io「Build settings」)。当社の CI では job の環境変数に pnpm_config_verify_deps_before_run: 'false' を足して直しました。pnpm 11 は npm_config_* の環境変数を読まないので、頭は pnpm_config_ にします(社の実測 2026-10-29)。
pnpm install の後に Cannot find module が大量に出るのはなぜ?
pnpm install は、Prisma Client のような生成物や、workspace の内部のパッケージのビルド結果を作らないためです(社の実測 2026-09-21)。生成のスクリプトと内部のパッケージのビルドを先に撃ってから、型検査やテストを測ります。
出典
- pnpm.io「Motivation」https://pnpm.io/motivation (2026-11-07 に確認)
- pnpm.io「pnpm install」https://pnpm.io/cli/install (2026-11-07 に確認)
- pnpm.io「pnpm update」https://pnpm.io/cli/update (2026-11-07 に確認)
- pnpm.io「Settings」https://pnpm.io/settings ・「Dependency resolution settings」https://pnpm.io/settings/dependency-resolution ・「Build settings」https://pnpm.io/settings/build (いずれも 2026-11-07 に確認)
- pnpm.io「Workspace」https://pnpm.io/workspaces (2026-11-07 に確認)
- pnpm.io「Filtering」https://pnpm.io/filtering (2026-11-07 に確認)
- pnpm.io「pnpm-workspace.yaml」https://pnpm.io/pnpm-workspace_yaml (2026-11-07 に確認)
- 社の実測 2026-09-18(overrides の無視、pnpm update の書き替えと --lockfile-only)・2026-09-21(新しい worktree の全件赤)・2026-10-29(CI の exit 254 と verifyDepsBeforeRun)
ブルーランプ 記事の編集部/更新日 2026-11-07