Skip to content

import Build from "/snippets/cli/build.mdx";

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 为实验性)

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './build',
});

CLI

bash
bun build ./index.tsx --outdir ./build

它非常快。下面的数字代表了在 esbuild 的 three.js 基准测试 中的性能表现。

为什么要打包?

打包器是 JavaScript 生态系统中的关键基础设施。简要概述为什么打包如此重要:

  • 减少 HTTP 请求。 node_modules 中的单个包可能由数百个文件组成,大型应用程序可能有数十个这样的依赖项。很快就会发现,通过单独的 HTTP 请求加载这些文件中的每一个变得不可行,因此打包器用于将我们的应用程序源代码转换为较少数量的自包含"bundle",可以通过单个请求加载。
  • 代码转换。 现代应用程序通常使用 TypeScript、JSX 和 CSS 模块等语言或工具构建,所有这些都必须在被浏览器使用之前转换为纯 JavaScript 和 CSS。打包器是配置这些转换的自然场所。
  • 框架功能。 框架依赖打包器插件和代码转换来实现常见模式,如文件系统路由、客户端 - 服务器代码共存(想想 getServerSideProps 或 Remix loaders)和服务器组件。
  • 全栈应用程序。 Bun 的打包器可以在单个命令中处理服务器和客户端代码,实现优化的生产构建和单文件可执行文件。通过构建时 HTML 导入,您可以将整个应用程序(前端资源和后端服务器)打包到单个可部署单元中。

让我们进入打包器 API。

NOTE

Bun 打包器不打算替代 `tsc` 进行类型检查或生成类型声明。

基本示例

让我们构建第一个打包。您有以下两个文件,它们实现了一个简单的客户端渲染 React 应用程序。

tsx
import * as ReactDOM from "react-dom/client";
import { Component } from "./Component";

const root = ReactDOM.createRoot(document.getElementById("root")!);
root.render(<Component message="Sup!" />);
tsx
export function Component(props: { message: string }) {
  return <h1>{props.message}</h1>;
}

这里,index.tsx 是我们应用程序的"入口点"。通常,这将是一个执行某些副作用的脚本,比如启动服务器或——在这种情况下——初始化 React 根。因为我们使用 TypeScript 和 JSX,所以在发送到浏览器之前需要打包我们的代码。

要创建我们的打包:

ts
await Bun.build({
  entrypoints: ["./index.tsx"],
  outdir: "./out",
});
bash
bun build ./index.tsx --outdir ./out

对于 entrypoints 中指定的每个文件,Bun 将生成一个新的打包。这个打包将写入磁盘到 ./out 目录(从当前工作目录解析)。运行构建后,文件系统看起来像这样:

text
.
├── index.tsx
├── Component.tsx
└── out
    └── index.js

out/index.js 的内容看起来像这样:

js
// out/index.js
// ...
// ~20k 行代码
// 包括 `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,
  ),
);

监听模式

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

bash
bun build ./index.tsx --outdir ./out --watch

内容类型

与 Bun 运行时一样,打包器支持一系列文件类型。下表分解了打包器的标准"加载器"集。有关完整文档,请参阅 打包器 > 文件类型

扩展名详情
.js .jsx .cjs .mjs .mts .cts .ts .tsx使用 Bun 的内置转译器解析文件并将 TypeScript/JSX 语法转译为纯 JavaScript。打包器执行一组默认转换,包括死代码消除和摇树优化。目前 Bun 不尝试向下转换语法;如果您使用最近的 ECMAScript 语法,这将反映在打包代码中。
.jsonJSON 文件被解析并作为 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/>
.tomlTOML 文件被解析并作为 JavaScript 对象内联到打包中。

js<br/>import config from "./bunfig.toml";<br/>config.logLevel; // => "debug"<br/>
.yaml .ymlYAML 文件被解析并作为 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/>
.htmlHTML 文件被处理,任何引用的资源(脚本、样式表、图像)都被打包。
.cssCSS 文件被打包在一起,输出到输出目录中的单个 .css 文件中。
.node .wasm这些文件由 Bun 运行时支持,但在打包期间它们被视为资源。

资源

如果打包器遇到带有无法识别扩展名的导入,它会将导入的文件视为外部文件。引用的文件按原样复制到 outdir,导入解析为文件的路径。

