# Esbuild

> esbuild rules - extremely fast bundler, build API and CLI, formats (ESM/CJS/IIFE), platforms, loaders, plugins, code splitting, watch mode (context API), TypeScript transpile-only, library bundling dual ESM/CJS, metafile analysis

- Skill: `14bryanespinoza/esbuild` (Agent Skill)
- Install (CLI): `npx skillmds@latest add 14bryanespinoza/esbuild`
- Raw SKILL.md: https://api.skillmd.com/api/skills/14bryanespinoza/esbuild/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: 14BryanEspinoza (https://skillmd.com/u/14bryanespinoza)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/14bryanespinoza/esbuild

---


# esbuild — Rules and Conventions

---

## 1. Philosophy

1. **Speed first** — Written in Go, parallelized, no AST overhead. 10-100x faster than JS bundlers.
2. **Library, not framework** — Use as a library (`build()` API) or CLI. No dev server, no HMR.
3. **Transpile only** — No type checking. Pair with `tsc --noEmit` for types.
4. **ESM-native** — Outputs ESM/CJS/IIFE. Tree-shaking by default.
5. **Minimal config** — Sensible defaults. Configure only what you need.

---

## 2. Minimum Versions

| Technology | Minimum Version |
| ---------- | --------------- |
| esbuild    | 0.23+           |
| Node.js    | 22+             |
| pnpm       | 11+             |

---

## 3. Build API — `build()`

### Basic usage

```ts
// build.ts
import { build } from "esbuild";

await build({
  entryPoints: ["src/index.ts"],
  bundle: true,
  outfile: "dist/index.js",
  format: "esm",
  platform: "browser",
  target: "es2022",
  sourcemap: true,
  minify: true,
  splitting: true,
  external: ["react", "react-dom"],
});
```

### Essential options

| Option               | Description         | Typical Value                                |
| -------------------- | ------------------- | -------------------------------------------- |
| `entryPoints`        | Input files         | `['src/index.ts']`                           |
| `bundle`             | Bundle deps         | `true`                                       |
| `outfile` / `outdir` | Single file vs dir  | `outfile` for lib, `outdir` for app          |
| `format`             | Output format       | `'esm'` \| `'cjs'` \| `'iife'`               |
| `platform`           | Runtime target      | `'browser'` \| `'node'` \| `'neutral'`       |
| `target`             | JS version          | `'es2022'` (Baseline 2023+)                  |
| `sourcemap`          | Source maps         | `true` \| `'inline'` \| `'external'`         |
| `minify`             | Minify output       | `true` (esbuild minifier)                    |
| `splitting`          | Code splitting      | `true` (requires `outdir`)                   |
| `external`           | Exclude from bundle | `['react', 'react-dom']`                     |
| `loader`             | File type handling  | `{ '.ts': 'ts', '.png': 'dataurl' }`         |
| `define`             | Global replacements | `{ 'process.env.NODE_ENV': '"production"' }` |
| `banner` / `footer`  | Inject code         | `'/* license */'`                            |
| `treeShaking`        | DCE                 | `true` (default)                             |
| `keepNames`          | Preserve names      | `true` for debugging                         |

---

## 4. Formats and Platforms — Defaults Matrix

| Format | Platform  | Use Case               | Global       |
| ------ | --------- | ---------------------- | ------------ |
| `esm`  | `browser` | Modern apps, modules   | `window`     |
| `esm`  | `node`    | Node ESM packages      | `globalThis` |
| `cjs`  | `node`    | Node CommonJS, legacy  | `global`     |
| `iife` | `browser` | Script tag, no bundler | `window`     |

### Dual ESM/CJS (library)

```ts
// Build both formats
await Promise.all([
  build({ ...common, format: "esm", outfile: "dist/index.mjs" }),
  build({ ...common, format: "cjs", outfile: "dist/index.cjs" }),
]);
```

> Package.json `exports` map handles resolution — see `package-manager` skill.

---

## 5. Loaders and Assets

### Built-in loaders

```ts
loader: {
  '.ts': 'ts',        // TypeScript (transpile only)
  '.tsx': 'tsx',      // TSX
  '.js': 'js',        // JavaScript
  '.jsx': 'jsx',      // JSX
  '.json': 'json',    // JSON as module
  '.css': 'css',      // CSS as string
  '.png': 'dataurl',  // Base64 inline
  '.jpg': 'file',     // Copy to outdir
  '.svg': 'text',     // Raw string
  '.woff2': 'file',   // Fonts
  '.wasm': 'file'     // WASM
}
```

### Rules

- **`ts`/`tsx`** = transpile only (no type check)
- **`css`** = returns CSS string, use with `inject: true` or extract
- **`dataurl`** for small assets (<10kb typical)
- **`file`** for production assets (copies to outdir)
- **Custom loader** only when built-in insufficient

---

## 6. Code Splitting

### Entry points (automatic splitting)

```ts
await build({
  entryPoints: ["src/app.ts", "src/admin.ts"],
  outdir: "dist",
  format: "esm",
  splitting: true,
  // Creates: dist/app.js, dist/admin.js, dist/chunk-*.js
});
```

### Dynamic import (manual splitting)

```ts
// app.ts
const HeavyModule = await import("./heavy-module");
```

### Rules Code Splitting

- **`splitting: true`** requires `outdir` (not `outfile`)
- **Entry points** = automatic split points
- **Dynamic `import()`** = lazy-loaded chunks
- **`external`** deps not bundled — loaded at runtime

