diff --git a/apps/desktop/scripts/dev.mjs b/apps/desktop/scripts/dev.mjs index 4a66e43550..46b96ac55f 100644 --- a/apps/desktop/scripts/dev.mjs +++ b/apps/desktop/scripts/dev.mjs @@ -20,12 +20,12 @@ import { existsSync } from 'node:fs'; import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { createServer } from 'vite'; +import { build as esbuildBuild } from 'esbuild'; const DESKTOP_DIR = resolve(fileURLToPath(new URL('..', import.meta.url))); const REPO_ROOT = resolve(DESKTOP_DIR, '..', '..'); const ON_WINDOWS = process.platform === 'win32'; const TSC_CLI = join(REPO_ROOT, 'node_modules', 'typescript', 'bin', 'tsc'); -const ESBUILD_CLI = join(REPO_ROOT, 'node_modules', 'esbuild', 'bin', 'esbuild'); // ── helpers ────────────────────────────────────────────────────────────────── @@ -71,7 +71,20 @@ await Promise.all([ () => log('build', 'libraries (all) — done'), (e) => { log('build', `libraries — FAILED: ${e.message}`); throw e; }, ), - runNodeTool(DESKTOP_DIR, ESBUILD_CLI, ['src/preload/preload.ts', '--bundle', '--platform=node', '--format=cjs', '--outfile=dist/preload/preload.cjs', '--external:electron']).then( + // esbuild via its JS API — NOT `node node_modules/esbuild/bin/esbuild`: + // esbuild's postinstall swaps that file for a platform-native binary, + // and executing a Mach-O file with node throws SyntaxError (broke + // `npm run dev` on any machine where postinstall ran). + esbuildBuild({ + absWorkingDir: DESKTOP_DIR, + entryPoints: ['src/preload/preload.ts'], + bundle: true, + platform: 'node', + format: 'cjs', + outfile: 'dist/preload/preload.cjs', + external: ['electron'], + logLevel: 'warning', + }).then( () => log('build', 'preload — done'), (e) => { log('build', `preload — FAILED: ${e.message}`); throw e; }, ), @@ -81,7 +94,17 @@ await Promise.all([ // tsconfig.main.json still compiles tests for `npm test` and typechecks // main-process code in verification commands. log('build', 'main — starting'); -await runNodeTool(DESKTOP_DIR, ESBUILD_CLI, ['src/main/main.ts', '--bundle', '--platform=node', '--format=esm', '--packages=external', '--outfile=dist/main/main.js', '--external:electron']); +await esbuildBuild({ + absWorkingDir: DESKTOP_DIR, + entryPoints: ['src/main/main.ts'], + bundle: true, + platform: 'node', + format: 'esm', + packages: 'external', + outfile: 'dist/main/main.js', + external: ['electron'], + logLevel: 'warning', +}); log('build', 'main — done'); const BUILD_MS = Date.now() - TIMER_START; diff --git a/apps/desktop/src/main/__tests__/dev-startup-contract.test.ts b/apps/desktop/src/main/__tests__/dev-startup-contract.test.ts index b699d62156..ff3cbc85bf 100644 --- a/apps/desktop/src/main/__tests__/dev-startup-contract.test.ts +++ b/apps/desktop/src/main/__tests__/dev-startup-contract.test.ts @@ -7,10 +7,13 @@ test('dev launcher bundles app main sources without compiling main-process tests const cwd = process.cwd(); const desktopRoot = cwd.endsWith(join('apps', 'desktop')) ? cwd : join(cwd, 'apps', 'desktop'); const devScript = await readFile(join(desktopRoot, 'scripts', 'dev.mjs'), 'utf8'); - assert.match(devScript, /esbuild/); + // dev.mjs now calls esbuild's JS API instead of `node bin/esbuild` + // (postinstall replaces that file with a platform-native binary that + // node cannot execute) — assert on the API options instead of CLI flags. + assert.match(devScript, /esbuildBuild\(/); assert.match(devScript, /src\/main\/main\.ts/); - assert.match(devScript, /--bundle/); - assert.match(devScript, /--packages=external/); + assert.match(devScript, /bundle:\s*true/); + assert.match(devScript, /packages:\s*'external'/); assert.doesNotMatch(devScript, /\['tsc', '-p'/); assert.doesNotMatch(devScript, /tsconfig\.main\.app\.json/); });