ts
// 打包入口点
import logo from "./logo.svg";
console.log(logo);
ts
// 打包输出
var logo = "./logo-a7305bdef.svg";
console.log(logo);

文件加载器的确切行为也受到 namingpublicPath 的影响。

插件

此表中描述的行为可以使用插件覆盖或扩展。有关完整文档,请参阅 打包器 > 加载器 页面。

API

entrypoints

应用程序入口点的路径数组。将为每个入口点生成一个打包。

JavaScript

ts
const result = await Bun.build({
  entrypoints: ["./index.ts"]
});

CLI

bash
bun build ./index.ts

outdir

输出文件将写入的目录。

JavaScript

ts
const result = await Bun.build({
  entrypoints: ['./index.ts'],
  outdir: './out'
});
// => { success: boolean, outputs: `BuildArtifact[]`, logs: `BuildMessage[]` }

CLI

bash
bun build ./index.ts --outdir ./out

如果未将 outdir 传递给 JavaScript API,打包的代码将不会写入磁盘。打包的文件在 BuildArtifact 对象数组中返回。这些对象是带有额外属性的 Blob;有关完整文档,请参阅 输出

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 时,BuildArtifact 上的 path 属性将是写入位置的绝对路径。

target

打包的预期执行环境。

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.ts'],
  outdir: './out',
  target: 'browser', // 默认
})

CLI

bash
bun build ./index.ts --outdir ./out --target browser

根据目标,Bun 将应用不同的模块解析规则和优化。

默认。 用于生成旨在由浏览器执行的打包。在解析导入时优先考虑 "browser" 导出条件。导入任何内置模块(如 node:eventsnode:path)将有效,但调用某些函数(如 fs.readFile)将无效。

用于生成旨在由 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.js 运行的打包。在解析导入时优先考虑 "node" 导出条件,并输出 .mjs。将来,这将自动填充 Bun 全局和其他内置 bun:* 模块,尽管这尚未实现。

format

指定在生成的打包中使用的模块格式。

Bun 默认为 "esm",并提供 "cjs""iife" 的实验性支持。

format: "esm" - ES 模块

这是默认格式,支持 ES 模块语法,包括顶层 await、import.meta 等。

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  format: "esm",
})

CLI

bash
bun build ./index.tsx --outdir ./out --format esm

要在浏览器中使用 ES 模块语法,将 format 设置为 "esm" 并确保您的 <script type="module"> 标签设置了 type="module"

format: "cjs" - CommonJS

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

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  format: "cjs",
})

CLI

bash
bun build ./index.tsx --outdir ./out --format cjs

format: "iife" - IIFE

待办:一旦我们支持 globalNames,就记录 IIFE。

jsx

配置 JSX 转换行为。允许细粒度控制 JSX 的编译方式。

经典运行时示例(使用 factoryfragment):

ts
await Bun.build({
  entrypoints: ["./app.tsx"],
  outdir: "./out",
  jsx: {
    factory: "h",
    fragment: "Fragment",
    runtime: "classic",
  },
});
bash
# JSX 配置通过 bunfig.toml 或 tsconfig.json 处理
bun build ./app.tsx --outdir ./out

自动运行时示例(使用 importSource):

ts
await Bun.build({
  entrypoints: ["./app.tsx"],
  outdir: "./out",
  jsx: {
    importSource: "preact",
    runtime: "automatic",
  },
});
bash
# JSX 配置通过 bunfig.toml 或 tsconfig.json 处理
bun build ./app.tsx --outdir ./out

splitting

是否启用代码分割。

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  splitting: false, // 默认
})

CLI

bash
bun build ./index.tsx --outdir ./out --splitting

当为 true 时,打包器将启用代码分割。当多个入口点都导入相同的文件、模块或一组文件/模块时,将共享代码分割到单独的打包中通常很有用。这个共享打包被称为 chunk。考虑以下文件:

ts
import { shared } from "./shared.ts";
ts
import { shared } from "./shared.ts";
ts
export const shared = "shared";

要打包 entry-a.tsentry-b.ts 并启用代码分割:

JavaScript

ts
await Bun.build({
  entrypoints: ['./entry-a.ts', './entry-b.ts'],
  outdir: './out',
  splitting: true,
})

CLI

bash
bun build ./entry-a.ts ./entry-b.ts --outdir ./out --splitting

运行此构建将生成以下文件:

