How do their APIs compare?
How do their APIs compare?
About evanw/esbuild · 2 messages · last activity 9/5/2026
My searches mostly surfaced internal bundler test fixtures and config-handling code rather than the public API entry-point definitions themselves, so I'll answer based on what's actually verifiable in the retrieved data, and be explicit about the boundary of what I can confirm.
What the retrieved evidence actually shows:
esbuild is architected as a two-phase pipeline underneath its "build" and "transform" API calls: a scan phase and a compile phase, as documented directly in the package comment: "Each operation has two phases. The first phase scans the module graph, and is represented by the ScanBundle function. The second phase generates the output files from the module graph, and is implemented by the Compile function." [internal/bundler/bundler.go:L1-L8]. Internally, everything funnels through a Go config.Options struct (seen populated throughout the bundler tests, e.g. Mode, AbsOutputFile, AbsOutputDir, OutputFormat, CodeSplitting, UnsupportedJSFeatures, MainFields [internal/bundler_tests/bundler_splitting_test.go:L554-L577], [internal/bundler_tests/bundler_lower_test.go:L780-L824], [internal/bundler_tests/bundler_dce_test.go:L560-L591]). This is consistent with esbuild's documented design goal of exposing one straightforward API across CLI, JS, and Go, per the README ("A straightforward API for CLI, JS, and Go" — README excerpt). The config.Options.Equal method for CSS options [internal/css_parser/css_parser.go:L97-L121] also hints at how esbuild structurally compares option sets (relevant to its incremental/watch rebuild logic).
Rollup, by contrast, is JS/TypeScript-native, and its API is object/plugin-hook-oriented rather than a flat options struct passed to a compiler. Evidence of this shows in Module.ts, where a module's public-facing ModuleInfo is built as a plain object with getters (importers, isIncluded, meta, moduleSideEffects, syntheticNamedExports, etc.) [rollup/rollup/src/Module.ts:L382-L398], and cached via cacheObjectGetters on demand for fields like dynamicallyImportedIds, dynamicImporters, exports, hasDefaultExport [rollup/rollup/src/Module.ts:L409-L421]. Rollup's internal AST/analysis layer also defines its own interaction typing system (NodeInteractionAccessed, NodeInteractionAssigned, INTERACTION_CALLED) used to model how plugins and the internal tree-shaker reason about reads/writes/calls on a node [rollup/rollup/src/ast/NodeInteractions.ts:L1-L31], which has no analogue in what's been retrieved from esbuild — esbuild does not expose an AST-interaction API to plugins in the same way.
Key architectural contrast I can support from the evidence:
- esbuild centers its API around a single
config.Options-style configuration object consumed by a scan → compile pipeline in Go, then exposed via thin CLI/JS/Go wrappers [internal/bundler/bundler.go:L1-L8]. - Rollup centers its API around a
Module/Chunkobject graph with plugin-visibleModuleInfogetters [src/Module.ts:L382-L421] and chunk-level export-name management (getChunkName,getExportNames,getFileName,getImportPathin [rollup/rollup/src/Chunk.ts:L558-L577]), giving plugins much deeper structural access to modules/chunks during the build.
What I can't confirm from this data: the exact shape of esbuild's public JS (build(), transform()) or Go (api.Build, api.Transform) function signatures, and Rollup's exact rollup.rollup()/generate()/write() public entry points and hook list — none of those top-level signatures appeared in the retrieved chunks, so I won't speculate on their parameter details.
FOLLOW_UPS:
- What does esbuild's ScanBundle function actually do?
- How does Rollup's plugin hook system work internally?
- What is esbuild's config.Options struct's full shape?
Want to ask your own question?
Open chat for evanw/esbuildPublic Q&A. Generated by RepoPilot from the actual source of evanw/esbuild. AI answers can be incomplete or stale — verify before relying on them.