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

一览

  • JS API: await Bun.build({ entrypoints, outdir })
  • CLI: bun build <入口文件> --outdir ./out
  • 监视:--watch 实现增量重建
  • 目标环境:--target browser|bun|node
  • 格式:--format esm|cjs|iife(cjs/iife 为实验性功能)
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './build',
});
它非常快。下面数字基于 esbuild 的 three.js benchmark 性能测试。

为什么要打包?

打包器是 JavaScript 生态系统中的关键基础设施。简要介绍打包为何如此重要:
  • 减少 HTTP 请求。 node_modules 中的单个包可能包含数百个文件,大型应用可能有数十个此类依赖。用单独的 HTTP 请求加载每个文件很快就不可行,因此打包器用来将源代码转换成较少数量的自包含“包”,能用一次请求加载。
  • 代码转换。 现代应用常使用 TypeScript、JSX、CSS Modules 等语言和工具,这些都必须转换成纯 JavaScript 和 CSS,浏览器才能识别。打包器是配置这些转换的自然位置。
  • 框架特性。 框架依赖打包器插件和代码转换来实现常见模式,比如文件系统路由、客户端-服务器代码共置(比如 getServerSideProps 或 Remix loaders)以及服务器组件。
  • 全栈应用。 Bun 的打包器可以同时处理服务端和客户端代码,只需一个命令,支持优化的生产构建和独立可执行文件。通过构建时的 HTML 导入,你可以将整个应用(前端资源和后端服务器)打包成一个可部署单元。
接下来一起看看打包器的 API。
Bun 打包器不替代 tsc 做类型检查或生成类型声明。

基本示例

让我们构建第一个 bundle。你有以下两个文件,实现了一个简单的客户端渲染 React 应用。
import * as ReactDOM from "react-dom/client";
import { Component } from "./Component";

const root = ReactDOM.createRoot(document.getElementById("root")!);
root.render(<Component message="Sup!" />);
这里,index.tsx 是应用的“入口点”。通常它是执行某些副作用的脚本,比如启动服务器,或者像这里这样,初始化 React 根节点。由于用到 TypeScript 和 JSX,代码需先打包才能发送给浏览器。 构建 bundle:
await Bun.build({
  entrypoints: ["./index.tsx"],
  outdir: "./out",
});
对于 entrypoints 数组中指定的每个文件,Bun 都会生成一个新的 bundle。bundle 会写入磁盘的 ./out 目录(相对于当前工作目录解析)。构建后文件系统如下:
file system
.
├── index.tsx
├── Component.tsx
└── out
    └── index.js
out/index.js 内容大致如下:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/javascript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=e1a9947d6e369be0e97814b29cf9f8cdout/index.js
// out/index.js
// ...
// 约 2 万行代码
// 包含了 `react-dom/client` 及所有依赖的内容
// 这里定义了 $jsxDEV 和 $createRoot 函数

// Component.tsx
function Component(props) {
  return $jsxDEV(
    "p",
    {
      children: props.message,
    },
    undefined,
    false,
    undefined,
    this,
  );
}

// index.tsx
var rootNode = document.getElementById("root");
var root = $createRoot(rootNode);
root.render(
  $jsxDEV(
    Component,
    {
      message: "Sup!",
    },
    undefined,
    false,
    undefined,
    this,
  ),
);

监听模式

和运行时与测试器一样,打包器原生支持监听模式。
terminal
bun build ./index.tsx --outdir ./out --watch

支持的内容类型

和 Bun 运行时一致,打包器开箱支持多种文件类型。下表介绍了默认的“加载器”。完整文档请参考打包器 > 文件类型
扩展名说明
.js .jsx .cjs .mjs .mts .cts .ts .tsx使用 Bun 内置转译器解析并转译 TypeScript/JSX 语法为普通 JavaScript。执行默认转换,包括无用代码消除和摇树优化。当前 Bun 不对语法做向下转换,使用较新 ECMAScript 语法会反映在打包后的代码中。
.json解析 JSON 文件,将其内联到 bundle 中,作为 JavaScript 对象。