text
.
├── entry-a.tsx
├── entry-b.tsx
├── shared.tsx
└── out
    ├── entry-a.js
    ├── entry-b.js
    └── chunk-2fce6291bf86559d.js

生成的 chunk-2fce6291bf86559d.js 文件包含共享代码。为避免冲突,文件名默认自动包含内容哈希。这可以使用 naming 自定义。

plugins

打包期间使用的插件列表。

ts
await Bun.build({
  entrypoints: ["./index.tsx"],
  outdir: "./out",
  plugins: [
    /* ... */
  ],
});

Bun 为 Bun 的运行时和打包器实现了通用插件系统。有关完整文档,请参阅 插件文档

env

控制打包期间如何处理环境变量。在内部,这使用 define 将环境变量注入到打包中,但更容易指定要注入的环境变量。

env: "inline"

通过将 process.env.FOO 引用转换为包含实际环境变量值的字符串字面量,将环境变量注入到打包输出中。

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  env: "inline",
})

CLI

bash
bun build ./index.tsx --outdir ./out --env inline

对于下面的输入:

js
// input.js
console.log(process.env.FOO);
console.log(process.env.BAZ);

生成的打包将包含以下代码:

js
// output.js
console.log("bar");
console.log("123");

env: "PUBLIC_*" (前缀)

内联与给定前缀(* 字符之前的部分)匹配的环境变量,将 process.env.FOO 替换为实际环境变量值。这对于选择性地内联环境变量(如公共 URL 或客户端令牌)很有用,而无需担心将私有凭据注入输出打包中。

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  
  // 内联所有以 "ACME_PUBLIC_" 开头的环境变量
  env: "ACME_PUBLIC_*",
})

CLI

bash
bun build ./index.tsx --outdir ./out --env ACME_PUBLIC_*

例如,给定以下环境变量:

bash
FOO=bar BAZ=123 ACME_PUBLIC_URL=https://acme.com

和源代码:

tsx
console.log(process.env.FOO);
console.log(process.env.ACME_PUBLIC_URL);
console.log(process.env.BAZ);

生成的打包将包含以下代码:

js
console.log(process.env.FOO);
console.log("https://acme.com");
console.log(process.env.BAZ);

env: "disable"

完全禁用环境变量注入。

sourcemap

指定要生成的 sourcemap 类型。

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  sourcemap: 'linked', // 默认 'none'
})

CLI

bash
bun build ./index.tsx --outdir ./out --sourcemap linked
描述
"none"默认。不生成 sourcemap。
"linked"使用 //# sourceMappingURL 注释链接两个文件,为每个 *.js 打包创建单独的 *.js.map 文件。需要设置 --outdir。这可以使用 --public-path 自定义。

js<br/>// <此处为打包代码><br/><br/>//# sourceMappingURL=bundle.js.map<br/>
"external"创建单独的 *.js.map 文件,与每个 *.js 打包一起,不插入 //# sourceMappingURL 注释。

生成的打包包含可用于将打包与其对应的 sourcemap 关联的调试 ID。此 debugId 作为注释添加到文件底部。

js<br/>// <生成的打包代码><br/><br/>//# debugId=<DEBUG ID><br/>
"inline"生成 sourcemap 并作为 base64 负载附加到生成的打包末尾。

js<br/>// <此处为打包代码><br/><br/>//# sourceMappingURL=data:application/json;base64,<encoded sourcemap here><br/>

关联的 *.js.map sourcemap 将是包含等效 debugId 属性的 JSON 文件。

minify

是否启用压缩。默认 false

NOTE

当目标为 `bun` 时,标识符将默认被压缩。

要启用所有压缩选项:

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  minify: true, // 默认 false
})

CLI

```bash
bun build ./index.tsx --outdir ./out --minify
```

要细粒度启用某些压缩:

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  minify: {
    whitespace: true,
    identifiers: true,
    syntax: true,
  },
})

CLI

bash
bun build ./index.tsx --outdir ./out --minify-whitespace --minify-identifiers --minify-syntax

external

要视为外部的导入路径列表。默认 []

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  external: ["lodash", "react"], // 默认:[]
})

CLI

bash
bun build ./index.tsx --outdir ./out --external lodash --external react

外部导入是不会包含在最终打包中的导入。相反,导入语句将保持原样,在运行时解析。

例如,考虑以下入口点文件:

tsx
import _ from "lodash";
import { z } from "zod";

const value = z.string().parse("Hello world!");
console.log(_.upperCase(value));

