esbuild
esbuild in Go+ — AST reshaped into real enums
Released —
v1.0.1.goforge.dev/gesbuildis a Go+ rewrite ofevanw/esbuild— the JS/TS/CSS bundler and minifier — at 100% feature parity (verified by esbuild’s own complete test suite under-race), authored as a goplus project with the AST core reshaped into real Go+ enums. The installable artifact is stillesbuild—go install goforge.dev/gesbuild/cmd/esbuild@v1.0.1.
GoForge esbuild targets evanw/esbuild v0.28.1 (pinned at commit 6ff1d8b).
Status — blessed v1.0.0
- Feature parity: 100%, verified. The entire Go test suite passes from the
.gp-generated source — bundler snapshots,js_parser,js_lexer,js_printer, the CSS pipeline,pkg/api(16 packages green, under-race) — and theesbuildbinary is preserved and behaves identically. Differential (esbuild’s snapshots) + property tested (internal/gpnum),goplus gen -checkandgo vetclean. - A goplus project — ~94,000 LOC in
.gpacross 105 packages, deterministic*_gp.gochecked in; only 15 platform//go:buildshims stay hand-.go. - AST reshaped into real Go+ enums. The expression, statement, and binding
node hierarchies (
E/40,S/33,B/4) are genuine Go+enums, not hand-written sealed interfaces — representation-compatible, so 100% parity holds. - Honest scope. goplus
matchbinds value patterns while esbuild boxes AST nodes as pointers, so the enums are consumed via type switches today (exhaustive-matchergonomics await goplus pointer-variant support; the enums are genuine closed sums regardless). TokenTstays auint8by design — the hottest path and amapkey, where the refinement idiom (used ininternal/gpnum) doesn’t fit. Full rationale in the repo’sPARITY.md.
The Go+ down-payment: internal/gpnum
A JavaScript numeric-literal scanner authored in Go+, exercising every idiom the
rewrite targets, and differentially verified against the vendored esbuild
lexer (classification and exact float64 value, across decimal / float /
exponent / separator / hex / octal / binary / legacy-octal / bigint literals):
- std/parsec — the numeric-separator grammar is a combinator, so
1_000is accepted and1__0/_1/ a trailing_are rejected structurally. - GADT —
NumberKindis a data-carrying enum (Integer(Base),Float,BigInt(Base)). - Refinement types —
SignificantDigits refine(value int) { value > 0 }witnesses a non-empty digit run. - Error recovery —
Classifyreturns a structured error;ClassifyRecovernever fails, degrading to the longest classifiable prefix.
Matching esbuild here meant reproducing a real subtlety: IsLegacyOctalLiteral
is a flag (any leading-zero-plus-digit, e.g. 0888) distinct from the value
base (base-8 only when the second digit is 0–7).
Ledgered deviations
The Go floor is raised 1.13 → 1.26 (Go+ and std need a modern toolchain), and
golang.org/x/sys resolves to a current version as a consequence — the fs
tests pass unchanged. Both are documented, not hidden.
Provenance
Upstream: evanw/esbuild, MIT (© Evan Wallace).
The upstream LICENSE.md and README are retained; .upstream/COMMIT pins the
exact commit parity was verified against.