前言

使用 Apple Silicon Mac 開發前端專案時,你有沒有遇過這種情況:npm install 明明成功了,但執行 npm run dev 時 Vite 卻突然翻臉不認人,噴出一堆關於 rollup.darwin-arm64.node 的錯誤?

更慘的是,macOS 還會彈出一個視窗說「無法打開 rollup.darwin-arm64.node,因為無法驗證開發者」——彷彿你的電腦在暗示你下載了什麼奇怪的東西。

別擔心,這不是你的問題(好吧,也不完全是 Apple 的問題),而是 npm + Gatekeeper 的經典組合技。本文將帶你徹底理解並解決這個惱人的問題。


問題現象

當你在 Apple Silicon Mac 上執行 Vite/Rollup 專案時,可能會遇到以下錯誤:

錯誤 1:模組找不到

Error: Cannot find module '@rollup/rollup-darwin-arm64'
Require stack:
- /project/node_modules/rollup/dist/native.js

錯誤 2:系統策略拒絕載入

Error: dlopen(/project/node_modules/@rollup/rollup-darwin-arm64/rollup.darwin-arm64.node, 0x0001):
code signature in (...) not valid for use in process: library load disallowed by system policy

錯誤 3:Gatekeeper 彈窗警告

macOS 直接彈出視窗:「無法打開 rollup.darwin-arm64.node,因為無法驗證開發者」


問題根源分析

這個問題其實是兩個獨立問題的組合:

Mermaid Diagram

原因 1:npm optionalDependencies Bug

Rollup 使用 optionalDependencies 來管理多平台的原生二進位檔:

  • @rollup/rollup-darwin-arm64 (Apple Silicon Mac)
  • @rollup/rollup-darwin-x64 (Intel Mac)
  • @rollup/rollup-linux-x64 (Linux)
  • @rollup/rollup-win32-x64-msvc (Windows)

但 npm 有個已知 Bug:在某些情況下會漏裝裝錯平台版本的 optional dependency,導致模組找不到。

原因 2:macOS Gatekeeper 隔離機制

macOS 會對從網路下載的可執行檔打上 com.apple.quarantine 隔離標籤。當 Node.js 嘗試載入這些 .node 原生模組時,Gatekeeper 會介入檢查,如果無法驗證簽章就會阻擋。

有趣的是:npm 下載的檔案會被標記為「從網路下載」,即使它來自官方 registry。這就是為什麼明明是合法套件,macOS 還是會擋。


快速修復步驟

90% 的情況,以下四個步驟就能解決問題:

Step 1:刪除舊依賴與鎖檔

rm -rf node_modules package-lock.json

Step 2:重新安裝所有依賴

npm install

Step 3:清除 Gatekeeper 隔離標籤

xattr -dr com.apple.quarantine node_modules/@rollup/rollup-darwin-arm64/*.node

如果你不確定檔案在哪,可以用:

find node_modules -name "*.node" -exec xattr -dr com.apple.quarantine {} \;

Step 4:重建原生模組(可選)

npm rebuild rollup

執行完以上步驟,通常就能順利 npm run dev 了。


進階解決方案

如果快速修復無效,或你想要更長久的解決方案,以下是幾個選項:

方案 A:升級 npm

npm v9.x 以上對 optionalDependencies 的處理更穩定:

npm install -g npm@latest
npm --version  # 確認版本 >= 9.x

方案 B:改用 Yarn 或 PNPM

這兩個套件管理器對 optionalDependencies 的支援更完整:

# 使用 PNPM(推薦)
npm install -g pnpm
pnpm install

# 或使用 Yarn
npm install -g yarn
yarn install

方案 C:強制使用 JavaScript Fallback

Rollup 有純 JavaScript 實作,雖然較慢但完全不需要原生模組:

# 單次執行
ROLLUP_IGNORE_NATIVE=1 npm run dev

# 或加入 .env 檔案
echo "ROLLUP_IGNORE_NATIVE=1" >> .env

方案 D:在 .npmrc 關閉 optionalDependencies

# .npmrc
optional=false

這會讓 npm 完全跳過所有 optional dependencies,Rollup 會自動退回 JS 實作。

方案 E:自動化清除 quarantine(團隊協作推薦)

package.json 加入 postinstall 腳本:

{
  "scripts": {
    "postinstall": "xattr -dr com.apple.quarantine node_modules/**/*.node 2>/dev/null || true"
  }
}

這樣每次 npm install 後都會自動清除隔離標籤。


各方案比較

方案優點缺點適用場景
重新安裝簡單快速治標不治本臨時解決
升級 npm根本解決可能影響其他專案個人開發
改用 PNPM效能更好、更穩定需要團隊配合新專案
JS Fallback完全避開問題打包速度較慢CI/CD 環境
postinstall自動化只對 Mac 有效團隊協作

預防措施

在專案 README 記錄

## macOS Apple Silicon 用戶注意

如果遇到 Rollup 相關錯誤,請執行:

\`\`\`bash
rm -rf node_modules package-lock.json
npm install
xattr -dr com.apple.quarantine node_modules/**/*.node
\`\`\`

在 CI/CD 中使用環境變數

# .gitlab-ci.yml 或 GitHub Actions
env:
  ROLLUP_IGNORE_NATIVE: 1

結論

macOS Gatekeeper + npm optionalDependencies Bug 是 Apple Silicon Mac 開發者的常見痛點。好消息是,這個問題有明確的解決方案:

TL;DR 快速解法:

rm -rf node_modules package-lock.json && npm install && xattr -dr com.apple.quarantine node_modules/**/*.node

長期解法:

  • 改用 PNPM 作為套件管理器
  • 或在 package.json 加入 postinstall 自動清除腳本

希望這篇文章能幫你省下幾小時的 debug 時間。畢竟人生苦短,不該浪費在跟 Gatekeeper 吵架上。


參考資源