Skip to content

perf(dev): use SSR bundling instead of subprocess for hot reload#72

Merged
LeeCheneler merged 5 commits into
mainfrom
feat/ssr-bundling-dev-perf
Dec 18, 2025
Merged

perf(dev): use SSR bundling instead of subprocess for hot reload#72
LeeCheneler merged 5 commits into
mainfrom
feat/ssr-bundling-dev-perf

Conversation

@LeeCheneler
Copy link
Copy Markdown
Contributor

Summary

  • Replaces subprocess-per-request architecture with esbuild-based SSR bundling
  • Eliminates subprocess spawn overhead (50-200ms per request)
  • Keeps Shiki highlighter warm in parent process (saves 200-500ms first render)
  • Removes redundant page scanning (subprocess rescanned on each request)

How it works

  1. Generates SSR entry code that imports page + layout components
  2. Bundles with esbuild (reads fresh files from disk, bypasses Deno's module cache)
  3. Writes bundle to unique temp file (so import() isn't cached)
  4. Imports and executes to get fresh components
  5. Renders in parent process

Performance

Before: 600-1200ms per page render
After: 150-330ms per page render

~50-75% reduction in dev server response time.

Test plan

  • All existing tests pass (52 tests, 806 steps)
  • Dev integration tests verify page serving, HMR injection, WebSocket endpoint
  • Lint and type check pass

🤖 Generated with Claude Code

LeeCheneler and others added 3 commits December 18, 2025 22:55
Replaces the subprocess-per-request architecture with esbuild-based SSR
bundling to bypass Deno's module cache. This eliminates:

- Subprocess spawn overhead (50-200ms per request)
- Shiki highlighter reinitialization (200-500ms first render)
- Redundant page scanning (subprocess rescanned on each request)

The new approach:
1. Generates SSR entry code that imports page + layout components
2. Bundles with esbuild (reads fresh files from disk)
3. Writes to unique temp file (bypasses Deno's import cache)
4. Imports and executes to get fresh components
5. Renders in parent process (keeps Shiki warm)

Measured improvement: 600-1200ms -> 150-330ms per page render.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The previous code called both stopSSRBundler() and stopEsbuild() in
parallel, but both called esbuild.stop() on the same global instance.
This caused race conditions in subprocess cleanup, leading to leaked
child processes detected by Deno's test runner.

Now we only call stopEsbuild() once, and removed the redundant
stopSSRBundler export.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@LeeCheneler LeeCheneler enabled auto-merge (squash) December 18, 2025 23:12
LeeCheneler and others added 2 commits December 18, 2025 23:15
Switch from esbuild.build() to esbuild.context() + ctx.dispose() for
SSR bundling. This gives explicit lifecycle control over the esbuild
child process, ensuring it's properly cleaned up after each build.

The previous approach relied on esbuild's global service and stop(),
which had race conditions in subprocess cleanup that caused test
failures in CI.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The esbuild.stop() function returns before the child process fully
terminates, causing subprocess leak detection failures in CI tests.
Adding a small delay ensures the process has fully terminated before
test cleanup runs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@LeeCheneler LeeCheneler merged commit b3f02ab into main Dec 18, 2025
2 checks passed
@LeeCheneler LeeCheneler deleted the feat/ssr-bundling-dev-perf branch December 18, 2025 23:23
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.

1 participant