Files
obelisk/docs/adr/0005-app-electron-vite-ts-esm.md
T
tommy0103andClaude Opus 4.8 60d47a852e refactor(app): consume shared indexing core, remove duplicated indexer (Phase 5d-3c-i)
The desktop app now indexes through the shared provider adapters + persist
layer (scripts/providers/{claude,codex}, scripts/persist, scripts/parsing)
instead of maintaining its own parallel indexer. buildIndex shrinks from
~1173 to ~592 lines, eliminating the skill<->app parse duplication that
Phase 5 set out to remove. electron-vite bundles the .ts core from source
with better-sqlite3 injected; the provider->parsing graph stays
node:sqlite-free so nothing drags node:sqlite into the app.

Also fix a misleading log: when a manual rebuild tears down the worker
mid-build, the cancelled background build is a deliberate stop, not a
failure. Guard the service's failure log with the stopped flag so it no
longer prints "Obelisk index build failed: Indexer worker stopped" on
every rebuild.

- CONTEXT.md: provider-adapter + single-persist + node:sqlite-free parsing.
- docs/adr/0005: app builds with electron-vite (TS+ESM), packages with
  electron-builder; preload CJS for sandbox; app consumes core from source.

Verified: full suite 121/121; a node:sqlite-adapter dogfood of the rebuild
path over real data (969 files, 285 sessions, FTS rebuilt) runs clean; app
Rebuild confirmed in real Electron/better-sqlite3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 12:46:02 +08:00

2.5 KiB

The app builds with electron-vite (TS + ESM), packages with electron-builder

Context. The desktop app must consume the shared TypeScript/ESM Core (providers/* + persist) instead of maintaining its own duplicate indexer, and the app itself should be TypeScript + ESM long-term. The app previously ran raw CommonJS on Electron's Node with only the Vue renderer built by Vite; the main process had no build step, and Electron's bundled Node (20 on Electron 33) can neither strip TypeScript nor use node:sqlite. Options for the main-process build were a hand-rolled tsc/esbuild step, vite-plugin-electron, or electron-vite.

Decision. Adopt electron-vite to build all three processes (main, preload, renderer) as TypeScript + ESM, and keep electron-builder for packaging (dmg/nsis/AppImage). electron-vite is purpose-built for the Electron three-process model and handles the parts a DIY build would force us to hand-maintain forever (per-process module format, native-module externalization, dev reload). Specific decisions within this:

  • Preload is emitted as CommonJS even though the app is ESM: the sandboxed renderer (sandbox is on by default since Electron 20, and we keep it on for security) does not support ESM preload. Source stays ESM; only the preload output format is CJS. main loads ../preload/index.js.
  • The app consumes the Core from source: electron-vite/rollup bundles scripts/providers/* + scripts/persist (and their scripts/parsing.mjs dependency) into the app's main/worker build, injecting better-sqlite3. This works because the provider→parsing import graph is node:sqlite-free (ADR-0001), so nothing drags node:sqlite into the app. The dist/ from build:core (ADR-0003) remains for the skill artifact; the app does not need it.
  • better-sqlite3 stays the app's binding, externalized (not bundled) and unpacked from the asar.

Consequences. The app is restructured into src/{main,preload,renderer} with electron.vite.config.ts; each main module is a build input so CommonJS-style require resolution and the indexer worker ({ type: 'module' }) resolve at runtime. npm run dev is electron-vite dev. Tests that loaded app modules moved to ESM imports, and app-main-settings was rewritten from CJS Module._load mocking to node:test mock.module (needs --experimental-test-module-mocks). A future contributor may be tempted to make the preload ESM or disable the sandbox — this ADR records that CJS preload under an on sandbox is the intended, secure default.