ts-r-build
Bundler and minifier. Replaces esbuild, Rollup, Vite build.
- Alpha
- On npm
ts-r-build bundles and minifies TypeScript and JavaScript for production. Its command line follows esbuild 0.28.2, so most esbuild commands work after swapping the binary name. Like esbuild, it strips types without checking them.
What switching looks like
ts-r-build reads the config you already have, so switching is a change to the command, not the project.
"scripts": {
- "build": "esbuild src/app.ts --bundle --minify --outdir=dist",
+ "build": "ts-r-build src/app.ts --bundle --minify --outdir=dist",
}
What works, and what doesn’t yet
Works today
- Bundling ES modules and CommonJS, with tree shaking that honours sideEffects and /*#__PURE__*/
- Output formats esm, cjs and iife, and code splitting with --splitting
- Platforms browser, node and neutral; tsconfig paths and baseUrl
- Minification of whitespace, identifiers and syntax
- Source maps: linked, external, inline or both
- JSX with the classic or the automatic runtime, --watch and a persistent build cache
Not yet
- No plugins and no JavaScript config file or API
- No CSS bundling; @import is not followed
- No lowering below ES2015: const, classes and destructuring are errors at --target=es5
- No --mangle-props, --drop, --pure or --keep-names
Measured
Every run, on the same pinned public projects as the home page, with the command and commit that recorded it.
ts-r-build against esbuild 0.28.2 on a dedicated Linux machine
three.js x10 (ten copies of three.js, minified IIFE with source maps) on four dedicated Linux cores, from the review box's bench of pull request #42 on 2026-10-24: these are the numbers in the README's benchmark table. Both bundlers produce the same exports from the same input. Without its persistent cache ts-r-build took 962.9 ms against esbuild's 1,409.8 ms; with the cache, 484.6 ms. The shared-laptop runs below are older (commit 2fb2555), and there the two were level without the cache. Peak memory and excalidraw were not measured on this run.
- ts-r
- Other tools
How we measured
- Measured
- Wall time of whole processes with hyperfine (cargo xtask bench --repos three-x10), median of 10 runs after 2 warm-ups; peak memory not measured on this run
- Machine
- Linux x86-64: four cores of the review box's bench machine, a cpuset nothing else is scheduled on while it measures (one bench slot); release build. The CPU model is not recorded with the result
- Software
- ts-r-build release build of PR #42's head d8a6a97 (merged as 8b6c630), benchmarked against main at 0697824: the two differ only in ts_r_run, which no scenario here runs, with --threads=4; esbuild 0.28.2 pinned in bench/package.json, with its default threads
- Input
- three.js x10 at the commit pinned in bench/repos.toml (bench/entry.js, minified IIFE with source maps)
- Command
The review box's pull request bench (bench.yml): cargo xtask bench --repos trpc,date-fns,rxjs,three-x10- Recorded in
- commit
1a20ed6
ts-r-build against esbuild 0.28.2 on the shared laptop
Bundling two public projects on the shared laptop, with older code than the Linux run above: three.js x10 (ten copies of three.js, minified IIFE with source maps) and the excalidraw app (minified ESM). Only three.js x10 is drawn as bars: both bundlers produce the same exports from the same input. At commit 2fb2555, without its persistent cache, ts-r-build was about as fast as esbuild there: three invocations of 10 runs each gave medians of 231.8, 233.9 and 255.0 ms against esbuild's 262.7, 238.5 and 246.2 ms, so esbuild was faster in the last one, which the bars show. On excalidraw ts-r-build does less work than esbuild: it does not bundle template-literal import() yet, so it leaves out the 61 locale chunks esbuild emits (7.2 MB of output against 9.3 MB). Those runs, the single bundle and the 20 rebuilds, are in the table with that note, not drawn next to esbuild. Rolldown and Vite's production build have not been measured.
- ts-r
- Other tools
| What | ts-r-build | esbuild 0.28.2 | Notes |
|---|---|---|---|
| Bundle excalidraw, minified: no cache / persistent cache | 255 / 132 ms | 201 ms | Median of 10 runs. Not like-for-like: ts-r-build leaves out the 61 locale chunks esbuild emits (7.2 MB of output against 9.3 MB) |
| excalidraw: first build plus 20 edit-and-rebuild cycles, one process | 1,358 ms | 3,457 ms (context.rebuild) | Median of 5 runs. Not like-for-like: every build leaves out the 61 locale chunks |
| Peak memory, excalidraw, no cache / cached | 317 / 152 MB | 624 MB | Without the 61 locale chunks |
| Peak memory, three.js x10, no cache / cached | 422 / 244 MB | 695 MB | Last of the three invocations |
| three.js x10 output | 7.88 MB (+2.2%), same exports | 7.71 MB | |
| esbuild's own bundler tests (oracle) | 832 of 872 compared cases pass (95.4%) | reference | Commit 07d2cdd |
How we measured
- Measured
- Wall time of whole processes with hyperfine (cargo xtask bench --repos), median; peak RSS of one extra run (--memory). Rebuilds: examples/build_loop.rs --edit against bench/js/esbuild-rebuild.mjs, editing excalidraw-app/components/AppWelcomeScreen.tsx
- Machine
- Apple M-series MacBook (macOS arm64, 16 cores, 48 GB), shared with other jobs while measuring; release build, 4 threads
- Software
- ts-r-build release build with 4 threads; esbuild 0.28.2 pinned in bench/package.json. The three.js x10 rows were measured again on 2026-10-21 at commit 2fb2555 (load average 11-14); excalidraw rows are from commit 9c3a371
- Input
- excalidraw (bench/dev/excalidraw/main.tsx, .woff2 as files, .scss empty) and three.js x10 at the commits pinned in bench/repos.toml
- Command
cargo xtask bench --fetch-repos --repos excalidraw,three-x10 && cargo xtask bench --repos excalidraw,three-x10 --memory- Recorded in
- commit
9c3a371
Correctness
Compared with the tool ts-r-build replaces, on its own tests and on real code.
- Bundler95.4%
832 of 872 compared cases pass (95.4%); 198 skipped
esbuild 0.28.2's end-to-end bundler tests. Commit 07d2cdd.
Command line
An excerpt of ts-r-build --help. You can also run it through the launcher as ts-r build.
Next to esbuild and Vite
Use ts-r-build where you call the esbuild command line today, or to build a library while the app keeps Vite. It has no JavaScript API yet, so scripts that call esbuild.build() from Node switch to the command line first. ts-r-dev uses the same bundler to pre-bundle an app’s dependencies.
Usage: ts-r-build [options] [entry points]
--bundle Bundle all dependencies into the output files
--format=... Output format (iife | cjs | esm)
--minify Minify the output
--outdir=... The output directory
--platform=... Platform target (browser | node | neutral)
--sourcemap Emit a source map
--splitting Enable code splitting (requires --format=esm)
--watch Rebuild when a source file changes