通常,打包 index.tsx 将生成包含 "zod" 包完整源代码的打包。相反,如果我们想保持导入语句原样,我们可以将其标记为外部:

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  external: ['zod'],
})

CLI

bash
bun build ./index.tsx --outdir ./out --external zod

生成的打包看起来像这样:

js
import { z } from "zod";

// ...
// "lodash" 包的内容
// 包括 `_.upperCase` 函数

var value = z.string().parse("Hello world!");
console.log(_.upperCase(value));

要将所有导入标记为外部,使用通配符 *

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  external: ['*'],
})

CLI

bash
bun build ./index.tsx --outdir ./out --external '*'

packages

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

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.ts'],
  packages: 'external',
})

CLI

bash
bun build ./index.ts --packages external

naming

自定义生成的文件名。默认 ./[dir]/[name].[ext]

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  naming: "[dir]/[name].[ext]", // 默认
})

CLI

bash
bun build ./index.tsx --outdir ./out --entry-naming "[dir]/[name].[ext]"

默认情况下,生成的打包名称基于相关入口点的名称。

text
.
├── index.tsx
└── out
    └── index.js

使用多个入口点时,生成的文件层次结构将反映入口点的目录结构。

text
.
├── 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"

我们可以组合这些标记来创建模板字符串。例如,要在生成的打包名称中包含哈希:

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  naming: 'files/[dir]/[name]-[hash].[ext]',
})

CLI

bash
bun build ./index.tsx --outdir ./out --entry-naming 'files/[dir]/[name]-[hash].[ext]'

此构建将生成以下文件结构:

text
.
├── index.tsx
└── out
    └── files
        └── index-a1b2c3d4.js

当为 naming 字段提供字符串时,它仅用于对应于入口点的打包。chunk 和复制资源的名称不受影响。使用 JavaScript API,可以为每种类型的生成文件指定单独的模板字符串。

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  naming: {
    // 默认值
    entry: '[dir]/[name].[ext]',
    chunk: '[name]-[hash].[ext]',
    asset: '[name]-[hash].[ext]',
  },
})

CLI

bash
bun build ./index.tsx --outdir ./out \
  --entry-naming '[dir]/[name].[ext]' \
  --chunk-naming '[name]-[hash].[ext]' \
  --asset-naming '[name]-[hash].[ext]'

root

项目的根目录。

JavaScript

ts
await Bun.build({
  entrypoints: ['./pages/a.tsx', './pages/b.tsx'],
  outdir: './out',
  root: '.',
})

CLI

bash
bun build ./pages/a.tsx ./pages/b.tsx --outdir ./out --root .

如果未指定,它被计算为所有入口点文件的第一个共同祖先。考虑以下文件结构:

text
.
└── pages
  └── index.tsx
  └── settings.tsx

我们可以在 pages 目录中构建两个入口点:

JavaScript

js
await Bun.build({
  entrypoints: ['./pages/index.tsx', './pages/settings.tsx'],
  outdir: './out',
})

CLI

bash
bun build ./pages/index.tsx ./pages/settings.tsx --outdir ./out

这将生成如下文件结构:

text
.
└── pages
  └── index.tsx
  └── settings.tsx
└── out
  └── index.js
  └── settings.js

由于 pages 目录是入口点文件的第一个共同祖先,它被视为项目根目录。这意味着生成的打包位于 out 目录的顶层;没有 out/pages 目录。

可以通过指定 root 选项覆盖此行为:

JavaScript

js
await Bun.build({
  entrypoints: ['./pages/index.tsx', './pages/settings.tsx'],
  outdir: './out',
  root: '.',
})

CLI

bash
bun build ./pages/index.tsx ./pages/settings.tsx --outdir ./out --root .

通过指定 . 作为 root,生成的文件结构将如下所示:

.
└── pages
  └── index.tsx
  └── settings.tsx
└── out
  └── pages
    └── index.js
    └── settings.js

publicPath

要附加到打包代码中任何导入路径的前缀。

在许多情况下,生成的打包将不包含导入语句。毕竟,打包的目标是将所有代码组合到一个文件中。但是,生成的打包将包含导入语句的情况有很多。

  • 资源导入 — 导入无法识别的文件类型(如 *.svg)时,打包器会移交给文件加载器,后者将文件按原样复制到 outdir。导入被转换为变量
  • 外部模块 — 文件和模块可以标记为外部,在这种情况下它们将不包含在打包中。相反,导入语句将保留在最终打包中。
  • 代码分割。 当启用 splitting 时,打包器可能生成单独的 "chunk" 文件,表示在多个入口点之间共享的代码。