js<br/>import pkg from "./package.json";<br/>pkg.name; // => "my-package"<br/>
.jsonc支持带注释的 JSON。解析后内联成 JavaScript 对象。

js<br/>import config from "./config.jsonc";<br/>config.name; // => "my-config"<br/>
.toml解析 TOML 文件,内联成 JavaScript 对象。

js<br/>import config from "./bunfig.toml";<br/>config.logLevel; // => "debug"<br/>
.yaml .yml解析 YAML 文件,内联成 JavaScript 对象。

js<br/>import config from "./config.yaml";<br/>config.name; // => "my-app"<br/>
.txt读取文本文件内容,内联成字符串。

js<br/>import contents from "./file.txt";<br/>console.log(contents); // => "Hello, world!"<br/>
.html处理 HTML 文件,打包引用的资源(脚本、样式表、图片等)。
.css将所有 CSS 文件打包成一个 .css 文件输出至目标目录。
.node .wasmBun 运行时支持这些文件,但打包时会作为资源处理。

资源文件

遇到无法识别的文件后缀时,打包器会将该文件视为外部资源。会将引用文件原封不动复制到 outdir 目录,同时导入被替换为文件路径。
// bundle入口文件
import logo from "./logo.svg";
console.log(logo);
文件加载器的具体行为也受 namingpublicPath 影响。
详情请参考打包器 > 加载器页面。

插件

上表中的行为可以通过插件覆盖或扩展。请查阅打包器 > 加载器页面完整说明。

API

entrypoints

必填 应用入口点文件路径数组。为每个入口生成一个 bundle。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
const result = await Bun.build({
  entrypoints: ["./index.ts"],
});
// => { success: boolean, outputs: BuildArtifact[], logs: BuildMessage[] }

outdir

输出文件目录。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
const result = await Bun.build({
  entrypoints: ['./index.ts'],
  outdir: './out'
});
// => { success: boolean, outputs: BuildArtifact[], logs: BuildMessage[] }
未传 outdir 时,打包后的代码不会写入磁盘。文件内容以 BuildArtifact 对象数组形式返回。BuildArtifact 是带附加属性的 Blob;详见输出
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
const result = await Bun.build({
  entrypoints: ["./index.ts"],
});

for (const res of result.outputs) {
  // 可作为 Blob 处理
  await res.text();

  // Bun 会自动设置 Content-Type 和 Etag 头
  new Response(res);

  // 也可手动写入,但此时推荐使用 `outdir` 选项。
  Bun.write(path.join("out", res.path), res);
}
设置了 outdir 时,BuildArtifactpath 是文件写入的绝对路径。

target

指定目标执行环境。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.ts'],
  outdir: './out',
  target: 'browser', // 默认
})
不同目标环境,Bun 会应用不同的模块解析规则和优化。

browser

默认。 生成面向浏览器的 bundle。解析导入时优先使用 "browser" export 条件。导入内建模块(如 node:eventsnode:path)可行,但调用部分函数(如 fs.readFile)无效。

bun

生成面向 Bun 运行时的 bundle。通常不强制要求将服务端代码打包,可直接执行源代码。但打包服务端代码有助于减少启动时间并提升运行性能。使用构建时 HTML 导入创建全栈应用时,应使用此目标,将客户端和服务端代码共享打包。所有带 target: "bun" 的 bundle 都会带有特殊的 // @bun 编译指令,告知 Bun 运行时无需重转译即可执行。若入口文件含 Bun shebang(#!/usr/bin/env bun),打包器默认改用 target: "bun"结合使用 target: "bun"format: "cjs" 时,会添加 // @bun @bun-cjs 编译指令,且 CommonJS 包装函数不兼容 Node.js。

node

生成面向 Node.js 的 bundle。解析导入时优先 "node" export 条件,输出 .mjs 文件。未来将自动为 Bun 全局对象以及 bun:* 内建模块提供 polyfill(尚未实现)。

