# Moleがnpm cache verifyよりキャッシュを多く残す理由

> npmのキャッシュをコピーして検証し、参照が残るデータ、Goの保持期間、見落としていたビルド成果物を見直しました。

Published: 2026-10-05

最近、MoleでMacをメンテナンスしてもnpmのキャッシュが残り、自分で整理したら8 GBから2 GBになったという声が届きました。これをきっかけに、開発ツールのキャッシュを見直しました。まだ使う内容、参照を失った内容、ツール自身がすでに整理している内容を分けないと、次のビルドを遅くするだけになってしまいます。

ここで紹介する調査と変更は10月5日のものです。実装は済んでいますが、公開中のPreview版にはまだ含まれておらず、今後のリリースで提供する予定です。

## npmのキャッシュが増え続ける理由

npmの既定のキャッシュは`~/.npm/_cacache`です。`content-v2`にパッケージやレジストリの応答を保存し、`index-v5`でリクエストと内容を対応付けます。ファイル名は内容のハッシュで、複数の索引項目が同じファイルを参照できます。

パッケージ情報が変わると新しい応答が保存され、その後の検索で古い索引項目が整理されても、内容のファイルは残ることがあります。[npmのドキュメント](https://docs.npmjs.com/cli/v11/commands/npm-cache/)にも、キャッシュは自動でデータを削除せず、インストールとともに増えると書かれています。

報告した方はすでに整理を終えていたため、実行前のコピーも、正確に使ったコマンドも確認できませんでした。8 GBから2 GBという数字は調査のきっかけであって、Moleが6 GBを回収した実測値ではなく、そのすべてが参照を失っていた証拠でもありません。

## 今回のテストでverifyが削除した内容

実験にはこのMacのキャッシュのコピーを使い、npm 11.19.1とcacache 20.0.4で確認しました。元のキャッシュは変更していません。作成から約1日、内容は309796162バイトで、何か月も使ったキャッシュとは違います。

| 判定 | 内容ファイル数 | 内容のバイト数 |
| --- | --- | --- |
| 有効な索引項目のどれからも参照されない | 1 | 614295 |
| `npm cache verify`が実際に削除 | 6 | 25469972 |

[cacacheのverify実装](https://github.com/npm/cacache/blob/v20.0.4/lib/verify.js)は内容を整理し、整合性を確認して索引を再構築します。同じキーでは最後の項目を残しますが、以前の項目にも、別のリクエストが使う完全版や簡略版のメタデータが残っている場合があります。単純な新旧関係ではありません。

コピーでverifyが削除したうち11199796バイトは、別のリクエスト形式の有効な項目から参照されていました。整理前はオフラインで読めたパッケージ情報の一部が、その後のオフライン`npm view`で`ENOTCACHED`になりました。オンラインなら再取得できますが、元のキャッシュでできていたことが変わっています。

Moleでは有効な索引項目が一つでも参照していれば残します。このサンプルで見つかったのが約0.6 MBでも、verifyの数字に近づけるために使える内容まで消すことはしません。

## 未参照のファイルをまとめて表示し、全体の削除は手動で選ぶ

新しい実装が調べるのは既定の`~/.npm/_cacache`だけです。参照がなく、1日より古い内容を開発ツールのグループに1行で表示し、既定で選択します。npmキャッシュ全体の削除には引き続き確認が必要で、重複する選択の容量は一度だけ数えます。

`npm install`や`npm ci`など、キャッシュに書き込むコマンドが動いている間は、これらのファイルを削除候補に出しません。索引を読めない場合も表示せず、削除前に参照を再確認します。索引ファイルや一時ディレクトリは変更せず、Moleからnpmの整理コマンドを実行することもありません。

索引が参照する内容を残しても、すべてのプロジェクトが常にオフラインでビルドできる保証にはなりません。ロックファイルはハッシュから直接パッケージを探すことがあり、非公開レジストリや古いバージョンが再取得できるとも限りません。ソース、ロックファイル、再生成できない成果物は別に保存する必要があります。カスタムのnpmキャッシュパスも今回の対象外です。

## Go、Cargo、pnpmは別々に判断する

Goはすでにビルドキャッシュを自動で整理します。[実装](https://go.dev/src/cmd/go/internal/cache/cache.go)では直近5日間に使った項目を残し、1時間の余裕を加えた121時間を基準にします。Moleは以前、このディレクトリ全体を既定の整理対象にしていましたが、同じ時間基準に絞りました。直近の作業まで消して再コンパイルさせる必要はありません。

Cargoにも[自動キャッシュ回収](https://doc.rust-lang.org/cargo/reference/config.html#cache)があり、このサンプルには期限を過ぎた内容がありませんでした。以前、展開済みのレジストリソースを消したあと、約29時間後に約157 MBを再ダウンロードし、約1.06 GBを展開していました。Moleではこの領域に別の自動回収を追加していません。既存のクリーンアップ項目は、引き続き手動で選択できます。

pnpmの共有ストアはさらに違います。調査したAPFSのサンプルでは、ハードリンク数から未使用を証明できず、その条件を移植するとストア全体が選ばれてしまいます。今回はpnpm用の回収処理を追加していません。この条件を使うには、そのファイルシステムでハードリンク数が実際の使用状況を表している必要があります。

## グローバルツールと古いXcodeプロジェクトのビルド成果物

グローバルにインストールした`pake-cli`には、約1.48 GiBの`src-tauri/target`がありました。TauriをビルドしたRustの出力で、npmのダウンロードキャッシュではありません。有効な`CACHEDIR.TAG`が付いたこうしたビルドディレクトリも、確認が必要な項目として表示します。ツール本体や`node_modules`の依存関係は残し、Rustのビルド中は処理しません。

Xcodeでは、記録されたプロジェクトパスが存在しないDerivedDataが24個、調査時点で約9 GiBありました。起動ディスク上でプロジェクトが消えたことを確認でき、Xcodeが対応するキャッシュを2週間以上使っていない場合だけ、ディレクトリ全体を削除候補に表示します。外付けドライブが未接続、読み取り権限がないという状態は、削除済みの証拠ではありません。この容量は調査時の結果で、すべてのMacで回収できる量ではなく、再びビルドすれば再生成にも時間がかかります。

## 整理する前に、対象のキャッシュを確認する

この読み取り専用コマンドで、カスタム設定も含めたnpmのキャッシュパスを確認できます。

```bash
npm config get cache
```

インストールやビルドが書き込んでいないことを確認してから、`npm cache verify`を実行するか決めます。これは閲覧だけのコマンドではなく、実際にキャッシュを変更します。プロジェクトの`node_modules`やグローバルのコマンドは別の層で、このコマンドでは整理されません。全体の手順は[開発ツールのキャッシュ整理](https://mole.fit/ja/blog/how-to-clear-dev-caches-mac)にまとめています。

---

Canonical HTML page: https://mole.fit/ja/blog/mole-developer-cache-gc
Blog index for agents: https://mole.fit/ja/blog/llms.txt
Site index for agents: https://mole.fit/llms.txt
