Skip to content

perf(dev): 6.5x faster dev boot + fix Windows spawn ENOENT - #196

Closed
Zen0-99 wants to merge 3 commits into
CoreBunch:mainfrom
Zen0-99:boot/launch-speed
Closed

Zen0-99 wants to merge 3 commits into
CoreBunch:mainfrom
Zen0-99:boot/launch-speed

Conversation

@Zen0-99

@Zen0-99 Zen0-99 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Gate the React Compiler to production builds only. It was running its full babel pass over every .tsx module on every dev start, accounting for a large share of the ~45s first-load transform cost.
  • Preload the editor body chunk in parallel with the page render, and skip the 9 sibling workspace-page prewarms in dev (they forced ~20s of Vite transforms and starved the editor of CPU).
  • Add a Windows fallback to scripts/lib/freePort.ts (netstat/tasklist) so the dev port probe works when lsof/ps are not on PATH.
  • Fix scripts/dev.ts child spawning on Windows: bun is a .cmd shim and vite is not on the child's PATH, so Bun.spawn(['bun', ...]) threw ENOENT. Children now run through process.execPath (the real bun.exe) - bun for its own entry, bun executing node_modules/vite/bin/vite.js for the dev server.

Measured result: dev boot 47.7s -> 7.4s (6.5x).

Why

bun run dev presented two waits: a ~20s waiting screen before the admin UI painted, then a second wait for the editor chunk ("Editor is still loading - Retry"). An instrumented boot corridor showed the dominant cost was Vite's per-module transform in dev - largely the React Compiler babel pass on every module every start - plus the editor chunk being fetched serially after first paint and the sibling workspace pages being prewarmed up front. Separately, the Windows spawn bug (a .cmd shim) blocked bun run dev entirely with ENOENT.

Test

  • bun run dev boots clean: [server] Listening on http://localhost:3001 and VITE ready in ~4s - no ENOENT.
  • Editor is interactive on /admin/site with no Retry screen; the sibling workspace pages (PluginPage, AiPage, ...) are gone from the first-load wait.
  • Confirmed process.execPath resolves to the real bun.exe, node_modules/vite/bin/vite.js exists, and Bun.which('vite') is null (so the explicit path is required).
  • bun test src/tests/architecture/bundle-size-budgets.test.ts unaffected: the editor body stays a separate lazy chunk (preload changes fetch timing, not bytes).

- React Compiler: run in build only (was adding a full compiler pass to every module transform in dev, ~45s first-load cost)

- Preload the editor body chunk in parallel with page render; skip the 9 sibling workspace-page prewarms in dev (they forced ~20s of transforms and starved the editor)

- freePort: add Windows netstat/tasklist fallback

- dev.ts: resolve bun via process.execPath and other bins via Bun.which so child spawns don't ENOENT on Windows shims
On Windows the launched 'bun' is a .cmd shim (Bun.spawn(['bun',...]) -> ENOENT) and 'vite' is not on the child's PATH (Bun.which('vite') is null). Resolve both through process.execPath: bun for its own entry, bun executing node_modules/vite/bin/vite.js for the dev server.
Adopt upstream CoreBunch#193's bunCommand/bunRunCommand helpers (process.execPath-based, same Windows fix) for child spawning; drop the now-redundant resolveCommand. Keep the Windows netstat/tasklist fallback in freePort.findPortHolders (superior to upstream's skip-on-Windows) and fix the re-probe loop's sleep spawn, which ENOENT'd on Windows.
@DavidBabinec

Copy link
Copy Markdown
Contributor

Hi @Zen0-99

Thanks for digging into dev boot time, it's something I care about too. I can't take this one as it is though, for a few reasons:

  1. Turning the React Compiler off in dev goes against how we run the app (compiler on everywhere, see CLAUDE.md). It would hide compiler-exposed bugs until the prod build.
  2. The branch drops the Plugin type import that vite.config.ts still uses, so bun run build fails on current main.
  3. killHolder became async but the call site doesn't await it, so the "could not free the port" path never fires.
  4. The spawn ENOENT fix already landed in fix(scripts): launch dev tools through current Bun executable #192, fix(scripts): launch vite through dev script on Windows #193 and fix(scripts): launch vite through entrypoint on Windows #202.

The 47s to 7s number was also measured before #503 cleaned up the Vite proxy, so it would need re-measuring on current main anyway.

The Bun.sleep swaps and a Windows netstat/tasklist fallback in freePort.ts are worth having. If you want to send those as a small separate PR with the async call site fixed, I would be happy to review it.

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants