Skip to content

Bun Module System

Bun supports multiple module systems, including ES Modules (ESM), CommonJS (CJS), and Bun's own module resolution. This chapter introduces Bun's module system and its features.

ES Modules (ESM)

ES Modules are JavaScript's standard module system, and the recommended way to use Bun.

Exporting Modules

typescript
// math.ts

// Named exports
export const PI = 3.14159;

export function add(a: number, b: number): number {
  return a + b;
}

export function multiply(a: number, b: number): number {
  return a * b;
}

// Default export
export default class Calculator {
  add(a: number, b: number) {
    return a + b;
  }
  
  subtract(a: number, b: number) {
    return a - b;
  }
}

Importing Modules

typescript
// main.ts

// Import default export
import Calculator from "./math";

// Import named exports
import { PI, add, multiply } from "./math";

// Import all and namespace
import * as math from "./math";

// Mixed import
import Calculator, { PI, add } from "./math";

// Use
const calc = new Calculator();
console.log(calc.add(2, 3));
console.log(add(2, 3));
console.log(math.PI);

Dynamic Import

typescript
// Dynamic import (returns Promise)
async function loadModule() {
  const math = await import("./math");
  console.log(math.add(2, 3));
  
  // Conditional import
  if (process.env.NODE_ENV === "development") {
    const devTools = await import("./dev-tools");
    devTools.init();
  }
}

Re-exporting

typescript
// index.ts - Module aggregation

// Re-export all
export * from "./math";
export * from "./string";

// Re-export some
export { add, multiply } from "./math";

// Rename export
export { add as sum } from "./math";

// Re-export default export
export { default as Calculator } from "./math";

CommonJS (CJS)

Bun fully supports the CommonJS module system, ensuring compatibility with the existing Node.js ecosystem.

CommonJS Exports

javascript
// utils.js

// Export single value
module.exports = function greet(name) {
  return `Hello, ${name}!`;
};

// Export multiple values
module.exports = {
  greet: function(name) {
    return `Hello, ${name}!`;
  },
  farewell: function(name) {
    return `Goodbye, ${name}!`;
  }
};

// Use exports shortcut
exports.greet = function(name) {
  return `Hello, ${name}!`;
};

exports.farewell = function(name) {
  return `Goodbye, ${name}!`;
};

CommonJS Imports

javascript
// main.js

// Import entire module
const utils = require("./utils");
console.log(utils.greet("Bun"));

// Destructuring import
const { greet, farewell } = require("./utils");
console.log(greet("Bun"));

ESM and CJS Interoperability

Importing CJS in ESM

typescript
// cjs-module.js (CommonJS)
module.exports = {
  name: "CJS Module",
  greet() {
    return "Hello from CJS!";
  }
};

// esm-main.ts (ESM)
import cjsModule from "./cjs-module.js";
console.log(cjsModule.greet());

// Named imports also work (Bun handles automatically)
import { name, greet } from "./cjs-module.js";

Importing ESM in CJS

javascript
// esm-module.ts (ESM)
export const message = "Hello from ESM!";
export default { name: "ESM Module" };

// cjs-main.js (CommonJS)
// Use dynamic import()
const loadESM = async () => {
  const esmModule = await import("./esm-module.ts");
  console.log(esmModule.message);
  console.log(esmModule.default);
};

loadESM();

Module Resolution

Resolution Order

Bun resolves modules in the following order:

  1. Built-in modules: bun:*, node:*
  2. Absolute paths: /path/to/module
  3. Relative paths: ./module, ../module
  4. node_modules: Search upward for node_modules

File Extensions

typescript
// Bun tries extensions in order
import { foo } from "./module";

// Attempt order:
// 1. ./module.ts
// 2. ./module.tsx
// 3. ./module.js
// 4. ./module.jsx
// 5. ./module/index.ts
// 6. ./module/index.tsx
// 7. ./module/index.js
// 8. ./module/index.jsx

Path Aliases

Configure in tsconfig.json:

json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/components/*"],
      "@utils/*": ["src/utils/*"]
    }
  }
}

Using aliases:

typescript
import { Button } from "@components/Button";
import { formatDate } from "@utils/date";
import config from "@/config";

Bun Built-in Modules

bun Module

typescript
// Use Bun global object directly
console.log(Bun.version);

// Or import bun module
import { $ } from "bun";

await $`echo "Hello from Bun Shell"`;

Common bun:* Modules

typescript
// SQLite
import { Database } from "bun:sqlite";

// FFI (Foreign Function Interface)
import { dlopen, FFIType } from "bun:ffi";

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

// Password hashing
import { password } from "bun";

Node.js Built-in Modules

Bun supports most Node.js built-in modules:

typescript
// File system
import fs from "fs";
import { readFile } from "fs/promises";

// Path handling
import path from "path";

// HTTP
import http from "http";
import https from "https";

// Other common modules
import os from "os";
import crypto from "crypto";
import util from "util";
import events from "events";
import stream from "stream";
import buffer from "buffer";

Using node: Prefix

typescript
// Recommended to use node: prefix (more explicit)
import fs from "node:fs";
import path from "node:path";
import { Buffer } from "node:buffer";

Importing JSON and Other Files

Importing JSON

typescript
// Import JSON directly
import config from "./config.json";
console.log(config.apiUrl);

// Type-safe JSON import
import packageJson from "./package.json" with { type: "json" };
console.log(packageJson.version);

Importing Text Files

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

Importing TOML

typescript
// Import TOML config
import config from "./config.toml";
console.log(config.database.host);

Module Caching

Caching Mechanism

Bun caches loaded modules; each module is executed only once:

typescript
// counter.ts
let count = 0;

export function increment() {
  return ++count;
}

// main.ts
import { increment } from "./counter";
import { increment as inc } from "./counter";

console.log(increment()); // 1
console.log(inc());       // 2 (same module instance)

Clearing Cache

typescript
// May need to clear cache in tests
delete require.cache[require.resolve("./module")];

Circular Dependencies

Handling Circular Dependencies

typescript
// a.ts
import { b } from "./b";
export const a = "A";
console.log("a.ts loaded, b =", b);

// b.ts
import { a } from "./a";
export const b = "B";
console.log("b.ts loaded, a =", a);

// main.ts
import { a } from "./a";
// Output:
// b.ts loaded, a = undefined
// a.ts loaded, b = B

Avoiding Circular Dependencies

typescript
// Method 1: Lazy import
export function getA() {
  const { a } = require("./a");
  return a;
}

// Method 2: Refactor code, extract shared module
// shared.ts - Shared code
// a.ts - imports shared
// b.ts - imports shared

package.json Configuration

type Field

json
{
  "type": "module"  // Use ESM
}
json
{
  "type": "commonjs"  // Use CJS (default)
}

exports Field

json
{
  "name": "my-package",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs",
      "types": "./dist/index.d.ts"
    },
    "./utils": {
      "import": "./dist/utils.mjs",
      "require": "./dist/utils.cjs"
    }
  }
}

Conditional Exports

json
{
  "exports": {
    ".": {
      "bun": "./src/index.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs",
      "default": "./dist/index.js"
    }
  }
}

Bun will prioritize the bun condition.

Summary

This chapter covered:

  • ✅ ES Modules import/export syntax
  • ✅ CommonJS module system
  • ✅ ESM and CJS interoperability
  • ✅ Module resolution rules and path aliases
  • ✅ Bun and Node.js built-in modules
  • ✅ JSON, TOML, and other file imports
  • ✅ Module caching and circular dependency handling

Next Steps

Continue reading TypeScript Support to learn about Bun's native TypeScript support.

Content is for learning and research only.