UNPKG

@electron-forge/core

Version:

A complete tool for building modern Electron applications

221 lines (194 loc) 6.51 kB
import { spawn, SpawnOptions } from 'child_process'; import { getElectronVersion, listrCompatibleRebuildHook } from '@electron-forge/core-utils'; import { ElectronProcess, ForgeArch, ForgeListrTask, ForgePlatform, ResolvedForgeConfig, StartOptions } from '@electron-forge/shared-types'; import chalk from 'chalk'; import debug from 'debug'; import { Listr } from 'listr2'; import locateElectronExecutable from '../util/electron-executable'; import getForgeConfig from '../util/forge-config'; import { getHookListrTasks, runHook } from '../util/hook'; import { readMutatedPackageJson } from '../util/read-package-json'; import resolveDir from '../util/resolve-dir'; const d = debug('electron-forge:start'); export { StartOptions }; type StartContext = { dir: string; forgeConfig: ResolvedForgeConfig; packageJSON: any; spawned: ElectronProcess; }; export default async ({ dir: providedDir = process.cwd(), appPath = '.', interactive = false, enableLogging = false, args = [], runAsNode = false, inspect = false, inspectBrk = false, }: StartOptions): Promise<ElectronProcess> => { const platform = process.env.npm_config_platform || process.platform; const arch = process.env.npm_config_arch || process.arch; const listrOptions = { concurrent: false, rendererOptions: { collapseErrors: false, }, rendererSilent: !interactive, rendererFallback: Boolean(process.env.DEBUG && process.env.DEBUG.includes('electron-forge')), }; const runner = new Listr<StartContext>( [ { title: 'Locating application', task: async (ctx) => { const resolvedDir = await resolveDir(providedDir); if (!resolvedDir) { throw new Error('Failed to locate startable Electron application'); } ctx.dir = resolvedDir; }, }, { title: 'Loading configuration', task: async (ctx) => { const { dir } = ctx; ctx.forgeConfig = await getForgeConfig(dir); ctx.packageJSON = await readMutatedPackageJson(dir, ctx.forgeConfig); if (!ctx.packageJSON.version) { throw new Error(`Please set your application's 'version' in '${dir}/package.json'.`); } }, }, { title: 'Preparing native dependencies', task: async ({ dir, forgeConfig, packageJSON }, task) => { await listrCompatibleRebuildHook( dir, await getElectronVersion(dir, packageJSON), platform as ForgePlatform, arch as ForgeArch, forgeConfig.rebuildConfig, task as ForgeListrTask<never> ); }, options: { persistentOutput: true, bottomBar: Infinity, showTimer: true, }, }, { title: `Running ${chalk.yellow('generateAssets')} hook`, task: async ({ forgeConfig }, task) => { return task.newListr(await getHookListrTasks(forgeConfig, 'generateAssets', platform, arch)); }, }, ], listrOptions ); await runner.run(); const { dir, forgeConfig, packageJSON } = runner.ctx; let lastSpawned: ElectronProcess | null = null; const forgeSpawn = async () => { let electronExecPath: string | null = null; // If a plugin has taken over the start command let's stop here let spawnedPluginChild = await forgeConfig.pluginInterface.overrideStartLogic({ dir, appPath, interactive, enableLogging, args, runAsNode, inspect, inspectBrk, }); if (typeof spawnedPluginChild === 'object' && 'tasks' in spawnedPluginChild) { const innerRunner = new Listr<never>([], listrOptions); for (const task of spawnedPluginChild.tasks) { innerRunner.add(task); } await innerRunner.run(); spawnedPluginChild = spawnedPluginChild.result; } let prefixArgs: string[] = []; if (typeof spawnedPluginChild === 'string') { electronExecPath = spawnedPluginChild; } else if (Array.isArray(spawnedPluginChild)) { [electronExecPath, ...prefixArgs] = spawnedPluginChild; } else if (spawnedPluginChild) { await runHook(forgeConfig, 'postStart', spawnedPluginChild); return spawnedPluginChild; } if (!electronExecPath) { electronExecPath = await locateElectronExecutable(dir, packageJSON); } d('Electron binary path:', electronExecPath); const spawnOpts = { cwd: dir, stdio: 'inherit', env: { ...process.env, ...(enableLogging ? { ELECTRON_ENABLE_LOGGING: 'true', ELECTRON_ENABLE_STACK_DUMPING: 'true', } : {}), } as NodeJS.ProcessEnv, }; if (runAsNode) { spawnOpts.env.ELECTRON_RUN_AS_NODE = 'true'; } else { delete spawnOpts.env.ELECTRON_RUN_AS_NODE; } if (inspect) { args = ['--inspect' as string | number].concat(args); } if (inspectBrk) { args = ['--inspect-brk' as string | number].concat(args); } const spawned = spawn( electronExecPath!, // eslint-disable-line @typescript-eslint/no-non-null-assertion prefixArgs.concat([appPath]).concat(args as string[]), spawnOpts as SpawnOptions ) as ElectronProcess; await runHook(forgeConfig, 'postStart', spawned); return spawned; }; const forgeSpawnWrapper = async () => { const spawned = await forgeSpawn(); // When the child app is closed we should stop listening for stdin if (spawned) { if (interactive && process.stdin.isPaused()) { process.stdin.resume(); } spawned.on('exit', () => { if (spawned.restarted) { return; } if (interactive && !process.stdin.isPaused()) { process.stdin.pause(); } }); } else if (interactive && !process.stdin.isPaused()) { process.stdin.pause(); } lastSpawned = spawned; return lastSpawned; }; if (interactive) { process.stdin.on('data', async (data) => { if (data.toString().trim() === 'rs' && lastSpawned) { console.info(chalk.cyan('\nRestarting App\n')); lastSpawned.restarted = true; lastSpawned.kill('SIGTERM'); lastSpawned.emit('restarted', await forgeSpawnWrapper()); } }); process.stdin.resume(); } const spawned = await forgeSpawnWrapper(); if (interactive) console.log(''); return spawned; };