format

指定生成 bundle 的模块格式。 Bun 默认为 "esm",并提供 "cjs""iife" 实验性支持。

格式: “esm” - ES 模块

默认格式,支持 ES 模块语法,包括顶层 await、import.meta 等。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  format: "esm",
})
浏览器中使用 ES 模块时,需确保 <script> 标签带有 type="module",并设置 format"esm"

格式: “cjs” - CommonJS

设置为 "cjs" 以构建 CommonJS 模块。此时默认 target 会从 "browser" (esm) 自动切换为 "node" (cjs)。使用 format: "cjs"target: "node" 转译的 CommonJS 模块,在 Bun 和 Node.js 都可执行(前提是 API 受支持)。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  format: "cjs",
})

格式: “iife” - IIFE

待完善文档,支持 globalNames 后发布。

jsx

配置 JSX 转换行为,精细控制 JSX 编译方式。 Classic 运行时示例(使用 factoryfragment):
await Bun.build({
  entrypoints: ["./app.tsx"],
  outdir: "./out",
  jsx: {
    factory: "h",
    fragment: "Fragment",
    runtime: "classic",
  },
});
自动运行时示例(使用 importSource):
await Bun.build({
  entrypoints: ["./app.tsx"],
  outdir: "./out",
  jsx: {
    importSource: "preact",
    runtime: "automatic",
  },
});

splitting

是否启用代码拆分。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  splitting: false, // 默认关闭
})
启用后,多个入口若共享同一文件、模块或模块集合,打包器会将共享代码拆分为独立 bundle,称为 chunk。示例文件如:
import { shared } from "./shared.ts";
启用拆分打包:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./entry-a.ts', './entry-b.ts'],
  outdir: './out',
  splitting: true,
})
打包结果文件结构:
file system
.
├── entry-a.tsx
├── entry-b.tsx
├── shared.tsx
└── out
    ├── entry-a.js
    ├── entry-b.js
    └── chunk-2fce6291bf86559d.js
chunk-2fce6291bf86559d.js 为共享代码 chunk,默认文件名包含 hash 防止冲突,可通过 naming 自定义。

plugins

打包时应用的插件列表。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ["./index.tsx"],
  outdir: "./out",
  plugins: [
    /* ... */
  ],
});
Bun 实现了统一的插件系统,适用于运行时和打包器。详见插件文档

env

控制打包时环境变量的处理方式。内部使用 define 注入环境变量,简化指定注入内容。

env: “inline”

process.env.FOO 替换为包含环境变量实际值的字符串字面量,内联进 bundle。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  env: "inline",
})
示例输入:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/javascript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=e1a9947d6e369be0e97814b29cf9f8cdinput.js
// input.js
console.log(process.env.FOO);
console.log(process.env.BAZ);
输出变为:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/javascript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=e1a9947d6e369be0e97814b29cf9f8cdoutput.js
// output.js
console.log("bar");
console.log("123");

env: “PUBLIC_*“(前缀)

内联所有匹配给定前缀的环境变量(即 * 之前的部分),替换对应的 process.env。适合选择性注入公共 URL 或客户端 token,避免私密信息泄漏。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',

  // 内联所有以 "ACME_PUBLIC_" 开头的环境变量
  env: "ACME_PUBLIC_*",
})
设环境变量如下:
terminal
FOO=bar BAZ=123 ACME_PUBLIC_URL=https://acme.com
源码:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79index.tsx
console.log(process.env.FOO);
console.log(process.env.ACME_PUBLIC_URL);
console.log(process.env.BAZ);
输出:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/javascript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=e1a9947d6e369be0e97814b29cf9f8cdoutput.js
console.log(process.env.FOO);
console.log("https://acme.com");
console.log(process.env.BAZ);

env: “disable”

完全禁用环境变量注入。

sourcemap

