Skip to content

Bun 的打包器實現了 --compile 標志,用於從 TypeScript 或 JavaScript 文件生成獨立二進制文件。

bash
bun build ./cli.ts --compile --outfile mycli
ts
console.log("Hello world!");

這將 cli.ts 打包為一個可直接執行的可執行文件:

bash
./mycli
txt
Hello world!

所有導入的文件和包都與 Bun 運行時的副本一起打包到可執行文件中。所有內置的 Bun 和 Node.js API 都受支持。


跨平台編譯

--target 標志允許您為不同於運行 bun build 的機器的操作系統、架構或 Bun 版本編譯獨立可執行文件。

為 Linux x64(大多數服務器)構建:

bash
bun build --compile --target=bun-linux-x64 ./index.ts --outfile myapp

# 要支持 2013 年之前的 CPU,使用 baseline 版本(nehalem)
bun build --compile --target=bun-linux-x64-baseline ./index.ts --outfile myapp

# 要僅支持 2013 年及之後的 CPU,使用 modern 版本(haswell)
# modern 更快,但 baseline 更兼容。
bun build --compile --target=bun-linux-x64-modern ./index.ts --outfile myapp

為 Linux ARM64(如 Graviton 或 Raspberry Pi)構建:

bash
# 注意:如果未指定架構,默認為 x64。
bun build --compile --target=bun-linux-arm64 ./index.ts --outfile myapp

為 Windows x64 構建:

bash
bun build --compile --target=bun-windows-x64 ./path/to/my/app.ts --outfile myapp

# 要支持 2013 年之前的 CPU,使用 baseline 版本(nehalem)
bun build --compile --target=bun-windows-x64-baseline ./path/to/my/app.ts --outfile myapp

# 要僅支持 2013 年及之後的 CPU,使用 modern 版本(haswell)
bun build --compile --target=bun-windows-x64-modern ./path/to/my/app.ts --outfile myapp

# 注意:如果未提供 .exe 擴展名,Bun 會自動為 Windows 可執行文件添加它

為 macOS arm64 構建:

bash
bun build --compile --target=bun-darwin-arm64 ./path/to/my/app.ts --outfile myapp

為 macOS x64 構建:

bash
bun build --compile --target=bun-darwin-x64 ./path/to/my/app.ts --outfile myapp

支持的目標

--target 標志的順序無關緊要,只要它們用 - 分隔。

--target操作系統架構ModernBaselineLibc
bun-linux-x64Linuxx64glibc
bun-linux-arm64Linuxarm64N/Aglibc
bun-windows-x64Windowsx64-
bun-windows-arm64Windowsarm64-
bun-darwin-x64macOSx64-
bun-darwin-arm64macOSarm64N/A-
bun-linux-x64-muslLinuxx64musl
bun-linux-arm64-muslLinuxarm64N/Amusl

構建時常量

使用 --define 標志將構建時常量注入到您的可執行文件中,如版本號、構建時間戳或配置值:

bash
bun build --compile --define BUILD_VERSION='"1.2.3"' --define BUILD_TIME='"2024-01-15T10:30:00Z"' src/cli.ts --outfile mycli

這些常量直接嵌入到編譯後的二進制文件中,提供零運行時開銷並支持死代碼消除優化。

NOTE

有關綜合示例和高級模式,請參閱 [構建時常量指南](/guides/runtime/build-time-constants)。

部署到生產環境

編譯後的可執行文件可減少內存使用並提高 Bun 的啟動時間。

通常,Bun 在 importrequire 時讀取並轉譯 JavaScript 和 TypeScript 文件。這是讓 Bun 的許多功能"開箱即用"的部分原因,但它不是免費的。從磁盤讀取文件、解析文件路徑、解析、轉譯和打印源代碼需要時間和內存。

使用編譯後的可執行文件,您可以將該成本從運行時轉移到構建時。

部署到生產環境時,我們推薦以下內容:

bash
bun build --compile --minify --sourcemap ./path/to/my/app.ts --outfile myapp

字節碼編譯

要提高啟動時間,啟用字節碼編譯:

bash
bun build --compile --minify --sourcemap --bytecode ./path/to/my/app.ts --outfile myapp

使用字節碼編譯,tsc 啟動速度提高 2 倍:

字節碼編譯將大型輸入文件的解析開銷從運行時移動到打包時間。您的應用程序啟動更快,以 bun build 命令稍慢為代價。它不會混淆源代碼。

這些標志的作用是什麼?

--minify 參數優化轉譯輸出代碼的大小。如果您有大型應用程序,這可以節省數兆字節的空間。對於較小的應用程序,它可能仍然會稍微改善啟動時間。

--sourcemap 參數嵌入使用 zstd 壓縮的 sourcemap,因此錯誤和堆棧跟蹤指向其原始位置而不是轉譯位置。當發生錯誤時,Bun 會自動解壓縮和解析 sourcemap。

--bytecode 參數啟用字節碼編譯。每次在 Bun 中運行 JavaScript 代碼時,JavaScriptCore(引擎)都會將源代碼編譯為字節碼。我們可以將這項解析工作從運行時移動到打包時間,為您節省啟動時間。


嵌入運行時參數

--compile-exec-argv="args" - 嵌入可通過 process.execArgv 使用的運行時參數:

bash
bun build --compile --compile-exec-argv="--smol --user-agent=MyBot" ./app.ts --outfile myapp
ts
// 在編譯後的應用中
console.log(process.execArgv); // ["--smol", "--user-agent=MyBot"]

禁用自動配置加載

默認情況下,獨立可執行文件在運行可執行文件的目錄中查找 .envbunfig.toml 文件。您可以在構建時禁用此行為,以實現確定性執行,無論用戶的工作目錄如何。

bash
# 禁用 .env 加載
bun build --compile --no-compile-autoload-dotenv ./app.ts --outfile myapp

# 禁用 bunfig.toml 加載
bun build --compile --no-compile-autoload-bunfig ./app.ts --outfile myapp

# 禁用兩者
bun build --compile --no-compile-autoload-dotenv --no-compile-autoload-bunfig ./app.ts --outfile myapp

您也可以通過 JavaScript API 配置此功能:

ts
await Bun.build({
  entrypoints: ["./app.ts"],
  compile: {
    autoloadDotenv: false, // 禁用 .env 加載
    autoloadBunfig: false, // 禁用 bunfig.toml 加載
  },
});

作為 Bun CLI 運行

NOTE

Bun v1.2.16 新增

通過設置 BUN_BE_BUN=1 環境變量,您可以將獨立可執行文件作為 bun CLI 本身運行。設置此變量時,可執行文件將忽略其捆綁的入口點,而是暴露 Bun CLI 的所有功能。

例如,考慮從簡單腳本編譯的可執行文件:

bash
echo "console.log(\"you shouldn't see this\");" > such-bun.js
bun build --compile ./such-bun.js
txt
[3ms] bundle 1 modules
[89ms] compile such-bun

通常,使用參數運行 ./such-bun 將執行腳本。

bash
# 可執行文件默認運行其自己的入口點
./such-bun install
txt
you shouldn't see this

但是,使用 BUN_BE_BUN=1 環境變量,它的作用就像 bun 二進制文件:

bash
# 使用環境變量,可執行文件的作用像 `bun` CLI
BUN_BE_BUN=1 ./such-bun install
txt
bun install v1.2.16-canary.1 (1d1db811)
Checked 63 installs across 64 packages (no changes) [5.00ms]

這對於在 Bun 之上構建可能需要安裝包、打包依賴項、運行不同或本地文件等的 CLI 工具很有用,而無需下載單獨的二進制文件或安裝 bun。


全棧可執行文件

NOTE

Bun v1.2.17 新增

Bun 的 --compile 標志可以創建包含服務器和客戶端代碼的獨立可執行文件,使其非常適合全棧應用程序。當您在服務器代碼中導入 HTML 文件時,Bun 會自動打包所有前端資源(JavaScript、CSS 等)並將它們嵌入到可執行文件中。當 Bun 在服務器上看到 HTML 導入時,它會啟動前端打包過程來打包 JavaScript、CSS 和其他資源。