在任何这些情况下,最终打包可能包含指向其他文件的路径。默认情况下,这些导入是相对的。这是一个简单资源导入的示例:

ts
import logo from "./logo.svg";
console.log(logo);
ts
var logo = "./logo-a7305bdef.svg";
console.log(logo);

设置 publicPath 将使用指定值作为所有文件路径的前缀。

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  publicPath: 'https://cdn.example.com/', // 默认是 undefined
})

CLI

bash
bun build ./index.tsx --outdir ./out --public-path 'https://cdn.example.com/'

输出文件现在看起来像这样。

js
var logo = "https://cdn.example.com/logo-a7305bdef.svg";

define

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

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  define: {
    STRING: JSON.stringify("value"),
    "nested.boolean": "true",
  },
})

CLI

bash
bun build ./index.tsx --outdir ./out --define STRING='"value"' --define nested.boolean=true

loader

文件扩展名到内置加载器名称的映射。这可用于快速自定义如何加载某些文件。

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  loader: {
    ".png": "dataurl",
    ".txt": "file",
  },
})

CLI

bash
bun build ./index.tsx --outdir ./out --loader .png:dataurl --loader .txt:file

要添加到最终打包的横幅,这可以是 React 的 use client 指令或代码的许可证注释块等。

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  banner: 'use client;'
})

CLI

bash
bun build ./index.tsx --outdir ./out --banner 'use client";'

要添加到最终打包的页脚,这可以是许可证的注释块或有趣的彩蛋。

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  footer: '// built with love in SF'
})

CLI

bash
bun build ./index.tsx --outdir ./out --footer '// built with love in SF'

drop

从打包中移除函数调用。例如,--drop=console 将移除所有对 console.log 的调用。调用的参数也将被移除,无论这些参数是否有副作用。丢弃 debugger 将移除所有 debugger 语句。

JavaScript

ts
await Bun.build({
  entrypoints: ['./index.tsx'],
  outdir: './out',
  drop: ["console", "debugger", "anyIdentifier.or.propertyAccess"],
})

CLI

bash
bun build ./index.tsx --outdir ./out --drop console --drop debugger

输出

Bun.build 函数返回 Promise<BuildOutput>,定义为:

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 数组包含构建生成的所有文件。每个工件都实现了 Blob 接口。

ts
const build = await Bun.build({
  /* */
});

for (const output of build.outputs) {
  await output.arrayBuffer(); // => ArrayBuffer
  await output.bytes(); // => Uint8Array
  await output.text(); // string
}

每个工件还包含以下属性:

属性描述
kind此文件是什么类型的构建输出。构建生成打包的入口点、代码分割 "chunks"、sourcemap、字节码和复制的资源(如图像)。
path磁盘上文件的绝对路径
loader用于解释文件的加载器。请参阅 打包器 > 加载器 了解 Bun 如何将文件扩展名映射到适当的内置加载器。
hash文件内容的哈希。始终为资源定义。
sourcemap与此文件对应的 sourcemap 文件(如果生成)。仅为入口点和 chunk 定义。

BunFile 类似,BuildArtifact 对象可以直接传递到 new Response() 中。

ts
const build = await Bun.build({
  /* */
});

const artifact = build.outputs[0];

// Content-Type 头自动设置
return new Response(artifact);

Bun 运行时实现了 BuildArtifact 对象的特殊漂亮打印,以便更容易调试。

ts
// build.ts
const build = await Bun.build({
  /* */
});

const artifact = build.outputs[0];
console.log(artifact);
bash
bun run build.ts

BuildArtifact (entry-point) {
  path: "./index.js",
  loader: "tsx",
  kind: "entry-point",
  hash: "824a039620219640",
  Blob (74756 bytes) {
    type: "text/javascript;charset=utf-8"
  },
  sourcemap: BuildArtifact (sourcemap) {
    path: "./index.js.map",
    loader: "file",
    kind: "sourcemap",
    hash: "e7178cda3e72e301",
    Blob (24765 bytes) {
      type: "application/json;charset=utf-8"
    },
    sourcemap: null
  }
}

字节码

