Files
talk/scripts/watcher/LongRunningExecutor.ts
T
Kiwi 044e1c2863 Watcher infrastructure (#1724)
* wip

* Adding chokidar and types

* specifiying build tasks

* new structure, new types, executor and watchers

* Adding log

* Fully implemented watchers

* adapt vscode launc

* Add .babelrc.js to toplevel tsconfig project

* Typo

* Get schema path from .graphqlconfig

* Use watcher binary

* Add joi validation to watcher

* Remove fb-watchman for now

* Use correct ignore path

* Fix dist folder

* Allow setting watcher

* Per default only spawn one process at a time

* Support runOnInit

* Rename RestartingExecutor to LongRunningExecutor

* Use debounce instead of throttle

* Remove console log

* Debounce command execution

* Simplify debounce

* Watcher name change

* Typos

* Rename "watcher" root level config to "backend"
2018-07-03 12:21:58 -06:00

89 lines
2.3 KiB
TypeScript

import { ChildProcess } from "child_process";
import spawn from "cross-spawn";
import { Cancelable, debounce } from "lodash";
import { Executor } from "./types";
interface LongRunningExecutorOptions {
args?: ReadonlyArray<string>;
// Specify the period in which the process is restarted at max once.
debounce?: number;
}
export default class LongRunningExecutor implements Executor {
private cmd: string;
private args?: ReadonlyArray<string>;
private process: ChildProcess | null = null;
private isRunning: boolean = false;
private shouldRestart: boolean = false;
private restartDebounced: (() => void) & Cancelable;
constructor(cmd: string, opts: LongRunningExecutorOptions = {}) {
this.cmd = cmd;
this.args = opts.args;
this.restartDebounced = debounce(
() => this.restart(),
opts.debounce || 500
);
}
private spawnProcess() {
this.isRunning = true;
this.process = spawn(this.cmd, this.args as string[], {
stdio: "inherit",
// Have all child processes in their own group.
// See `process.kill` below.
detached: true,
shell: !this.args,
});
this.process!.on("exit", (code: number) => {
this.isRunning = false;
if (code !== 0 && code !== null) {
// tslint:disable-next-line: no-console
console.error(`Exit code returned ${code}`);
return;
}
if (this.shouldRestart) {
this.spawnProcess();
}
});
}
private restart(): void {
this.shouldRestart = true;
// Using the `-` will kill all child procceses in the group.
// See: https://azimi.me/2014/12/31/kill-child_process-node-js.html
process.kill(-this.process!.pid, "SIGTERM");
}
private kill(): void {
this.shouldRestart = false;
// Using the `-` will kill all child procceses in the group.
// See: https://azimi.me/2014/12/31/kill-child_process-node-js.html
process.kill(-this.process!.pid, "SIGTERM");
}
// This is called before watching starts.
public onInit(): void {
this.spawnProcess();
}
// This is called before exiting.
public onCleanup() {
this.restartDebounced.cancel();
if (this.isRunning) {
this.kill();
}
}
public execute(filePath: string) {
if (this.isRunning) {
this.restartDebounced();
return;
}
this.spawnProcess();
}
}