in-parallel-lit 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +406 -381
- package/dist/bin.js +381 -377
- package/dist/index.d.ts +9 -2
- package/package.json +53 -59
package/dist/bin.js
CHANGED
|
@@ -1,411 +1,415 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
2
|
import { createRequire } from "node:module";
|
|
6
3
|
import sade from "sade";
|
|
7
|
-
import {
|
|
4
|
+
import { Transform, Writable } from "node:stream";
|
|
8
5
|
import { StringDecoder } from "node:string_decoder";
|
|
9
|
-
import { spawn
|
|
6
|
+
import { spawn } from "cross-spawn";
|
|
10
7
|
import os from "node:os";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
8
|
+
//#region src/lib/get-signal-num.ts
|
|
9
|
+
var signals = {
|
|
10
|
+
SIGABRT: 6,
|
|
11
|
+
SIGALRM: 14,
|
|
12
|
+
SIGBUS: 10,
|
|
13
|
+
SIGCHLD: 20,
|
|
14
|
+
SIGCONT: 19,
|
|
15
|
+
SIGFPE: 8,
|
|
16
|
+
SIGHUP: 1,
|
|
17
|
+
SIGILL: 4,
|
|
18
|
+
SIGINT: 2,
|
|
19
|
+
SIGKILL: 9,
|
|
20
|
+
SIGPIPE: 13,
|
|
21
|
+
SIGQUIT: 3,
|
|
22
|
+
SIGSEGV: 11,
|
|
23
|
+
SIGSTOP: 17,
|
|
24
|
+
SIGTERM: 15,
|
|
25
|
+
SIGTRAP: 5,
|
|
26
|
+
SIGTSTP: 18,
|
|
27
|
+
SIGTTIN: 21,
|
|
28
|
+
SIGTTOU: 22,
|
|
29
|
+
SIGUSR1: 30,
|
|
30
|
+
SIGUSR2: 31
|
|
33
31
|
};
|
|
32
|
+
/**
|
|
33
|
+
* Converts a signal name to a number.
|
|
34
|
+
*/
|
|
34
35
|
function getSignalNumber(signal) {
|
|
35
|
-
|
|
36
|
+
return signals[signal] || 0;
|
|
36
37
|
}
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/lib/get-stream-kind.ts
|
|
40
|
+
/**
|
|
41
|
+
* Converts a given stream to an option for `child_process.spawn`.
|
|
42
|
+
*/
|
|
37
43
|
function getStreamKind(stream, std) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
if (stream == null) return "ignore";
|
|
45
|
+
if (stream !== std || !std.isTTY) return "pipe";
|
|
46
|
+
return stream;
|
|
41
47
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/lib/in-parallel-error.ts
|
|
50
|
+
var InParallelError = class extends Error {
|
|
51
|
+
code;
|
|
52
|
+
results;
|
|
53
|
+
constructor(causeResult, allResults) {
|
|
54
|
+
super(`"${causeResult.name}" exited with ${causeResult.code}.`);
|
|
55
|
+
this.name = causeResult.name;
|
|
56
|
+
this.code = causeResult.code;
|
|
57
|
+
this.results = allResults;
|
|
58
|
+
}
|
|
53
59
|
};
|
|
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
|
-
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/lib/memory-writable.ts
|
|
62
|
+
var noop = () => {};
|
|
63
|
+
var MemoryWritable = class extends Writable {
|
|
64
|
+
queue = [];
|
|
65
|
+
data;
|
|
66
|
+
constructor(data = null) {
|
|
67
|
+
super();
|
|
68
|
+
this.data = Array.isArray(data) ? data : [data];
|
|
69
|
+
this.queue = [];
|
|
70
|
+
for (let chunk of this.data) {
|
|
71
|
+
if (chunk == null) continue;
|
|
72
|
+
if (!(chunk instanceof Buffer)) chunk = Buffer.from(chunk);
|
|
73
|
+
this.queue.push(chunk);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
_write(chunk, enc, cb = noop) {
|
|
77
|
+
let decoder = null;
|
|
78
|
+
try {
|
|
79
|
+
decoder = enc && enc !== "buffer" ? new StringDecoder(enc) : null;
|
|
80
|
+
} catch (err) {
|
|
81
|
+
return cb(err);
|
|
82
|
+
}
|
|
83
|
+
let decodedChunk = decoder != null ? decoder.write(chunk) : chunk;
|
|
84
|
+
this.queue.push(Buffer.isBuffer(decodedChunk) ? decodedChunk : Buffer.from(decodedChunk));
|
|
85
|
+
cb();
|
|
86
|
+
}
|
|
87
|
+
_getQueueSize() {
|
|
88
|
+
let size = 0;
|
|
89
|
+
for (let i = 0; i < this.queue.length; i++) size += this.queue[i].length;
|
|
90
|
+
return size;
|
|
91
|
+
}
|
|
92
|
+
toString() {
|
|
93
|
+
let str = "";
|
|
94
|
+
for (const chunk of this.queue) str += chunk.toString();
|
|
95
|
+
return str;
|
|
96
|
+
}
|
|
97
|
+
toBuffer() {
|
|
98
|
+
const buffer = Buffer.alloc(this._getQueueSize());
|
|
99
|
+
let currentOffset = 0;
|
|
100
|
+
for (const chunk of this.queue) {
|
|
101
|
+
chunk.copy(buffer, currentOffset);
|
|
102
|
+
currentOffset += chunk.length;
|
|
103
|
+
}
|
|
104
|
+
return buffer;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/lib/remove-from-arr.ts
|
|
109
|
+
/**
|
|
110
|
+
* removeFromArr removes an `item` from `arr`.
|
|
111
|
+
*/
|
|
106
112
|
function removeFromArr(arr, item) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
arr.splice(index, 1);
|
|
110
|
-
}
|
|
113
|
+
const index = arr.indexOf(item);
|
|
114
|
+
if (index !== -1) arr.splice(index, 1);
|
|
111
115
|
}
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/lib/kill-pids.ts
|
|
118
|
+
/**
|
|
119
|
+
* Wrap crossSpawn in a Promise
|
|
120
|
+
*/
|
|
112
121
|
function crossSpawnPromise(cmd, args = [], options = {}) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
});
|
|
132
|
-
});
|
|
122
|
+
return new Promise((resolve, reject) => {
|
|
123
|
+
let stdout = "";
|
|
124
|
+
let stderr = "";
|
|
125
|
+
const ch = spawn(cmd, args, options);
|
|
126
|
+
if (ch.stdout === null || ch.stderr === null) return reject("stdout/stderr is null");
|
|
127
|
+
ch.stdout.on("data", (d) => {
|
|
128
|
+
stdout += d.toString();
|
|
129
|
+
});
|
|
130
|
+
ch.stderr.on("data", (d) => {
|
|
131
|
+
stderr += d.toString();
|
|
132
|
+
});
|
|
133
|
+
ch.on("error", (err) => reject(err));
|
|
134
|
+
ch.on("close", (code) => {
|
|
135
|
+
if (stderr) return reject(stderr);
|
|
136
|
+
if (code !== 0) return reject(`${cmd} exited with code ${code}`);
|
|
137
|
+
return resolve(stdout);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
133
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Kills a process by ID and all its subprocesses.
|
|
143
|
+
*/
|
|
134
144
|
async function killPids(pid, platform = process.platform) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
} catch (err) {
|
|
174
|
-
}
|
|
145
|
+
if (platform === "win32") {
|
|
146
|
+
spawn("taskkill", [
|
|
147
|
+
"/F",
|
|
148
|
+
"/T",
|
|
149
|
+
"/PID",
|
|
150
|
+
String(pid)
|
|
151
|
+
]);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
const stdoutRows = (await crossSpawnPromise("ps", [
|
|
156
|
+
"-A",
|
|
157
|
+
"-o",
|
|
158
|
+
"ppid,pid"
|
|
159
|
+
])).split(os.EOL);
|
|
160
|
+
let pidExists = false;
|
|
161
|
+
const pidTree = {};
|
|
162
|
+
for (let i = 1; i < stdoutRows.length; i++) {
|
|
163
|
+
stdoutRows[i] = stdoutRows[i].trim();
|
|
164
|
+
if (!stdoutRows[i]) continue;
|
|
165
|
+
const stdoutTuple = stdoutRows[i].split(/\s+/);
|
|
166
|
+
const stdoutPpid = parseInt(stdoutTuple[0], 10);
|
|
167
|
+
const stdoutPid = parseInt(stdoutTuple[1], 10);
|
|
168
|
+
if (!pidExists && stdoutPid === pid || !pidExists && stdoutPpid === pid) pidExists = true;
|
|
169
|
+
if (pidTree[stdoutPpid]) pidTree[stdoutPpid].push(stdoutPid);
|
|
170
|
+
else pidTree[stdoutPpid] = [stdoutPid];
|
|
171
|
+
}
|
|
172
|
+
if (!pidExists) return;
|
|
173
|
+
let idx = 0;
|
|
174
|
+
const pids = [pid];
|
|
175
|
+
while (idx < pids.length) {
|
|
176
|
+
const curpid = pids[idx++];
|
|
177
|
+
if (!pidTree[curpid]) continue;
|
|
178
|
+
for (let j = 0; j < pidTree[curpid].length; j++) pids.push(pidTree[curpid][j]);
|
|
179
|
+
delete pidTree[curpid];
|
|
180
|
+
}
|
|
181
|
+
for (const pid of pids) process.kill(pid);
|
|
182
|
+
} catch {}
|
|
175
183
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/lib/spawn.ts
|
|
186
|
+
/**
|
|
187
|
+
* Launches a new process with the given command.
|
|
188
|
+
* This is almost same as `child_process.spawn`, but it adds a `kill` method
|
|
189
|
+
* that kills the instance process and its sub processes.
|
|
190
|
+
*/
|
|
191
|
+
function spawn$1(command, args, options) {
|
|
192
|
+
const child = spawn(command, args, options);
|
|
193
|
+
child.kill = function kill() {
|
|
194
|
+
killPids(this.pid);
|
|
195
|
+
return true;
|
|
196
|
+
};
|
|
197
|
+
return child;
|
|
183
198
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/lib/prefix-transform.ts
|
|
201
|
+
var ALL_BR = /\n/g;
|
|
202
|
+
var PrefixTransform = class extends Transform {
|
|
203
|
+
prefix;
|
|
204
|
+
lastPrefix;
|
|
205
|
+
lastIsLinebreak;
|
|
206
|
+
constructor(prefix) {
|
|
207
|
+
super();
|
|
208
|
+
this.prefix = prefix;
|
|
209
|
+
this.lastPrefix = null;
|
|
210
|
+
this.lastIsLinebreak = true;
|
|
211
|
+
}
|
|
212
|
+
_transform(chunk, _enc, cb) {
|
|
213
|
+
const prefixed = `${this.lastIsLinebreak ? this.prefix : this.lastPrefix !== this.prefix ? "\n" : ""}${chunk}`.replace(ALL_BR, `\n${this.prefix}`);
|
|
214
|
+
const index = prefixed.indexOf(this.prefix, Math.max(0, prefixed.length - this.prefix.length));
|
|
215
|
+
this.lastPrefix = this.prefix;
|
|
216
|
+
this.lastIsLinebreak = index !== -1;
|
|
217
|
+
cb(null, index !== -1 ? prefixed.slice(0, index) : prefixed);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region src/lib/select-color.ts
|
|
222
|
+
var FORCE_COLOR;
|
|
223
|
+
var NODE_DISABLE_COLORS;
|
|
224
|
+
var NO_COLOR;
|
|
225
|
+
var TERM;
|
|
226
|
+
var isTTY = true;
|
|
227
|
+
var enabled;
|
|
228
|
+
/**
|
|
229
|
+
* Check if color support is enabled.
|
|
230
|
+
*/
|
|
190
231
|
function checkColorSupport() {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
232
|
+
if (enabled != null) return;
|
|
233
|
+
({FORCE_COLOR, NODE_DISABLE_COLORS, NO_COLOR, TERM} = process.env);
|
|
234
|
+
isTTY = process.stdout?.isTTY;
|
|
235
|
+
enabled = !NODE_DISABLE_COLORS && NO_COLOR == null && TERM !== "dumb" && (FORCE_COLOR != null && FORCE_COLOR !== "0" || isTTY);
|
|
195
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Returns functions that wrap a given string with color char codes.
|
|
239
|
+
*/
|
|
196
240
|
function createColor(x, y) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
241
|
+
const rgx = new RegExp(`\\x1b\\[${y}m`, "g");
|
|
242
|
+
const open = `\x1b[${x}m`;
|
|
243
|
+
const close = `\x1b[${y}m`;
|
|
244
|
+
return function(txt) {
|
|
245
|
+
checkColorSupport();
|
|
246
|
+
if (!enabled) return txt;
|
|
247
|
+
return open + (~`${txt}`.indexOf(close) ? txt.replace(rgx, close + open) : txt) + close;
|
|
248
|
+
};
|
|
205
249
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
// magenta
|
|
213
|
-
createColor(33, 39),
|
|
214
|
-
// yellow
|
|
215
|
-
createColor(31, 39)
|
|
216
|
-
// red
|
|
250
|
+
var colors = [
|
|
251
|
+
createColor(36, 39),
|
|
252
|
+
createColor(32, 39),
|
|
253
|
+
createColor(35, 39),
|
|
254
|
+
createColor(33, 39),
|
|
255
|
+
createColor(31, 39)
|
|
217
256
|
];
|
|
218
|
-
|
|
219
|
-
|
|
257
|
+
var colorIndex = 0;
|
|
258
|
+
var taskNamesToColors = /* @__PURE__ */ new Map();
|
|
259
|
+
/**
|
|
260
|
+
* Select a color from given task name.
|
|
261
|
+
*/
|
|
220
262
|
function selectColor(taskName) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
const ALL_BR = /\n/g;
|
|
230
|
-
class PrefixTransform extends Transform {
|
|
231
|
-
constructor(prefix) {
|
|
232
|
-
super();
|
|
233
|
-
__publicField(this, "prefix");
|
|
234
|
-
__publicField(this, "lastPrefix");
|
|
235
|
-
__publicField(this, "lastIsLinebreak");
|
|
236
|
-
this.prefix = prefix;
|
|
237
|
-
this.lastPrefix = null;
|
|
238
|
-
this.lastIsLinebreak = true;
|
|
239
|
-
}
|
|
240
|
-
_transform(chunk, _enc, cb) {
|
|
241
|
-
const firstPrefix = this.lastIsLinebreak ? this.prefix : this.lastPrefix !== this.prefix ? "\n" : "";
|
|
242
|
-
const prefixed = `${firstPrefix}${chunk}`.replace(
|
|
243
|
-
ALL_BR,
|
|
244
|
-
`
|
|
245
|
-
${this.prefix}`
|
|
246
|
-
);
|
|
247
|
-
const index = prefixed.indexOf(
|
|
248
|
-
this.prefix,
|
|
249
|
-
Math.max(0, prefixed.length - this.prefix.length)
|
|
250
|
-
);
|
|
251
|
-
this.lastPrefix = this.prefix;
|
|
252
|
-
this.lastIsLinebreak = index !== -1;
|
|
253
|
-
cb(null, index !== -1 ? prefixed.slice(0, index) : prefixed);
|
|
254
|
-
}
|
|
263
|
+
let color = taskNamesToColors.get(taskName);
|
|
264
|
+
if (color == null) {
|
|
265
|
+
color = colors[colorIndex];
|
|
266
|
+
colorIndex = (colorIndex + 1) % colors.length;
|
|
267
|
+
taskNamesToColors.set(taskName, color);
|
|
268
|
+
}
|
|
269
|
+
return color;
|
|
255
270
|
}
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region src/lib/wrap-stream-with-label.ts
|
|
273
|
+
/**
|
|
274
|
+
* Wraps stdout/stderr with a transform stream to add the task name as prefix.
|
|
275
|
+
*/
|
|
256
276
|
function wrapStreamWithLabel(source, label) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
target.pipe(source);
|
|
262
|
-
return target;
|
|
277
|
+
if (source == null) return source;
|
|
278
|
+
const target = new PrefixTransform((source.isTTY ? selectColor(label) : (x) => x)(`[${label}] `));
|
|
279
|
+
target.pipe(source);
|
|
280
|
+
return target;
|
|
263
281
|
}
|
|
282
|
+
//#endregion
|
|
283
|
+
//#region src/index.ts
|
|
284
|
+
/**
|
|
285
|
+
* prog represents the main program logic.
|
|
286
|
+
*/
|
|
264
287
|
function prog(opts, proc) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
);
|
|
340
|
-
}
|
|
341
|
-
let end = tasks.length;
|
|
342
|
-
if (typeof options["max-parallel"] === "number" && options["max-parallel"] > 0) {
|
|
343
|
-
end = Math.min(tasks.length, options["max-parallel"]);
|
|
344
|
-
}
|
|
345
|
-
for (let i = 0; i < end; ++i) {
|
|
346
|
-
next();
|
|
347
|
-
}
|
|
348
|
-
});
|
|
288
|
+
const { _: tasks, ...options } = opts;
|
|
289
|
+
const customTaskNames = options.names != null ? options.names.split(",").map((n) => n.trim()) : [];
|
|
290
|
+
return new Promise((resolve, reject) => {
|
|
291
|
+
let results = [];
|
|
292
|
+
let queue = [];
|
|
293
|
+
let promises = [];
|
|
294
|
+
let error = null;
|
|
295
|
+
let aborted = false;
|
|
296
|
+
if (tasks.length === 0) return done();
|
|
297
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
298
|
+
results.push({
|
|
299
|
+
name: tasks[i],
|
|
300
|
+
code: void 0
|
|
301
|
+
});
|
|
302
|
+
queue.push({
|
|
303
|
+
name: tasks[i],
|
|
304
|
+
index: i
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
function done() {
|
|
308
|
+
if (error == null) return resolve(results);
|
|
309
|
+
return reject(error);
|
|
310
|
+
}
|
|
311
|
+
function abort() {
|
|
312
|
+
if (aborted) return;
|
|
313
|
+
aborted = true;
|
|
314
|
+
if (promises.length === 0) return done();
|
|
315
|
+
for (const p of promises) p.abort();
|
|
316
|
+
return Promise.all(promises).then(done, reject);
|
|
317
|
+
}
|
|
318
|
+
function next() {
|
|
319
|
+
if (aborted) return;
|
|
320
|
+
if (queue.length === 0) {
|
|
321
|
+
if (promises.length === 0) return done();
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const task = queue.shift();
|
|
325
|
+
if (task == null) return;
|
|
326
|
+
const originalOutputStream = proc.stdout;
|
|
327
|
+
const optionsClone = {
|
|
328
|
+
stdout: proc.stdout,
|
|
329
|
+
stderr: proc.stderr,
|
|
330
|
+
stdin: proc.stdin,
|
|
331
|
+
customName: customTaskNames[task.index]
|
|
332
|
+
};
|
|
333
|
+
const writer = new MemoryWritable();
|
|
334
|
+
if (options["aggregate-output"]) optionsClone.stdout = writer;
|
|
335
|
+
const promise = runTask(task.name, optionsClone);
|
|
336
|
+
promises.push(promise);
|
|
337
|
+
promise.then((result) => {
|
|
338
|
+
removeFromArr(promises, promise);
|
|
339
|
+
if (aborted) return;
|
|
340
|
+
if (options["aggregate-output"]) originalOutputStream.write(writer.toString());
|
|
341
|
+
if (result.code === null && result.signal !== null) result.code = 128 + getSignalNumber(result.signal);
|
|
342
|
+
results[task.index].code = result.code;
|
|
343
|
+
if (result.code) {
|
|
344
|
+
error = new InParallelError(result, results);
|
|
345
|
+
if (!options["continue-on-error"]) return abort();
|
|
346
|
+
}
|
|
347
|
+
next();
|
|
348
|
+
}, (err) => {
|
|
349
|
+
removeFromArr(promises, promise);
|
|
350
|
+
if (!options["continue-on-error"]) {
|
|
351
|
+
error = err;
|
|
352
|
+
return abort();
|
|
353
|
+
}
|
|
354
|
+
next();
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
let end = tasks.length;
|
|
358
|
+
if (typeof options["max-parallel"] === "number" && options["max-parallel"] > 0) end = Math.min(tasks.length, options["max-parallel"]);
|
|
359
|
+
for (let i = 0; i < end; ++i) next();
|
|
360
|
+
});
|
|
349
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* runTask executes a single task as a child process.
|
|
364
|
+
*/
|
|
350
365
|
function runTask(name, opts) {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
366
|
+
let proc = null;
|
|
367
|
+
const task = new Promise((resolve, reject) => {
|
|
368
|
+
const stdin = opts.stdin;
|
|
369
|
+
const stdout = wrapStreamWithLabel(opts.stdout, opts.customName || name);
|
|
370
|
+
const stderr = wrapStreamWithLabel(opts.stderr, opts.customName || name);
|
|
371
|
+
const stdinKind = getStreamKind(stdin, process.stdin);
|
|
372
|
+
const stdoutKind = getStreamKind(stdout, process.stdout);
|
|
373
|
+
const stderrKind = getStreamKind(stderr, process.stderr);
|
|
374
|
+
const [spawnName, ...spawnArgs] = name.split(" ");
|
|
375
|
+
proc = spawn$1(spawnName, spawnArgs, { stdio: [
|
|
376
|
+
stdinKind,
|
|
377
|
+
stdoutKind,
|
|
378
|
+
stderrKind
|
|
379
|
+
] });
|
|
380
|
+
if (proc == null) return reject("Failed to spawn process");
|
|
381
|
+
if (stdinKind === "pipe") stdin.pipe(proc.stdin);
|
|
382
|
+
if (stdoutKind === "pipe") proc.stdout.pipe(stdout, { end: false });
|
|
383
|
+
if (stderrKind === "pipe") proc.stderr.pipe(stderr, { end: false });
|
|
384
|
+
proc.on("error", (err) => {
|
|
385
|
+
proc = null;
|
|
386
|
+
return reject(err);
|
|
387
|
+
});
|
|
388
|
+
proc.on("close", (code, signal) => {
|
|
389
|
+
proc = null;
|
|
390
|
+
return resolve({
|
|
391
|
+
name,
|
|
392
|
+
code,
|
|
393
|
+
signal
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
task.abort = () => {
|
|
398
|
+
if (proc == null) return;
|
|
399
|
+
proc.kill();
|
|
400
|
+
proc = null;
|
|
401
|
+
};
|
|
402
|
+
return task;
|
|
388
403
|
}
|
|
404
|
+
//#endregion
|
|
405
|
+
//#region src/bin.ts
|
|
389
406
|
async function run(argv) {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
sade("in-parallel", true).version(packageJson.version).describe(packageJson.description).option(
|
|
396
|
-
`-n, --names`,
|
|
397
|
-
`List of custom names to be used in prefix template.`
|
|
398
|
-
).example(`-n "first,second" "ping google.com" "ping 172.0.0.1"`).option(
|
|
399
|
-
`-c, --continue-on-error`,
|
|
400
|
-
`Set the flag to continue executing other/subsequent tasks even if a task threw an error. 'in-parallel' itself will exit with non-zero code if one or more tasks threw error(s).`
|
|
401
|
-
).option(
|
|
402
|
-
`--max-parallel`,
|
|
403
|
-
`Set the maximum number of parallelism. Default is unlimited.`,
|
|
404
|
-
0
|
|
405
|
-
).option(
|
|
406
|
-
`--aggregate-output`,
|
|
407
|
-
`Avoid interleaving output by delaying printing of each command's output until it has finished.`,
|
|
408
|
-
false
|
|
409
|
-
).action((opts) => prog(opts, process)).parse(argv);
|
|
407
|
+
const packageJson = createRequire(import.meta.url)("../package.json");
|
|
408
|
+
process.stdout.setMaxListeners(0);
|
|
409
|
+
process.stderr.setMaxListeners(0);
|
|
410
|
+
process.stdin.setMaxListeners(0);
|
|
411
|
+
sade("in-parallel", true).version(packageJson.version).describe(packageJson.description).option(`-n, --names`, `List of custom names to be used in prefix template.`).example(`-n "first,second" "ping google.com" "ping 172.0.0.1"`).option(`-c, --continue-on-error`, `Set the flag to continue executing other/subsequent tasks even if a task threw an error. 'in-parallel' itself will exit with non-zero code if one or more tasks threw error(s).`).option(`--max-parallel`, `Set the maximum number of parallelism. Default is unlimited.`, 0).option(`--aggregate-output`, `Avoid interleaving output by delaying printing of each command's output until it has finished.`, false).action((opts) => prog(opts, process)).parse(argv);
|
|
410
412
|
}
|
|
411
413
|
run(process.argv);
|
|
414
|
+
//#endregion
|
|
415
|
+
export {};
|