Repository navigation
Expand file tree
/
Copy pathutils.ts
More file actions
130 lines (121 loc) · 4.18 KB
/
Copy pathutils.ts
File metadata and controls
130 lines (121 loc) · 4.18 KB
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
123
124
125
126
127
128
129
130
import * as fs from "fs";
import * as path from "path";
import * as net from "net";
import * as child_process from "child_process";
import { JAVA_BINARY } from "./constants";
/**
* Finds the Java executable in the system, either in JAVA_HOME or in PATH
* MIT Licensed code from: https://github.com/georgewfraser/vscode-javac
*/
export function findJavaExecutable(): string | null {
const binname = process.platform === "win32" ? `${JAVA_BINARY}.exe` : JAVA_BINARY;
// First search each JAVA_HOME bin folder
if (process.env["JAVA_HOME"]) {
for (const workspace of process.env["JAVA_HOME"].split(path.delimiter)) {
const binpath = path.join(workspace, "bin", binname);
if (fs.existsSync(binpath)) return binpath;
}
}
// Then search PATH parts
if (process.env["PATH"]) {
for (const part of process.env["PATH"].split(path.delimiter)) {
const binpath = path.join(part, binname);
if (fs.existsSync(binpath)) return binpath;
}
}
// Else return null
return null;
}
/**
* Gets an available port in the OS
* @returns A promise to the available port number
*/
export async function getAvailablePort(): Promise<number> {
return new Promise((resolve, reject) => {
const server = net.createServer();
server.listen(0, "localhost", () => {
const port = (server.address() as net.AddressInfo).port;
server.close();
resolve(port);
});
server.on("error", reject);
});
}
/**
* Connects to the process on the given port, retrying until timeout
* @param port The port to connect to
* @param timeout The timeout duration in milliseconds
* @param attemptInterval The interval between connection attempts in milliseconds
* @param connectionTimeout The timeout for each individual connection attempt in milliseconds
* @returns A promise to the connected socket
*/
export async function connectToPort(
port: number,
timeout = 10000,
attemptInterval = 500,
connectionTimeout = 500
): Promise<net.Socket> {
const deadline = Date.now() + timeout;
while (Date.now() < deadline) {
try {
return await new Promise((resolve, reject) => {
const s = net.connect({ port });
// connection timeout
const t = setTimeout(() => {
s.destroy(new Error("connection timeout"));
reject(new Error("connection timeout"));
}, connectionTimeout);
// success
s.once("connect", () => {
clearTimeout(t);
resolve(s);
});
// failure
s.once("error", (e) => {
clearTimeout(t);
reject(e);
});
});
} catch {
// wait and retry
await new Promise((r) => setTimeout(r, attemptInterval));
}
}
throw new Error(`Server not reachable on port ${port} within ${timeout}ms`);
}
/**
* Kills the given process if it is running
* @param proc The process to kill
* @returns A promise that resolves when the process has been killed
*/
export async function killProcess(proc?: child_process.ChildProcess) {
return new Promise<void>((resolve, reject) => {
if (!proc || proc.killed || proc.exitCode !== null || proc.pid === undefined) {
// already killed
resolve();
return;
}
if (process.platform === "win32") {
// Windows
child_process.exec(`taskkill /pid ${proc.pid} /T /F`, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
} else {
// Unix
try {
process.kill(proc.pid, "SIGKILL");
resolve();
} catch (err) {
reject(err);
}
}
});
}
export function normalizeFilePath(fsPath: string): string {
// uppercase windows drive letter (c:\ -> C:\)
return path.normalize(fsPath).replace(/^([a-z]):\\/, (_, drive) => drive.toUpperCase() + ':\\');
}