From e9fb5dec6735a9ed732418f4b72f3634b0301a90 Mon Sep 17 00:00:00 2001 From: heyongqi10 Date: Mon, 12 Oct 2026 19:51:12 +0800 Subject: [PATCH 1/2] Add Bun as a react-1k benchmark tool. Wire Bun into the harness via case wrappers and CI setup so startup, HMR, and build metrics can be compared alongside existing bundlers. Co-authored-by: Cursor --- .github/workflows/benchmark.yml | 3 ++ README.md | 2 +- cases/react-1k/benchmark-config.mjs | 1 + cases/react-1k/bun-build.mjs | 34 ++++++++++++++++++++ cases/react-1k/bun-dev.mjs | 26 +++++++++++++++ cases/react-1k/package.json | 2 ++ scripts/benchmark.ts | 49 ++++++++++++++++++++++++++--- 7 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 cases/react-1k/bun-build.mjs create mode 100644 cases/react-1k/bun-dev.mjs diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 9a948d038..ff5d3c35e 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -49,6 +49,9 @@ jobs: with: node-version: 24.21.0 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + - name: Install Global Dependencies run: | npm i cross-env -g diff --git a/README.md b/README.md index dc539a05f..3164b846e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Build Tools Comparison -Benchmark comparing JavaScript bundlers and build tools ([Rspack](https://github.com/web-infra-dev/rspack), [Rsbuild](https://github.com/web-infra-dev/rsbuild), [webpack](https://github.com/webpack/webpack), [Vite](https://github.com/vitejs/vite), [Rolldown](https://github.com/rolldown/rolldown), [esbuild](https://github.com/evanw/esbuild), [Rollup](https://github.com/rollup/rollup), [Parcel](https://github.com/parcel-bundler/parcel), [Farm](https://github.com/farm-fe/farm) and [Utoo](https://github.com/utooland/utoo)) for dev server startup time, build performance and bundle size for applications with different module sizes. +Benchmark comparing JavaScript bundlers and build tools ([Rspack](https://github.com/web-infra-dev/rspack), [Rsbuild](https://github.com/web-infra-dev/rsbuild), [webpack](https://github.com/webpack/webpack), [Vite](https://github.com/vitejs/vite), [Rolldown](https://github.com/rolldown/rolldown), [esbuild](https://github.com/evanw/esbuild), [Rollup](https://github.com/rollup/rollup), [Parcel](https://github.com/parcel-bundler/parcel), [Farm](https://github.com/farm-fe/farm), [Utoo](https://github.com/utooland/utoo) and [Bun](https://bun.com)) for dev server startup time, build performance and bundle size for applications with different module sizes. ## Metrics diff --git a/cases/react-1k/benchmark-config.mjs b/cases/react-1k/benchmark-config.mjs index a74b1da0d..231d08871 100644 --- a/cases/react-1k/benchmark-config.mjs +++ b/cases/react-1k/benchmark-config.mjs @@ -7,6 +7,7 @@ export const config = { 'farm', 'parcel', 'utoo', + 'bun', ], rootFile: 'f0.jsx', leafFile: 'd0/d0/d0/f0.jsx', diff --git a/cases/react-1k/bun-build.mjs b/cases/react-1k/bun-build.mjs new file mode 100644 index 000000000..cfd97c57d --- /dev/null +++ b/cases/react-1k/bun-build.mjs @@ -0,0 +1,34 @@ +import { spawn } from 'node:child_process'; + +// Printed before the Bun process starts so the harness can measure build time. +console.log('Benchmark Start Time:', Date.now()); + +const child = spawn( + 'bun', + [ + 'build', + './index.html', + '--outdir=dist', + '--target=browser', + '--minify', + '--production', + '--define', + 'process.env.NODE_ENV:"production"', + ], + { + stdio: 'inherit', + cwd: import.meta.dirname, + env: { + ...process.env, + NODE_ENV: 'production', + }, + }, +); + +child.on('exit', (code, signal) => { + if (signal) { + process.kill(process.pid, signal); + return; + } + process.exit(code ?? 1); +}); diff --git a/cases/react-1k/bun-dev.mjs b/cases/react-1k/bun-dev.mjs new file mode 100644 index 000000000..0c1225b53 --- /dev/null +++ b/cases/react-1k/bun-dev.mjs @@ -0,0 +1,26 @@ +import { spawn } from 'node:child_process'; + +// Printed before the Bun process starts so the harness can measure startup. +console.log('Benchmark Start Time:', Date.now()); + +// Keep in sync with the Bun BuildTool port in scripts/benchmark.ts. +const port = '3000'; +const child = spawn('bun', ['--port', port, './index.html'], { + stdio: 'inherit', + cwd: import.meta.dirname, + env: process.env, +}); + +child.on('exit', (code, signal) => { + if (signal) { + process.kill(process.pid, signal); + return; + } + process.exit(code ?? 1); +}); + +for (const signal of ['SIGINT', 'SIGTERM']) { + process.on(signal, () => { + child.kill(signal); + }); +} diff --git a/cases/react-1k/package.json b/cases/react-1k/package.json index 330a2e931..42d061416 100644 --- a/cases/react-1k/package.json +++ b/cases/react-1k/package.json @@ -2,6 +2,7 @@ "name": "react-1k", "version": "0.0.0", "scripts": { + "build:bun": "node ./bun-build.mjs", "build:farm": "farm build", "build:parcel": "parcel build index.html", "build:rolldown": "cross-env NODE_ENV=production rolldown -c ./rolldown.config.mjs", @@ -10,6 +11,7 @@ "build:utoo": "cross-env NODE_ENV=production up build", "build:vite": "vite build", "build:webpack": "cross-env NODE_ENV=production webpack build --mode production", + "start:bun": "node ./bun-dev.mjs", "start:farm": "farm start", "start:parcel": "parcel -p 3200 index.html", "start:rsbuild": "rsbuild dev", diff --git a/scripts/benchmark.ts b/scripts/benchmark.ts index 7ca53844d..f61311346 100644 --- a/scripts/benchmark.ts +++ b/scripts/benchmark.ts @@ -1,4 +1,8 @@ -import { spawn, type ChildProcess } from 'node:child_process'; +import { + execFileSync, + spawn, + type ChildProcess, +} from 'node:child_process'; import os from 'node:os'; import { monitorProcess, @@ -144,7 +148,12 @@ interface BuildToolOptions { startScript: string; startedRegex: RegExp; buildScript: string; - binFilePath: string; + /** + * Path under `node_modules` to a CLI entry that can be patched with the + * benchmark start timestamp. Omit for tools that ship their own wrapper + * (for example Bun) and already print `Benchmark Start Time`. + */ + binFilePath?: string; } class BuildTool { @@ -153,7 +162,7 @@ class BuildTool { private readonly startScript: string; private readonly startedRegex: RegExp; private readonly buildScript: string; - private readonly binFilePath: string; + private readonly binFilePath?: string; constructor({ name, @@ -168,8 +177,10 @@ class BuildTool { this.startScript = startScript; this.startedRegex = startedRegex; this.buildScript = buildScript; - this.binFilePath = path.join(process.cwd(), 'node_modules', binFilePath); - this.hackBinFile(); + if (binFilePath) { + this.binFilePath = path.join(process.cwd(), 'node_modules', binFilePath); + this.hackBinFile(); + } } cleanCache(): void { @@ -180,6 +191,8 @@ class BuildTool { 'node_modules/.vite', 'node_modules/.farm', '.turbopack', + // Bun may write compile/cache artifacts under the case directory. + '.bun', ]) { fse.removeSync(path.join(dir, cache)); } @@ -188,6 +201,8 @@ class BuildTool { // Add a `console.log('Benchmark start', Date.now())` to the bin file's second line private hackBinFile(): void { + if (!this.binFilePath) return; + logger.info( 'Setup bin file for', color.green(this.name), @@ -439,6 +454,30 @@ parseToolNames().forEach((name) => { }), ); break; + case 'bun': { + // Bun is a system binary (not an npm package). Wrappers under the case + // directory print `Benchmark Start Time` themselves. + let bunVersion: string; + try { + bunVersion = execFileSync('bun', ['--version'], { + encoding: 'utf8', + }).trim(); + } catch { + throw new Error( + 'Bun is not installed or not on PATH. Install Bun to run this benchmark tool.', + ); + } + buildTools.push( + new BuildTool({ + name: 'Bun ' + bunVersion, + port: 3000, + startScript: 'start:bun', + startedRegex: /dev server ready in (.+) (s|ms)/, + buildScript: 'build:bun', + }), + ); + break; + } } }); From 489a2264ae6815c68089bab31baa5cd886ee1ca5 Mon Sep 17 00:00:00 2001 From: heyongqi10 Date: Tue, 13 Oct 2026 09:38:53 +0800 Subject: [PATCH 2/2] fix(ci): pin setup-bun action to full commit SHA Repository policy rejects version tags on third-party actions. Co-authored-by: Cursor --- .github/workflows/benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index ff5d3c35e..6ec4f843b 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -50,7 +50,7 @@ jobs: node-version: 24.21.0 - name: Setup Bun - uses: oven-sh/setup-bun@v2 + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 - name: Install Global Dependencies run: |