@epic-web/app-launcher
Version:
Utility for launching your applications on a per-test basis.
64 lines • 2.33 kB
JavaScript
import { spawn } from 'node:child_process';
import { DeferredPromise } from '@open-draft/deferred-promise';
import { invariant } from '@epic-web/invariant';
export const kUrl = Symbol('kUrl');
export const kLaunch = Symbol('kLaunch');
export class AppProcess {
options;
io;
[kUrl];
constructor(options) {
this.options = options;
}
get url() {
const url = this[kUrl];
invariant(url != null, 'Failed to get a URL of the launched application: application not running. Did you forget to call `await launcher.run()`?');
return url;
}
/**
* Spawns the child process with the configured application.
* @note This method must never be used publicly. Use `launcher.run()` instead.
*/
async [kLaunch]() {
const [command, ...args] = this.options.command.split(' ');
invariant(command != null, 'Failed to launch application: "command" could not be parsed');
this.io = spawn(command, args, {
cwd: this.options.cwd,
env: {
...process.env,
...(this.options.env || {}),
},
});
const spawnPromise = new DeferredPromise();
this.io.once('spawn', () => spawnPromise.resolve());
this.io.once('error', (error) => spawnPromise.reject(error));
await spawnPromise;
return this.io;
}
/**
* Stop the running application.
*/
async dispose() {
invariant(this.io != null, 'Failed to dispose of a launched application: application is not running. Did you forget to run `await launcher.run()`?');
// The application has been exited by other means (e.g. unhandled exception).
if (this.io.exitCode !== null) {
return;
}
const exitPromise = new DeferredPromise();
this.io.once('exit', (exitCode) => {
if (exitCode === 0) {
exitPromise.resolve();
return;
}
exitPromise.reject(new Error(`Process exited with code ${exitCode}`));
});
if (!this.io.kill('SIGINT')) {
exitPromise.reject('SIGINT did not succeed');
}
await exitPromise;
}
async [Symbol.asyncDispose]() {
await this.dispose();
}
}
//# sourceMappingURL=app-process.js.map