본문으로 건너뛰기 Bun 1.1 Complete Guide — Build, Bundling, Testing, Performance vs Node.js, Framework Integration

Bun 1.1 Complete Guide — Build, Bundling, Testing, Performance vs Node.js, Framework Integration

Bun 1.1 Complete Guide — Build, Bundling, Testing, Performance vs Node.js, Framework Integration

이 글의 핵심

Bun 1.1 brings Windows support, a unified toolchain (runtime, package manager, bundler, tests), and practical patterns for Next.js, React, Astro, Docker, and CI—with caveats for fast-moving native and framework stacks.

Key takeaways

Compared with 1.0, Bun 1.1 shipped 1,700+ commits and thousands of bug fixes, maturing as an all-in-one JavaScript runtime that combines runtime, package manager, bundler, and test runner in one toolchain. Official support for Windows 10+ greatly widened accessibility, while the speed of bun install, bun run, and bunx, --watch-driven iteration, and stronger Node.js API compatibility are now noticeable in day-to-day work.

This article covers Bun 1.1’s core features (ultra-fast runtime, built-in bundler, Jest-compatible testing) and a performance lens vs Node.js and npm (benchmark numbers vary by environment—interpretation matters). It then connects patterns with Next.js, React, and Astro, a small end-to-end sample, and deployment from Docker, CI, and hosting angles.

Caution: Bun moves fast. Before production, run regression tests against your framework, plugins, and native module combinations.


1. What Bun 1.1 changed: one-line summary

AreaHighlights in Bun 1.1
PlatformFull support on Windows for runtime, bundler, tests, and package manager
Packagesbun install speed, parallel lifecycle scripts, lockfile migration via bun pm migrate
RuntimeLarge-file transpile cache, Bun Shell (Bun.$), import.meta.env, broader Node-compatible APIs
BundlerStable --target=node, --compile with NAPI (.node) embedding, built-in modules in macros
TestsExpanded Jest-style matchers, ESM/CJS mocks, runtime in-place mocking

2. Core feature ① Ultra-fast runtime and unified tooling

2.1 Bun as a runtime

Bun is a JavaScriptCore-based JavaScript runtime that runs TypeScript and JSX with no extra setup. Where Node.js became the de facto server-side standard through npm, core APIs, and ecosystem, Bun aims to cut tool-switching cost by shipping install, run, bundle, and test in a single binary.

2.2 Transpiler cache and CLI perceived performance

In Bun 1.1, content-addressed caching for files over 50KB reduces repeated transpile work. As a result, running CLI tools like tsc on Bun has been reported to feel up to roughly 2× faster than before (depends on project size, disk, and CPU).

2.3 Bun Shell and cross-platform scripting

Bun 1.1 emphasizes Bun Shell so bash-like syntax stays consistent on Windows. When you run package.json scripts via bun run, the goal is to reduce breakage from cmd vs sh differences.

import { $ } from "bun";

// Pipe to stdout
await $`ls *.ts`;

// Result as string
const listing = await $`ls *.ts`.text();