指定生成哪种类型的 sourcemap。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  sourcemap: 'linked', // 默认 'none'
})
说明
"none"默认。不生成 sourcemap。
"linked"生成独立的 *.js.map 文件,与每个 *.js bundle 同目录。bundle 中插入 //# sourceMappingURL 注释关联两者。需确保设置了 --outdir。可通过 --public-path 自定义基础路径。

示例:
js<br/>// <打包代码><br/><br/>//# sourceMappingURL=bundle.js.map<br/>
"external"生成独立的 *.js.map 文件,不插入 //# sourceMappingURL 注释。bundle 包含调试 id(debugId),可用于关联 map 文件,该 id 作为注释添加至 bundle 底部。

示例:
js<br/>// <打包代码><br/><br/>//# debugId=<DEBUG ID><br/>
"inline"生成 sourcemap 并以 base64 编码数据嵌入到 bundle 末尾。

示例:
js<br/>// <打包代码><br/><br/>//# sourceMappingURL=data:application/json;base64,<编码数据><br/>
对应的 *.js.map 文件是 JSON 格式,包含同样的 debugId 字段。

minify

是否启用代码压缩,默认 false
若目标为 bun,默认会压缩标识符。
启用所有压缩选项:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  minify: true, // 默认 false
})
细粒度开启压缩:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  minify: {
    whitespace: true,
    identifiers: true,
    syntax: true,
  },
})

external

外部依赖列表,默认空数组 []
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  external: ["lodash", "react"], // 默认 []
})
外部导入不会打包进最终文件,导入语句保留原样,运行时解析。 示例入口:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79index.tsx
import _ from "lodash";
import { z } from "zod";

const value = z.string().parse("Hello world!");
console.log(_.upperCase(value));
默认打包会将整个 “zod” 包源码包含进 bundle。如果想保留导入不打包,可标记为外部:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  external: ['zod'],
})
生成的 bundle 可能如下:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/javascript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=e1a9947d6e369be0e97814b29cf9f8cdout/index.js
import { z } from "zod";

// ...
// lodash 源码
// 包括 `_.upperCase` 函数

var value = z.string().parse("Hello world!");
console.log(_.upperCase(value));
可使用通配符 * 标记所有导入为外部:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  external: ['*'],
})

packages

控制是否将包依赖(非相对或绝对路径)打包。可选值:bundle(默认)或 external
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.ts'],
  packages: 'external',
})

naming

自定义生成文件名。默认 './[dir]/[name].[ext]'
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  naming: "[dir]/[name].[ext]", // 默认
})
默认情况下,生成文件名基于入口文件名。
file system
.
├── index.tsx
└── out
    └── index.js
多入口项目,生成文件层级反映入口目录结构:
file system
.
├── index.tsx
└── nested
    └── index.tsx
└── out
    ├── index.js
    └── nested
        └── index.js
naming 自定义名称,支持以下变量:
  • [name] - 入口文件名(无扩展名)
  • [ext] - 生成文件扩展名
  • [hash] - 文件内容哈希值
  • [dir] - 项目根目录相对路径到源文件父目录
举例:
示例文件[name][ext][hash][dir]
./index.tsxindexjsa1b2c3d4""(空字符串)
./nested/entry.tsentryjsc3d4e5f6"nested"
示例设置让文件名包含 hash:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  naming: 'files/[dir]/[name]-[hash].[ext]',
})
示例输出结构:
file system
.
├── index.tsx
└── out
    └── files
        └── index-a1b2c3d4.js
若只传字符串到 naming,仅作用于入口对应的 bundle,chunk 和资源文件名不受影响。JS API 允许分别为入口、chunk 和资源指定模板字符串。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  naming: {
    // 默认值
    entry: '[dir]/[name].[ext]',
    chunk: '[name]-[hash].[ext]',
    asset: '[name]-[hash].[ext]',
  },
})

root

项目根目录。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./pages/a.tsx', './pages/b.tsx'],
  outdir: './out',
  root: '.',
})
默认为所有入口文件的第一个公共祖先目录。示例文件结构:
file system
.
└── pages
  └── index.tsx
  └── settings.tsx
