# esbuild Admin & Architecture Guide

This guide is for engineers integrating esbuild into a build system, maintainers who need to understand its internals, and architects evaluating it. It covers the system architecture, the multi-language codebase, configuration surface, the native-binary distribution model, security posture, operations, and contribution workflow.

For end-user, task-oriented documentation (CLI flags, JS API recipes, troubleshooting installs), see the **esbuild User Guide**.

> [!NOTE]
> Documentation target: esbuild **v0.28.0** (source commit `6a794df`). esbuild is MIT-licensed and authored by Evan Wallace. The repository is a polyglot project: a Go core, TypeScript wrapper libraries, and a small TypeScript support project for compatibility tables.

---

## 1. Executive Summary

esbuild is a JavaScript/TypeScript/JSX/CSS **bundler and minifier** whose core is written in **Go** and compiled to a native binary. Its defining characteristic is speed: it parses, links, and prints in parallel across CPU cores using compiled code, making it commonly 10–100× faster than bundlers written in JavaScript.

| Attribute | Value |
|---|---|
| Core language | Go (engine in `internal/`, public API in `pkg/`) |
| Wrapper language | TypeScript (npm/Deno/browser libraries in `lib/`) |
| Distribution | Native per-platform binaries + WebAssembly fallback |
| Public surfaces | CLI, Go library, Node.js library, browser (WASM) library, Deno library |
| License | MIT |
| Runtime dependencies (Go core) | **None** — the Go module has no third-party imports in the build path |
| Node.js requirement | ≥ 18 (for the npm packages) |

**What it is not:** esbuild is not a type checker, not a long-running production server, and not a plugin-rich framework like webpack. It deliberately trades configurability and type-checking for speed and a small, sharp feature set.

---

## 2. Architecture

### 2.1 High-level component map

```
                         ┌─────────────────────────────────────────────┐
   CLI binary            │            Public APIs (Go)                  │
   cmd/esbuild ────────► │   pkg/cli  (arg parsing)                     │
                         │   pkg/api  (Build / Transform / Context)     │
   Go consumers ───────► │                                             │
                         └───────────────────┬─────────────────────────┘
                                             │
                         ┌───────────────────▼─────────────────────────┐
                         │              Core engine (internal/)         │
   JS / Deno / browser   │                                             │
   libraries (lib/)      │   resolver → bundler → linker                │
        │  IPC over       │      │          │         │                 │
        │  stdin/stdout   │   js_lexer   js_parser  js_printer          │
        └───────────────► │   css_lexer  css_parser css_printer         │
   (--service mode)       │   js_ast / css_ast / ast / graph            │
                         │   renamer · compat · cache · sourcemap · fs  │
                         └─────────────────────────────────────────────┘
```

### 2.2 The two-process model (JS ⇄ Go)

The JavaScript libraries do **not** reimplement esbuild. Instead, the first API call spawns the native esbuild binary as a **long-lived child process** and communicates with it over `stdin`/`stdout` using a length-prefixed binary protocol. This is the single most important architectural fact for anyone integrating or debugging esbuild from JavaScript.

- The Node library (`lib/npm/node.ts`) calls `child_process.spawn(binary, ['--service=<version>', '--ping'])`.
- The binary, started with `--service=`, enters `runService()` in `cmd/esbuild/service.go` and loops reading packets from stdin.
- Each request (`build`, `transform`, `resolve`, `rebuild`, `watch`, `serve`, `cancel`, `dispose`, `format-msgs`, `analyze-metafile`) is dispatched on its own goroutine.
- Packets are framed as `[uint32 length][payload]`, encoded by `lib/shared/stdio_protocol.ts` (JS side) and the matching Go encoder (`cmd/esbuild/stdio_protocol.go`).
- The child process is kept alive across calls (`unref()`'d so it doesn't block process exit) for performance — re-spawning per call would dominate runtime.

