GoForge — Build | Test | Ship | Collaborate GoForge — Build | Test | Ship | Collaborate
v1.0.1

esbuild

esbuild in Go+ — AST reshaped into real enums

$ go install goforge.dev/gesbuild/cmd/esbuild@v1.0.1

Released — v1.0.1. goforge.dev/gesbuild is a Go+ rewrite of evanw/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 still esbuild — 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 the esbuild binary is preserved and behaves identically. Differential (esbuild’s snapshots) + property tested (internal/gpnum), goplus gen -check and go vet clean.
  • A goplus project — ~94,000 LOC in .gp across 105 packages, deterministic *_gp.go checked in; only 15 platform //go:build shims 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 match binds value patterns while esbuild boxes AST nodes as pointers, so the enums are consumed via type switches today (exhaustive-match ergonomics await goplus pointer-variant support; the enums are genuine closed sums regardless). Token T stays a uint8 by design — the hottest path and a map key, where the refinement idiom (used in internal/gpnum) doesn’t fit. Full rationale in the repo’s PARITY.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_000 is accepted and 1__0 / _1 / a trailing _ are rejected structurally.
  • GADT — NumberKind is 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 — Classify returns a structured error; ClassifyRecover never 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.