构建两个入口:
await Bun.build({
  entrypoints: ['./pages/index.tsx', './pages/settings.tsx'],
  outdir: './out',
})
结果:
file system
.
└── pages
  └── index.tsx
  └── settings.tsx
└── out
  └── index.js
  └── settings.js
由于 pages 是入口文件第一个公共祖先,视为项目根,生成 bundle 放在 out 顶层,而非 out/pages 可通过 root 参数覆写:
await Bun.build({
  entrypoints: ['./pages/index.tsx', './pages/settings.tsx'],
  outdir: './out',
  root: '.',
})
此时文件结构变为:
.
└── pages
  └── index.tsx
  └── settings.tsx
└── out
  └── pages
    └── index.js
    └── settings.js

publicPath

打包后代码中所有导入路径的前缀。 通常打包会合并代码,不留导入语句,但以下情况例外:
  • 资源导入 — 导入类似 *.svg 这类非代码资源时,文件复制到 outdir,导入变变量引用
  • 外部模块 — 标记为外部的模块,导入语句保留
  • 代码拆分 chunk — 当启用拆分时,共享代码会抽成多个文件,导入路径不能相对。
例如:
import logo from "./logo.svg";
console.log(logo);
设置 publicPath 后,所有路径被加前缀:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  publicPath: 'https://cdn.example.com/', // 默认为 undefined
})
生成文件示例:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/javascript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=e1a9947d6e369be0e97814b29cf9f8cdout/index.js
var logo = "https://cdn.example.com/logo-a7305bdef.svg";

define

构建时全局标识符替换映射。键为标识符名,值是 JSON 字符串字面量,内联替代。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  define: {
    STRING: JSON.stringify("value"),
    "nested.boolean": "true",
  },
})

loader

文件扩展名和内置加载器名称映射,快速定制加载行为。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  loader: {
    ".png": "dataurl",
    ".txt": "file",
  },
})
在最终 bundle 顶部添加 banner,比如 React 的 "use client" 指令或版权声明注释。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  banner: '"use client";'
})
在最终 bundle 底部添加内容,比如许可证注释或彩蛋。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  footer: '// built with love in SF'
})

drop

从 bundle 中移除函数调用。例如,--drop=console 会移除所有 console.log 调用,参数也会被移除(无论参数是否有副作用)。移除 debugger 时,会删除所有 debugger 语句。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  drop: ["console", "debugger", "anyIdentifier.or.propertyAccess"],
})

features

编译时启用特性标记以实现死代码删除。通过 import { feature } from "bun:bundle" 条件包含代码。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79app.ts
import { feature } from "bun:bundle";

if (feature("PREMIUM")) {
  // 仅 PREMIUM 标记启用时包含
  initPremiumFeatures();
}

if (feature("DEBUG")) {
  // 仅 DEBUG 标记启用时包含
  console.log("Debug mode");
}
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ['./app.ts'],
  outdir: './out',
  features: ["PREMIUM"],  // PREMIUM=true, DEBUG=false
})
feature() 在打包时替换为布尔值,结合压缩,可剔除不可达分支:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79输入
import { feature } from "bun:bundle";
const mode = feature("PREMIUM") ? "premium" : "free";
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/javascript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=e1a9947d6e369be0e97814b29cf9f8cd输出(启用 --feature PREMIUM --minify)
var mode = "premium";
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/javascript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=e1a9947d6e369be0e97814b29cf9f8cd输出(不启用 --feature PREMIUM,启用 --minify)
var mode = "free";
主要注意事项:
  • feature() 只能接受字符串字面量参数,不支持动态值
  • bun:bundle 导入会被移除,不会出现在输出代码
  • 支持 bun buildbun runbun test
  • 可启用多条特性:--feature FLAG_A --feature FLAG_B
  • 为类型安全,可扩展 Registry 接口限制只能使用指定特性(见下)
