Skip to main content
Bun 的打包器实现了 --compile 标志,用于从 TypeScript 或 JavaScript 文件生成独立的二进制文件。
terminal
cli.ts
这将 cli.ts 打包成一个你可以直接运行的可执行文件:
terminal
所有导入的文件和包都被打包到可执行文件中,同时还有一份 Bun 运行时的副本。所有内置的 Bun 和 Node.js API 都受支持。

交叉编译到其他平台

使用 --target 标志将你的独立可执行文件编译为与你运行 bun build 的机器不同的操作系统、架构或 Bun 版本。 要为 Linux x64(大多数服务器)构建:
terminal
要为 Linux ARM64 构建(例如 Graviton 或 Raspberry Pi):
terminal
要为 Windows x64 构建:
terminal
要为 Windows arm64 构建:
terminal
要为 macOS arm64 构建:
terminal
要为 macOS x64 构建:
terminal

支持的目标平台

--target 值的各段可以用任何顺序出现,只要它们由 - 分隔即可。
在 x64 平台上,Bun 使用需要 AVX2 指令集的 SIMD 优化。没有这些指令的旧 CPU 应使用 -baseline 版本的 Bun。 Bun 安装程序会检测要使用的版本,但在交叉编译时你可能不知道目标 CPU。这主要在 Windows x64 和 Linux x64 上很重要,在 Darwin x64 上很少见。如果你或你的用户看到 "Illegal instruction" 错误,你可能需要使用 baseline 版本。

构建时常量

使用 --define 标志将构建时常量注入到你的可执行文件中,例如版本号、构建时间戳或配置值:
terminal
Bun 在构建时将这些常量内联到二进制文件中,因此运行时零成本,并支持死代码消除。
更多示例和模式,请参见构建时常量指南

部署到生产环境

编译后的可执行文件减少了内存使用并改善了 Bun 的启动时间。 通常,Bun 在 importrequire 时读取并转译 JavaScript 和 TypeScript 文件。这是 Bun 许多功能”开箱即用”的原因之一,但并非免费:从磁盘读取文件、解析路径、解析、转译和打印源代码都会消耗时间和内存。 编译后的可执行文件将这些成本从运行时转移到了构建时。 部署到生产环境时,我们建议:
terminal

字节码编译

为了改善启动时间,启用字节码编译:
terminal
使用字节码编译,tsc 启动快 2 倍:
字节码性能对比
字节码编译将大型输入文件的解析开销从运行时转移到打包时。你的应用启动更快,代价是让 bun build 命令稍微慢一点。它不会混淆源代码。
--compile 一起使用时,字节码编译同时支持 cjsesm 格式。

这些标志做了什么?

--minify 参数减少转译后输出代码的大小。对于大型应用,这可以节省数兆字节的空间。对于较小的应用,它可能仍然能略微改善启动时间。 --sourcemap 参数嵌入一个用 zstd 压缩的 sourcemap,这样错误和堆栈跟踪指向原始位置而不是转译后的位置。Bun 在发生错误时自动解压缩并解析 sourcemap。 --bytecode 参数启用字节码编译。每次你在 Bun 中运行 JavaScript 代码时,JavaScriptCore(引擎)都会将你的源代码编译为字节码。--bytecode 将该解析工作从运行时转移到打包时,从而缩短启动时间。

嵌入运行时参数

--compile-exec-argv="args" - 嵌入运行时参数,在运行时可通过 process.execArgv 获取:
terminal
app.ts

通过 BUN_OPTIONS 传递运行时参数

独立可执行文件会读取 BUN_OPTIONS 环境变量,因此你可以传递运行时标志而无需重新编译:
terminal

自动配置加载

独立可执行文件可以自动从运行目录加载配置文件。默认情况下:
  • tsconfig.jsonpackage.json 加载已禁用——这些通常只在开发时需要,打包器在编译时已经使用了它们
  • .envbunfig.toml 加载已启用——这些通常包含可能因部署而异的运行时配置
在未来的 Bun 版本中,.envbunfig.toml 可能也会默认禁用,以实现更确定的行为。

在运行时启用配置加载

如果你的可执行文件需要在运行时读取 tsconfig.jsonpackage.json,使用这些标志选择加入:
terminal

