Skip to main content
使用 Bun 的原生打包器,通过 bun build CLI 命令或 Bun.build() JavaScript API。

一览

  • JS API:await Bun.build({ entrypoints, outdir })
  • CLI:bun build <entry> --outdir ./out
  • 监视模式:--watch 用于增量重新构建
  • 目标:--target browser|bun|node
  • 格式:--format esm|cjs|iife(cjs/iife 为实验性)
build.ts
它很快。以下数据来自 esbuild 的 three.js 基准测试

为什么要打包?

打包器解决了几个问题:
  • 减少 HTTP 请求。 node_modules 中的单个包可能包含数百个文件,大型应用可能有几十个这样的依赖。用单独的 HTTP 请求加载每个文件是不可行的,因此打包器将你的应用源代码转换为数量更少的自包含”包”,可以通过单个请求加载。
  • 代码转换。 现代应用通常使用 TypeScript、JSX 和 CSS 模块等语言或工具构建,所有这些在浏览器消费之前必须转换为普通 JavaScript 和 CSS。打包器是配置这些转换的自然位置。
  • 框架功能。 框架依赖打包器插件和代码转换来实现常见模式,如文件系统路由、客户端-服务器代码共置(比如 getServerSideProps 或 Remix loader)以及服务器组件。
  • 全栈应用。 Bun 的打包器可以在单个命令中处理服务器和客户端代码,实现优化后的生产构建和单文件可执行文件。通过构建时的 HTML 导入,你可以将整个应用程序(前端资源和后端服务器)打包到一个可部署的单元中。
Bun 打包器不打算替代 tsc 进行类型检查或生成类型声明。

基本示例

构建你的第一个包。你有以下两个文件,实现了一个客户端渲染的 React 应用。
这里,index.tsx 是应用的”入口点”:打包器开始的文件。通常这是一个执行某些副作用的脚本,比如启动服务器,或者在本例中初始化 React 根节点。因为这些文件使用了 TypeScript 和 JSX,代码必须先打包才能发送到浏览器。 要创建包:
对于 entrypoints 中指定的每个文件,Bun 生成一个新的包并将其写入 ./out 目录(从当前工作目录解析)。运行构建后,文件系统看起来像这样:
file system
out/index.js 的内容看起来像这样:
out/index.js

监视模式

与运行时和测试运行器一样,打包器原生支持监视模式。
terminal

内容类型

与 Bun 运行时一样,打包器默认支持一系列文件类型。下表列出了打包器的标准”加载器”。请参见加载器

资源

如果打包器遇到无法识别的扩展名的导入,它会将导入的文件视为外部文件。引用的文件被原样复制到 outdir 中,导入被解析为文件的路径。
文件加载器的确切行为还取决于 namingpublicPath
关于文件加载器的更多信息,请参见加载器

插件

插件可以覆盖或扩展此表中描述的行为。请参见加载器

API

entrypoints

必需 对应应用入口点的路径数组。Bun 为每个入口点生成一个包。
build.ts

files

一个文件路径到文件内容的映射,用于内存中打包:打包磁盘上不存在的虚拟文件,或覆盖确实存在的文件内容。此选项仅在 JavaScript API 中可用。 文件内容可以作为 stringBlobTypedArrayArrayBuffer 提供。

完全从内存中打包

你可以在没有任何磁盘文件的情况下打包代码,方法是在 files 中提供所有源代码:
build.ts
当所有入口点都在 files 映射中时,当前工作目录被用作根目录。

覆盖磁盘上的文件

内存中的文件优先于磁盘上的文件,因此你可以在保持代码库其余部分不变的同时覆盖特定文件:
build.ts

混合磁盘和虚拟文件

磁盘上的真实文件可以导入虚拟文件,虚拟文件也可以导入真实文件:
build.ts
用于代码生成、注入构建时常量或使用模拟模块进行测试。

outdir

