From 3e2d0f2118ec1f2fe2a93ceb99fdd2e91c56c5db Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sun, 30 Aug 2026 22:57:04 -0700 Subject: [PATCH] perf(build): minify desktop JavaScript bundles without dropping crash context (#17527) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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. --- config/electron-builder.config.cjs | 4 ++++ .../electron-vite-output-contract.test.ts | 20 +++++++++++++++++++ electron.vite.config.ts | 9 +++++++++ 3 files changed, 33 insertions(+) diff --git a/config/electron-builder.config.cjs b/config/electron-builder.config.cjs index d9dbf85bce1..1a31af9dfaf 100644 --- a/config/electron-builder.config.cjs +++ b/config/electron-builder.config.cjs @@ -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). diff --git a/config/scripts/electron-vite-output-contract.test.ts b/config/scripts/electron-vite-output-contract.test.ts index 0937fbf0860..4badf375a0d 100644 --- a/config/scripts/electron-vite-output-contract.test.ts +++ b/config/scripts/electron-vite-output-contract.test.ts @@ -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)) { diff --git a/electron.vite.config.ts b/electron.vite.config.ts index 4faa844ee4c..4ed4641cde1 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -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