```
JS build({...})  ──encodePacket──►  stdin   ──►  Go service goroutine
   ▲                                                    │
   │  ◄────────── stdout ◄──── encodePacket ◄───── api.Build(options)
 BuildResult
```

> [!IMPORTANT]
> Because the JS API depends on this child process, two things follow: (1) you must **not bundle the `esbuild` package** into your own tool — it locates the binary via a relative path that breaks when bundled (mark it `external`); and (2) in **Deno** you must call `stop()` manually, because Deno does not auto-reap the child process.

### 2.3 The build pipeline (inside the Go core)

A `Build` flows through these stages, each a package under `internal/`:

| Stage | Package(s) | Responsibility |
|---|---|---|
| **1. Resolve** | `internal/resolver` | Turn import specifiers into file paths: `node_modules` walking, `package.json` `main`/`module`/`exports`/`imports`, `tsconfig.json` `paths`, Yarn PnP, data URLs, symlinks |
| **2. Lex** | `internal/js_lexer`, `internal/css_lexer` | Tokenize source bytes (UTF-8 aware) |
| **3. Parse** | `internal/js_parser`, `internal/css_parser` | Build an AST; also performs TypeScript/JSX lowering decisions and per-file syntax transforms |
| **4. Represent** | `internal/js_ast`, `internal/css_ast`, `internal/ast`, `internal/graph` | AST node types and the module dependency graph |
| **5. Link** | `internal/linker`, `internal/bundler` | Tree-shaking, scope hoisting, code splitting, chunk assignment, cross-module renaming |
| **6. Rename** | `internal/renamer` | Identifier minification and `--keep-names` preservation |
| **7. Print** | `internal/js_printer`, `internal/css_printer` | Emit final source text |
| **8. Source maps** | `internal/sourcemap` | Generate/merge source maps |

