source: trip-planner-front/node_modules/execa/index.js@ 6c1585f

Last change on this file since 6c1585f was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 7.5 KB
Line 
1'use strict';
2const path = require('path');
3const childProcess = require('child_process');
4const crossSpawn = require('cross-spawn');
5const stripEof = require('strip-eof');
6const npmRunPath = require('npm-run-path');
7const isStream = require('is-stream');
8const _getStream = require('get-stream');
9const pFinally = require('p-finally');
10const onExit = require('signal-exit');
11const errname = require('./lib/errname');
12const stdio = require('./lib/stdio');
13
14const TEN_MEGABYTES = 1000 * 1000 * 10;
15
16function handleArgs(cmd, args, opts) {
17 let parsed;
18
19 opts = Object.assign({
20 extendEnv: true,
21 env: {}
22 }, opts);
23
24 if (opts.extendEnv) {
25 opts.env = Object.assign({}, process.env, opts.env);
26 }
27
28 if (opts.__winShell === true) {
29 delete opts.__winShell;
30 parsed = {
31 command: cmd,
32 args,
33 options: opts,
34 file: cmd,
35 original: {
36 cmd,
37 args
38 }
39 };
40 } else {
41 parsed = crossSpawn._parse(cmd, args, opts);
42 }
43
44 opts = Object.assign({
45 maxBuffer: TEN_MEGABYTES,
46 buffer: true,
47 stripEof: true,
48 preferLocal: true,
49 localDir: parsed.options.cwd || process.cwd(),
50 encoding: 'utf8',
51 reject: true,
52 cleanup: true
53 }, parsed.options);
54
55 opts.stdio = stdio(opts);
56
57 if (opts.preferLocal) {
58 opts.env = npmRunPath.env(Object.assign({}, opts, {cwd: opts.localDir}));
59 }
60
61 if (opts.detached) {
62 // #115
63 opts.cleanup = false;
64 }
65
66 if (process.platform === 'win32' && path.basename(parsed.command) === 'cmd.exe') {
67 // #116
68 parsed.args.unshift('/q');
69 }
70
71 return {
72 cmd: parsed.command,
73 args: parsed.args,
74 opts,
75 parsed
76 };
77}
78
79function handleInput(spawned, input) {
80 if (input === null || input === undefined) {
81 return;
82 }
83
84 if (isStream(input)) {
85 input.pipe(spawned.stdin);
86 } else {
87 spawned.stdin.end(input);
88 }
89}
90
91function handleOutput(opts, val) {
92 if (val && opts.stripEof) {
93 val = stripEof(val);
94 }
95
96 return val;
97}
98
99function handleShell(fn, cmd, opts) {
100 let file = '/bin/sh';
101 let args = ['-c', cmd];
102
103 opts = Object.assign({}, opts);
104
105 if (process.platform === 'win32') {
106 opts.__winShell = true;
107 file = process.env.comspec || 'cmd.exe';
108 args = ['/s', '/c', `"${cmd}"`];
109 opts.windowsVerbatimArguments = true;
110 }
111
112 if (opts.shell) {
113 file = opts.shell;
114 delete opts.shell;
115 }
116
117 return fn(file, args, opts);
118}
119
120function getStream(process, stream, {encoding, buffer, maxBuffer}) {
121 if (!process[stream]) {
122 return null;
123 }
124
125 let ret;
126
127 if (!buffer) {
128 // TODO: Use `ret = util.promisify(stream.finished)(process[stream]);` when targeting Node.js 10
129 ret = new Promise((resolve, reject) => {
130 process[stream]
131 .once('end', resolve)
132 .once('error', reject);
133 });
134 } else if (encoding) {
135 ret = _getStream(process[stream], {
136 encoding,
137 maxBuffer
138 });
139 } else {
140 ret = _getStream.buffer(process[stream], {maxBuffer});
141 }
142
143 return ret.catch(err => {
144 err.stream = stream;
145 err.message = `${stream} ${err.message}`;
146 throw err;
147 });
148}
149
150function makeError(result, options) {
151 const {stdout, stderr} = result;
152
153 let err = result.error;
154 const {code, signal} = result;
155
156 const {parsed, joinedCmd} = options;
157 const timedOut = options.timedOut || false;
158
159 if (!err) {
160 let output = '';
161
162 if (Array.isArray(parsed.opts.stdio)) {
163 if (parsed.opts.stdio[2] !== 'inherit') {
164 output += output.length > 0 ? stderr : `\n${stderr}`;
165 }
166
167 if (parsed.opts.stdio[1] !== 'inherit') {
168 output += `\n${stdout}`;
169 }
170 } else if (parsed.opts.stdio !== 'inherit') {
171 output = `\n${stderr}${stdout}`;
172 }
173
174 err = new Error(`Command failed: ${joinedCmd}${output}`);
175 err.code = code < 0 ? errname(code) : code;
176 }
177
178 err.stdout = stdout;
179 err.stderr = stderr;
180 err.failed = true;
181 err.signal = signal || null;
182 err.cmd = joinedCmd;
183 err.timedOut = timedOut;
184
185 return err;
186}
187
188function joinCmd(cmd, args) {
189 let joinedCmd = cmd;
190
191 if (Array.isArray(args) && args.length > 0) {
192 joinedCmd += ' ' + args.join(' ');
193 }
194
195 return joinedCmd;
196}
197
198module.exports = (cmd, args, opts) => {
199 const parsed = handleArgs(cmd, args, opts);
200 const {encoding, buffer, maxBuffer} = parsed.opts;
201 const joinedCmd = joinCmd(cmd, args);
202
203 let spawned;
204 try {
205 spawned = childProcess.spawn(parsed.cmd, parsed.args, parsed.opts);
206 } catch (err) {
207 return Promise.reject(err);
208 }
209
210 let removeExitHandler;
211 if (parsed.opts.cleanup) {
212 removeExitHandler = onExit(() => {
213 spawned.kill();
214 });
215 }
216
217 let timeoutId = null;
218 let timedOut = false;
219
220 const cleanup = () => {
221 if (timeoutId) {
222 clearTimeout(timeoutId);
223 timeoutId = null;
224 }
225
226 if (removeExitHandler) {
227 removeExitHandler();
228 }
229 };
230
231 if (parsed.opts.timeout > 0) {
232 timeoutId = setTimeout(() => {
233 timeoutId = null;
234 timedOut = true;
235 spawned.kill(parsed.opts.killSignal);
236 }, parsed.opts.timeout);
237 }
238
239 const processDone = new Promise(resolve => {
240 spawned.on('exit', (code, signal) => {
241 cleanup();
242 resolve({code, signal});
243 });
244
245 spawned.on('error', err => {
246 cleanup();
247 resolve({error: err});
248 });
249
250 if (spawned.stdin) {
251 spawned.stdin.on('error', err => {
252 cleanup();
253 resolve({error: err});
254 });
255 }
256 });
257
258 function destroy() {
259 if (spawned.stdout) {
260 spawned.stdout.destroy();
261 }
262
263 if (spawned.stderr) {
264 spawned.stderr.destroy();
265 }
266 }
267
268 const handlePromise = () => pFinally(Promise.all([
269 processDone,
270 getStream(spawned, 'stdout', {encoding, buffer, maxBuffer}),
271 getStream(spawned, 'stderr', {encoding, buffer, maxBuffer})
272 ]).then(arr => {
273 const result = arr[0];
274 result.stdout = arr[1];
275 result.stderr = arr[2];
276
277 if (result.error || result.code !== 0 || result.signal !== null) {
278 const err = makeError(result, {
279 joinedCmd,
280 parsed,
281 timedOut
282 });
283
284 // TODO: missing some timeout logic for killed
285 // https://github.com/nodejs/node/blob/master/lib/child_process.js#L203
286 // err.killed = spawned.killed || killed;
287 err.killed = err.killed || spawned.killed;
288
289 if (!parsed.opts.reject) {
290 return err;
291 }
292
293 throw err;
294 }
295
296 return {
297 stdout: handleOutput(parsed.opts, result.stdout),
298 stderr: handleOutput(parsed.opts, result.stderr),
299 code: 0,
300 failed: false,
301 killed: false,
302 signal: null,
303 cmd: joinedCmd,
304 timedOut: false
305 };
306 }), destroy);
307
308 crossSpawn._enoent.hookChildProcess(spawned, parsed.parsed);
309
310 handleInput(spawned, parsed.opts.input);
311
312 spawned.then = (onfulfilled, onrejected) => handlePromise().then(onfulfilled, onrejected);
313 spawned.catch = onrejected => handlePromise().catch(onrejected);
314
315 return spawned;
316};
317
318// TODO: set `stderr: 'ignore'` when that option is implemented
319module.exports.stdout = (...args) => module.exports(...args).then(x => x.stdout);
320
321// TODO: set `stdout: 'ignore'` when that option is implemented
322module.exports.stderr = (...args) => module.exports(...args).then(x => x.stderr);
323
324module.exports.shell = (cmd, opts) => handleShell(module.exports, cmd, opts);
325
326module.exports.sync = (cmd, args, opts) => {
327 const parsed = handleArgs(cmd, args, opts);
328 const joinedCmd = joinCmd(cmd, args);
329
330 if (isStream(parsed.opts.input)) {
331 throw new TypeError('The `input` option cannot be a stream in sync mode');
332 }
333
334 const result = childProcess.spawnSync(parsed.cmd, parsed.args, parsed.opts);
335 result.code = result.status;
336
337 if (result.error || result.status !== 0 || result.signal !== null) {
338 const err = makeError(result, {
339 joinedCmd,
340 parsed
341 });
342
343 if (!parsed.opts.reject) {
344 return err;
345 }
346
347 throw err;
348 }
349
350 return {
351 stdout: handleOutput(parsed.opts, result.stdout),
352 stderr: handleOutput(parsed.opts, result.stderr),
353 code: 0,
354 failed: false,
355 signal: null,
356 cmd: joinedCmd,
357 timedOut: false
358 };
359};
360
361module.exports.shellSync = (cmd, opts) => handleShell(module.exports.sync, cmd, opts);
Note: See TracBrowser for help on using the repository browser.