Skip to content

perf(plugin-vite): run production builds in isolated subprocesses#4163

Merged
MarshallOfSound merged 1 commit intonextfrom
sam/vite-subprocess-builds
Mar 13, 2026
Merged

perf(plugin-vite): run production builds in isolated subprocesses#4163
MarshallOfSound merged 1 commit intonextfrom
sam/vite-subprocess-builds

Conversation

@MarshallOfSound
Copy link
Member

@MarshallOfSound MarshallOfSound commented Mar 12, 2026

Summary

Run each production Vite build target (main, preload, renderer) in its own Node subprocess during forge package, instead of calling vite.build() in-process for every target concurrently.

Why

When all targets share one Node process, concurrent Rollup instances thrash the shared V8 heap and event loop. For apps with many build targets and large bundles, this serializes a lot of work that should be parallel. On one real-world app with a dozen+ targets this cut total Vite build time by ~45–55% — subprocesses can actually saturate multiple CPU cores instead of fighting over one event loop.

Dev mode (forge start) is unchanged — dev servers and watchers stay in-process.

How

  • subprocess-worker.ts receives the full plugin config + a target index via env vars, re-resolves the config via ViteConfigGenerator, and runs a single vite.build()
  • The worker gets the full renderer[] list (not just one spec) because getBuildDefine() reads it to generate ${NAME}_VITE_NAME defines for main targets
  • Subprocess runs with cwd: projectDir so relative entry paths in rollupOptions.input (preload) resolve correctly
  • Respects the existing concurrent config option

Test plan

  • subprocess-worker.spec.ts — 7 tests covering main/preload/renderer builds, define injection, non-zero index, and error surfacing
  • Manual forge package run on a multi-target app
  • CI green

Each main/preload/renderer build target now runs in its own Node
subprocess during `forge package`. This prevents Vite's global state
(plugin caches, esbuild instances, module graph) from leaking across
targets, which previously caused intermittent misresolution when
building multiple targets in the same process.

The plugin passes a serializable snapshot of the build/renderer config
to each worker via env vars. Dev server mode is unchanged — this only
affects production (`isProd`) builds.
@MarshallOfSound MarshallOfSound requested a review from a team as a code owner March 12, 2026 05:21
@MarshallOfSound MarshallOfSound changed the title feat(plugin-vite): isolate production builds in subprocesses fix(plugin-vite): run production builds in isolated subprocesses Mar 12, 2026
@MarshallOfSound MarshallOfSound changed the title fix(plugin-vite): run production builds in isolated subprocesses perf(plugin-vite): run production builds in isolated subprocesses Mar 12, 2026
@MarshallOfSound MarshallOfSound enabled auto-merge (squash) March 12, 2026 07:11
@erickzhao erickzhao added the next label Mar 12, 2026
Copy link
Member

@erickzhao erickzhao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overall LGTM, two questions

FORGE_VITE_INDEX: String(index),
FORGE_VITE_CONFIG: JSON.stringify(pluginConfig),
},
stdio: ['ignore', 'ignore', 'pipe'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also pipe out stdout back to the parent process in case any logs/warnings are emitted from the Vite build?

});

child.on('error', reject);
child.on('close', (code) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we handle signal as well here as the second param?

@MarshallOfSound MarshallOfSound merged commit eb4a292 into next Mar 13, 2026
17 of 21 checks passed
@MarshallOfSound MarshallOfSound deleted the sam/vite-subprocess-builds branch March 13, 2026 01:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants