# esbuild 0.28.2: a module that pins Go 1.13, and a test target narrower than the release one

> esbuild is a Go-implemented bundler for the web, MIT licensed, with JavaScript, CSS, TypeScript, and JSX handled in one tool and no cache required for its speed claim. The engineering decisions are unusually explicit: go.mod declares Go 1.13 on purpose and freezes a 2022 version of golang.org/x/sys, the Makefile configures reproducible builds with trimpath and an empty build id, and the everyday test target deliberately runs less than the release target.

**evanw/esbuild** — GitHub describes it as An extremely fast bundler for the web. The repository metadata lists Go as its primary language. The metadata lists the MIT license. This article stays within the project description and details documented in the GitHub repository README.

- Repository: https://github.com/evanw/esbuild
- Website: https://esbuild.github.io/
- Stars: 40,071 · Forks: 1,350
- Language: Go
- License: MIT
- Published: 2026-08-13 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/evanw-esbuild

## go.mod holds Go 1.13 and a 2022 x/sys, on purpose

The module file is annotated like a legal document. Above the language version it says that support for Go 1.13 is deliberate so people can build esbuild themselves for old OS versions, and asks that it not be changed. Above the single dependency it says the dependency cannot be upgraded or esbuild would no longer compile with Go 1.13, again asking that it not be changed, and it points at a FAQ anchor for the reasoning. The requirement itself is golang.org/x/sys at a 2022 pseudo-version.

Consequence for you: this is one of the few Go tools you can still build on a decade-old operating system, and that capability is the reason the dependency graph looks stale. It also means your build inherits a 2022 standard library surface, and a dependency audit will show a 2022 pseudo-version that a scanner cannot map to a release. If you vendor esbuild, leave those two lines alone; the FAQ entry is the place the trade-off is explained.

## Reproducible builds are configured in the Makefile, not assumed

The build flags are chosen for byte-level stability, and the comments say so. The linker flags strip debug info and clear the build id, the build information from version control is turned off, and a further flag avoids embedding the build path in the executable. CGO is disabled, and the compiler invocation is assembled with an explicit GOROOT and a PATH that is prefixed with the pinned toolchain directory rather than the one on your system.

Consequence for you: two people building the same commit get the same binary, which is what makes it possible to compare a release against a rebuild instead of trusting a checksum somebody published. It also means a fork that quietly drops the trimpath or buildid flag produces a different binary with the same version string, so if you are verifying artifacts, check the build recipe and not only the tag.

## The version and the toolchain are two files the Makefile reads

Two small files at the repository root drive the build. The version is read from version.txt and the Go toolchain from go.version, both with a shell cat at the top of the Makefile, and the toolchain directory is then used to construct the compiler invocation. A separate target, check-go-version, greps the output of go version for the exact version string and fails with a request to install that Go version if it is not found.

Consequence for you: contributing means matching the toolchain exactly, not approximately, and the check is a string match on the version, so a patch release difference fails it. That is a small annoyance for a contributor and a real benefit for a project that ships binaries, since the same pin applies to the person building the release and to the person verifying it.

## The everyday test target runs less than the release target

There are two entry points and the difference is deliberate. The default test target fans out to the development set: the Go tests, vet, a no-filepath check, source map verification, end-to-end tests, the JavaScript API tests, plugin tests, a register test, node-unref tests, and decorator tests. The wider release target adds TypeScript type tests, a Node WebAssembly test, a browser WebAssembly test, a library typecheck, and a Yarn Plug'n'Play test, with a comment noting that the extra tests are not in the default target because they are slow.

Consequence for you: a green local run is not a release signal, and a release that skips the wider target can ship a WebAssembly or TypeScript type regression that no developer saw. Schedule the wider target before publishing, and treat a passing default run as a fast check rather than as a gate.

## The race detector is deliberately left out of the default run

There is a comment above the test configuration explaining why the race flag is not added by default. The Go race detector is only supported on a specific list of operating system and architecture combinations, covering amd64 and arm64 on macOS, Linux and Windows, amd64 on FreeBSD and NetBSD, and ppc64le on Linux. The comment adds that support is not guaranteed on older operating system versions even when the combination is listed, giving macOS 10.9 as the example.

Consequence for you: your local test run is not race-checked unless you ask for it, and on a platform outside that list you cannot add the flag and expect it to work. For a bundler that parses other people's code and runs it through plugins, a concurrency bug would surface under a flag most contributors are not running, so decide in your own pipeline whether that gap is acceptable.

## The changelog is seven files, and the main one covers one year

Alongside the ordinary changelog the repository keeps one file per past year, from 2020 through 2025, each with a dated name. A reader who follows a link to the changelog therefore sees the current year's entries and has to know that the rest lives in the neighbouring files.

Consequence for you: searching for when a behaviour changed means opening the right year file, and a release notes summary that only links the main changelog will look complete while missing most of the history. If you are writing a migration note or auditing a version bump, read the file for the year of that release rather than the newest one.

## The license file is LICENSE.md, not LICENSE

The project is MIT licensed, and the text sits in a file named LICENSE.md at the root of the repository. There is no file called LICENSE. Alongside it the tree carries a staticcheck configuration for static analysis, a compat-table directory, a dl.sh download script, the npm directory for the JavaScript package wrapper, pkg and lib for the Go and JavaScript APIs, and a RUNBOOK document.

Consequence for you: a license scanner or an automated compliance check that looks for a file named LICENSE will find nothing here, and someone reading that result may record a gap that is not real. Point the check at LICENSE.md. The rest of the tree tells you the same story about packaging, by the way: the npm distribution is a wrapper maintained in this repository rather than a separate project, so an npm release and a Go release are one project's problem.

## The front page is a pitch with no install command

The README is short enough to read in a minute, and it contains no command. It links a website, a getting started page, documentation, a plugins page, and a FAQ. Its argument is one sentence about current web build tools being 10 to 100 times slower than they could be, illustrated by a benchmark image rather than a table, followed by the goal of a new era of build tool performance and an easy-to-use modern bundler.

The feature list is where the substance is: extreme speed without needing a cache, JavaScript, CSS, TypeScript, and JSX built in, a straightforward API for the CLI, JavaScript, and Go, bundling of ESM and CommonJS modules, CSS including CSS modules, tree shaking, minification, and source maps, and a local server, watch mode, and plugins.

Consequence for you: the 10 to 100 times figure is a claim about the category of tools, not about a named competitor, and it is illustrated rather than tabulated, so treat it as a direction rather than a measurement. Everything operational, including how to install and how to configure each feature, is on the website, not in the repository.

## Conclusion

esbuild fits a team that wants one bundler for JavaScript, TypeScript, JSX, and CSS, is willing to read a documentation website rather than a README, and values a build you can reproduce byte for byte, because the Makefile is already configured for that with CGO disabled, trimpath, and a cleared build id. It does not fit a contributor on a modern Go toolchain expecting current dependencies, because the module is held at Go 1.13 on purpose with a frozen 2022 x/sys, and it does not fit a release engineer who assumes the local test run is the release gate, since the WASM, TypeScript type, and Yarn PnP tests only run in the wider target. Before you build from source, read the old Go version note in the FAQ, and before you cut a release, run the wider target.

## FAQ

### What is ESBuild used for?

esbuild is a bundler for the web, written in Go, whose major features include JavaScript, CSS, TypeScript, and JSX handled in one tool, bundling of ESM and CommonJS modules, CSS including CSS modules, tree shaking, minification, and source maps, plus a local server, watch mode, and plugins. It offers a straightforward API for the CLI, JavaScript, and Go.

### Does ESBuild do tree shaking?

Yes. Tree shaking is named in the project's major features list, alongside minification and source maps, and next to bundling of ESM and CommonJS modules and of CSS including CSS modules. The same list also claims extreme speed without needing a cache.

### what is esbuild written in

Go. The repository's primary language is Go and its module is github.com/evanw/esbuild, and the Makefile builds the command from cmd/esbuild with CGO disabled. Alongside that, the project ships an API for JavaScript and the tree carries npm/ for the package wrapper, pkg/ and lib/ for the Go and JavaScript APIs.

### how to install esbuild

The README contains no install command; it points to the getting started page on the project website. To build from source, the Makefile compiles the command with CGO_ENABLED=0 and flags that strip debug info, clear the build id, and trim the build path, using the Go toolchain version recorded in the go.version file.

### Should I use ESBuild or Webpack?

The repository makes no comparison with Webpack. Its own stated goal is to bring about a new era of build tool performance and an easy-to-use modern bundler, with the claim that current web build tools are 10 to 100 times slower than they could be. That claim is illustrated by an image rather than a table, so the choice has to be made from the feature list and your own measurement.

### Is ESBuild faster than vite?

The repository does not compare esbuild with Vite. The only performance statement on the front page is that current web build tools for the web are 10 to 100 times slower than they could be, and the feature list claims extreme speed without needing a cache. A comparison with any specific tool has to be measured on your own project.

## Sources

- [Official documentation](https://esbuild.github.io/)
- [Official README](https://github.com/evanw/esbuild#readme)
- [Project repository](https://github.com/evanw/esbuild)
- [Release notes](https://github.com/evanw/esbuild/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/evanw-esbuild
