diff --git a/packages/api/cli/src/electron-forge-start.ts b/packages/api/cli/src/electron-forge-start.ts index f5b90c7bb0..3a675d94fb 100644 --- a/packages/api/cli/src/electron-forge-start.ts +++ b/packages/api/cli/src/electron-forge-start.ts @@ -1,5 +1,6 @@ import { api, StartOptions } from '@electron-forge/core'; +import { ChildProcess } from 'child_process'; import fs from 'fs-extra'; import path from 'path'; import program from 'commander'; @@ -64,12 +65,26 @@ import './util/terminate'; const spawned = await api.start(opts); await new Promise((resolve) => { - spawned.on('exit', (code: number) => { - if ((spawned as any).restarted) return; - if (code !== 0) { - process.exit(code); - } - resolve(); - }); + const listenForExit = (child: ChildProcess) => { + const removeListeners = () => { + child.removeListener('exit', onExit); + child.removeListener('restarted', onRestart); + }; + const onExit = (code: number) => { + removeListeners(); + if ((spawned as any).restarted) return; + if (code !== 0) { + process.exit(code); + } + resolve(); + }; + const onRestart = (newChild: ChildProcess) => { + removeListeners(); + listenForExit(newChild); + }; + child.on('exit', onExit); + child.on('restarted', onRestart); + }; + listenForExit(spawned); }); })(); diff --git a/packages/api/core/src/api/start.ts b/packages/api/core/src/api/start.ts index 402aebfd12..ce8e9a47fa 100644 --- a/packages/api/core/src/api/start.ts +++ b/packages/api/core/src/api/start.ts @@ -53,6 +53,21 @@ export default async ({ const forgeSpawnWrapper = async () => { lastSpawned = await forgeSpawn(); + // When the child app is closed we should stop listening for stdin + if (lastSpawned) { + if (interactive && process.stdin.isPaused()) { + process.stdin.resume(); + } + lastSpawned.on('exit', () => { + if ((lastSpawned as any).restarted) return; + + if (!process.stdin.isPaused()) process.stdin.pause(); + }); + } else { + if (interactive && !process.stdin.isPaused()) { + process.stdin.pause(); + } + } return lastSpawned; }; @@ -114,12 +129,12 @@ export default async ({ }; if (interactive) { - process.stdin.on('data', (data) => { + process.stdin.on('data', async (data) => { if (data.toString().trim() === 'rs' && lastSpawned) { console.info('\nRestarting App\n'.cyan); (lastSpawned as any).restarted = true; lastSpawned.kill('SIGTERM'); - forgeSpawnWrapper(); + lastSpawned.emit('restarted', await forgeSpawnWrapper()); } }); } diff --git a/packages/api/core/test/fast/start_spec.ts b/packages/api/core/test/fast/start_spec.ts index d84d47c07b..9c13ef6749 100644 --- a/packages/api/core/test/fast/start_spec.ts +++ b/packages/api/core/test/fast/start_spec.ts @@ -11,7 +11,7 @@ describe('start', () => { let packageJSON: any; let resolveStub: SinonStub; let spawnStub: SinonStub; - let shouldOverride: boolean; + let shouldOverride: any; let processOn: SinonStub; beforeEach(() => { @@ -58,7 +58,7 @@ describe('start', () => { it('should not spawn if a plugin overrides the start command', async () => { resolveStub.returnsArg(0); - shouldOverride = true; + shouldOverride = { on: () => {} }; await start({ dir: __dirname, interactive: false, @@ -172,12 +172,13 @@ describe('start', () => { it('should resolve with a handle to the spawned instance', async () => { resolveStub.returnsArg(0); - spawnStub.returns('child'); + const fakeChild = { on: () => {} }; + spawnStub.returns(fakeChild); await expect(start({ dir: __dirname, interactive: false, enableLogging: true, - })).to.eventually.equal('child'); + })).to.eventually.equal(fakeChild); }); });