Supporting packages: `internal/compat` (feature-support tables that drive what gets lowered for each target), `internal/cache` (caches parsed ASTs and file reads across rebuilds), `internal/fs` (a filesystem abstraction with real, mock, and zip-backed implementations), `internal/logger` (messages, locations, colored output), `internal/runtime` (esbuild's injected runtime helpers, e.g. `__esm`, `__commonJS`), `internal/xxhash` and `internal/helpers` (hashing and utilities).

### 2.4 Parallelism and caching

esbuild parses files concurrently across goroutines; the linker is the synchronization point that assembles the parallel results. The `internal/cache` package memoizes parsed ASTs and file system reads so that **incremental rebuilds** (via `context().rebuild()` / `--watch`) only re-process changed files. When the process does a single one-shot build with at most one entry point, the CLI even disables Go's garbage collector (`debug.SetGCPercent(-1)`), because the process is about to exit anyway — a measurable speedup.

### 2.5 Plugins run in the host language, proxied to Go

esbuild plugins (`onResolve`, `onLoad`, `onStart`, `onEnd`, `onDispose`) execute in **JavaScript** (or Go), not inside the core. When a JS build registers plugins, the Node library sends a single proxy Go plugin to the binary; during the build, the Go side issues `on-resolve`/`on-load` requests back over the same stdio channel, and the JS side runs the user's callbacks and replies. This is why plugins require the asynchronous API — synchronous calls have no channel to call back on.

---

## 3. Repository Structure

```
esbuild/
├── cmd/esbuild/          CLI entry point + service-mode protocol (Go)
│   ├── main.go           Arg pre-scan, help text, process lifecycle
│   ├── service.go        Long-running stdio service for the JS APIs
│   └── stdio_protocol.go Binary packet encoding
├── pkg/
│   ├── api/              Public Go library: Build, Transform, Context, Serve
│   └── cli/              Public Go CLI arg parser (ParseBuildOptions, Run)
├── internal/             The engine (not a stable API)
│   ├── resolver/  bundler/  linker/  graph/
│   ├── js_lexer/  js_parser/  js_ast/  js_printer/
│   ├── css_lexer/ css_parser/ css_ast/ css_printer/
│   ├── compat/  renamer/  runtime/  sourcemap/  cache/  fs/
│   ├── logger/  helpers/  xxhash/  config/  cli_helpers/
│   └── bundler_tests/    Snapshot-based integration tests
├── lib/                  TypeScript wrapper libraries
│   ├── shared/           types.ts (the typed API), common.ts (IPC marshaling)
│   ├── npm/              node.ts, node-platform.ts, node-install.ts, browser.ts
│   └── deno/             mod.ts (Deno entry)
├── npm/                  Published npm package manifests
│   ├── esbuild/          The main "esbuild" package
│   ├── esbuild-wasm/     The WebAssembly package
│   └── @esbuild/*/       ~25 platform-specific binary packages
├── compat-table/         TS project that generates internal/compat tables
├── scripts/              Test harnesses, fuzzers, end-to-end suites
└── docs/                 architecture.md, development.md
```

> [!TIP]
> `internal/` is private by Go convention — code outside the module cannot import it, and its API is explicitly **unstable**. Integrate only through `pkg/api`, `pkg/cli`, or the `lib/` libraries.

---

## 4. Configuration Mastery

esbuild has **no configuration file format of its own**. All configuration is passed as CLI flags or as options objects to the API. It does, however, *read* `tsconfig.json` and `package.json` from your project to influence resolution and transformation.

### 4.1 The option model

Every option exists in three equivalent forms:

| Form | Example | Source of truth |
|---|---|---|
| CLI flag | `--minify-syntax` | `pkg/cli/cli_impl.go` parser |
| JS/TS option | `minifySyntax: true` | `lib/shared/types.ts` (`BuildOptions`/`TransformOptions`) |
| Go field | `MinifySyntax: true` | `pkg/api/api.go` structs |

`BuildOptions` and `TransformOptions` both extend a shared `CommonOptions` set. **Build-only** options (file-system oriented) include `bundle`, `splitting`, `outfile`/`outdir`/`outbase`, `external`, `packages`, `alias`, `loader` (by extension), `resolveExtensions`, `mainFields`, `conditions`, `inject`, `entryNames`/`chunkNames`/`assetNames`, `publicPath`, `metafile`, `write`, `allowOverwrite`, `tsconfig`, `nodePaths`, `absWorkingDir`, and `plugins`. **Common** options (apply to both) include all the minify flags, `target`/`supported`, `format`, `platform`, `sourcemap`, `define`, `drop`, `jsx*`, `charset`, `treeShaking`, `legalComments`, `logLevel`/`logLimit`/`logOverride`, and `tsconfigRaw`.

### 4.2 Enumerated option values (from source)

| Option | Allowed values |
|---|---|
| `platform` | `browser`, `node`, `neutral` |
| `format` | `iife`, `cjs`, `esm` |
| `loader` | `base64`, `binary`, `copy`, `css`, `dataurl`, `default`, `empty`, `file`, `global-css`, `js`, `json`, `jsx`, `local-css`, `text`, `ts`, `tsx` |
| `sourcemap` | `true`/`linked`, `inline`, `external`, `both` |
| `legalComments` | `none`, `inline`, `eof`, `linked`, `external` |
| `charset` | `ascii`, `utf8` |
| `jsx` | `transform`, `automatic`, `preserve` |
| `drop` | `console`, `debugger` |
| `packages` | `bundle`, `external` |
| `logLevel` | `verbose`, `debug`, `info`, `warning`, `error`, `silent` |
| `target` (version) | `es5`, `es6`/`es2015`, `es2016`–`es2025`, `esnext` |
| `target` (engine) | `engineVERSION`, e.g. `chrome58`, `firefox57`, `safari11`, `edge16`, `node10`, `ie9`, `opera45` |
| `absPaths` | `code`, `log`, `metafile` |

> [!WARNING]
> esbuild validates these strictly. An unknown value yields an error with a "Valid values are …" note (generated in `pkg/cli/cli_impl.go`). An unknown **option key** in the JS API throws `Invalid option …` — esbuild uses a `SameShape` TypeScript trick plus a runtime key check to reject typos rather than silently ignore them.

### 4.3 tsconfig.json fields esbuild honors

esbuild reads a subset of `compilerOptions`: `target`, `jsx`, `jsxFactory`, `jsxFragmentFactory`, `jsxImportSource`, `useDefineForClassFields`, `experimentalDecorators`, `importsNotUsedAsValues`, `preserveValueImports`, `verbatimModuleSyntax`, `alwaysStrict`/`strict`, `baseUrl`, and `paths`. You can override the file with `--tsconfig=path`, or inline the whole thing with `--tsconfig-raw=<json>` (useful for the transform API, which has no project on disk).

### 4.4 Environment variables

| Variable | Read by | Effect |
|---|---|---|
| `NO_COLOR` | binary (`main.go`) | Disables ANSI colors (per <https://no-color.org>) |
| `NODE_PATH` | CLI/Node lib | Additional module resolution roots |
| `ESBUILD_BINARY_PATH` | `lib/npm/node-platform.ts` | Override which binary the JS API executes (ignored if set to the broken value `/usr/bin/esbuild`) |
| `ESBUILD_WORKER_THREADS` | `lib/npm/node.ts` | `0` disables the worker thread backing the sync APIs |
| `ESBUILD_MAX_BUFFER` | `lib/npm/node.ts` | Max output bytes for the sync API (default 16 MB) |
| `NPM_CONFIG_REGISTRY` | Deno loader | Registry used to fetch the binary in Deno |
| `XDG_CACHE_HOME` / `LOCALAPPDATA` / `HOME` | Deno loader | Cache directory for the downloaded Deno binary |

> [!NOTE]
> These are **not** discoverable from any config schema — they are read directly via `os.LookupEnv`/`process.env` scattered across the entry code. The table above is the complete runtime environment surface as of this commit.

---

## 5. Distribution & Supply-Chain Model

esbuild's install mechanism is unusually intricate and is the source of most operational issues, so it deserves a dedicated section.

### 5.1 Platform-specific binary packages

The published `esbuild` package contains the JS wrapper but **no binary**. The correct native binary arrives through npm `optionalDependencies`: one of ~25 `@esbuild/<os>-<arch>` packages (e.g. `@esbuild/linux-x64`, `@esbuild/win32-x64`, `@esbuild/darwin-arm64`). Each carries `os`/`cpu` constraints in its `package.json`, so npm installs only the one matching the host.

At runtime, `lib/npm/node-platform.ts` builds a key like `"linux x64 LE"` from `process.platform`, `os.arch()`, and `os.endianness()` and looks it up in `knownWindowsPackages` / `knownUnixlikePackages` / `knownWebAssemblyFallbackPackages` to find the package and the subpath (`bin/esbuild`, or `esbuild.exe` on Windows). A few platforms (Android ARM/x64, OpenHarmony) ship a **WebAssembly shim** rather than a native binary.

### 5.2 Install-time fallback chain

The postinstall script (`lib/npm/node-install.ts`) resolves the binary with a layered fallback:

1. Honor `ESBUILD_BINARY_PATH` if set and valid.
2. `require.resolve` the optional `@esbuild/<platform>` package (the normal path).
3. If missing (e.g. `--no-optional`), run a nested `npm install` of the exact-versioned package into a temp dir.
4. If that fails, download the `.tgz` **directly from the npm registry** over HTTPS and extract the binary.

After obtaining the binary it runs an **integrity check** and (on non-Windows, non-Yarn, non-WASM installs) replaces the JS shim with the binary via a hard link for a faster `esbuild` command.

### 5.3 Binary integrity verification

`binaryIntegrityCheck()` computes the SHA-256 of the downloaded binary and compares it against a per-file hash stored under the `esbuild.binaryHashes` key in the main package's `package.json`. A mismatch aborts the install. This protects the direct-download fallback path against a tampered or corrupted artifact.

> [!CAUTION]
> The integrity check covers the **fallback download** path; the normal `optionalDependencies` path relies on npm's own lockfile integrity (`package-lock.json` `integrity` hashes). Keep your lockfile committed and use `npm ci` in CI so the binary package's hash is enforced.

### 5.4 Version lockstep

The binary self-reports its version on startup; both the install script and the runtime check that it equals the JS package version (`Expected "0.28.0" but got …`). This catches partially-copied `node_modules`, stale caches, and mismatched manual installs early instead of producing confusing build failures later.

---

## 6. Security

esbuild is a build tool, not a network service, so its attack surface is narrow but not empty. Evaluate it along these axes:

### 6.1 Trust model

| Concern | Posture |
|---|---|
| Remote code execution at build time | **Your build runs your dependencies' code paths through esbuild, and any plugins you add run arbitrary JS/Go.** Treat plugins and the code you bundle as trusted. |
| Network access by the core | The Go core does **not** make network calls during a build. The only network activity is the npm/Deno install fallback fetching the binary. |
| Secrets in output | `--define` substitutions and bundled `process.env.*` values are written verbatim into output. Do not `--define` secrets into client bundles. |
| Dev server exposure | The `--serve` server has **no authentication** and is intended for localhost only. |

### 6.2 Dev server hardening

If you must bind the dev server beyond localhost:

- It supports HTTPS via `--certfile`/`--keyfile` (or `certfile`/`keyfile` in `ServeOptions`).
- It supports CORS allow-listing via `--cors-origin` / `cors.origin`.
- It still performs no auth and serves your build output and `servedir` contents. Put it behind a reverse proxy with access control, or keep it bound to `127.0.0.1`.

> [!WARNING]
> Never expose the esbuild dev server directly to untrusted networks. It is a development convenience, not a production web server. Build static assets and serve them with a hardened server (nginx, a CDN, etc.) in production.

### 6.3 Supply-chain considerations

- **Postinstall script:** esbuild runs a postinstall script (`node-install.ts`). In high-security environments that disable install scripts (`--ignore-scripts`), combine that with the normal `optionalDependencies` resolution (do **not** also pass `--no-optional`), or vendor the platform package. There is an open discussion (issue #4475) about reconsidering the postinstall step.
- **Integrity:** rely on a committed `package-lock.json` + `npm ci` so binary packages are pinned and hash-verified.
- **WASM alternative:** `esbuild-wasm` contains no native binary and no postinstall download, at a performance cost. It's the most locked-down option for restricted environments.

### 6.4 Toolchain CVEs

Because the binary is compiled with the Go toolchain, Go runtime/standard-library CVEs can apply transitively. The project tracks these and rebuilds against patched Go releases (e.g. issue #4464 requests rebuilding against Go 1.26.2 for CVE-2026-33810). For compliance, track the Go version each esbuild release was built with (`go.version` / `go.mod` in the repo) and upgrade esbuild when the underlying Go is patched.

---

## 7. Operations

### 7.1 Integrating into a build system

The recommended integration is the **JS `context()` API** for dev (incremental rebuild + serve + watch) and a one-shot `build()` for production. A typical build script:

```js
import * as esbuild from 'esbuild'

const shared = {
  entryPoints: ['src/index.tsx'],
  bundle: true,
  target: ['es2018'],
  loader: { '.png': 'file', '.svg': 'dataurl' },
  logLevel: 'info',
}

if (process.argv.includes('--watch')) {
  const ctx = await esbuild.context({ ...shared, outdir: 'www/js', sourcemap: true })
  await ctx.watch()
  await ctx.serve({ servedir: 'www' })
} else {
  await esbuild.build({ ...shared, outfile: 'dist/app.js', minify: true })
}
```

### 7.2 Integrating from Go

```go
result := api.Build(api.BuildOptions{
    EntryPoints: []string{"src/app.ts"},
    Bundle:      true,
    Outdir:      "dist",
    Write:       true,
    Engines:     []api.Engine{{Name: api.EngineChrome, Version: "80"}},
    LogLevel:    api.LogLevelInfo,
})
if len(result.Errors) > 0 {
    os.Exit(1)
}
```

For watch/serve from Go, use `api.Context(options)` and the returned context's `Watch`/`Serve`/`Rebuild`/`Dispose` methods. To reuse the CLI's exact argument parsing inside a Go program, use `pkg/cli` (`cli.Run`, `cli.ParseBuildOptions`).

### 7.3 Logging and diagnostics

| Lever | Effect |
|---|---|
| `--log-level` | Controls verbosity (`silent`…`verbose`) |
| `--log-limit=N` | Caps the number of messages (default 6; `0` = unlimited) |
| `--log-override:ID=level` | Re-classify a specific message ID (e.g. promote a warning to an error) |
| `--color=true/false` | Force colored output on or off |
| `--metafile=meta.json` | Emit machine-readable build metadata |
| `--analyze[=verbose]` | Human-readable bundle composition report |

### 7.4 Performance profiling (debug-only flags)

The binary exposes hidden profiling flags intended for debugging esbuild itself (output format is undocumented and unstable):

| Flag | Tool |
|---|---|
| `--cpuprofile=file` | `go tool pprof` / speedscope.app |
| `--heap=file` | `go tool pprof` (heap profile) |
| `--trace=file` | `go tool trace file` |
| `--timing` | Internal timing breakdown |

> [!NOTE]
> These flags are not part of the supported interface and may change or disappear between releases. Use them only when diagnosing esbuild performance, not in production pipelines.

### 7.5 Known operational gotchas (from community issues)

| Symptom | Notes |
|---|---|
| `all goroutines are asleep - deadlock!` on a build (#3636) | Rare; typically a plugin or environment deadlock. Reproduce with a minimal config and report. |
| `BuildResult` lacks `metafile` when a build fails (#4466) | On failed builds, don't rely on `metafile` being present in plugin `onEnd`. |
| `var` in block statements not renamed when bundling (#4471) | A correctness edge case — validate output if you rely heavily on block-scoped `var` collisions across modules. |
| TypeScript namespace/decorator-metadata edge cases (#4450, #3680) | esbuild's TS transform is not 1:1 with `tsc` for some namespace and `emitDecoratorMetadata` scenarios. Keep `tsc` for type-level guarantees. |

---

## 8. Build, Test & Contribution Workflow

### 8.1 Building from source

esbuild is a standard Go module. The `Makefile` orchestrates building the binary, running tests, and producing the npm/WASM packages. The minimum to build the CLI:

```bash
go build ./cmd/esbuild
```

The required Go version is pinned in `go.version` / `go.mod`. There are no third-party Go dependencies to fetch for the core build.

### 8.2 Test architecture

| Layer | Location | Style |
|---|---|---|
| Unit tests | `internal/**/[*]_test.go` | Standard Go tests per package (parser, printer, lexer, resolver, etc.) |
| Bundler integration | `internal/bundler_tests/` | **Snapshot tests** — inputs are bundled and compared against `snapshots/*.txt` |
| End-to-end & ecosystem | `scripts/*.js` | JS-driven suites: `end-to-end-tests.js`, `js-api-tests.js`, `plugin-tests.js`, plus third-party conformance (`terser-tests.js`, `uglify-tests.js`, `test262.js`) and fuzzers |
| Real-world bundling | `require/` | Fixtures that bundle rollup, webpack5, parcel2, Yarn PnP, old TS, etc. |

> [!TIP]
> The snapshot tests in `internal/bundler_tests` are the fastest way to understand expected output for a given option combination — each `snapshots_*.txt` file shows exact bundler output for a category (DCE, splitting, CSS, loaders, tsconfig, importstar, etc.).

### 8.3 Code conventions

- Go core code uses no external dependencies and a vendored `xxhash` for content hashing.
- `staticcheck.conf` configures the linter used in CI.
- The public CLI in `pkg/cli` is deliberately implemented **on top of** the public `pkg/api` (not internal APIs), so CLI tests also exercise the public API.
- Changelogs are split per year (`CHANGELOG-2020.md` … `CHANGELOG.md`); read `CHANGELOG.md` before upgrading, since breaking changes ship in `0.x` minors.

---

## 9. Architecture Decision Notes & Improvement Areas

These reflect design choices and open threads visible in the source and issue tracker — useful when deciding whether and how to adopt esbuild.

- **No type checking is intentional.** It's a core speed decision, not a missing feature. Pair esbuild with `tsc --noEmit` for type safety.
- **Pre-1.0 versioning.** There is an open request to move to `1.0.0` (#2544). Until then, pin exact versions.
- **Postinstall download model.** Powerful and resilient, but a friction point in locked-down environments; there's active discussion about revisiting it (#4475). For zero-postinstall installs, prefer `esbuild-wasm`.
- **Plugin model is host-side.** Plugins are flexible but run over the JS↔Go IPC channel, which is why they can't be used synchronously and add per-callback IPC overhead. For hot paths, prefer built-in options (`loader`, `define`, `inject`, `external`) over plugins.
- **CSS feature coverage is growing.** Several open feature requests target CSS modules ergonomics (e.g. minified class-name prefixes #3484, class-name mapping from `local-css` #4452). If you depend on advanced CSS-module workflows, verify support against your exact version.

---

## 10. Quick Reference

### Public entry points

| Surface | Import / command | Key functions |
|---|---|---|
| CLI | `esbuild` | `esbuild [options] [entry points]` |
| Go library | `github.com/evanw/esbuild/pkg/api` | `Build`, `Transform`, `Context`, `Serve`, `AnalyzeMetafile`, `FormatMessages` |
| Go CLI parser | `github.com/evanw/esbuild/pkg/cli` | `Run`, `RunWithPlugins`, `ParseBuildOptions`, `ParseTransformOptions`, `ParseServeOptions` |
| Node library | `esbuild` | `build`, `context`, `transform`, `buildSync`, `transformSync`, `formatMessages`, `analyzeMetafile`, `stop`, `version` |
| Browser library | `esbuild-wasm` | Same as Node minus `*Sync`; requires `initialize({ wasmURL })` first |
| Deno library | `https://deno.land/x/esbuild/mod.js` | Same as Node minus `*Sync`; call `stop()` when done |

### Internal package responsibilities

| Package | Responsibility |
|---|---|
| `internal/resolver` | Module resolution (node_modules, exports/imports, tsconfig paths, Yarn PnP, symlinks) |
| `internal/bundler` | Orchestrates parse → link → print for a build |
| `internal/linker` | Tree-shaking, scope hoisting, code splitting, chunking |
| `internal/js_parser` / `internal/css_parser` | Source → AST + per-file transforms |
| `internal/js_printer` / `internal/css_printer` | AST → output text |
| `internal/renamer` | Identifier minification / `keep-names` |
| `internal/compat` | Per-engine feature-support tables driving lowering |
| `internal/cache` | Incremental rebuild caches (ASTs, file reads) |
| `internal/fs` | Filesystem abstraction (real / mock / zip) |
| `internal/sourcemap` | Source map generation and merging |
| `internal/logger` | Messages, source locations, colored output |

### Service-mode protocol commands (JS ⇄ Go)

`build`, `transform`, `resolve`, `rebuild`, `watch`, `serve`, `cancel`, `dispose`, `on-start`, `on-resolve`, `on-load`, `on-end`, `error`, `format-msgs`, `analyze-metafile`, `ping`.

---

_Generated June 2026 from evanw/esbuild at 6a794df. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
