Files
hermes-agent/apps/desktop/scripts/bundle-electron-main.mjs
Brooklyn Nicholson c0a0f097a8 fix(desktop): ship entry.ts as the Electron main bundle entry
The bundler rewrite predates #113247, and merging it back kept main.ts as
the esbuild entry. entry.ts, which picks the ozone platform and relaunches
before main loads, was no longer bundled: the WSLg Wayland relaunch never
ran. Bundle entry.ts again and run the built electron-main.mjs in a test
to prove the relaunch ships.
2026-09-27 06:26:35 -05:00

93 lines
3.9 KiB
JavaScript

#!/usr/bin/env node
import { execFileSync } from 'node:child_process'
import { mkdirSync, readFileSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { pathToFileURL } from 'node:url'
import { parseArgs } from 'node:util'
import { isMain, workspaceTool } from '../../../scripts/build/frontend-common.mjs'
import { environmentDefaultsBanner } from './bundle-env.mjs'
const repoRoot = resolve(import.meta.dirname, '../../..')
// Evaluate the one identity module in a fresh process. Its CJS cache and the
// caller's environment must not carry a previous variant into this artifact.
function productIdentity(source, stamp) {
const variant = stamp.updateMechanism === 'microsoft-store' ? 'store'
: stamp.payload === 'bootstrap' ? '' : stamp.payload
if (!['', 'bundled', 'light', 'store'].includes(variant)) {
throw new Error(`Invalid desktop stamp payload: ${stamp.payload}`)
}
return execFileSync(process.execPath, ['-e', 'console.log(JSON.stringify(require(process.argv[1])))',
join(source, 'apps/desktop/product-identity.cjs')], {
env: { ...process.env, HERMES_DESKTOP_VARIANT: variant, HERMES_PAYLOAD_TAG: stamp.tag || '',
// Commit builds stamp source='commit-build'; the display name carries
// the short SHA (see product-identity.cjs). Tagged builds pass ''.
HERMES_BUILD_COMMIT: stamp.source === 'commit-build' ? (stamp.commit || '') : '' },
encoding: 'utf8',
}).trim()
}
/** Bundle main/preload using only the prepared workspace's compiler. */
export async function bundleElectronMain({ source, out, stamp, dev = false }) {
source = resolve(source)
out = resolve(out)
const { build } = await import(pathToFileURL(workspaceTool(source, 'apps/desktop', 'esbuild')).href)
// Defaults must run before bundled modules resolve paths or onboarding flags.
// Dev bundles leave the environment alone so source-tree resolution keeps working.
const envBanner = dev ? '' : environmentDefaultsBanner(process.env.HERMES_BUNDLE_ENV_JSON || '{}')
const define = {}
if (!dev) {
if (!stamp) throw new Error('A prepared install stamp is required')
const raw = readFileSync(stamp, 'utf8')
const metadata = JSON.parse(raw)
define['process.env.HERMES_DESKTOP_IS_PACKAGED'] = JSON.stringify(true)
define.__HERMES_INSTALL_STAMP__ = raw
define.__HERMES_PRODUCT_IDENTITY__ = productIdentity(source, metadata)
}
mkdirSync(out, { recursive: true })
const common = {
absWorkingDir: join(source, 'apps/desktop'),
bundle: true,
platform: 'node',
target: 'node20',
external: ['electron', 'node-pty', 'get-windows', 'fs'],
define,
logLevel: 'info',
}
await build({
...common,
// entry.ts picks the Linux ozone platform (native Wayland/WSLg) and
// relaunches before main.ts loads; Electron reads it only from argv.
entryPoints: [join(source, 'apps/desktop/electron/entry.ts')],
format: 'esm',
outfile: join(out, 'electron-main.mjs'),
banner: { js: "import { createRequire } from 'module'; const require = createRequire(import.meta.url);" + envBanner },
})
await build({
...common,
entryPoints: [join(source, 'apps/desktop/electron/preload.ts')],
format: 'cjs',
outfile: join(out, 'electron-preload.js'),
})
// Preview-pane <webview> guest preload; main.ts hands this path to the
// preview webview via will-attach-webview.
await build({
...common,
entryPoints: [join(source, 'apps/desktop/electron/preview-guest-preload-entry.ts')],
format: 'cjs',
outfile: join(out, 'preview-guest-preload.js'),
})
}
if (isMain(import.meta.url)) {
const { values } = parseArgs({ options: {
source: { type: 'string', default: repoRoot },
out: { type: 'string' }, stamp: { type: 'string' }, dev: { type: 'boolean' },
} })
await bundleElectronMain({
...values,
out: values.out || join(values.source, 'apps/desktop/dist'),
stamp: values.stamp || join(values.source, 'apps/desktop/build/install-stamp.json'),
})
}