perf(build): minify desktop JavaScript bundles without dropping crash context (#17527)

* perf(build): minify desktop JavaScript bundles

* perf(build): minify with rolldown's oxc and emit hidden main source maps

'esbuild' made rolldown disable its own minifier and re-print every chunk
through esbuild, which is not a declared dependency and resolves only via
pnpm's shamefullyHoist from electron-vite's tree (0.25.12 against a declared
peer of ^0.27.0). Switching to rolldown's in-process 'oxc' minifier drops
that second pass: main+renderer build falls 23.2s -> 11.9s and ships ~2.7MB
less JavaScript.

keepNames is dropped with it — it cost ~1.5MB and only recovered function
names. main now builds with sourcemap:'hidden', which restores names *and*
locations without emitting a sourceMappingURL. Packaging excludes
out/**/*.map so app.asar is unaffected; release CI publishes the maps.
This commit is contained in:
Neil
2026-08-30 22:57:04 -07:00
committed by GitHub
parent 9f0b94d9b6
commit 3e2d0f2118
3 changed files with 33 additions and 0 deletions
+4
View File
@@ -148,6 +148,10 @@ module.exports = {
'!Casks{,/**/*}',
'!{AGENTS.md,CLAUDE.md,DEVELOPING.md,bundle-size-progress.md,ORCHESTRATION_IMPLEMENTATION_CHECKLIST.md,ORCHESTRATION_STRUCTURED_OUTPUT_DESIGN.md}',
'!out/**/*.test.js',
// Why: main builds with sourcemap:'hidden' so release CI can publish maps
// for decoding minified crash traces. The app never loads them (no
// sourceMappingURL is emitted), and packing them would add ~34MB to app.asar.
'!out/**/*.map',
// Why: Vite's manifest is only used to project the paired web client.
'!out/renderer/.vite{,/**/*}',
// Why: out/electron-dev caches `pnpm dev`'s per-branch Electron.app copies (~270MB each).
@@ -16,6 +16,7 @@ import {
BOOTSTRAP_FATAL_LOG_FILE_NAME,
createBootstrapFatalExitBanner
} from '../build-plugins/bootstrap-fatal-exit-banner'
import { createRequire } from 'node:module'
import { electronViteConfig } from '../../electron.vite.config'
import { BOOTSTRAP_FATAL_EXIT_GUARD_KEY } from '../../src/main/startup/bootstrap-fatal-exit-guard'
@@ -72,7 +73,26 @@ function failBootstrapWithBanner(options: {
return processMock
}
const electronBuilderConfig = createRequire(import.meta.url)('../electron-builder.config.cjs') as {
files: string[]
}
describe('Electron Vite output contract', () => {
it("minifies main and renderer with rolldown's in-process minifier", () => {
// Why: 'esbuild' routes every chunk through a second, undeclared transpiler.
expect(electronViteConfig.main?.build?.minify).toBe('oxc')
expect(electronViteConfig.renderer?.build?.minify).toBe('oxc')
expect(electronViteConfig.main?.esbuild).toBeUndefined()
expect(electronViteConfig.renderer?.esbuild).toBeUndefined()
})
it('emits hidden main source maps that packaging strips from app.asar', () => {
// Hidden maps decode minified crash traces without the bundle referencing
// files that the packaged app never ships.
expect(electronViteConfig.main?.build?.sourcemap).toBe('hidden')
expect(electronBuilderConfig.files).toContain('!out/**/*.map')
})
it('keeps main-process and plain-Node entries at stable CommonJS paths', () => {
const output = electronViteConfig.main?.build?.rollupOptions?.output
if (!output || Array.isArray(output)) {
+9
View File
@@ -194,6 +194,14 @@ function createMainBootstrapPlugin() {
export const electronViteConfig: UserConfig = {
main: {
build: {
// Why: 'esbuild' makes rolldown disable its own minifier and re-print every
// chunk through esbuild, which is undeclared here and only resolves via
// pnpm hoisting. 'oxc' is rolldown's in-process minifier.
minify: 'oxc',
// Why: 'hidden' emits .js.map with no sourceMappingURL, so the shipped
// bundle never references maps that packaging strips out. Release CI
// uploads them so minified crash traces stay decodable.
sourcemap: 'hidden',
// Why: daemon-entry.js is asar-unpacked so child_process.fork() can
// execute it from disk. Node's module resolution from the unpacked
// directory cannot reach into app.asar; startup-critical pure JS must
@@ -298,6 +306,7 @@ export const electronViteConfig: UserConfig = {
build: {
manifest: true,
modulePreload: { polyfill: true },
minify: 'oxc',
target: 'es2020',
// Why: the pop-out dashboard is a second top-level window with its own
// React root. It gets its own HTML entry so it can boot independently of