-
Notifications
You must be signed in to change notification settings - Fork 30.6k
/
Copy pathprocesses.ts
135 lines (120 loc) · 4.84 KB
/
processes.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
123
124
125
126
127
128
129
130
131
132
133
134
135
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as cp from 'child_process';
import { Stats, promises } from 'fs';
import { getCaseInsensitive } from '../common/objects.js';
import * as path from '../common/path.js';
import * as Platform from '../common/platform.js';
import * as process from '../common/process.js';
import { CommandOptions, ForkOptions, Source, SuccessData, TerminateResponse, TerminateResponseCode } from '../common/processes.js';
import * as Types from '../common/types.js';
import * as pfs from './pfs.js';
export { type CommandOptions, type ForkOptions, type SuccessData, Source, type TerminateResponse, TerminateResponseCode };
export type ValueCallback<T> = (value: T | Promise<T>) => void;
export type ErrorCallback = (error?: any) => void;
export type ProgressCallback<T> = (progress: T) => void;
export function getWindowsShell(env = process.env as Platform.IProcessEnvironment): string {
return env['comspec'] || 'cmd.exe';
}
export interface IQueuedSender {
send: (msg: any) => void;
}
// Wrapper around process.send() that will queue any messages if the internal node.js
// queue is filled with messages and only continue sending messages when the internal
// queue is free again to consume messages.
// On Windows we always wait for the send() method to return before sending the next message
// to workaround https://github.com/nodejs/node/issues/7657 (IPC can freeze process)
export function createQueuedSender(childProcess: cp.ChildProcess): IQueuedSender {
let msgQueue: string[] = [];
let useQueue = false;
const send = function (msg: any): void {
if (useQueue) {
msgQueue.push(msg); // add to the queue if the process cannot handle more messages
return;
}
const result = childProcess.send(msg, (error: Error | null) => {
if (error) {
console.error(error); // unlikely to happen, best we can do is log this error
}
useQueue = false; // we are good again to send directly without queue
// now send all the messages that we have in our queue and did not send yet
if (msgQueue.length > 0) {
const msgQueueCopy = msgQueue.slice(0);
msgQueue = [];
msgQueueCopy.forEach(entry => send(entry));
}
});
if (!result || Platform.isWindows /* workaround https://github.com/nodejs/node/issues/7657 */) {
useQueue = true;
}
};
return { send };
}
async function fileExistsDefault(path: string): Promise<boolean> {
if (await pfs.Promises.exists(path)) {
let statValue: Stats | undefined;
try {
statValue = await promises.stat(path);
} catch (e) {
if (e.message.startsWith('EACCES')) {
// it might be symlink
statValue = await promises.lstat(path);
}
}
return statValue ? !statValue.isDirectory() : false;
}
return false;
}
export async function findExecutable(command: string, cwd?: string, paths?: string[], env: Platform.IProcessEnvironment = process.env as Platform.IProcessEnvironment, fileExists: (path: string) => Promise<boolean> = fileExistsDefault): Promise<string | undefined> {
// If we have an absolute path then we take it.
if (path.isAbsolute(command)) {
return await fileExists(command) ? command : undefined;
}
if (cwd === undefined) {
cwd = process.cwd();
}
const dir = path.dirname(command);
if (dir !== '.') {
// We have a directory and the directory is relative (see above). Make the path absolute
// to the current working directory.
const fullPath = path.join(cwd, command);
return await fileExists(fullPath) ? fullPath : undefined;
}
const envPath = getCaseInsensitive(env, 'PATH');
if (paths === undefined && Types.isString(envPath)) {
paths = envPath.split(path.delimiter);
}
// No PATH environment. Make path absolute to the cwd.
if (paths === undefined || paths.length === 0) {
const fullPath = path.join(cwd, command);
return await fileExists(fullPath) ? fullPath : undefined;
}
// We have a simple file name. We get the path variable from the env
// and try to find the executable on the path.
for (const pathEntry of paths) {
// The path entry is absolute.
let fullPath: string;
if (path.isAbsolute(pathEntry)) {
fullPath = path.join(pathEntry, command);
} else {
fullPath = path.join(cwd, pathEntry, command);
}
if (await fileExists(fullPath)) {
return fullPath;
}
if (Platform.isWindows) {
let withExtension = fullPath + '.com';
if (await fileExists(withExtension)) {
return withExtension;
}
withExtension = fullPath + '.exe';
if (await fileExists(withExtension)) {
return withExtension;
}
}
}
const fullPath = path.join(cwd, command);
return await fileExists(fullPath) ? fullPath : undefined;
}