输出文件写入的目录。
build.ts
如果 JavaScript API 没有传入 outdir,打包后的代码不会写入磁盘。打包后的文件以 BuildArtifact 对象数组的形式返回。这些对象是带有额外属性的 Blob;请参见输出
build.ts
当设置了 outdir 时,BuildArtifact 上的 path 属性是其写入的绝对路径。

target

打包产物的预期执行环境。
build.ts
根据目标的不同,Bun 会应用不同的模块解析规则和优化。

browser

默认。 适用于在浏览器中运行的包。解析导入时优先考虑 "browser" 导出条件。 导入内置模块如 node:eventsnode:path 可以工作,但调用某些函数,如 fs.readFile, 则不行。

bun

适用于在 Bun 运行时中运行的包。在许多情况下,没有必要打包服务器端代码;你可以直接执行源代码而无需修改。然而,打包你的服务器代码可以减少启动时间并提高运行性能。将此目标用于具有构建时 HTML 导入的全栈应用,其中服务器和客户端代码被打包在一起。所有以 target: "bun" 生成的包都带有 // @bun 编译指示,这告诉 Bun 运行时在执 行前无需重新转译文件。如果任何入口点包含 Bun shebang(#!/usr/bin/env bun),打包器默认使用 target: "bun" 而不是 "browser"当同时使用 target: "bun"format: "cjs" 时,会添加 // @bun @bun-cjs 编译指示,并且 CommonJS 包装函数与 Node.js 不兼容。

node

适用于在 Node.js 中运行的包。解析导入时优先考虑 "node" 导出条件。Bun 不会 polyfill Bun 全局变量或内置的 bun:* 模块。

format

指定生成打包产物的模块格式。 Bun 默认使用 "esm",并提供了对 "cjs""iife" 的实验性支持。

format: “esm” - ES Module

默认格式。支持 ES Module 语法,包括顶层 await 和 import.meta
build.ts
要在浏览器中使用 ES Module 语法,将 format 设置为 "esm",并使用 <script type="module"> 标签加载包。

format: “cjs” - CommonJS

要构建 CommonJS 模块,将 format 设置为 "cjs"。选择 "cjs" 时,默认目标从 "browser"(esm)变为 "node"(cjs)。使用 format: "cjs"target: "node" 转译的 CommonJS 模块在 Bun 和 Node.js 中都运行(假设使用的 API 两者都支持)。
build.ts

format: “iife” - IIFE

TODO:在支持 globalNames 后记录 IIFE。

jsx

配置 JSX 的编译方式。 经典运行时示例(使用 factoryfragment):
自动运行时示例(使用 importSource):

splitting

是否启用代码分割。
build.ts
当为 true 时,打包器启用代码分割。当多个入口点导入相同的文件或模块时,打包器可以将共享代码分割为单独的包,称为。考虑以下文件:
要打包启用了代码分割的 entry-a.tsentry-b.ts
build.ts
运行此构建会生成以下文件:
file system
生成的 chunk-2fce6291bf86559d.js 文件包含共享代码。为避免冲突,文件名默认包含内容哈希。使用 naming 自定义。

plugins

在打包期间使用的插件列表。
build.ts
Bun 的插件系统由运行时和打包器共享。请参见插件

env

控制打包期间环境变量的处理方式。内部使用 define 将环境变量注入打包产物;env 是指定哪些变量的简写。

env: “inline”

通过将 process.env.FOO 引用转换为包含实际环境变量值的字符串字面量,将环境变量注入打包输出。
build.ts
对于以下输入:
input.js
生成的包包含以下代码:
output.js

env: “PUBLIC_*“(前缀)

内联匹配给定前缀(* 字符之前的部分)的环境变量,将 process.env.FOO 替换为实际的环境变量值。使用前缀可只内联公共值,如面向外部的 URL 或客户端令牌,而无需将私有凭据注入输出包中。
build.ts
例如,给定以下环境变量:
terminal
以及源代码:
index.tsx
生成的包包含以下代码:
output.js

env: “disable”

完全禁用环境变量注入。

