---
# System prepended metadata

title: 從 pnpm 設定到 nexe 打包：打造可獨立運行的 Next.js Standalone 專案
tags: [next.js]

---

# 從 pnpm 設定到 nexe 打包：打造可獨立運行的 Next.js Standalone 專案

## 更改 pnpm 設定

最一開始使用 pnpm 編出來的 standalone，發現只能在當前目錄中運行，如果把 standalone 目錄整個移到新的資料夾空間將會導致無法運行。這是因為 pnpm 原本的 node_modules 機制 (軟連結對照) 所造成的，我們可以透過更改 pnpm 設定讓 node_modules 資料夾結構跟原本 npm 的結構一樣

### 更改 pnpm-workspace.yaml
加入以下設定

```
nodeLinker: hoisted
```

- nodeLinker: 將常用依賴提升 (hoisted) 到最上層的 node_modules，與 npm 類似

## 更改 next.config.ts

在輸出 standalone 之後，運行程式時發現，有些 module 會無法運行，筆者就剛好遇到 java-bridge 這個套件需要 jar 檔而沒有複製到的問題，這時候就可以在 next.config.ts 當中把套件的路徑加入到 outputFileTracingIncludes 設定中，這樣建置時就會把相關檔案複製到輸出目錄當中。

```
{
    outputFileTracingIncludes: {
        "/api/**/*": [
            "../../node_modules/java-bridge/**", // 加入有額外檔案的套件路徑
            "../../node_modules/raccoon-dcm4che-bridge/**",
        ],
        "*": [
            "public/**/*",
            ".next/static/**/*",
        ]
    }
}
```

## 更改路徑相關的程式碼

:::danger
這段非常重要，沒更改的話，基本上讀寫檔皆無法正常運作
:::

- 更改所有的 `process.cwd()` 程式碼，因為 nexe 會使用虛擬 filesystem 包裹起來，如果使用 `process.cwd()` 的話會得到 `/snapshot` 開頭的路徑
- 這時，必須寫個 function 來判斷是否在虛擬 filesystem 中，並用 process.execPath 替代

```
export const getWritableRoot = () => {
    const cwd = process.cwd();
    
    // 如果當前路徑是在 nexe 的虛擬 filesystem 中，返回執行檔的真實目錄
    if (cwd.startsWith("/snapshot")) {
        return path.dirname(process.execPath);
    }

    return cwd;
}
```

## 使用 nexe 包裹成單一執行檔

因為筆者建完 standalone 之後，覺得再附上單一執行檔的話，就可以很方便地用成一個 zip 給別人使用，所以就嘗試使用 nexe 來包裹

:::info
請務必先安裝以下工具 (windows)

  - NASM: https://www.nasm.us/pub/nasm/releasebuilds/
  - python > 3.9
  - MS build tool: 可參考 https://github.com/nexe/nexe?tab=readme-ov-file#windows
:::

- 進到 `.next/standalone` 運行以下指令

```
nexe server.js --build -t windows
```

:::info
如果要把 public 和 standalone 都打包進去，可以運行以下指令

```bash
nexe .next/standalone/server.js \
  -r "public/**/*" \
  -r ".next/standalone/**/*" \
  -o next-app-executable
```
:::

- 建置完之後，就會多出一個 server.exe，建議透過 cmd 執行，可以方便查看錯誤，如果使用雙擊打開蠻有機會出現跳出 cmd 小黑窗直接消失的情況

:::warning
另外，非常建議將整個專案與 standalone 一起打包。筆者在執行時，曾遇過 module not found 或 .next 資料夾遺失的問題，最後乾脆把 nexe 當成替使用者自動下載 Node.js 的方案來解決。
:::