应用场景:
  • 平台差异代码(feature("SERVER") vs feature("CLIENT")
  • 环境变量控制功能(feature("DEVELOPMENT")
  • 渐进式功能发布
  • A/B 测试
  • 付费版功能区分
类型安全: 默认 feature() 可传任意字符串。想实现自动提示防错,可新建 env.d.ts 文件(或补充到已有 .d.ts)并声明扩展:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79env.d.ts
declare module "bun:bundle" {
  interface Registry {
    features: "DEBUG" | "PREMIUM" | "BETA_FEATURES";
  }
}
确保文件包含在 tsconfig.json(如 "include": ["src", "env.d.ts"])内。此后 feature() 仅接受这些字符串,使用其他字符串则会提示类型错误。

输出

Bun.build 返回一个 Promise<BuildOutput>,定义如下:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
interface BuildOutput {
  outputs: BuildArtifact[];
  success: boolean;
  logs: Array<object>; // 详见文档
}

interface BuildArtifact extends Blob {
  kind: "entry-point" | "chunk" | "asset" | "sourcemap";
  path: string;
  loader: Loader;
  hash: string | null;
  sourcemap: BuildArtifact | null;
}
outputs 数组包含构建生成的所有文件,每个 Artifact 实现了 Blob 接口。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
const build = await Bun.build({
  /* */
});

for (const output of build.outputs) {
  await output.arrayBuffer(); // => ArrayBuffer
  await output.bytes(); // => Uint8Array
  await output.text(); // string
}
每个 Artifact 还拥有:
属性说明
kind文件类型:入口点、代码拆分块、资源、sourcemap、字节码等
path磁盘上的绝对路径
loader用于加载该文件的加载器,详见打包器 > 加载器
hash文件内容哈希。资源文件必定含有
sourcemap该文件对应的 sourcemap 文件,若生成则存在。仅入口点和 chunk 有值。
BuildArtifact 对象可以直接传给 new Response()
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
const build = await Bun.build({
  /* */
});

const artifact = build.outputs[0];

// 会自动设置 Content-Type 头
return new Response(artifact);
Bun 运行时支持对 BuildArtifact 对象的漂亮打印,便于调试:
// build.ts
const build = await Bun.build({
  /* */
});

const artifact = build.outputs[0];
console.log(artifact);

字节码

bytecode: boolean 选项用于生成入口的字节码,显著提高大型应用启动速度。仅支持 "cjs" 格式,只能与 target: "bun" 配合使用,且依赖 Bun 版本匹配。每个入口会生成对应 .jsc 文件。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
await Bun.build({
  entrypoints: ["./index.tsx"],
  outdir: "./out",
  bytecode: true,
})

可执行文件

Bun 支持将 JavaScript/TypeScript 入口“编译”为独立可执行文件,其中包含 Bun 二进制。
terminal
bun build ./cli.tsx --outfile mycli --compile
./mycli
详见打包器 > 可执行文件

日志和错误

构建失败时,Bun.build 返回一个拒绝的 AggregateError,可打印查看列表错误,或用 try/catch 捕获处理。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
try {
  const result = await Bun.build({
    entrypoints: ["./index.tsx"],
    outdir: "./out",
  });
} catch (e) {
  // TS 不允许在 catch 加类型注解,故用断言
  const error = e as AggregateError;
  console.error("构建失败");

  // 示例:内置格式化打印
  console.error(error);

  // 示例:序列化成 JSON 字符串打印
  console.error(JSON.stringify(error, null, 2));
}
通常无需 try/catch,Bun 会优雅打印未捕获异常。直接顶层 await 即可。 错误列表中每条均为 BuildMessageResolveMessage 实例(继承自 Error),含详细信息:
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
class BuildMessage {
  name: string;
  position?: Position;
  message: string;
  level: "error" | "warning" | "info" | "debug" | "verbose";
}

class ResolveMessage extends BuildMessage {
  code: string;
  referrer: string;
  specifier: string;
  importKind: ImportKind;
}
构建成功时,返回对象有 logs 字段,包含打包警告和信息。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79build.ts
const result = await Bun.build({
  entrypoints: ["./index.tsx"],
  outdir: "./out",
});

if (result.logs.length > 0) {
  console.warn("构建成功但有警告:");
  for (const message of result.logs) {
    // Bun 将美化打印信息
    console.warn(message);
  }
}

参考

https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79Typescript Definitions
interface Bun {
  build(options: BuildOptions): Promise<BuildOutput>;
}

interface BuildConfig {
  entrypoints: string[]; // 入口文件数组
  outdir?: string; // 输出目录
  target?: Target; // 默认 "browser"
  /**
   * 输出模块格式,顶层 await 仅支持 "esm"
   *
   * 可选:
   * - "esm"
   * - "cjs" (**实验性**)
   * - "iife" (**实验性**)
   *
   * @default "esm"
   */
  format?: "esm" | "cjs" | "iife";
  /**
   * JSX 配置对象,控制 JSX 转换行为
   */
  jsx?: {
    runtime?: "automatic" | "classic";
    importSource?: string;
    factory?: string;
    fragment?: string;
    sideEffects?: boolean;
    development?: boolean;
  };
  naming?:
    | string
    | {
        chunk?: string;
        entry?: string;
        asset?: string;
      };
  root?: string; // 项目根目录
  splitting?: boolean; // 默认 true,启用代码拆分
  plugins?: BunPlugin[];
  external?: string[];
  packages?: "bundle" | "external";
  publicPath?: string;
  define?: Record<string, string>;
  loader?: { [k in string]: Loader };
  sourcemap?: "none" | "linked" | "inline" | "external" | boolean; // 默认 "none",true 相当于 "inline"
  /**
   * package.json 的 `exports` 条件,用于解析导入
   *
   * 等同于 CLI 中 `--conditions` 参数
   *
   * https://nodejs.org/api/packages.html#exports
   */
  conditions?: Array<string> | string;

  /**
   * 打包时环境变量处理方式
   *
   * 可选:
   * - "inline": 将 `process.env.FOO` 替换为实际值字符串字面量
   * - "disable": 禁用环境变量注入
   * - 以 "*" 结尾的字符串: 内联匹配前缀的环境变量,如 "MY_PUBLIC_*"
   */
  env?: "inline" | "disable" | `${string}*`;
  minify?:
    | boolean
    | {
        whitespace?: boolean;
        syntax?: boolean;
        identifiers?: boolean;
      };
  /**
   * 忽略无用代码消除相关注释和 package.json 中 "sideEffects" 设置,仅作为库注释错误的临时解决方案
   */
  ignoreDCEAnnotations?: boolean;
  /**
   * 即使 minify.whitespace 为 true,也强制保留 @__PURE__ 注释
   */
  emitDCEAnnotations?: boolean;

  /**
   * 生成字节码,可显著减少冷启动时间,但会增加体积和内存占用。
   *
   * 仅支持 CommonJS (`format: "cjs"`),必须配合 `target: "bun"` 使用
   * @default false
   */
  bytecode?: boolean;
  /**
   * 添加代码顶部 banner,例如 "use client";
   */
  banner?: string;
  /**
   * 添加代码底部内容,如版权注释
   */
  footer?: string;

  /**
   * 移除匹配的函数调用
   */
  drop?: string[];

  /**
   * - true 时构建失败时拒绝并抛出 AggregateError;
   * - false 时返回 `{success: false}` 的 BuildOutput
   *
   * @default true
   */
  throw?: boolean;

  /**
   * 指定自定义 tsconfig.json 路径,用于路径解析
   * 等同于 CLI 的 `--tsconfig-override`
   */
  tsconfig?: string;

  outdir?: string;
}

interface BuildOutput {
  outputs: BuildArtifact[];
  success: boolean;
  logs: Array<BuildMessage | ResolveMessage>;
}

interface BuildArtifact extends Blob {
  path: string;
  loader: Loader;
  hash: string | null;
  kind: "entry-point" | "chunk" | "asset" | "sourcemap" | "bytecode";
  sourcemap: BuildArtifact | null;
}

type Loader =
  | "js"
  | "jsx"
  | "ts"
  | "tsx"
  | "css"
  | "json"
  | "jsonc"
  | "toml"
  | "yaml"
  | "text"
  | "file"
  | "napi"
  | "wasm"
  | "html";

interface BuildOutput {
  outputs: BuildArtifact[];
  success: boolean;
  logs: Array<BuildMessage | ResolveMessage>;
}

declare class ResolveMessage {
  readonly name: "ResolveMessage";
  readonly position: Position | null;
  readonly code: string;
  readonly message: string;
  readonly referrer: string;
  readonly specifier: string;
  readonly importKind:
    | "entry_point"
    | "stmt"
    | "require"
    | "import"
    | "dynamic"
    | "require_resolve"
    | "at"
    | "at_conditional"
    | "url"
    | "internal";
  readonly level: "error" | "warning" | "info" | "debug" | "verbose";

  toString(): string;
}

CLI 用法

bun build <入口>

通用配置

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

输出与文件处理

--outdir
string
default:"dist"
输出目录(用于构建多个入口点时)
--outfile
string
输出到指定文件
--sourcemap
string
default:"none"
生成源码映射。可选 linkedinlineexternalnone
--banner
string
在输出文件前添加标头(例如 React 服务器组件的 “use client”
在输出文件尾部添加注释(例如 // built with bun!
--format
string
default:"esm"
输出包的模块格式。可选 esmcjsiife

文件命名

--entry-naming
string
default:"[dir]/[name].[ext]"
自定义入口点的文件名格式
--chunk-naming
string
default:"[name]-[hash].[ext]"
自定义代码块文件名格式
--asset-naming
string
default:"[name]-[hash].[ext]"
自定义资源文件名格式

打包选项

--root
string
打包多个入口点时使用的根目录
--splitting
boolean
启用共享模块的代码分割
--public-path
string
添加到打包代码中导入路径的前缀
--external
string
从包中排除模块(支持通配符)。别名:-e
--packages
string
default:"bundle"
依赖处理方式:externalbundle
--no-bundle
boolean
只进行转译,不打包
--css-chunking
boolean
合并 CSS 文件以减少重复(仅当多个入口点引入 CSS时生效)

压缩与优化

--emit-dce-annotations
boolean
default:"true"
重新输出死代码消除注释。在使用 —minify-whitespace 时禁用
--minify
boolean
启用所有压缩选项
--minify-syntax
boolean
压缩语法并内联常量
--minify-whitespace
boolean
压缩空白字符
--minify-identifiers
boolean
压缩变量和函数标识符
--keep-names
boolean
压缩时保留原始的函数和类名称

开发功能

--watch
boolean
文件变化时自动重建
--no-clear-screen
boolean
使用 —watch 时不清屏
--react-fast-refresh
boolean
启用 React 快速刷新转换(用于开发测试)

独立可执行文件

--compile
boolean
生成包含包的独立 Bun 可执行文件。隐含使用 —production
--compile-exec-argv
string
向独立可执行文件的 execArgv 前置参数

Windows 可执行文件详情

--windows-hide-console
boolean
运行编译后的 Windows 可执行文件时防止打开控制台窗口
--windows-icon
string
设置 Windows 可执行文件图标
--windows-title
string
设置 Windows 可执行文件产品名称
--windows-publisher
string
设置 Windows 可执行文件公司名称
--windows-version
string
设置 Windows 可执行文件版本(例如 1.2.3.4
--windows-description
string
设置 Windows 可执行文件描述
设置 Windows 可执行文件版权声明

实验性功能及应用构建

--app
boolean
(实验性) 使用 Bun Bake 构建生产环境的 Web 应用
--server-components
boolean
(实验性) 启用 React 服务器组件
--debug-dump-server-files
boolean
当设置了 —app 时,即使是静态构建也将所有服务器文件导出到磁盘
--debug-no-minify
boolean
当设置了 —app 时,禁用所有压缩