在运行时禁用配置加载

要禁用 .envbunfig.toml 加载以实现确定性执行:
terminal

作为 Bun CLI 运行

Bun v1.2.16 新增
设置 BUN_BE_BUN=1 环境变量,使独立可执行文件像 bun CLI 本身一样运行。可执行文件会忽略其打包的入口点,而是暴露完整的 bun CLI。 例如,考虑从这个脚本编译的可执行文件:
terminal
通常,运行 ./such-bun 时会带参数执行脚本。
terminal
然而,使用 BUN_BE_BUN=1 环境变量时,它就像 bun 二进制文件一样运行:
terminal
基于 Bun 构建的 CLI 工具可以使用此功能来安装包、打包依赖关系或运行其他文件,而无需下载单独的二进制文件或安装 Bun。

全栈可执行文件

Bun v1.2.17 新增
--compile 标志可以创建一个包含服务器和客户端代码的独立可执行文件,适用于全栈应用程序。当你在服务器代码中导入 HTML 文件时,Bun 会打包前端资源(JavaScript、CSS 等)并将其嵌入到可执行文件中。
要将其构建为单个可执行文件:
terminal
这会创建一个自包含的二进制文件,包含:
  • 你的服务器代码
  • Bun 运行时
  • 所有前端资源(HTML、CSS、JavaScript)
  • 服务器使用的任何 npm 包
结果是一个可以在任何地方部署的单一文件,无需安装 Node.js、Bun 或任何依赖:
terminal
Bun 以前端资源所需的正确 MIME 类型和缓存头提供它们。HTML 导入被替换为一个清单对象,Bun.serve 使用它来提供预打包的资源。 有关构建全栈应用的更多信息,请参阅全栈指南

Worker

要在独立可执行文件中使用 worker,将 worker 的入口点添加到构建中:
terminal
然后,在你的代码中引用 worker:
index.ts
当你向独立可执行文件添加多个入口点时,每个入口点都会被单独打包到可执行文件中。 我们将来可能会自动检测 new Worker(path) 中静态已知的路径并自动打包它们,但目前你需要将 worker 文件列为入口点,如上例所示。 如果你使用相对路径引用未包含在独立可执行文件中的文件,Bun 会相对于进程当前工作目录从磁盘加载该路径,如果文件不存在则会报错。

SQLite

你可以将 bun:sqlite 导入与 bun build --compile 一起使用。 默认情况下,数据库相对于进程的当前工作目录进行解析。
index.ts
这意味着如果可执行文件在 /usr/bin/hello 而用户终端在 /home/me/Desktop,Bun 会在 /home/me/Desktop/my.db 中查找。
terminal

嵌入资源与文件

独立可执行文件可以将文件直接嵌入到二进制文件中,因此单个可执行文件可以携带你的应用所需的图片、JSON 配置、模板或任何其他资源。

工作原理

使用 with { type: "file" } 导入属性来嵌入文件:
index.ts
导入返回一个指向嵌入文件的路径字符串。在构建时,Bun:
  1. 读取文件内容
  2. 将数据嵌入到可执行文件中
  3. 将导入替换为内部路径(以 /$bunfs/ 为前缀)
然后你可以使用 Bun.file() 或 Node.js fs API 读取这个嵌入的文件。

使用 Bun.file() 读取嵌入文件

Bun.file() 是读取嵌入文件的推荐方式:
index.ts

使用 Node.js fs 读取嵌入文件

嵌入文件与 Node.js 文件系统 API 兼容:
index.ts

实用示例

嵌入 JSON 配置文件

index.ts

在 HTTP 服务器中提供静态资源

Bun.serve() 中使用 static 路由实现高效的静态文件服务:
server.ts
Bun 自动处理静态路由的 Content-Type 头和缓存。

嵌入模板

index.ts

嵌入二进制文件

index.ts

嵌入 SQLite 数据库

