From 25b3093c48e5e2700dc3e830a4039df78fa457de Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Fri, 17 Apr 2026 12:29:29 -0400 Subject: [PATCH] fix: bundle xterm deps into daemon-entry to fix packaged build crash (#759) --- electron.vite.config.ts | 18 ++++++++++++++++++ src/main/index.ts | 7 ++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/electron.vite.config.ts b/electron.vite.config.ts index 096a677e..a9ed3e92 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -6,12 +6,30 @@ import tailwindcss from '@tailwindcss/vite' export default defineConfig({ main: { build: { + // 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, so pure-JS dependencies used + // by the daemon must be bundled rather than externalized. + externalizeDeps: { + exclude: ['@xterm/headless', '@xterm/addon-serialize'] + }, rollupOptions: { input: { index: resolve('src/main/index.ts'), 'daemon-entry': resolve('src/main/daemon/daemon-entry.ts') } } + }, + // Why: @xterm/headless declares "exports": null in package.json, which + // prevents Vite's default resolver from finding the CJS entry. Point + // directly at the published main file so the bundler can inline it. + resolve: { + alias: { + '@xterm/headless': resolve('node_modules/@xterm/headless/lib-headless/xterm-headless.js'), + '@xterm/addon-serialize': resolve( + 'node_modules/@xterm/addon-serialize/lib/addon-serialize.js' + ) + } } }, preload: { diff --git a/src/main/index.ts b/src/main/index.ts index b246bcc4..0602e262 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -155,7 +155,12 @@ app.whenReady().then(async () => { // Why: daemon must start before openMainWindow because registerPtyHandlers // (called inside) relies on the provider already being set. Starting it // alongside the other parallel servers keeps cold-start latency flat. - await initDaemonPtyProvider() + // Why: catch so the app still opens even if the daemon fails. The local + // PTY provider remains as the fallback — terminals will still work, just + // without cross-restart persistence. + await initDaemonPtyProvider().catch((error) => { + console.error('[daemon] Failed to start daemon PTY provider, falling back to local:', error) + }) // Why: both server binds are independent and neither blocks window creation. // Parallelizing them with the window open shaves ~100-200ms off cold start.