Skip to content

fix(preload): bundle the preload so it loads under Electron's sandbox - #29

Merged
killerwolf merged 1 commit into
mainfrom
fix/preload-sandbox-bundle
Sep 7, 2026
Merged

fix(preload): bundle the preload so it loads under Electron's sandbox#29
killerwolf merged 1 commit into
mainfrom
fix/preload-sandbox-bundle

Conversation

@killerwolf

Copy link
Copy Markdown
Owner

The app on main opens to a blank window

refactor(ipc): centralize the main/preload IPC contract (ef10eda) added a relative import to the preload:

import { type AppSettings, CHANNELS, ... } from "./ipc-types";

tsc compiles that to require("./ipc-types"). Electron sandboxes the renderer by default, and a sandboxed preload can only require Electron's own builtins — never a relative file.

So the preload throws on load → contextBridge never runs → window.electronAPI is undefined → the renderer crashes on its first property access (onUpdateStatus). The window comes up empty.

Reproduced against the real, untouched dist/main.js on main:

sandbox = true
Unable to load preload script: dist/preload.js
  Error: module not found: ./ipc-types
typeof window.electronAPI = undefined
rendered body text = ""

No released version is affected — v1.4.0 and earlier predate the refactor. But the next tag cut from main would have shipped this.

Why CI was green

The type-checker is happy (the import is valid TypeScript), the unit tests never boot Electron, and npm run pack builds the app without launching it. The PR that introduced this passed all checks.

The fix

Bundle the preload with esbuild as an IIFE, so it arrives in the sandbox self-contained with no runtime require of local files.

  • --format=iife specifically: the CJS output references module, which a sandboxed preload also doesn't provide.
  • tsc still type-checks electron/preload.ts through tsconfig.main.json, so the single-source-of-truth IPC contract from ef10eda is kept intact — this only changes how the file is emitted.
  • esbuild was already present transitively via Vite; this promotes it to an explicit devDependency rather than relying on a transitive one.

Verification

From a clean rm -rf dist dist-react && npm run build:

typeof window.electronAPI = object
body = "QuickToss  Organize your files quickly with keyboard shortcuts and buttons.  Select Folder to Organize"

lint, format, typecheck and all 54 tests pass.

Follow-up worth doing separately

A smoke test that boots Electron and asserts window.electronAPI is defined would close this gap permanently. Not included here to keep the fix minimal.

🤖 Generated with Claude Code

Since ef10eda the preload imports CHANNELS from ./ipc-types, which tsc
compiles to require("./ipc-types"). Electron sandboxes the renderer by
default, and a sandboxed preload can only require Electron's own builtins
— never a relative file. So the preload threw on load, contextBridge never
ran, window.electronAPI was undefined, and the renderer crashed on its
first property access. The window came up blank.

Nothing caught it: the type-checker is happy, the unit tests never boot
Electron, and `npm run pack` builds the app without launching it.

Bundle the preload with esbuild as an IIFE instead, so it reaches the
sandbox self-contained. tsc still type-checks it via tsconfig.main.json,
so the single-source-of-truth IPC contract that ef10eda introduced is
kept intact — this only changes how the file is emitted.

No released version is affected; v1.4.0 and earlier predate the refactor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@killerwolf
killerwolf force-pushed the fix/preload-sandbox-bundle branch from 999641a to 11a433c Compare September 7, 2026 13:41
@killerwolf
killerwolf merged commit 3c2bb51 into main Sep 7, 2026
2 checks passed
@killerwolf
killerwolf deleted the fix/preload-sandbox-bundle branch September 7, 2026 13:53
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