sourcemap

指定生成的 sourcemap 类型。
build.ts
相关的 *.js.map sourcemap 是一个 JSON 文件,包含等效的 debugId 属性。

minify

是否启用压缩。默认为 false 要启用所有压缩选项:
build.ts
要精细启用某些压缩:
build.ts

external

要视为外部的导入路径列表。默认为 []
build.ts
外部导入不会包含在最终包中。相反,导入语句原样保留,在运行时解析。 例如,考虑以下入口点文件:
index.tsx
通常,打包 index.tsx 会生成包含 “zod” 包完整源代码的包。要原样保留导入语句,将其标记为外部:
build.ts
生成的包看起来像这样:
out/index.js
要将所有导入标记为外部,使用通配符 *
build.ts

packages

控制是否在包中包含包依赖项。可能的值:bundle(默认)、external。Bun 将任何路径不以 .../ 开头的导入视为包。
build.ts

naming

自定义生成的文件名。默认为 ./[dir]/[name].[ext]
build.ts
默认情况下,生成的包名称基于关联入口点的名称。
file system
对于多个入口点,生成的文件层次结构反映入口点的目录结构。
file system
naming 字段自定义生成文件的名称和位置。它接受一个模板字符串,用于所有对应入口点的包,其中以下令牌被替换为其值:
  • [name] - 入口点文件的名称,不带扩展名。
  • [ext] - 生成包的扩展名。
  • [hash] - 包内容的哈希值。
  • [dir] - 从项目根目录到源文件父目录的相对路径。
例如: 组合这些令牌以创建模板字符串。例如,在生成的包名称中包含哈希:
build.ts
此构建将产生以下文件结构:
file system
当为 naming 字段提供字符串时,仅用于对应入口点的包。块和复制资源的名称不受影响。在 JavaScript API 中,你可以为每种类型的生成文件指定单独的模板字符串。
build.ts

root

项目的根目录。
build.ts
如果未指定,它会计算为所有入口点文件的第一个公共祖先。考虑以下文件结构:
file system
构建 pages 目录中的两个入口点:
这将产生如下文件结构:
file system
由于 pages 目录是入口点文件的第一个公共祖先,它被视为项目根目录,因此生成的包位于 out 目录的顶层;没有 out/pages 目录。 通过指定 root 选项覆盖此行为:
.root,生成的文件结构如下:

publicPath

添加到打包代码中任何导入路径的前缀。 在许多情况下,生成的包不包含导入语句;打包的目标是将所有代码合并到一个文件中。然而,在少数情况下,生成的包确实包含导入语句:
  • 资源导入 — 当导入无法识别的文件类型如 *.svg 时,打包器委托文件加载器处理,该加载器将文件原样复制到 outdir 中。导入被转换为变量。
  • 外部模块 — 标记为外部的文件和模块不会包含在包中。相反,导入语句保留在最终包中。
  • 分块。当启用 splitting 时,打包器可能会生成代表多个入口点间共享代码的单独”块”文件。
在任何这些情况下,最终包可能包含指向其他文件的路径。默认情况下,这些导入是相对的。以下是一个资源导入的示例:
设置 publicPath 会为所有文件路径添加指定值作为前缀。
build.ts
输出文件现在看起来像这样:
out/index.js

define

要在构建时替换的全局标识符映射。此对象的键是标识符名称,值是内联的 JSON 字符串。
build.ts

loader

文件扩展到内置加载器名称的映射。用于自定义某些文件的加载方式。
build.ts
添加到最终包的 banner。可以是像 React 的 "use client" 这样的指令,或像许可证这样的注释块。
build.ts
添加到最终包的 footer。可以是许可证的注释块或有趣的彩蛋。
build.ts

drop

从包中移除函数调用。例如,--drop=console 移除对 console.log 的所有调用。被丢弃调用的参数也会被移除,即使它们有副作用。丢弃 debugger 会移除所有 debugger 语句。
build.ts

features

