.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / os-locale / node_modules / execa / index.js
1 'use strict';
2 const path = require('path');
3 const childProcess = require('child_process');
4 const crossSpawn = require('cross-spawn');
5 const stripEof = require('strip-eof');
6 const npmRunPath = require('npm-run-path');
7 const isStream = require('is-stream');
8 const _getStream = require('get-stream');
9 const pFinally = require('p-finally');
10 const onExit = require('signal-exit');
11 const errname = require('./lib/errname');
12 const stdio = require('./lib/stdio');
13
14 const TEN_MEGABYTES = 1000 * 1000 * 10;
15
16 function 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
79 function 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
91 function handleOutput(opts, val) {
92         if (val && opts.stripEof) {
93                 val = stripEof(val);
94         }
95
96         return val;
97 }
98
99 function 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
120 function 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
150 function 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
188 function 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
198 module.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
319 module.exports.stdout = (...args) => module.exports(...args).then(x => x.stdout);
320
321 // TODO: set `stdout: 'ignore'` when that option is implemented
322 module.exports.stderr = (...args) => module.exports(...args).then(x => x.stderr);
323
324 module.exports.shell = (cmd, opts) => handleShell(module.exports, cmd, opts);
325
326 module.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
361 module.exports.shellSync = (cmd, opts) => handleShell(module.exports.sync, cmd, opts);