# npm 快取越用越大，Mole 這次只清沒有索引參照的資料

> 在快取副本上比較兩種清理方式，說明 npm 的索引參照、Go 的使用時間，還有全域工具和舊 Xcode 專案裡的建置產物。

Published: 2026-10-05

最近有使用者提到，用 Mole 維護完 Mac，npm 的快取還在，自己清理以後從 8 GB 變成了 2 GB。這則回饋讓我重新看了一遍開發工具的快取，哪些內容還能用，哪些已經沒有參照，還有哪些工具自己就在清，Mole 再清一次反而讓下一次建置多花時間。

這次調查和改動發生在 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，更不能說這 6 GB 全是沒有參照的內容。

## 副本測試中，verify 也刪掉了仍可使用的資料

實驗用的是本機快取的副本，npm 11.19.1、cacache 20.0.4，原快取沒有改動，它剛建立約一天，內容檔案一共 309796162 位元組，還不是一份累積了幾個月的快取。

| 判定方式 | 內容檔案數 | 內容位元組數 |
| --- | --- | --- |
| 沒有任何有效索引項目參照 | 1 | 614295 |
| `npm cache verify` 實際刪除 | 6 | 25469972 |

差別來自 [cacache 的 verify 實作](https://github.com/npm/cacache/blob/v20.0.4/lib/verify.js)，它清理內容、驗證完整性，再重建索引，同一個 key 只保留最後一筆，舊項目裡卻可能還有不同請求需要的完整中繼資料和精簡中繼資料，不能簡單當成同一份內容的新舊版本。

在副本上，verify 刪除的內容裡有 11199796 位元組仍被有效的另一種請求項目參照，清理前能離線讀取的幾個套件資訊，清理後再離線執行 `npm view`，卻出現 `ENOTCACHED` 錯誤，連線以後可以重新取得，但快取原來能做的事已經變了。

所以這裡選了更窄的判定，只要任何一筆有效索引還參照著它，就留著，即使這次只找到約 0.6 MB，也不為了接近 verify 的數字去刪另一種請求仍然需要的內容。

## 清理頁列出未被參照的資料，整份快取仍可手動選擇

Mole 的新實作只掃描預設的 `~/.npm/_cacache`，沒有參照、超過一天的內容會合成一列，放在清理頁的開發工具分組裡，預設勾選，整個 npm 快取仍然需要自己確認，兩種選擇重疊的部分也只計算一次。

掃描時遇到 `npm install`、`npm ci` 這類正在寫入快取的指令就先不處理，讀不清索引也不列出來，刪除前還會重新確認參照，索引檔案和暫存目錄都不改，Mole 也不會在背景呼叫 npm 的清理指令。

保留索引仍然指向的內容，不等於保證每個專案永遠能離線建置，鎖定檔還可能按雜湊直接找某個套件，私有套件來源和舊版本也未必始終能重新下載，原始碼、鎖定檔和需要保留的產物仍然要另外保存。如果設定了其他 npm 快取路徑，這一輪也沒有涵蓋。

## Go、Cargo 和 pnpm，不能照著 npm 再做一遍

Go 本來就會自動整理建置快取，[實作裡](https://go.dev/src/cmd/go/internal/cache/cache.go)保留最近五天用過的項目，再留一小時的時間餘裕，也就是 121 小時，Mole 以前把整個建置快取目錄列進預設清理，這次改成同樣的時間規則，保留最近還會用到的項目，不用在下次建置時全部重新編譯。

Cargo 已經有[自動快取回收](https://doc.rust-lang.org/cargo/reference/config.html#cache)，這次樣本裡沒有到期內容，之前清掉解壓後的套件來源，約 29 小時後又下載了約 157 MB、解壓出約 1.06 GB，所以這次沒有再做一份 Cargo 自動回收，原來需要自己確認的清理項仍然保留。

pnpm 的共用套件儲存區又不同，這次 APFS 樣本的硬連結數量無法判斷檔案是否仍被使用，直接套用這個條件會把整個儲存區都選進去，所以這次沒有新增 pnpm 自動回收，要用硬連結數量來清理，得先確認它在這個檔案系統上真的能反映使用狀態。

## 全域工具和舊 Xcode 專案裡的建置產物

全域安裝的 `pake-cli` 裡有一個 `src-tauri/target`，約 1.48 GiB，是它建置 Tauri 時留下的 Rust 產物，不是 npm 下載快取。現在這種由工具寫下有效 `CACHEDIR.TAG` 標記的建置目錄，也會作為待確認項列出來，工具本身和它的 `node_modules` 相依套件不會跟著刪，Rust 建置還在執行時也先留著。

Xcode 那邊還找到 24 份原專案路徑已經不存在的 DerivedData，調查時約 9 GiB，只有啟動磁碟上的專案能確認已經刪除、Xcode 至少兩週沒有用過對應快取，才會列出整個目錄，外接磁碟沒接上、沒有讀取權限，都不能算作專案不存在。這兩個大小是當時找到的內容，不是承諾每台 Mac 都能釋放這麼多，再需要建置時，仍然要花時間重新產生。

## 自己處理時，先確認正在清哪一層

想看 npm 快取在哪，可以先執行下面這個唯讀指令，自訂路徑會由 npm 自己回報。

```bash
npm config get cache
```

確認沒有安裝和建置正在寫入這份快取，再決定是否執行 `npm cache verify`，它會實際修改快取，不只是查看，專案的 `node_modules` 和全域安裝的指令又是另外兩層，不會被這個指令一起處理，完整的整理順序可以接著看[開發工具快取清理](https://mole.fit/tw/blog/how-to-clear-dev-caches-mac)。

---

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