/
index.js
129 lines (99 loc) · 3.28 KB
/
index.js
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
"use strict";
const os = require("os");
const chalk = require("chalk");
const execa = require("execa");
const logTransformer = require("strong-log-transformer");
// bookkeeping for spawned processes
let children = 0;
// when streaming children are spawned, use this color for prefix
const colorWheel = ["cyan", "magenta", "blue", "yellow", "green", "red"];
const NUM_COLORS = colorWheel.length;
function exec(command, args, opts) {
const options = Object.assign({ stdio: "pipe" }, opts);
const spawned = spawnProcess(command, args, options);
return wrapError(spawned);
}
function execSync(command, args, opts) {
return execa.sync(command, args, opts).stdout;
}
function spawn(command, args, opts) {
const options = Object.assign({}, opts, { stdio: "inherit" });
const spawned = spawnProcess(command, args, options);
return wrapError(spawned);
}
// istanbul ignore next
function spawnStreaming(command, args, opts, prefix) {
const options = Object.assign({}, opts);
options.stdio = ["ignore", "pipe", "pipe"];
const colorName = colorWheel[children % NUM_COLORS];
const color = chalk[colorName];
const spawned = spawnProcess(command, args, options);
const stdoutOpts = {};
const stderrOpts = {}; // mergeMultiline causes escaped newlines :P
if (prefix) {
stdoutOpts.tag = `${color.bold(prefix)}:`;
stderrOpts.tag = `${color(prefix)}:`;
}
// Avoid "Possible EventEmitter memory leak detected" warning due to piped stdio
if (children > process.stdout.listenerCount("close")) {
process.stdout.setMaxListeners(children);
process.stderr.setMaxListeners(children);
}
spawned.stdout.pipe(logTransformer(stdoutOpts)).pipe(process.stdout);
spawned.stderr.pipe(logTransformer(stderrOpts)).pipe(process.stderr);
return wrapError(spawned);
}
function getChildProcessCount() {
return children;
}
function getExitCode(result) {
// https://nodejs.org/docs/latest-v6.x/api/child_process.html#child_process_event_close
if (typeof result.code === "number") {
return result.code;
}
// https://nodejs.org/docs/latest-v6.x/api/errors.html#errors_error_code
// istanbul ignore else
if (typeof result.code === "string") {
return os.constants.errno[result.code];
}
// istanbul ignore next: extremely weird
throw new TypeError(`Received unexpected exit code value ${JSON.stringify(result.code)}`);
}
function spawnProcess(command, args, opts) {
children += 1;
const child = execa(command, args, opts);
const drain = (code, signal) => {
children -= 1;
// don't run repeatedly if this is the error event
if (signal === undefined) {
child.removeListener("exit", drain);
}
};
child.once("exit", drain);
child.once("error", drain);
if (opts.pkg) {
child.pkg = opts.pkg;
}
return child;
}
function wrapError(spawned) {
if (spawned.pkg) {
return spawned.catch(err => {
// istanbul ignore else
if (err.code) {
// ensure code is always a number
err.code = getExitCode(err);
// log non-lerna error cleanly
err.pkg = spawned.pkg;
}
throw err;
});
}
return spawned;
}
exports.exec = exec;
exports.execSync = execSync;
exports.spawn = spawn;
exports.spawnStreaming = spawnStreaming;
exports.getChildProcessCount = getChildProcessCount;
exports.getExitCode = getExitCode;