/
Thread.ts
122 lines (106 loc) · 3.52 KB
/
Thread.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import { spawn, ChildProcessWithoutNullStreams } from "child_process";
import cliCursor from "cli-cursor";
import killPort from "kill-port";
import { Config } from "../../interfaces";
import Provider from "../higher-order/Provider";
import { clearScreen } from "../../utils";
/**
* Handles the cleanup and restart of the command executer.
*
* Kills the spawned process, kills the process running on the port to avoid conflicts and restarts a clean process.
* @extends Provider
* @category Core
*/
export default class Thread extends Provider {
/**
* The child process generated using `child_process.spawn`.
*/
private childProcess: ChildProcessWithoutNullStreams = null;
/**
* The main command derived from `config.exec` which is passed to `childProcess` of this instance.
*/
private command: string = null;
/**
* The arguments derived from `config.exec` which is passed to `childProcess` of this instance.
*/
private args: string[] = [];
/**
* Create an instance of `Thread` which is responsible for managing the entire child process spawning and management.
* @param {Config} config The configuration object.
*/
constructor(config: Config) {
super(config);
[this.command, ...this.args] = config.exec.split(/\s/);
}
/**
* Kill the process if it has been spawned. If not, fail silently.
*/
private async killProcess(): Promise<void> {
try {
await this.childProcess.kill();
this.log.debug(
"Process with pid",
this.childProcess.pid,
"terminated successfully."
);
} catch (error) {
this.log.error(error);
this.log.debug(
"Process with pid",
this.childProcess.pid,
"has already been terminated."
);
}
}
/**
* Try to kill the process running on the port specified in `config.port`. If not, fail silently.
*/
private async killPort(): Promise<void> {
try {
await killPort(this.config.port);
this.log.debug(
`Process running on port ${this.config.port} killed.`
);
} catch {
this.log.debug(`No process running on port ${this.config.port}`);
}
}
/**
* Start the thread, executing the command specified in `config.exec`.
* @param {string} root The root path to start the process.
* @param {boolean} [restart] Whether this is a restart or not.
*/
public async start(root: string, restart = false): Promise<void> {
if (restart && this.config.clearScreen) {
clearScreen();
}
cliCursor.hide();
await this.killPort();
if (restart) {
await this.killProcess();
}
if (restart) {
this.display.onBeforeRestart();
} else {
this.display.onBeforeStart();
}
this.childProcess = await spawn(this.command, this.args, {
cwd: root,
stdio: "inherit",
shell: true,
env: process.env
});
if (restart) {
this.display.onRestart();
} else {
this.display.onStart();
}
}
/**
* Restart the thread, executing the command specified in `config.exec`.
* @param {string} root The root path to start the process.
*/
public async restart(root: string): Promise<void> {
await this.start(root, true);
}
}