ts
import { serve } from "bun";
import index from "./index.html";

const server = serve({
  routes: {
    "/": index,
    "/api/hello": { GET: () => Response.json({ message: "Hello from API" }) },
  },
});

console.log(`Server running at http://localhost:${server.port}`);
html
<!DOCTYPE html>
<html>
  <head>
    <title>My App</title>
    <link rel="stylesheet" href="./styles.css" />
  </head>
  <body>
    <h1>Hello World</h1>
    <script src="./app.ts"></script>
  </body>
</html>
ts
console.log("Hello from the client!");
css
body {
  background-color: #f0f0f0;
}

要將此構建為單個可執行文件:

bash
bun build --compile ./server.ts --outfile myapp

這將創建一個自包含的二進制文件,包括:

  • 您的服務器代碼
  • Bun 運行時
  • 所有前端資源(HTML、CSS、JavaScript)
  • 服務器使用的任何 npm 包

結果是單個文件,可以在任何地方部署,無需安裝 Node.js、Bun 或任何依賴項。只需運行:

bash
./myapp

Bun 自動處理使用適當的 MIME 類型和緩存頭提供前端資源。HTML 導入被替換為清單對象,Bun.serve 使用該對象有效地提供預打包資源。

有關使用 Bun 構建全棧應用程序的更多詳細信息,請參閱 全棧指南


Worker

要在獨立可執行文件中使用 worker,將 worker 的入口點添加到 CLI 參數:

bash
bun build --compile ./index.ts ./my-worker.ts --outfile myapp

然後,在您的代碼中引用 worker:

ts
console.log("Hello from Bun!");

// 這些都可以工作:
new Worker("./my-worker.ts");
new Worker(new URL("./my-worker.ts", import.meta.url));
new Worker(new URL("./my-worker.ts", import.meta.url).href);

當您將多個入口點添加到獨立可執行文件時,它們將分別打包到可執行文件中。

將來,我們可能會自動檢測 new Worker(path) 中靜態已知路徑的使用,然後將它們打包到可執行文件中,但目前,您需要像上面的示例一樣手動將其添加到 shell 命令中。

如果您使用相對路徑引用未包含在獨立可執行文件中的文件,它將嘗試從進程當前工作目錄相對於磁盤加載該路徑(如果不存在則出錯)。


SQLite

您可以在 bun build --compile 中使用 bun:sqlite 導入。

默認情況下,數據庫相對於進程的當前工作目錄解析。

ts
import db from "./my.db" with { type: "sqlite" };

console.log(db.query("select * from users LIMIT 1").get());

這意味著如果可執行文件位於 /usr/bin/hello,用戶的終端位於 /home/me/Desktop,它將查找 /home/me/Desktop/my.db

bash
cd /home/me/Desktop
./hello

嵌入資源和文件

獨立可執行文件支持嵌入文件。

要使用 bun build --compile 將文件嵌入到可執行文件中,請在您的代碼中導入文件。

ts
// 這成為內部文件路徑
import icon from "./icon.png" with { type: "file" };
import { file } from "bun";

export default {
  fetch(req) {
    // 嵌入的文件可以從 Response 對象流式傳輸
    return new Response(file(icon));
  },
};

嵌入的文件可以使用 Bun.file 的函數或 Node.js fs.readFile 函數(在 "node:fs" 中)讀取。

例如,要讀取嵌入文件的內容:

ts
import icon from "./icon.png" with { type: "file" };
import { file } from "bun";

const bytes = await file(icon).arrayBuffer();
// await fs.promises.readFile(icon)
// fs.readFileSync(icon)

嵌入 SQLite 數據庫

如果您的應用程序想要嵌入 SQLite 數據庫,請在導入屬性中設置 type: "sqlite"embed 屬性為 "true"

ts
import myEmbeddedDb from "./my.db" with { type: "sqlite", embed: "true" };

console.log(myEmbeddedDb.query("select * from users LIMIT 1").get());

此數據庫是可讀寫的,但所有更改在可執行文件退出時都會丟失(因為它存儲在內存中)。

嵌入 N-API 插件

您可以將 .node 文件嵌入到可執行文件中。

