Skip to content

Bun Bundler

Bun includes a high-performance bundler that can package JavaScript/TypeScript code for browser or server environments. This chapter introduces how to use the Bun bundler.

Basic Bundling

Command Line Bundling

bash
# Basic bundling
bun build ./src/index.ts --outdir ./dist

# Specify output file
bun build ./src/index.ts --outfile ./dist/bundle.js

# Generate source map
bun build ./src/index.ts --outdir ./dist --sourcemap

# Minify code
bun build ./src/index.ts --outdir ./dist --minify

API Bundling

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

if (!result.success) {
  console.error("Build failed:");
  for (const log of result.logs) {
    console.error(log);
  }
} else {
  console.log("Build successful!");
  for (const output of result.outputs) {
    console.log("Output:", output.path);
  }
}

Build Options

Full Configuration

typescript
const result = await Bun.build({
  // Entry files (can be multiple)
  entrypoints: ["./src/index.ts", "./src/worker.ts"],
  
  // Output directory
  outdir: "./dist",
  
  // Target environment
  target: "browser", // "browser" | "bun" | "node"
  
  // Module format
  format: "esm", // "esm" | "cjs" | "iife"
  
  // Code splitting
  splitting: true,
  
  // Minification
  minify: true,
  // Or set separately
  // minify: {
  //   whitespace: true,
  //   identifiers: true,
  //   syntax: true,
  // },
  
  // Source Map
  sourcemap: "external", // "none" | "inline" | "external"
  
  // Naming pattern
  naming: {
    entry: "[name].[hash].js",
    chunk: "[name]-[hash].js",
    asset: "[name]-[hash][ext]",
  },
  
  // External dependencies (not bundled)
  external: ["react", "react-dom"],
  
  // Define global constants
  define: {
    "process.env.NODE_ENV": JSON.stringify("production"),
    __VERSION__: JSON.stringify("1.0.0"),
  },
  
  // Plugins
  plugins: [],
  
  // Loader configuration
  loader: {
    ".png": "file",
    ".svg": "text",
  },
  
  // Public path
  publicPath: "/assets/",
  
  // Root directory
  root: "./src",
});

Target Environment

Browser Target

typescript
await Bun.build({
  entrypoints: ["./src/app.ts"],
  outdir: "./dist",
  target: "browser",
  
  // Browser-specific options
  format: "esm",
  splitting: true,
  minify: true,
});

Node.js Target

typescript
await Bun.build({
  entrypoints: ["./src/server.ts"],
  outdir: "./dist",
  target: "node",
  
  // Node.js specific
  format: "cjs", // or "esm"
  external: ["*"], // don't bundle node_modules
});

Bun Target

typescript
await Bun.build({
  entrypoints: ["./src/app.ts"],
  outdir: "./dist",
  target: "bun",
  
  // Bun runtime optimizations
});

Code Splitting

Automatic Code Splitting

typescript
await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  splitting: true, // Enable code splitting
});

Dynamic Import

typescript
// src/index.ts
// Dynamic imports will be automatically split into separate chunks
const module = await import("./heavy-module.ts");
module.doSomething();

// Conditional import
if (needFeature) {
  const feature = await import("./feature.ts");
  feature.init();
}

Multi-Entry Splitting

typescript
await Bun.build({
  entrypoints: [
    "./src/pages/home.ts",
    "./src/pages/about.ts",
    "./src/pages/contact.ts",
  ],
  outdir: "./dist",
  splitting: true,
});

// Shared code will be automatically extracted into common chunks

Asset Handling

Built-in Loaders

typescript
await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  loader: {
    // Output as files (return URL)
    ".png": "file",
    ".jpg": "file",
    ".gif": "file",
    ".woff2": "file",
    
    // Import as text
    ".txt": "text",
    ".md": "text",
    ".html": "text",
    
    // Import as JSON
    ".json": "json",
    
    // Import as Base64 data URL
    ".svg": "dataurl",
    
    // Import as binary data
    ".bin": "binary",
    
    // JavaScript/TypeScript
    ".js": "js",
    ".ts": "ts",
    ".jsx": "jsx",
    ".tsx": "tsx",
    
    // CSS
    ".css": "css",
  },
});

Importing Assets

typescript
// Import images (returns URL)
import logoUrl from "./logo.png";
const img = document.createElement("img");
img.src = logoUrl;

// Import text
import readme from "./README.md" with { type: "text" };
console.log(readme);

// Import JSON
import config from "./config.json";
console.log(config.version);

CSS Handling

Importing CSS

typescript
// Import CSS file
import "./styles.css";

// CSS modules (experimental)
import styles from "./component.module.css";
element.className = styles.container;

CSS Bundling

typescript
await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  
  // CSS will be bundled automatically
});

External Dependencies

Excluding Dependencies

typescript
await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  
  // Don't bundle these dependencies
  external: ["react", "react-dom", "lodash"],
});