要将 SQLite 数据库嵌入到编译后的可执行文件中,在导入属性中设置 type: "sqlite" 并将 embed 属性设置为 "true" 数据库文件必须已经在磁盘上存在。然后,在你的代码中导入它:
index.ts
最后,编译为独立可执行文件:
terminal
运行 bun build --compile 时,数据库文件必须存在于磁盘上。embed: "true" 属性告诉打包器将数据库内容包含在编译后的可执行文件中。当正常使用 bun run 运行时,数据库文件就像常规的 SQLite 导入一样从磁盘加载。
在编译后的可执行文件中,嵌入的数据库是可读写的,但所有更改在可执行文件退出时都会丢失(因为它存储在内存中)。

嵌入 N-API 插件

你可以将 .node 文件嵌入到可执行文件中。
index.ts
如果你使用 @mapbox/node-pre-gyp 或类似工具,.node 文件必须被直接 require,否则无法正确打包。

嵌入目录

要使用 bun build --compile 嵌入目录,在构建中包含文件模式:
terminal
然后,你可以从代码中引用这些文件:
index.ts
这是一种变通方案,我们期待用更直接的 API 替代它。

在运行时检测独立模式

使用 Bun.isStandaloneExecutable 检查当前进程是否从编译后的二进制文件运行:
index.ts
Bun.embeddedFiles.length > 0 不同,此检查不会为每个嵌入文件分配 Blob 对象,因此在嵌入大量资源的二进制文件中,在启动时调用是安全的。

列出嵌入文件

Bun.embeddedFiles 将所有嵌入文件作为 Blob 对象暴露:
index.ts
Bun.embeddedFiles 中的每一项都是一个带有 name 属性的 Blob
使用它通过 static 路由提供每个嵌入资源:
server.ts
Bun.embeddedFiles 排除打包的源代码(.ts.js 等),以帮助保护你的应用源码。

内容哈希

默认情况下,嵌入文件的名称会附加一个内容哈希,这有助于在通过 URL 或 CDN 提供时进行缓存失效。要保留原始名称,请配置资源命名:
terminal

压缩

要缩减可执行文件的大小,启用压缩:
terminal
这使用 Bun 的压缩器来减小代码大小。不过整体来说,Bun 的二进制文件仍然太大,我们需要让它变得更小。

Windows 专用标志

在 Windows 上编译独立可执行文件时,平台特定的选项可以自定义生成的 .exe 文件的元数据:
terminal
可用的 Windows 选项:
  • icon - 可执行文件图标的 .ico 文件路径
  • hideConsole - 禁用后台终端(用于 GUI 应用)
  • title - 文件属性中的应用程序标题
  • publisher - 文件属性中的发布者名称
  • version - 文件属性中的版本字符串
  • description - 文件属性中的描述
  • copyright - 文件属性中的版权声明
这些标志在交叉编译时不能使用,因为它们依赖于 Windows API。

macOS 上的代码签名

要在 macOS 上对独立可执行文件进行代码签名(修复 Gatekeeper 警告),使用 codesign 命令。
terminal
我们建议包含一个带有 JIT 权限的 entitlements.plist 文件。
info.plist
要使用 JIT 支持进行代码签名,将 --entitlements 标志传递给 codesign
terminal
代码签名后,验证可执行文件:
terminal
代码签名支持需要 Bun v1.2.4 或更新版本。

代码分割

独立可执行文件支持代码分割。使用 --compile--splitting 创建一个可执行文件,在运行时加载代码分割的块。
terminal
代码分割的块会紧挨着可执行文件生成。注意,代码分割的块不会被嵌入到可执行文件中——它们作为独立的文件保留在文件系统上。

异步插件

你可以在 Bun.build({ plugins }) 中使用插件和 --compile。这使你可以在构建时应用自定义转换。
build.ts
用例示例——在构建时嵌入环境配置:
cli.ts
插件可以执行任何转换:编译 YAML/TOML 配置、内联 SQL 查询、生成类型安全的 API 客户端或预处理模板。请参阅插件文档

不支持的 CLI 参数

--compile 标志不支持以下参数:
  • --outdir —— 改用 outfile
  • --public-path
  • --target=node
  • --target=browser(不带 HTML 入口点——请参阅独立 HTML 了解带 .html 文件的 --compile --target=browser
  • --no-bundle——Bun 总是将所有内容打包到可执行文件中。

API 参考

Bun.build() 中的 compile 选项接受三种形式:
types
使用形式:

支持的目标

Bun.Build.CompileTarget

完整示例

build.ts