Bun 的打包器實現了 --compile 標志,用於從 TypeScript 或 JavaScript 文件生成獨立二進制文件。
bun build ./cli.ts --compile --outfile mycliconsole.log("Hello world!");這將 cli.ts 打包為一個可直接執行的可執行文件:
./mycliHello world!所有導入的文件和包都與 Bun 運行時的副本一起打包到可執行文件中。所有內置的 Bun 和 Node.js API 都受支持。
跨平台編譯
--target 標志允許您為不同於運行 bun build 的機器的操作系統、架構或 Bun 版本編譯獨立可執行文件。
為 Linux x64(大多數服務器)構建:
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)構建:
# 注意:如果未指定架構,默認為 x64。
bun build --compile --target=bun-linux-arm64 ./index.ts --outfile myapp為 Windows x64 構建:
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 構建:
bun build --compile --target=bun-darwin-arm64 ./path/to/my/app.ts --outfile myapp為 macOS x64 構建:
bun build --compile --target=bun-darwin-x64 ./path/to/my/app.ts --outfile myapp支持的目標
--target 標志的順序無關緊要,只要它們用 - 分隔。
| --target | 操作系統 | 架構 | Modern | Baseline | Libc |
|---|---|---|---|---|---|
| bun-linux-x64 | Linux | x64 | ✅ | ✅ | glibc |
| bun-linux-arm64 | Linux | arm64 | ✅ | N/A | glibc |
| bun-windows-x64 | Windows | x64 | ✅ | ✅ | - |
| ❌ | ❌ | - | |||
| bun-darwin-x64 | macOS | x64 | ✅ | ✅ | - |
| bun-darwin-arm64 | macOS | arm64 | ✅ | N/A | - |
| bun-linux-x64-musl | Linux | x64 | ✅ | ✅ | musl |
| bun-linux-arm64-musl | Linux | arm64 | ✅ | N/A | musl |
構建時常量
使用 --define 標志將構建時常量注入到您的可執行文件中,如版本號、構建時間戳或配置值:
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 在 import 和 require 時讀取並轉譯 JavaScript 和 TypeScript 文件。這是讓 Bun 的許多功能"開箱即用"的部分原因,但它不是免費的。從磁盤讀取文件、解析文件路徑、解析、轉譯和打印源代碼需要時間和內存。
使用編譯後的可執行文件,您可以將該成本從運行時轉移到構建時。
部署到生產環境時,我們推薦以下內容:
bun build --compile --minify --sourcemap ./path/to/my/app.ts --outfile myapp字節碼編譯
要提高啟動時間,啟用字節碼編譯:
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 使用的運行時參數:
bun build --compile --compile-exec-argv="--smol --user-agent=MyBot" ./app.ts --outfile myapp// 在編譯後的應用中
console.log(process.execArgv); // ["--smol", "--user-agent=MyBot"]禁用自動配置加載
默認情況下,獨立可執行文件在運行可執行文件的目錄中查找 .env 和 bunfig.toml 文件。您可以在構建時禁用此行為,以實現確定性執行,無論用戶的工作目錄如何。
# 禁用 .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 配置此功能:
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 的所有功能。
例如,考慮從簡單腳本編譯的可執行文件:
echo "console.log(\"you shouldn't see this\");" > such-bun.js
bun build --compile ./such-bun.js[3ms] bundle 1 modules
[89ms] compile such-bun通常,使用參數運行 ./such-bun 將執行腳本。
# 可執行文件默認運行其自己的入口點
./such-bun installyou shouldn't see this但是,使用 BUN_BE_BUN=1 環境變量,它的作用就像 bun 二進制文件:
# 使用環境變量,可執行文件的作用像 `bun` CLI
BUN_BE_BUN=1 ./such-bun installbun 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 和其他資源。
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}`);<!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>console.log("Hello from the client!");body {
background-color: #f0f0f0;
}要將此構建為單個可執行文件:
bun build --compile ./server.ts --outfile myapp這將創建一個自包含的二進制文件,包括:
- 您的服務器代碼
- Bun 運行時
- 所有前端資源(HTML、CSS、JavaScript)
- 服務器使用的任何 npm 包
結果是單個文件,可以在任何地方部署,無需安裝 Node.js、Bun 或任何依賴項。只需運行:
./myappBun 自動處理使用適當的 MIME 類型和緩存頭提供前端資源。HTML 導入被替換為清單對象,Bun.serve 使用該對象有效地提供預打包資源。
有關使用 Bun 構建全棧應用程序的更多詳細信息,請參閱 全棧指南。
Worker
要在獨立可執行文件中使用 worker,將 worker 的入口點添加到 CLI 參數:
bun build --compile ./index.ts ./my-worker.ts --outfile myapp然後,在您的代碼中引用 worker:
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 導入。
默認情況下,數據庫相對於進程的當前工作目錄解析。
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。
cd /home/me/Desktop
./hello嵌入資源和文件
獨立可執行文件支持嵌入文件。
要使用 bun build --compile 將文件嵌入到可執行文件中,請在您的代碼中導入文件。
// 這成為內部文件路徑
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" 中)讀取。
例如,要讀取嵌入文件的內容:
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"。
import myEmbeddedDb from "./my.db" with { type: "sqlite", embed: "true" };
console.log(myEmbeddedDb.query("select * from users LIMIT 1").get());此數據庫是可讀寫的,但所有更改在可執行文件退出時都會丟失(因為它存儲在內存中)。
嵌入 N-API 插件
您可以將 .node 文件嵌入到可執行文件中。
const addon = require("./addon.node");
console.log(addon.hello());不幸的是,如果您使用 @mapbox/node-pre-gyp 或其他類似工具,您需要確保直接 require .node 文件,否則它將無法正確打包。
嵌入目錄
要使用 bun build --compile 嵌入目錄,請在 bun build 命令中使用 shell glob:
bun build --compile ./index.ts ./public/**/*.png然後,您可以在代碼中引用文件:
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:
import "./icon.png" with { type: "file" };
import { embeddedFiles } from "bun";
console.log(embeddedFiles[0].name); // `icon-${hash}.png`Bun.embeddedFiles 返回 Blob 對象數組,您可以使用它們獲取文件的大小、內容和其他屬性。
embeddedFiles: Blob[]嵌入文件列表不包括捆綁的源代碼,如 .ts 和 .js 文件。
內容哈希
默認情況下,嵌入的文件名稱附加了內容哈希。這對於想要從 URL 或 CDN 提供文件並希望減少緩存失效問題的情況很有用。但有時,這是意外的,您可能想要原始名稱:
要禁用內容哈希,將 --asset-naming 傳遞給 bun build --compile,如下所示:
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 命令。
codesign --deep --force -vvvv --sign "XXXXXXXXXX" ./myapp我們建議包含具有 JIT 權限的 entitlements.plist 文件。
<?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。
codesign --deep --force -vvvv --sign "XXXXXXXXXX" --entitlements entitlements.plist ./myapp代碼簽名後,驗證可執行文件:
codesign -vvv --verify ./myapp
./myapp: valid on disk
./myapp: satisfies its Designated Requirement代碼分割
獨立可執行文件支持代碼分割。使用 --compile 與 --splitting 創建在運行時加載代碼分割塊的可執行文件。
bun build --compile --splitting ./src/entry.ts --outdir ./buildconsole.log("Entrypoint loaded");
const lazy = await import("./lazy.ts");
lazy.hello();export function hello() {
console.log("Lazy module loaded");
}./build/entryEntrypoint loaded
Lazy module loaded不支持的 CLI 參數
目前,--compile 標志一次只能接受單個入口點,不支持以下標志:
--outdir— 改用outfile(除非與--splitting一起使用)。--public-path--target=node或--target=browser--no-bundle- 我們總是將所有內容打包到可執行文件中。