Excluding All node_modules

typescript
await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  
  // Use wildcard to exclude all
  external: ["*"],
});

Dynamic Exclusion

typescript
import { dependencies } from "./package.json";

await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  external: Object.keys(dependencies),
});

Environment Variable Replacement

define Option

typescript
await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  define: {
    "process.env.NODE_ENV": JSON.stringify("production"),
    "process.env.API_URL": JSON.stringify("https://api.example.com"),
    __DEV__: "false",
    __VERSION__: JSON.stringify("1.0.0"),
  },
});

Usage in Code

typescript
// src/index.ts
if (process.env.NODE_ENV === "development") {
  console.log("Development mode");
}

console.log("Version:", __VERSION__);
console.log("API:", process.env.API_URL);

Plugin System

Plugin Interface

typescript
import type { BunPlugin } from "bun";

const myPlugin: BunPlugin = {
  name: "my-plugin",
  
  setup(build) {
    // Intercept module resolution
    build.onResolve({ filter: /^virtual:/ }, (args) => {
      return {
        path: args.path,
        namespace: "virtual",
      };
    });
    
    // Intercept module loading
    build.onLoad({ filter: /.*/, namespace: "virtual" }, (args) => {
      return {
        contents: `export default "${args.path}"`,
        loader: "js",
      };
    });
  },
};

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

YAML Plugin Example

typescript
import { parse as parseYaml } from "yaml";

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

// Usage
import config from "./config.yaml";
console.log(config);

Environment Variable Plugin

typescript
const envPlugin: BunPlugin = {
  name: "env-plugin",
  
  setup(build) {
    build.onResolve({ filter: /^env:/ }, (args) => {
      return {
        path: args.path.slice(4), // Remove "env:" prefix
        namespace: "env",
      };
    });
    
    build.onLoad({ filter: /.*/, namespace: "env" }, (args) => {
      const value = Bun.env[args.path] || "";
      return {
        contents: `export default ${JSON.stringify(value)}`,
        loader: "js",
      };
    });
  },
};

// Usage
import apiKey from "env:API_KEY";

Build Scripts

package.json Configuration

json
{
  "scripts": {
    "build": "bun run build.ts",
    "build:dev": "bun run build.ts --mode development",
    "build:prod": "bun run build.ts --mode production"
  }
}

Complete Build Script

typescript
// build.ts
const mode = Bun.argv.includes("--mode")
  ? Bun.argv[Bun.argv.indexOf("--mode") + 1]
  : "production";

const isDev = mode === "development";

console.log(`Build mode: ${mode}`);

// Clean output directory
await Bun.$`rm -rf dist`;

// Build client code
const clientResult = await Bun.build({
  entrypoints: ["./src/client/index.tsx"],
  outdir: "./dist/public",
  target: "browser",
  format: "esm",
  splitting: true,
  minify: !isDev,
  sourcemap: isDev ? "inline" : "external",
  define: {
    "process.env.NODE_ENV": JSON.stringify(mode),
  },
  naming: {
    entry: isDev ? "[name].js" : "[name].[hash].js",
    chunk: isDev ? "[name].js" : "[name].[hash].js",
  },
});

if (!clientResult.success) {
  console.error("Client build failed");
  process.exit(1);
}

// Build server code
const serverResult = await Bun.build({
  entrypoints: ["./src/server/index.ts"],
  outdir: "./dist",
  target: "bun",
  format: "esm",
  minify: !isDev,
  external: ["*"], // Don't bundle dependencies
});

if (!serverResult.success) {
  console.error("Server build failed");
  process.exit(1);
}

// Copy static files
await Bun.$`cp -r ./public/* ./dist/public/`;

console.log("Build complete!");
console.log("Client files:");
clientResult.outputs.forEach(o => console.log("  ", o.path));
console.log("Server files:");
serverResult.outputs.forEach(o => console.log("  ", o.path));

Comparison with Other Tools

Speed Comparison

Bundling React app (with dependencies):
┌────────────────────────────────────────────┐
│ webpack  ██████████████████████████  12s   │
│ esbuild  ████                        1.5s  │
│ bun      ███                         1.1s  │
└────────────────────────────────────────────┘

Feature Comparison

FeatureBunesbuildwebpackVite
SpeedVery FastVery FastSlowFast
Config ComplexityLowLowHighMedium
Plugin EcosystemGrowingLimitedRichRich
Code Splitting
HMR
TypeScriptPlugin needed

Summary

This chapter covered:

  • ✅ Command line and API bundling
  • ✅ Build options and target environments
  • ✅ Code splitting and asset handling
  • ✅ External dependencies and environment variables
  • ✅ Plugin system
  • ✅ Complete build script examples

Next Steps

Continue reading Test Runner to learn about Bun's built-in testing framework.

Content is for learning and research only.