为死代码消除启用编译时特性标志:使用 import { feature } from "bun:bundle" 在打包时有条件地包含或排除代码路径。
app.ts
build.ts
feature() 函数在打包时被替换为 truefalse。结合压缩,不可达代码被消除:
输入
输出(带 --feature PREMIUM --minify)
输出(不带 --feature PREMIUM,带 --minify)
关键行为:
  • feature() 需要字符串字面量参数——不支持动态值
  • bun:bundle 导入完全从输出中移除
  • 适用于 bun buildbun runbun test
  • 多个标志可以同时启用:--feature FLAG_A --feature FLAG_B
  • 对于类型安全,扩展 Registry 接口以将 feature() 限制为已知标志
用例:
  • 平台特定代码(feature("SERVER") vs feature("CLIENT")
  • 基于环境的功能(feature("DEVELOPMENT")
  • 渐进式功能发布
  • A/B 测试变体
  • 付费层级功能
类型安全: 默认情况下,feature() 接受任何字符串。要获得自动补全并在编译时捕获拼写错误,创建一个 env.d.ts 文件(或添加到现有的 .d.ts)并扩展 Registry 接口:
env.d.ts
确保该文件包含在你的 tsconfig.json 中(例如,"include": ["src", "env.d.ts"])。现在 feature() 只接受这些标志,像 feature("TYPO") 这样的无效字符串会变成类型错误。

optimizeImports

跳过对 barrel 文件(重新导出的索引文件)中未使用子模块的解析。当你从大型库中只导入少量命名导出时,通常打包器会解析 barrel 重新导出的每个文件。使用 optimizeImports,只解析你使用的子模块。
build.ts
例如,import { Button } from 'antd' 通常会解析 antd/index.js 重新导出的所有约 3000 个模块。使用 optimizeImports: ['antd'],只解析 Button 子模块。 这适用于纯 barrel 文件——每个命名导出都是重新导出的文件(export { X } from './x')。如果 barrel 文件有任何本地导出(export const foo = ...),或者任何导入者使用 import *,所有子模块都会被加载。 export * 重新导出总是被加载(从不延迟)以避免循环解析问题。只有不被任何导入者使用的命名重新导出(export { X } from './x')会被延迟。 自动模式:package.json 中带有 "sideEffects": false 的包会自动获得 barrel 优化——无需配置 optimizeImports。对于没有此字段的包,使用 optimizeImports 插件: Resolve 和 load 插件与 barrel 优化一起工作。延迟的子模块在最终加载时会经过插件流水线。

metafile

以结构化格式生成关于构建的元数据。元数据描述每个输入和输出文件:大小、导入和导出。用于:
  • 包分析:了解哪些内容对包体积有贡献
  • 可视化:输入到 esbuild 的包分析器等工具
  • 依赖跟踪:查看应用程序的完整导入图
  • CI 集成:跟踪包体积随时间的变化
build.ts

Markdown 元数据文件

使用 --metafile-md 生成 markdown 格式的元数据文件,对 LLM 友好,终端可读:
terminal
--metafile--metafile-md 可以同时使用:
terminal

metafile 选项格式

在 JavaScript API 中,metafile 接受多种形式:
build.ts
元数据结构包含:

输出

Bun.build 函数返回一个 Promise<BuildOutput>,定义如下:
build.ts
outputs 数组包含构建生成的所有文件。每个 artifact 都实现了 Blob 接口。
build.ts
每个 artifact 还包含以下属性: BunFile 类似,BuildArtifact 对象可以直接传递给 new Response()
build.ts
Bun 运行时漂亮地打印 BuildArtifact 对象以简化调试。

字节码

bytecode: boolean 选项为任何 JavaScript/TypeScript 入口点生成字节码,这可以大大改善大型应用的启动时间。需要 "target": "bun" 和匹配的 Bun 版本。
  • CommonJS:可以使用或不使用 compile: true。在每个入口点旁边生成一个 .jsc 文件。
  • ESM:需要 compile: true。字节码和模块元数据嵌入到独立可执行文件中。
没有显式的 format 时,字节码默认为 CommonJS。
build.ts

可执行文件

Bun 支持将 JavaScript/TypeScript 入口点”编译”为独立可执行文件。此可执行文件包含一份 Bun 二进制文件的副本。
terminal
请参见独立可执行文件

日志和错误

失败时,Bun.build 返回一个带有 AggregateError 的 rejected promise。将其记录到控制台以漂亮打印错误列表,或使用 try/catch 块以编程方式读取。
build.ts
大多数时候,不需要显式的 try/catch,因为 Bun 会打印未捕获的异常。你可以在 Bun.build 调用上使用顶层 await。 error.errors 中的每个项都是 BuildMessageResolveMessageError 的子类)的实例,包含每个错误的详细信息。
build.ts
构建成功时,返回的对象包含一个 logs 属性,其中包含打包器的警告和信息消息。
build.ts

参考

TypeScript 定义

CLI 用法

通用配置

boolean
设置 NODE_ENV=production 并启用压缩
boolean
编译时使用字节码缓存
string
default:"browser"
打包的预期执行环境。可选:browserbunnode
string
传递自定义解析条件
string
default:"disable"
将环境变量内联到打包文件中,作为 process.env.$。要内联匹配前缀的变量,使用类似 FOO_PUBLIC_* 的 glob

输出与文件处理

string
default:"dist"
输出目录(构建多个入口点时使用)
string
将输出写入特定文件
string
default:"none"
生成 source map。可选:linkedinlineexternalnone
string
在输出中添加横幅(例如 React Server Components 的 “use client”
在输出中添加页脚(例如 // built with bun!
string
default:"esm"
输出包的模块格式。可选:esmcjsiife。使用 —bytecode 时默认为 cjs

文件命名

string
default:"[dir]/[name].[ext]"
自定义入口点文件名
string
default:"[name]-[hash].[ext]"
自定义代码块文件名
string
default:"[name]-[hash].[ext]"
自定义资源文件名

打包选项

string
打包多个入口点时使用的根目录
boolean
为共享模块启用代码分割
string
添加到打包代码中导入路径的前缀
string
从打包中排除模块(支持通配符)。别名:-e
string
default:"bundle"
如何处理依赖:externalbundle
boolean
仅转译——不打包
boolean
将 CSS 文件合并打包以减少重复(仅在多个入口点导入 CSS 时)

压缩与优化

boolean
default:"true"
重新发出死代码消除注解。使用 —minify-whitespace 时禁用
boolean
启用所有压缩选项
boolean
压缩语法和内联常量
boolean
压缩空白
boolean
压缩变量和函数标识符
boolean
压缩时保留原始函数和类名

开发功能

boolean
文件变化时自动重新构建
boolean
使用 —watch 重新构建时不清除终端屏幕
boolean
启用 React Fast Refresh 转换(用于开发测试)
boolean
.jsx/.tsx 文件上运行 React Compiler,自动记忆组件和钩子。输出模式由 —target 决定(browser → 客户端,bun/node → ssr)。实验性功能。

独立可执行文件

boolean
生成包含打包文件的独立 Bun 可执行文件
string
将参数预先附加到独立可执行文件的 execArgv

Windows 可执行文件详情

boolean
运行编译后的 Windows 可执行文件时阻止控制台窗口打开
string
设置 Windows 可执行文件的图标
string
设置 Windows 可执行文件的产品名称
string
设置 Windows 可执行文件的公司名称
string
设置 Windows 可执行文件的版本(例如 1.2.3.4
string
设置 Windows 可执行文件的描述
设置 Windows 可执行文件的版权声明

实验性与应用构建

boolean
(实验性)使用 Bun Bake 构建生产版 Web 应用
boolean
(实验性)启用 React Server Components
boolean
当设置了 —app 时,即使在静态构建中也将所有服务端文件转储到磁盘
boolean
当设置了 —app 时,禁用所有压缩