---

## 7. TypeScript — Transpile Only

```ts
await build({
  entryPoints: ["src/index.ts"],
  loader: { ".ts": "ts", ".tsx": "tsx" },
  tsconfig: "tsconfig.json", // Uses compilerOptions from tsconfig
  // esbuild ignores: types, interfaces, enums (use 'transform' for enums)
});
```

### What esbuild handles

| Feature                | Support                                             |
| ---------------------- | --------------------------------------------------- |
| `import`/`export`      | ✅                                                  |
| `type` imports/exports | ✅ (stripped)                                       |
| JSX / TSX              | ✅                                                  |
| `enum`                 | ⚠️ `transform` option (default: preserve as object) |
| `namespace`            | ❌                                                  |
| Decorators             | ⚠️ experimental                                     |
| Type checking          | ❌ (use `tsc --noEmit`)                             |

> **Full TypeScript rules**: see `typescript` skill. esbuild = transpiler only.

---

## 8. Watch Mode — `context` API

```ts
// Dev build with watch
const ctx = await build({
  entryPoints: ["src/index.ts"],
  outfile: "dist/index.js",
  format: "esm",
  watch: {
    onRebuild(error, result) {
      if (error) console.error("Rebuild failed:", error);
      else console.log("Rebuilt:", new Date().toLocaleTimeString());
    },
  },
});

// Later: stop watching
await ctx.dispose();
```

### Context API (advanced)

```ts
const ctx = await esbuild.context({
  entryPoints: ["src/index.ts"],
  outfile: "dist/index.js",
  format: "esm",
});
await ctx.watch();
await ctx.rebuild(); // Trigger manual rebuild
await ctx.dispose();
```

---

## 9. Plugins

### Core philosophy

- **Minimal API** — `setup(build)` with `onLoad`, `onResolve`, `onStart`, `onEnd`
- **Namespace** — `file://` for virtual modules, `http://` for remote
- **Order** — plugins run in array order

### Essential patterns

```ts
// shim plugin
const shimPlugin = {
  name: "shim",
  setup(build) {
    build.onResolve({ filter: /^buffer$/ }, () => ({
      path: "buffer",
      external: true,
    }));
    build.onLoad({ filter: /.*/, namespace: "file" }, async (args) => ({
      contents: await fs.readFile(args.path, "utf8"),
      loader: "js",
    }));
  },
};

// alias plugin
const aliasPlugin = {
  name: "alias",
  setup(build) {
    build.onResolve({ filter: /^@\// }, (args) => ({
      path: resolve(__dirname, "src", args.path.slice(2)),
      namespace: "file",
    }));
  },
};

// env replacement
const envPlugin = {
  name: "env",
  setup(build) {
    build.onResolve({ filter: /^env$/ }, () => ({
      path: "env",
      namespace: "env-ns",
    }));
    build.onLoad({ filter: /.*/, namespace: "env-ns" }, () => ({
      contents: `export default ${JSON.stringify(process.env)}`,
      loader: "js",
    }));
  },
};
```

### Rules plugin

- **Prefer built-in** options (`define`, `external`, `alias` via `resolve.alias` in Vite)
- **Custom plugins** only for: shims, virtual modules, custom loaders
- **Keep plugins small** — single responsibility

---

## 10. Bundle Analysis

### Metafile

```ts
const result = await build({
  entryPoints: ["src/index.ts"],
  outfile: "dist/index.js",
  format: "esm",
  metafile: true,
});

// result.metafile = { inputs, outputs, inputs: { bytes, imports } }
await fs.writeFile("metafile.json", JSON.stringify(result.metafile, null, 2));
```

### Visualize

```bash
# esbuild built-in
npx esbuild --analyze metafile.json

# Or use third-party
npx esbuild-visualizer metafile.json
```

### Key metrics

- **Bundle size** (gzipped)
- **Duplicate modules** (should be 0)
- **Large deps** (candidates for `external` or code splitting)
- **Unused code** (tree-shaking verification)

---

## 11. Methodology

Before using ANY esbuild config/plugin/pattern not documented in this skill:

1. **MCP Context7** (priority): `context7_resolve-library-id` + `context7_query-docs` for esbuild.
2. **Official docs**: esbuild.github.io — verify current API + options.
3. **Project config**: `build.ts`, `esbuild.config.js`, `package.json` scripts — verify against actual setup.
4. **HARD RULE**: If not in this skill AND cannot be verified against 2 authoritative sources → DO NOT USE IT. Document as assumption or risk in report to orchestrator.

---

## 12. Prohibitions

- ❌ Do not use esbuild for type checking — run `tsc --noEmit` separately
- ❌ Do not use `enum` without `transform` option — outputs verbose objects
- ❌ Do not bundle `external` deps (React, etc.) — bloats bundle
- ❌ Do not use `outfile` with `splitting: true` — requires `outdir`
- ❌ Do not use experimental decorators without testing
- ❌ Do not skip `metafile: true` in CI — no bundle visibility
- ❌ Do not use as dev server — Vite owns that (see `vite` skill)

---

## 13. References

> **Note:** For TypeScript rules, see [TypeScript](../typescript/SKILL.md)
> **Note:** For package manager conventions and library publishing, see [Package Manager](../package-manager/SKILL.md)
> **Note:** For Vite (dev server + build), see [Vite](../vite/SKILL.md)

---

Last updated: 2026-08