ts
const addon = require("./addon.node");

console.log(addon.hello());

不幸的是,如果您使用 @mapbox/node-pre-gyp 或其他類似工具,您需要確保直接 require .node 文件,否則它將無法正確打包。

嵌入目錄

要使用 bun build --compile 嵌入目錄,請在 bun build 命令中使用 shell glob:

bash
bun build --compile ./index.ts ./public/**/*.png

然後,您可以在代碼中引用文件:

ts
import icon from "./public/assets/icon.png" with { type: "file" };
import { file } from "bun";

export default {
  fetch(req) {
    // 嵌入的文件可以從 Response 對象流式傳輸
    return new Response(file(icon));
  },
};

老實說,這是一種變通方法,我們期望將來通過更直接的 API 來改進這一點。

列出嵌入的文件

要獲取所有嵌入文件的列表,請使用 Bun.embeddedFiles

ts
import "./icon.png" with { type: "file" };
import { embeddedFiles } from "bun";

console.log(embeddedFiles[0].name); // `icon-${hash}.png`

Bun.embeddedFiles 返回 Blob 對象數組,您可以使用它們獲取文件的大小、內容和其他屬性。

ts
embeddedFiles: Blob[]

嵌入文件列表不包括捆綁的源代碼,如 .ts.js 文件。

內容哈希

默認情況下,嵌入的文件名稱附加了內容哈希。這對於想要從 URL 或 CDN 提供文件並希望減少緩存失效問題的情況很有用。但有時,這是意外的,您可能想要原始名稱:

要禁用內容哈希,將 --asset-naming 傳遞給 bun build --compile,如下所示:

bash
bun build --compile --asset-naming="[name].[ext]" ./index.ts

壓縮

要稍微減小可執行文件的大小,將 --minify 傳遞給 bun build --compile。這使用 Bun 的壓縮器來減少代碼大小。但總體而言,Bun 的二進制文件仍然太大,我們需要使其更小。


Windows 特定標志

在 Windows 上編譯獨立可執行文件時,有兩個特定於平台的選項可用於自定義生成的 .exe 文件的元數據:

  • --windows-icon=path/to/icon.ico 用於自定義可執行文件圖標。
  • --windows-hide-console 用於禁用後台終端,可用於不需要 TTY 的應用程序。

macOS 上的代碼簽名

要在 macOS 上對獨立可執行文件進行代碼簽名(修復 Gatekeeper 警告),請使用 codesign 命令。

bash
codesign --deep --force -vvvv --sign "XXXXXXXXXX" ./myapp

我們建議包含具有 JIT 權限的 entitlements.plist 文件。

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.cs.allow-jit</key>
    <true/>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
    <key>com.apple.security.cs.disable-executable-page-protection</key>
    <true/>
    <key>com.apple.security.cs.allow-dyld-environment-variables</key>
    <true/>
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>
</dict>
</plist>

要使用 JIT 支持進行代碼簽名,將 --entitlements 標志傳遞給 codesign

bash
codesign --deep --force -vvvv --sign "XXXXXXXXXX" --entitlements entitlements.plist ./myapp

代碼簽名後,驗證可執行文件:

bash
codesign -vvv --verify ./myapp
./myapp: valid on disk
./myapp: satisfies its Designated Requirement

代碼分割

獨立可執行文件支持代碼分割。使用 --compile--splitting 創建在運行時加載代碼分割塊的可執行文件。

bash
bun build --compile --splitting ./src/entry.ts --outdir ./build
ts
console.log("Entrypoint loaded");
const lazy = await import("./lazy.ts");
lazy.hello();
ts
export function hello() {
  console.log("Lazy module loaded");
}
bash
./build/entry
txt
Entrypoint loaded
Lazy module loaded

不支持的 CLI 參數

目前,--compile 標志一次只能接受單個入口點,不支持以下標志:

  • --outdir — 改用 outfile(除非與 --splitting 一起使用)。
  • --public-path
  • --target=node--target=browser
  • --no-bundle - 我們總是將所有內容打包到可執行文件中。

Bun學習網由www.bunjs.com.cn整理維護