.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / os-locale / node_modules / cross-spawn / lib / parse.js
1 'use strict';
2
3 const path = require('path');
4 const niceTry = require('nice-try');
5 const resolveCommand = require('./util/resolveCommand');
6 const escape = require('./util/escape');
7 const readShebang = require('./util/readShebang');
8 const semver = require('semver');
9
10 const isWin = process.platform === 'win32';
11 const isExecutableRegExp = /\.(?:com|exe)$/i;
12 const isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;
13
14 // `options.shell` is supported in Node ^4.8.0, ^5.7.0 and >= 6.0.0
15 const supportsShellOption = niceTry(() => semver.satisfies(process.version, '^4.8.0 || ^5.7.0 || >= 6.0.0', true)) || false;
16
17 function detectShebang(parsed) {
18     parsed.file = resolveCommand(parsed);
19
20     const shebang = parsed.file && readShebang(parsed.file);
21
22     if (shebang) {
23         parsed.args.unshift(parsed.file);
24         parsed.command = shebang;
25
26         return resolveCommand(parsed);
27     }
28
29     return parsed.file;
30 }
31
32 function parseNonShell(parsed) {
33     if (!isWin) {
34         return parsed;
35     }
36
37     // Detect & add support for shebangs
38     const commandFile = detectShebang(parsed);
39
40     // We don't need a shell if the command filename is an executable
41     const needsShell = !isExecutableRegExp.test(commandFile);
42
43     // If a shell is required, use cmd.exe and take care of escaping everything correctly
44     // Note that `forceShell` is an hidden option used only in tests
45     if (parsed.options.forceShell || needsShell) {
46         // Need to double escape meta chars if the command is a cmd-shim located in `node_modules/.bin/`
47         // The cmd-shim simply calls execute the package bin file with NodeJS, proxying any argument
48         // Because the escape of metachars with ^ gets interpreted when the cmd.exe is first called,
49         // we need to double escape them
50         const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
51
52         // Normalize posix paths into OS compatible paths (e.g.: foo/bar -> foo\bar)
53         // This is necessary otherwise it will always fail with ENOENT in those cases
54         parsed.command = path.normalize(parsed.command);
55
56         // Escape command & arguments
57         parsed.command = escape.command(parsed.command);
58         parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars));
59
60         const shellCommand = [parsed.command].concat(parsed.args).join(' ');
61
62         parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`];
63         parsed.command = process.env.comspec || 'cmd.exe';
64         parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped
65     }
66
67     return parsed;
68 }
69
70 function parseShell(parsed) {
71     // If node supports the shell option, there's no need to mimic its behavior
72     if (supportsShellOption) {
73         return parsed;
74     }
75
76     // Mimic node shell option
77     // See https://github.com/nodejs/node/blob/b9f6a2dc059a1062776133f3d4fd848c4da7d150/lib/child_process.js#L335
78     const shellCommand = [parsed.command].concat(parsed.args).join(' ');
79
80     if (isWin) {
81         parsed.command = typeof parsed.options.shell === 'string' ? parsed.options.shell : process.env.comspec || 'cmd.exe';
82         parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`];
83         parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped
84     } else {
85         if (typeof parsed.options.shell === 'string') {
86             parsed.command = parsed.options.shell;
87         } else if (process.platform === 'android') {
88             parsed.command = '/system/bin/sh';
89         } else {
90             parsed.command = '/bin/sh';
91         }
92
93         parsed.args = ['-c', shellCommand];
94     }
95
96     return parsed;
97 }
98
99 function parse(command, args, options) {
100     // Normalize arguments, similar to nodejs
101     if (args && !Array.isArray(args)) {
102         options = args;
103         args = null;
104     }
105
106     args = args ? args.slice(0) : []; // Clone array to avoid changing the original
107     options = Object.assign({}, options); // Clone object to avoid changing the original
108
109     // Build our parsed object
110     const parsed = {
111         command,
112         args,
113         options,
114         file: undefined,
115         original: {
116             command,
117             args,
118         },
119     };
120
121     // Delegate further parsing to shell or non-shell
122     return options.shell ? parseShell(parsed) : parseNonShell(parsed);
123 }
124
125 module.exports = parse;