bytecode: boolean 选项可用于为任何 JavaScript/TypeScript 入口点生成字节码。这可以大大提高大型应用程序的启动时间。仅支持 "cjs" 格式,仅支持 "target": "bun" 并依赖于匹配的 Bun 版本。这为每个入口点添加相应的 .jsc 文件。

JavaScript

ts
await Bun.build({
  entrypoints: ["./index.tsx"],
  outdir: "./out",
  bytecode: true,
})

CLI

bash
bun build ./index.tsx --outdir ./out --bytecode

可执行文件

Bun 支持将 JavaScript/TypeScript 入口点"编译"为独立的可执行文件。此可执行文件包含 Bun 二进制文件的副本。

bash
bun build ./cli.tsx --outfile mycli --compile
./mycli

有关完整文档,请参阅 打包器 > 可执行文件

日志和错误

失败时,Bun.build 返回带有 AggregateError 的拒绝 Promise。这可以记录到控制台以漂亮打印错误列表,或使用 try/catch 块以编程方式读取。

ts
try {
  const result = await Bun.build({
    entrypoints: ["./index.tsx"],
    outdir: "./out",
  });
} catch (e) {
  // TypeScript 不允许在 catch 子句上标注
  const error = e as AggregateError;
  console.error("Build Failed");

  // 示例:使用内置格式化器
  console.error(error);

  // 示例:将失败序列化为 JSON 字符串。
  console.error(JSON.stringify(error, null, 2));
}

大多数时候,不需要显式的 try/catch,因为 Bun 会整洁地打印未捕获的异常。只需在 Bun.build 调用上使用顶层 await 就足够了。

error.errors 中的每个项目都是 BuildMessageResolveMessage 的实例(Error 的子类),包含每个错误的详细信息。

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 属性,其中包含打包器警告和信息消息。

ts
const result = await Bun.build({
  entrypoints: ["./index.tsx"],
  outdir: "./out",
});

if (result.logs.length > 0) {
  console.warn("Build succeeded with warnings:");
  for (const message of result.logs) {
    // Bun 将漂亮打印消息对象
    console.warn(message);
  }
}

参考

ts
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` 条件
   *
   * 等同于 `bun build` 或 `bun run` 中的 `--conditions`。
   *
   * https://nodejs.org/api/packages.html#exports
   */
  conditions?: Array<string> | string;

  /**
   * 控制打包期间如何处理环境变量。
   *
   * 可以是以下之一:
   * - `"inline"`:通过将 `process.env.FOO` 引用转换为包含实际环境变量值的字符串字面量,将环境变量注入到打包输出中
   * - `"disable"`:完全禁用环境变量注入
   * - 以 `*` 结尾的字符串:内联与给定前缀匹配的环境变量。
   *   例如,`"MY_PUBLIC_*"` 将仅包含以 "MY_PUBLIC_" 开头的环境变量
   */
  env?: "inline" | "disable" | `${string}*`;
  minify?:
    | boolean
    | {
        whitespace?: boolean;
        syntax?: boolean;
        identifiers?: boolean;
      };
  /**
   * 忽略死代码消除/摇树优化注释,如 @__PURE__ 和 package.json
   * "sideEffects" 字段。这应仅用作库中不正确注释的临时解决方案。
   */
  ignoreDCEAnnotations?: boolean;
  /**
   * 即使 minify.whitespace 为 true 也强制发出 @__PURE__ 注释。
   */
  emitDCEAnnotations?: boolean;

  /**
   * 为输出生成字节码。这可以大大提高冷
   * 启动时间,但会使最终输出更大并略微增加
   * 内存使用。
   *
   * 字节码目前仅支持 CommonJS(`format: "cjs"`)。
   *
   * 必须是 `target: "bun"`
   * @default false
   */
  bytecode?: boolean;
  /**
   * 向打包代码添加横幅,如 "use client";
   */
  banner?: string;
  /**
   * 向打包代码添加页脚,如注释块
   *
   * `// made with bun!`
   */
  footer?: string;

  /**
   * 丢弃与属性访问匹配的函数调用。
   */
  drop?: string[];

  /**
   * - 当设置为 `true` 时,当构建失败时,返回的 Promise 以 AggregateError 拒绝。
   * - 当设置为 `false` 时,返回 `{success: false}` 的 {@link 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;
}

Bun学习网由www.bunjs.com.cn整理维护