Chapter 07

bun build:打包器

一个内置于运行时的打包器,兼顾浏览器与服务器,适合库、CLI、函数式部署。

7.1 命令行用法

$ bun build src/index.ts --outdir dist
$ bun build src/index.ts --outfile dist/bundle.js

$ bun build src/index.ts \
    --target=browser \
    --minify \
    --sourcemap=linked \
    --splitting \
    --outdir dist

7.2 三种 target

browser(默认)
产物面向浏览器——bundle 所有依赖(含 node_modules),不含 Node.js 专属 API。
bun
面向 Bun 运行时——保留 Bun 专属 API(Bun.file 等),externals 不 bundle 本地依赖。
node
面向 Node.js——产物兼容 Node,external 所有 node:* 内置模块。
$ bun build src/cli.ts --target=node   --outfile dist/cli.cjs
$ bun build src/app.ts --target=bun    --outfile dist/app.js
$ bun build src/main.ts --target=browser --outfile dist/main.js

7.3 JS API

import { $ } from "bun";

const r = await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  target: "browser",
  format: "esm",                    // esm | cjs(仅 node target)| iife
  splitting: true,
  minify: true,
  sourcemap: "linked",
  external: ["react", "react-dom"],
  define: { "process.env.NODE_ENV": '"production"' },
});
if (!r.success) for (const e of r.logs) console.error(e);

7.4 code splitting

--splittingsplitting: true 启用——每个动态 import() 会切成独立 chunk:

// src/main.ts
document.getElementById("btn").addEventListener("click", async () => {
  const { bigFeature } = await import("./bigFeature");
  bigFeature();
});

产物:

dist/
├── main.js          # 主 chunk(首屏需要)
└── bigFeature-xxx.js # 按需加载

7.5 插件(bun build plugin)

API 和 esbuild 插件类似:

import type { BunPlugin } from "bun";

const yamlPlugin: BunPlugin = {
  name: "yaml",
  setup(build) {
    build.onLoad({ filter: /\.ya?ml$/ }, async (args) => {
      const text = await Bun.file(args.path).text();
      const yaml = await import("yaml");
      return {
        contents: `export default ${JSON.stringify(yaml.parse(text))}`,
        loader: "js",
      };
    });
  },
};

await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "dist",
  plugins: [yamlPlugin],
});

运行时注册插件

插件也可注册到运行时 loader——让 import config from "./config.yaml" 直接工作:

// preload.ts
import { plugin } from "bun";
plugin(yamlPlugin);
# bunfig.toml
preload = ["./preload.ts"]

7.6 naming:自定义输出文件名

Bun.build({
  entrypoints: ["src/a.ts", "src/b.ts"],
  outdir: "dist",
  naming: {
    entry: "[dir]/[name].[ext]",
    chunk: "chunks/[name]-[hash].[ext]",
    asset: "assets/[name]-[hash].[ext]",
  },
});

7.7 loader 类型

Bun 对常见资源内置 loader:

扩展名loader产物
.ts/.tsx/.js/.jsx默认编译后 JS
.jsonjsonJSON 对象
.txt/.htmltext字符串
.tomltoml对象
.png/.jpg/.webp/.svgfileURL + 哈希文件
.wasmwasmWebAssembly.Module

7.8 bun build vs Vite / esbuild / tsup

bun buildesbuildVitetsup
dev server
HMR
打包速度极快极快快(Rollup)
库打包
--compile 单文件

适用场景:

7.9 常见配方

打包一个 npm 库(ESM + CJS + .d.ts)

# ESM
bun build src/index.ts --outdir dist --target=node --format=esm --external '*'
# CJS
bun build src/index.ts --outdir dist --target=node --format=cjs --external '*'
# 类型声明(tsc 生成)
tsc --emitDeclarationOnly --outDir dist

打包 CLI(shebang)

bun build src/cli.ts --target=node --outfile dist/cli.js --banner='#!/usr/bin/env node'

7.10 小结