Why it matters: On Windows, shebangs (#!/usr/bin/env node) are ignored, commands like rm -rf are not available by default, and env var syntax differs. With mixed Windows, macOS, and Linux teams, shell portability is productivity.

Security note: Do not pass raw user input into the shell; follow Bun Shell’s argument escaping patterns.


3. Core feature ② Built-in bundler (bun build)

Bun’s bundler can emit for browser, Bun, and Node.js targets, handling transpile, bundle, and minification in one pipeline.

3.1 Node target: --target=node

Use --target=node when you want a single file that runs on Node.js for libraries or CLIs. Issues such as require-related errors reported in 1.0 were addressed in 1.1, making bundles that include built-ins like node:fs easier to run under Node.

bun build ./src/cli.ts --target=node --outfile=dist/cli.mjs
node dist/cli.mjs

When to use: CLI packages on npm, single-file serverless handlers, or pinning output for legacy Node environments.

3.2 Single executable: --compile

--compile bundles TypeScript/JavaScript into a single binary. In Bun 1.1, embedding NAPI addons (.node) makes it easier to ship tools that depend on canvas or native code as one artifact.

bun build --compile ./src/app.ts --outfile=myapp
./myapp

Limits: Outputs can differ by native modules, platform ABI, and shared libraries—plan per-OS build pipelines.

3.3 Macros and build-time execution

Bun macros run code at build time and embed results into the bundle. In Bun 1.1, you can import built-ins like node:fs reads and child_process inside macros, simplifying build-time code generation.

import { readFileSync } from "node:fs" with { type: "macro" };

export const buildTimeText = readFileSync("./version.txt", "utf8");

Production tip: Macros can break reproducible builds. In CI, pin inputs—keep read files in the repo under version control.


4. Core feature ③ Test runner (bun test)

Bun tests expose a Jest-like API (describe, test, expect) and run TypeScript tests with minimal configuration.

4.1 Richer matchers

Bun 1.1 greatly expanded expect matchers—partial object matching, whitespace-insensitive comparison, Promise result matching—suited to codebases that prefer explicit assertions over snapshots.

import { expect, test } from "bun:test";

test("partial object match", () => {
  expect({ a: { b: 1, c: 2 } }).toEqual({
    a: expect.objectContaining({ b: 1 }),
  });
});

4.2 Module mocks and ESM/CJS

Bun stresses mocking both ESM and CommonJS, including in-place updates for already-imported modules at runtime—often easing Jest-style hoisting / mock order pain.

import { mock, test, expect } from "bun:test";
import { fn } from "./math";

test("local module mock", () => {
  mock.module("./math", () => ({ fn: () => 42 }));
  expect(fn()).toBe(42);
});

Caution: Mocking can leave global test pollution. Pair with per-file isolation strategies (e.g. --preload reset hooks).

4.3 --watch and the dev loop

bun test --watch reruns quickly on file changes. The Bun 1.1 announcement also highlights Windows optimizations to cut save-to-rerun latency. In large monorepos, narrow test scope to directories when possible.


5. Performance vs Node.js and npm: how to read numbers

5.1 Official Windows-oriented examples

Bun 1.1 materials cite a Vite React template on Windows where bun install was up to ~18× faster than yarn and ~30× faster than npm in their scenario; bun run vs npm run up to ~11×, and bunx vs npx up to ~11×.

How to interpret:

  1. Ratios swing with cache state, whether node_modules was removed, network, disk, and antivirus.
  2. Production runtime performance and install speed are different problems—the former depends on app code, I/O, and GC.
  3. Compatibility trumps speed—validate against core dependencies.

5.2 .bunx and execution paths on Windows

Windows often degrades scripting via symlinks, shebangs, and batch prompts (“Terminate batch job?”). Bun 1.1 introduced .bunx to mitigate this. Even if you use Bun only as a package manager, generated executables are intended to work alongside Node—important in practice.

5.3 Filesystem API micro-benchmarks

The same API can differ in speed by implementation—for example, fs.readdir() on Windows has shown meaningful gains vs Node in Bun’s benchmarks. For APIs not yet optimized, track issues and plan workarounds.


6. Integration with Next.js, React, and Astro

6.1 Common baseline: Bun as package manager

The safest first step in most frontend projects is replacing npm/pnpm installs with bun install and running scripts with bun run. If you have package-lock.json, bun pm migrate (or Bun’s automatic migration) can produce bun.lockb.

bun install
bun run dev

6.2 React (e.g. Vite / CRA-style)

React itself does not require Node at runtime; the build chain matters. With Vite + React, Bun usually accelerates installs, script execution, and sometimes tests (bun test). Whether to replace the bundler entirely depends on team standards and plugin compatibility.

6.3 Next.js: separate runtime from bundler

Next.js owns its build pipeline (Turbopack/Webpack, etc.). A common pattern is Bun for installs and the dev server process while leaving Next’s bundler unchanged. Some platforms (e.g. Vercel) offer Bun as a function runtime option.

For local Bun + Next, project-specific scripts may look like this (adjust to your template and Next version):

{
  "scripts": {
    "dev": "bun --bun next dev",
    "build": "bun --bun next build",
    "start": "bun --bun next start"
  }
}

Recommendation: Next releases quickly and combines native modules, Server Components, and edge runtimes—validate staging E2E and critical API paths.

6.4 Astro (SSG, edge-friendly)

Astro focuses on static output and islands to shrink bundles. In this blog’s stack (Astro + Cloudflare Pages), Bun fits accelerating the dev loop.

bun create astro@latest my-astro-site
cd my-astro-site
bun install
bun run dev

Cloudflare adapter, image optimization, MDX, and other plugin combos may still assume Node—track issues if something breaks.


7. Hands-on sample: mini API + tests + single-binary deploy

Below is a minimal flow: Bun HTTP server, unit tests, and compiled output.

7.1 Project layout

mini-bun-service/
├─ package.json
├─ src/
│  ├─ server.ts
│  └─ server.test.ts
└─ README.md

7.2 package.json

{
  "name": "mini-bun-service",
  "type": "module",
  "scripts": {
    "dev": "bun --watch src/server.ts",
    "start": "bun src/server.ts",
    "test": "bun test",
    "build:bin": "bun build --compile src/server.ts --outfile=dist/server"
  }
}

bun --watch restarts the server on changes for a faster local iteration loop.

7.3 src/server.ts

const port = Number(process.env.PORT ?? 3000);

export function createServer() {
  return Bun.serve({
    port,
    fetch(req) {
      const url = new URL(req.url);
      if (url.pathname === "/health") {
        return Response.json({ ok: true, bun: process.versions.bun ?? "dev" });
      }
      return new Response("Not Found", { status: 404 });
    },
  });
}

if (import.meta.main) {
  const server = createServer();
  console.log(`listening on ${server.url}`);
}

Note: Bun.serve stands up a dev API server with very little code. In production, you typically add a reverse proxy, logging, rate limits, and security headers.

7.4 src/server.test.ts

import { describe, test, expect } from "bun:test";
import { createServer } from "./server.ts";

describe("/health", () => {
  test("returns 200 and JSON body", async () => {
    const server = createServer();
    try {
      const res = await fetch(`${server.url}health`);
      expect(res.status).toBe(200);
      const body = await res.json();
      expect(body.ok).toBe(true);
    } finally {
      server.stop();
    }
  });
});

7.5 Build and run

bun test
bun run build:bin
./dist/server

Extensions: env validation (e.g. Zod), structured logging, OpenTelemetry, aligning /health with load balancer health checks.


8. Deployment strategy

8.1 Containers (Docker)

Prefer official Bun images or multi-stage builds: install deps → test → copy --compile artifact. Slim runtime images help cold start and attack surface.

# Example: run tests in the build stage
FROM oven/bun:1 AS build
WORKDIR /app
COPY package.json bun.lockb* ./
RUN bun install --frozen-lockfile
COPY . .
RUN bun test && bun build --compile src/server.ts --outfile /out/server

FROM gcr.io/distroless/base-debian12
COPY --from=build /out/server /server
ENV PORT=8080
USER nonroot:nonroot
ENTRYPOINT ["/server"]

Note: Match base image and libc when embedding .node binaries.

8.2 Static and hybrid frontends (Astro, Next export, etc.)

  • Static output (Astro dist/) goes to static hosting (Cloudflare Pages, Netlify, S3+CloudFront). Bun speeds the build pipeline.
  • For server features, set edge/serverless targets per adapter and confirm in platform docs whether the runtime is Bun or Node.

8.3 CI (GitHub Actions, etc.)

Include bun.lockb in cache keys and use bun install --frozen-lockfile for reproducible installs. Add quality gates with bun test --coverage (as configured).

# Conceptual example—tune to your repo
- uses: oven-sh/setup-bun@v2
  with:
    bun-version: latest
- run: bun install --frozen-lockfile
- run: bun test

8.4 Rollback and observability

Tag releases clearly so you can roll back to the previous binary or assets quickly, and dashboard error rate, latency, and CPU. Switching to Bun does not simplify your application’s algorithmic complexity.


9. Troubleshooting checklist

  1. Native modules: better-sqlite3, sharp, etc. tie to Node ABI—evaluate Bun-native options (bun:sqlite, etc.) or keep those paths on Node.
  2. Next / bundler plugins: Experimental combos can be flaky—build a minimal repro repo when debugging.
  3. Dates, regex, engine differences: Bun uses JavaScriptCore; subtle differences vs V8 may remain. Bun 1.1 improved areas like Date.parse, but time zones and formats still need tests.
  4. Windows paths: Path-related bugs land across versions—always use path.join-style normalization.

10. Summary

Bun 1.1 raises “production readiness” across Windows support, toolchain completeness, perceived speed vs Node/npm, and unified bundle/compile/test. Still, frameworks, platforms, and native deps vary by project—adopt package management, scripts, and tests first, and switch production runtime only after validation.

If you want follow-on topics (monorepos, Bun runtime settings on a specific host, migration checklists), say what you need.