@zkochan/cmd-shim 5.1.3 → 5.2.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/index.js CHANGED
@@ -1,478 +1,490 @@
1
- 'use strict';
2
- cmdShim.ifExists = cmdShimIfExists;
3
- const util_1 = require("util");
4
- const path = require("path");
5
- const isWindows = require("is-windows");
6
- const shebangExpr = /^#!\s*(?:\/usr\/bin\/env)?\s*([^ \t]+)(.*)$/;
7
- const DEFAULT_OPTIONS = {
8
- // Create PowerShell file by default if the option hasn't been specified
9
- createPwshFile: true,
10
- createCmdFile: isWindows(),
11
- fs: require('fs')
12
- };
13
- /**
14
- * Map from extensions of files that this module is frequently used for to their runtime.
15
- * @type {Map<string, string>}
16
- */
17
- const extensionToProgramMap = new Map([
18
- ['.js', 'node'],
19
- ['.cjs', 'node'],
20
- ['.mjs', 'node'],
21
- ['.cmd', 'cmd'],
22
- ['.bat', 'cmd'],
23
- ['.ps1', 'pwsh'],
24
- ['.sh', 'sh']
25
- ]);
26
- function ingestOptions(opts) {
27
- const opts_ = { ...DEFAULT_OPTIONS, ...opts };
28
- const fs = opts_.fs;
29
- opts_.fs_ = {
30
- chmod: fs.chmod ? util_1.promisify(fs.chmod) : (async () => { }),
31
- mkdir: util_1.promisify(fs.mkdir),
32
- readFile: util_1.promisify(fs.readFile),
33
- stat: util_1.promisify(fs.stat),
34
- unlink: util_1.promisify(fs.unlink),
35
- writeFile: util_1.promisify(fs.writeFile)
36
- };
37
- return opts_;
38
- }
39
- /**
40
- * Try to create shims.
41
- *
42
- * @param src Path to program (executable or script).
43
- * @param to Path to shims.
44
- * Don't add an extension if you will create multiple types of shims.
45
- * @param opts Options.
46
- * @throws If `src` is missing.
47
- */
48
- async function cmdShim(src, to, opts) {
49
- const opts_ = ingestOptions(opts);
50
- await opts_.fs_.stat(src);
51
- await cmdShim_(src, to, opts_);
52
- }
53
- /**
54
- * Try to create shims.
55
- *
56
- * Does nothing if `src` doesn't exist.
57
- *
58
- * @param src Path to program (executable or script).
59
- * @param to Path to shims.
60
- * Don't add an extension if you will create multiple types of shims.
61
- * @param opts Options.
62
- */
63
- function cmdShimIfExists(src, to, opts) {
64
- return cmdShim(src, to, opts).catch(() => { });
65
- }
66
- /**
67
- * Try to unlink, but ignore errors.
68
- * Any problems will surface later.
69
- *
70
- * @param path File to be removed.
71
- */
72
- function rm(path, opts) {
73
- return opts.fs_.unlink(path).catch(() => { });
74
- }
75
- /**
76
- * Try to create shims **even if `src` is missing**.
77
- *
78
- * @param src Path to program (executable or script).
79
- * @param to Path to shims.
80
- * Don't add an extension if you will create multiple types of shims.
81
- * @param opts Options.
82
- */
83
- async function cmdShim_(src, to, opts) {
84
- const srcRuntimeInfo = await searchScriptRuntime(src, opts);
85
- // Always tries to create all types of shims by calling `writeAllShims` as of now.
86
- // Append your code here to change the behavior in response to `srcRuntimeInfo`.
87
- // Create 3 shims for (Ba)sh in Cygwin / MSYS, no extension) & CMD (.cmd) & PowerShell (.ps1)
88
- await writeShimsPreCommon(to, opts);
89
- return writeAllShims(src, to, srcRuntimeInfo, opts);
90
- }
91
- /**
92
- * Do processes before **all** shims are created.
93
- * This must be called **only once** for one call of `cmdShim(IfExists)`.
94
- *
95
- * @param target Path of shims that are going to be created.
96
- */
97
- function writeShimsPreCommon(target, opts) {
98
- return opts.fs_.mkdir(path.dirname(target), { recursive: true });
99
- }
100
- /**
101
- * Write all types (sh & cmd & pwsh) of shims to files.
102
- * Extensions (`.cmd` and `.ps1`) are appended to cmd and pwsh shims.
103
- *
104
- *
105
- * @param src Path to program (executable or script).
106
- * @param to Path to shims **without extensions**.
107
- * Extensions are added for CMD and PowerShell shims.
108
- * @param srcRuntimeInfo Return value of `await searchScriptRuntime(src)`.
109
- * @param opts Options.
110
- */
111
- function writeAllShims(src, to, srcRuntimeInfo, opts) {
112
- const opts_ = ingestOptions(opts);
113
- const generatorAndExts = [{ generator: generateShShim, extension: '' }];
114
- if (opts_.createCmdFile) {
115
- generatorAndExts.push({ generator: generateCmdShim, extension: '.cmd' });
116
- }
117
- if (opts_.createPwshFile) {
118
- generatorAndExts.push({ generator: generatePwshShim, extension: '.ps1' });
119
- }
120
- return Promise.all(generatorAndExts.map((generatorAndExt) => writeShim(src, to + generatorAndExt.extension, srcRuntimeInfo, generatorAndExt.generator, opts_)));
121
- }
122
- /**
123
- * Do processes before writing shim.
124
- *
125
- * @param target Path to shim that is going to be created.
126
- */
127
- function writeShimPre(target, opts) {
128
- return rm(target, opts);
129
- }
130
- /**
131
- * Do processes after writing the shim.
132
- *
133
- * @param target Path to just created shim.
134
- */
135
- function writeShimPost(target, opts) {
136
- // Only chmoding shims as of now.
137
- // Some other processes may be appended.
138
- return chmodShim(target, opts);
139
- }
140
- /**
141
- * Look into runtime (e.g. `node` & `sh` & `pwsh`) and its arguments
142
- * of the target program (script or executable).
143
- *
144
- * @param target Path to the executable or script.
145
- * @return Promise of infomation of runtime of `target`.
146
- */
147
- async function searchScriptRuntime(target, opts) {
148
- const data = await opts.fs_.readFile(target, 'utf8');
149
- // First, check if the bin is a #! of some sort.
150
- const firstLine = data.trim().split(/\r*\n/)[0];
151
- const shebang = firstLine.match(shebangExpr);
152
- if (!shebang) {
153
- // If not, infer script type from its extension.
154
- // If the inference fails, it's something that'll be compiled, or some other
155
- // sort of script, and just call it directly.
156
- const targetExtension = path.extname(target).toLowerCase();
157
- return {
158
- // undefined if extension is unknown but it's converted to null.
159
- program: extensionToProgramMap.get(targetExtension) || null,
160
- additionalArgs: ''
161
- };
162
- }
163
- return {
164
- program: shebang[1],
165
- additionalArgs: shebang[2]
166
- };
167
- }
168
- /**
169
- * Write shim to the file system while executing the pre- and post-processes
170
- * defined in `WriteShimPre` and `WriteShimPost`.
171
- *
172
- * @param src Path to the executable or script.
173
- * @param to Path to the (sh) shim(s) that is going to be created.
174
- * @param srcRuntimeInfo Result of `await searchScriptRuntime(src)`.
175
- * @param generateShimScript Generator of shim script.
176
- * @param opts Other options.
177
- */
178
- async function writeShim(src, to, srcRuntimeInfo, generateShimScript, opts) {
179
- const defaultArgs = opts.preserveSymlinks ? '--preserve-symlinks' : '';
180
- // `Array.prototype.filter` removes ''.
181
- // ['--foo', '--bar'].join(' ') and [].join(' ') returns '--foo --bar' and '' respectively.
182
- const args = [srcRuntimeInfo.additionalArgs, defaultArgs].filter(arg => arg).join(' ');
183
- opts = Object.assign({}, opts, {
184
- prog: srcRuntimeInfo.program,
185
- args: args
186
- });
187
- await writeShimPre(to, opts);
188
- await opts.fs_.writeFile(to, generateShimScript(src, to, opts), 'utf8');
189
- return writeShimPost(to, opts);
190
- }
191
- /**
192
- * Generate the content of a shim for CMD.
193
- *
194
- * @param src Path to the executable or script.
195
- * @param to Path to the shim to be created.
196
- * It is highly recommended to end with `.cmd` (or `.bat`).
197
- * @param opts Options.
198
- * @return The content of shim.
199
- */
200
- function generateCmdShim(src, to, opts) {
201
- // `shTarget` is not used to generate the content.
202
- const shTarget = path.relative(path.dirname(to), src);
203
- let target = shTarget.split('/').join('\\');
204
- const quotedPathToTarget = path.isAbsolute(target) ? `"${target}"` : `"%~dp0\\${target}"`;
205
- let longProg;
206
- let prog = opts.prog;
207
- let args = opts.args || '';
208
- const nodePath = normalizePathEnvVar(opts.nodePath).win32;
209
- if (!prog) {
210
- prog = quotedPathToTarget;
211
- args = '';
212
- target = '';
213
- }
214
- else {
215
- longProg = `"%~dp0\\${prog}.exe"`;
216
- target = quotedPathToTarget;
217
- }
218
- let progArgs = opts.progArgs ? `${opts.progArgs.join(` `)} ` : '';
219
- // @IF EXIST "%~dp0\node.exe" (
220
- // "%~dp0\node.exe" "%~dp0\.\node_modules\npm\bin\npm-cli.js" %*
221
- // ) ELSE (
222
- // SETLOCAL
223
- // SET PATHEXT=%PATHEXT:;.JS;=;%
224
- // node "%~dp0\.\node_modules\npm\bin\npm-cli.js" %*
225
- // )
226
- let cmd = '@SETLOCAL\r\n';
227
- if (nodePath) {
228
- cmd += `\
229
- @IF NOT DEFINED NODE_PATH (\r
230
- @SET "NODE_PATH=${nodePath}"\r
231
- ) ELSE (\r
232
- @SET "NODE_PATH=%NODE_PATH%;${nodePath}"\r
233
- )\r
234
- `;
235
- }
236
- if (longProg) {
237
- cmd += `\
238
- @IF EXIST ${longProg} (\r
239
- ${longProg} ${args} ${target} ${progArgs}%*\r
240
- ) ELSE (\r
241
- @SET PATHEXT=%PATHEXT:;.JS;=;%\r
242
- ${prog} ${args} ${target} ${progArgs}%*\r
243
- )\r
244
- `;
245
- }
246
- else {
247
- cmd += `@${prog} ${args} ${target} ${progArgs}%*\r\n`;
248
- }
249
- return cmd;
250
- }
251
- /**
252
- * Generate the content of a shim for (Ba)sh in, for example, Cygwin and MSYS(2).
253
- *
254
- * @param src Path to the executable or script.
255
- * @param to Path to the shim to be created.
256
- * It is highly recommended to end with `.sh` or to contain no extension.
257
- * @param opts Options.
258
- * @return The content of shim.
259
- */
260
- function generateShShim(src, to, opts) {
261
- let shTarget = path.relative(path.dirname(to), src);
262
- let shProg = opts.prog && opts.prog.split('\\').join('/');
263
- let shLongProg;
264
- shTarget = shTarget.split('\\').join('/');
265
- const quotedPathToTarget = path.isAbsolute(shTarget) ? `"${shTarget}"` : `"$basedir/${shTarget}"`;
266
- let args = opts.args || '';
267
- const shNodePath = normalizePathEnvVar(opts.nodePath).posix;
268
- if (!shProg) {
269
- shProg = quotedPathToTarget;
270
- args = '';
271
- shTarget = '';
272
- }
273
- else {
274
- shLongProg = `"$basedir/${opts.prog}"`;
275
- shTarget = quotedPathToTarget;
276
- }
277
- let progArgs = opts.progArgs ? `${opts.progArgs.join(` `)} ` : '';
278
- // #!/bin/sh
279
- // basedir=`dirname "$0"`
280
- //
281
- // case `uname` in
282
- // *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
283
- // esac
284
- //
285
- // export NODE_PATH="<nodepath>"
286
- //
287
- // if [ -x "$basedir/node.exe" ]; then
288
- // exec "$basedir/node.exe" "$basedir/node_modules/npm/bin/npm-cli.js" "$@"
289
- // else
290
- // exec node "$basedir/node_modules/npm/bin/npm-cli.js" "$@"
291
- // fi
292
- let sh = `\
293
- #!/bin/sh
294
- basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')")
295
-
296
- case \`uname\` in
297
- *CYGWIN*) basedir=\`cygpath -w "$basedir"\`;;
298
- esac
299
-
300
- `;
301
- if (opts.nodePath) {
302
- sh += `\
303
- if [ -z "$NODE_PATH" ]; then
304
- export NODE_PATH="${shNodePath}"
305
- else
306
- export NODE_PATH="$NODE_PATH:${shNodePath}"
307
- fi
308
- `;
309
- }
310
- if (shLongProg) {
311
- sh += `\
312
- if [ -x ${shLongProg} ]; then
313
- exec ${shLongProg} ${args} ${shTarget} ${progArgs}"$@"
314
- else
315
- exec ${shProg} ${args} ${shTarget} ${progArgs}"$@"
316
- fi
317
- `;
318
- }
319
- else {
320
- sh += `\
321
- ${shProg} ${args} ${shTarget} ${progArgs}"$@"
322
- exit $?
323
- `;
324
- }
325
- return sh;
326
- }
327
- /**
328
- * Generate the content of a shim for PowerShell.
329
- *
330
- * @param src Path to the executable or script.
331
- * @param to Path to the shim to be created.
332
- * It is highly recommended to end with `.ps1`.
333
- * @param opts Options.
334
- * @return The content of shim.
335
- */
336
- function generatePwshShim(src, to, opts) {
337
- let shTarget = path.relative(path.dirname(to), src);
338
- const shProg = opts.prog && opts.prog.split('\\').join('/');
339
- let pwshProg = shProg && `"${shProg}$exe"`;
340
- let pwshLongProg;
341
- shTarget = shTarget.split('\\').join('/');
342
- const quotedPathToTarget = path.isAbsolute(shTarget) ? `"${shTarget}"` : `"$basedir/${shTarget}"`;
343
- let args = opts.args || '';
344
- let normalizedPathEnvVar = normalizePathEnvVar(opts.nodePath);
345
- const nodePath = normalizedPathEnvVar.win32;
346
- const shNodePath = normalizedPathEnvVar.posix;
347
- if (!pwshProg) {
348
- pwshProg = quotedPathToTarget;
349
- args = '';
350
- shTarget = '';
351
- }
352
- else {
353
- pwshLongProg = `"$basedir/${opts.prog}$exe"`;
354
- shTarget = quotedPathToTarget;
355
- }
356
- let progArgs = opts.progArgs ? `${opts.progArgs.join(` `)} ` : '';
357
- // #!/usr/bin/env pwsh
358
- // $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
359
- //
360
- // $ret=0
361
- // $exe = ""
362
- // if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
363
- // # Fix case when both the Windows and Linux builds of Node
364
- // # are installed in the same directory
365
- // $exe = ".exe"
366
- // }
367
- // if (Test-Path "$basedir/node") {
368
- // # Support pipeline input
369
- // if ($MyInvocation.ExpectingInput) {
370
- // $input | & "$basedir/node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args
371
- // } else {
372
- // & "$basedir/node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args
373
- // }
374
- // $ret=$LASTEXITCODE
375
- // } else {
376
- // # Support pipeline input
377
- // if ($MyInvocation.ExpectingInput) {
378
- // $input | & "node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args
379
- // } else {
380
- // & "node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args
381
- // }
382
- // $ret=$LASTEXITCODE
383
- // }
384
- // exit $ret
385
- let pwsh = `\
386
- #!/usr/bin/env pwsh
387
- $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
388
-
389
- $exe=""
390
- ${opts.nodePath ? `\
391
- $pathsep=":"
392
- $env_node_path=$env:NODE_PATH
393
- $new_node_path="${nodePath}"
394
- ` : ''}\
395
- if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
396
- # Fix case when both the Windows and Linux builds of Node
397
- # are installed in the same directory
398
- $exe=".exe"
399
- ${opts.nodePath ? ' $pathsep=";"\n' : ''}\
400
- }`;
401
- if (opts.nodePath) {
402
- pwsh += `\
403
- else {
404
- $new_node_path="${shNodePath}"
405
- }
406
- if ([string]::IsNullOrEmpty($env_node_path)) {
407
- $env:NODE_PATH=$new_node_path
408
- } else {
409
- $env:NODE_PATH="$env_node_path$pathsep$new_node_path"
410
- }
411
- `;
412
- }
413
- if (pwshLongProg) {
414
- pwsh += `
415
- $ret=0
416
- if (Test-Path ${pwshLongProg}) {
417
- # Support pipeline input
418
- if ($MyInvocation.ExpectingInput) {
419
- $input | & ${pwshLongProg} ${args} ${shTarget} ${progArgs}$args
420
- } else {
421
- & ${pwshLongProg} ${args} ${shTarget} ${progArgs}$args
422
- }
423
- $ret=$LASTEXITCODE
424
- } else {
425
- # Support pipeline input
426
- if ($MyInvocation.ExpectingInput) {
427
- $input | & ${pwshProg} ${args} ${shTarget} ${progArgs}$args
428
- } else {
429
- & ${pwshProg} ${args} ${shTarget} ${progArgs}$args
430
- }
431
- $ret=$LASTEXITCODE
432
- }
433
- ${opts.nodePath ? '$env:NODE_PATH=$env_node_path\n' : ''}\
434
- exit $ret
435
- `;
436
- }
437
- else {
438
- pwsh += `
439
- # Support pipeline input
440
- if ($MyInvocation.ExpectingInput) {
441
- $input | & ${pwshProg} ${args} ${shTarget} ${progArgs}$args
442
- } else {
443
- & ${pwshProg} ${args} ${shTarget} ${progArgs}$args
444
- }
445
- ${opts.nodePath ? '$env:NODE_PATH=$env_node_path\n' : ''}\
446
- exit $LASTEXITCODE
447
- `;
448
- }
449
- return pwsh;
450
- }
451
- /**
452
- * Chmod just created shim and make it executable
453
- *
454
- * @param to Path to shim.
455
- */
456
- function chmodShim(to, opts) {
457
- return opts.fs_.chmod(to, 0o755);
458
- }
459
- function normalizePathEnvVar(nodePath) {
460
- if (!nodePath) {
461
- return {
462
- win32: '',
463
- posix: ''
464
- };
465
- }
466
- let split = (typeof nodePath === 'string' ? nodePath.split(path.delimiter) : Array.from(nodePath));
467
- let result = {};
468
- for (let i = 0; i < split.length; i++) {
469
- const win32 = split[i].split('/').join('\\');
470
- const posix = isWindows() ? split[i].split('\\').join('/').replace(/^([^:\\/]*):/, (_, $1) => `/mnt/${$1.toLowerCase()}`) : split[i];
471
- result.win32 = result.win32 ? `${result.win32};${win32}` : win32;
472
- result.posix = result.posix ? `${result.posix}:${posix}` : posix;
473
- result[i] = { win32, posix };
474
- }
475
- return result;
476
- }
477
- module.exports = cmdShim;
1
+ 'use strict';
2
+ cmdShim.ifExists = cmdShimIfExists;
3
+ const util_1 = require("util");
4
+ const path = require("path");
5
+ const isWindows = require("is-windows");
6
+ const shebangExpr = /^#!\s*(?:\/usr\/bin\/env)?\s*([^ \t]+)(.*)$/;
7
+ const DEFAULT_OPTIONS = {
8
+ // Create PowerShell file by default if the option hasn't been specified
9
+ createPwshFile: true,
10
+ createCmdFile: isWindows(),
11
+ fs: require('fs')
12
+ };
13
+ /**
14
+ * Map from extensions of files that this module is frequently used for to their runtime.
15
+ * @type {Map<string, string>}
16
+ */
17
+ const extensionToProgramMap = new Map([
18
+ ['.js', 'node'],
19
+ ['.cjs', 'node'],
20
+ ['.mjs', 'node'],
21
+ ['.cmd', 'cmd'],
22
+ ['.bat', 'cmd'],
23
+ ['.ps1', 'pwsh'],
24
+ ['.sh', 'sh']
25
+ ]);
26
+ function ingestOptions(opts) {
27
+ const opts_ = { ...DEFAULT_OPTIONS, ...opts };
28
+ const fs = opts_.fs;
29
+ opts_.fs_ = {
30
+ chmod: fs.chmod ? util_1.promisify(fs.chmod) : (async () => { }),
31
+ mkdir: util_1.promisify(fs.mkdir),
32
+ readFile: util_1.promisify(fs.readFile),
33
+ stat: util_1.promisify(fs.stat),
34
+ unlink: util_1.promisify(fs.unlink),
35
+ writeFile: util_1.promisify(fs.writeFile)
36
+ };
37
+ return opts_;
38
+ }
39
+ /**
40
+ * Try to create shims.
41
+ *
42
+ * @param src Path to program (executable or script).
43
+ * @param to Path to shims.
44
+ * Don't add an extension if you will create multiple types of shims.
45
+ * @param opts Options.
46
+ * @throws If `src` is missing.
47
+ */
48
+ async function cmdShim(src, to, opts) {
49
+ const opts_ = ingestOptions(opts);
50
+ await opts_.fs_.stat(src);
51
+ await cmdShim_(src, to, opts_);
52
+ }
53
+ /**
54
+ * Try to create shims.
55
+ *
56
+ * Does nothing if `src` doesn't exist.
57
+ *
58
+ * @param src Path to program (executable or script).
59
+ * @param to Path to shims.
60
+ * Don't add an extension if you will create multiple types of shims.
61
+ * @param opts Options.
62
+ */
63
+ function cmdShimIfExists(src, to, opts) {
64
+ return cmdShim(src, to, opts).catch(() => { });
65
+ }
66
+ /**
67
+ * Try to unlink, but ignore errors.
68
+ * Any problems will surface later.
69
+ *
70
+ * @param path File to be removed.
71
+ */
72
+ function rm(path, opts) {
73
+ return opts.fs_.unlink(path).catch(() => { });
74
+ }
75
+ /**
76
+ * Try to create shims **even if `src` is missing**.
77
+ *
78
+ * @param src Path to program (executable or script).
79
+ * @param to Path to shims.
80
+ * Don't add an extension if you will create multiple types of shims.
81
+ * @param opts Options.
82
+ */
83
+ async function cmdShim_(src, to, opts) {
84
+ const srcRuntimeInfo = await searchScriptRuntime(src, opts);
85
+ // Always tries to create all types of shims by calling `writeAllShims` as of now.
86
+ // Append your code here to change the behavior in response to `srcRuntimeInfo`.
87
+ // Create 3 shims for (Ba)sh in Cygwin / MSYS, no extension) & CMD (.cmd) & PowerShell (.ps1)
88
+ await writeShimsPreCommon(to, opts);
89
+ return writeAllShims(src, to, srcRuntimeInfo, opts);
90
+ }
91
+ /**
92
+ * Do processes before **all** shims are created.
93
+ * This must be called **only once** for one call of `cmdShim(IfExists)`.
94
+ *
95
+ * @param target Path of shims that are going to be created.
96
+ */
97
+ function writeShimsPreCommon(target, opts) {
98
+ return opts.fs_.mkdir(path.dirname(target), { recursive: true });
99
+ }
100
+ /**
101
+ * Write all types (sh & cmd & pwsh) of shims to files.
102
+ * Extensions (`.cmd` and `.ps1`) are appended to cmd and pwsh shims.
103
+ *
104
+ *
105
+ * @param src Path to program (executable or script).
106
+ * @param to Path to shims **without extensions**.
107
+ * Extensions are added for CMD and PowerShell shims.
108
+ * @param srcRuntimeInfo Return value of `await searchScriptRuntime(src)`.
109
+ * @param opts Options.
110
+ */
111
+ function writeAllShims(src, to, srcRuntimeInfo, opts) {
112
+ const opts_ = ingestOptions(opts);
113
+ const generatorAndExts = [{ generator: generateShShim, extension: '' }];
114
+ if (opts_.createCmdFile) {
115
+ generatorAndExts.push({ generator: generateCmdShim, extension: '.cmd' });
116
+ }
117
+ if (opts_.createPwshFile) {
118
+ generatorAndExts.push({ generator: generatePwshShim, extension: '.ps1' });
119
+ }
120
+ return Promise.all(generatorAndExts.map((generatorAndExt) => writeShim(src, to + generatorAndExt.extension, srcRuntimeInfo, generatorAndExt.generator, opts_)));
121
+ }
122
+ /**
123
+ * Do processes before writing shim.
124
+ *
125
+ * @param target Path to shim that is going to be created.
126
+ */
127
+ function writeShimPre(target, opts) {
128
+ return rm(target, opts);
129
+ }
130
+ /**
131
+ * Do processes after writing the shim.
132
+ *
133
+ * @param target Path to just created shim.
134
+ */
135
+ function writeShimPost(target, opts) {
136
+ // Only chmoding shims as of now.
137
+ // Some other processes may be appended.
138
+ return chmodShim(target, opts);
139
+ }
140
+ /**
141
+ * Look into runtime (e.g. `node` & `sh` & `pwsh`) and its arguments
142
+ * of the target program (script or executable).
143
+ *
144
+ * @param target Path to the executable or script.
145
+ * @return Promise of infomation of runtime of `target`.
146
+ */
147
+ async function searchScriptRuntime(target, opts) {
148
+ const data = await opts.fs_.readFile(target, 'utf8');
149
+ // First, check if the bin is a #! of some sort.
150
+ const firstLine = data.trim().split(/\r*\n/)[0];
151
+ const shebang = firstLine.match(shebangExpr);
152
+ if (!shebang) {
153
+ // If not, infer script type from its extension.
154
+ // If the inference fails, it's something that'll be compiled, or some other
155
+ // sort of script, and just call it directly.
156
+ const targetExtension = path.extname(target).toLowerCase();
157
+ return {
158
+ // undefined if extension is unknown but it's converted to null.
159
+ program: extensionToProgramMap.get(targetExtension) || null,
160
+ additionalArgs: ''
161
+ };
162
+ }
163
+ return {
164
+ program: shebang[1],
165
+ additionalArgs: shebang[2]
166
+ };
167
+ }
168
+ /**
169
+ * Write shim to the file system while executing the pre- and post-processes
170
+ * defined in `WriteShimPre` and `WriteShimPost`.
171
+ *
172
+ * @param src Path to the executable or script.
173
+ * @param to Path to the (sh) shim(s) that is going to be created.
174
+ * @param srcRuntimeInfo Result of `await searchScriptRuntime(src)`.
175
+ * @param generateShimScript Generator of shim script.
176
+ * @param opts Other options.
177
+ */
178
+ async function writeShim(src, to, srcRuntimeInfo, generateShimScript, opts) {
179
+ const defaultArgs = opts.preserveSymlinks ? '--preserve-symlinks' : '';
180
+ // `Array.prototype.filter` removes ''.
181
+ // ['--foo', '--bar'].join(' ') and [].join(' ') returns '--foo --bar' and '' respectively.
182
+ const args = [srcRuntimeInfo.additionalArgs, defaultArgs].filter(arg => arg).join(' ');
183
+ opts = Object.assign({}, opts, {
184
+ prog: srcRuntimeInfo.program,
185
+ args: args
186
+ });
187
+ await writeShimPre(to, opts);
188
+ await opts.fs_.writeFile(to, generateShimScript(src, to, opts), 'utf8');
189
+ return writeShimPost(to, opts);
190
+ }
191
+ /**
192
+ * Generate the content of a shim for CMD.
193
+ *
194
+ * @param src Path to the executable or script.
195
+ * @param to Path to the shim to be created.
196
+ * It is highly recommended to end with `.cmd` (or `.bat`).
197
+ * @param opts Options.
198
+ * @return The content of shim.
199
+ */
200
+ function generateCmdShim(src, to, opts) {
201
+ // `shTarget` is not used to generate the content.
202
+ const shTarget = path.relative(path.dirname(to), src);
203
+ let target = shTarget.split('/').join('\\');
204
+ const quotedPathToTarget = path.isAbsolute(target) ? `"${target}"` : `"%~dp0\\${target}"`;
205
+ let longProg;
206
+ let prog = opts.prog;
207
+ let args = opts.args || '';
208
+ const nodePath = normalizePathEnvVar(opts.nodePath).win32;
209
+ if (!prog) {
210
+ prog = quotedPathToTarget;
211
+ args = '';
212
+ target = '';
213
+ }
214
+ else if (prog === 'node' && opts.nodeExecPath) {
215
+ prog = `"${opts.nodeExecPath}"`;
216
+ target = quotedPathToTarget;
217
+ }
218
+ else {
219
+ longProg = `"%~dp0\\${prog}.exe"`;
220
+ target = quotedPathToTarget;
221
+ }
222
+ let progArgs = opts.progArgs ? `${opts.progArgs.join(` `)} ` : '';
223
+ // @IF EXIST "%~dp0\node.exe" (
224
+ // "%~dp0\node.exe" "%~dp0\.\node_modules\npm\bin\npm-cli.js" %*
225
+ // ) ELSE (
226
+ // SETLOCAL
227
+ // SET PATHEXT=%PATHEXT:;.JS;=;%
228
+ // node "%~dp0\.\node_modules\npm\bin\npm-cli.js" %*
229
+ // )
230
+ let cmd = '@SETLOCAL\r\n';
231
+ if (nodePath) {
232
+ cmd += `\
233
+ @IF NOT DEFINED NODE_PATH (\r
234
+ @SET "NODE_PATH=${nodePath}"\r
235
+ ) ELSE (\r
236
+ @SET "NODE_PATH=%NODE_PATH%;${nodePath}"\r
237
+ )\r
238
+ `;
239
+ }
240
+ if (longProg) {
241
+ cmd += `\
242
+ @IF EXIST ${longProg} (\r
243
+ ${longProg} ${args} ${target} ${progArgs}%*\r
244
+ ) ELSE (\r
245
+ @SET PATHEXT=%PATHEXT:;.JS;=;%\r
246
+ ${prog} ${args} ${target} ${progArgs}%*\r
247
+ )\r
248
+ `;
249
+ }
250
+ else {
251
+ cmd += `@${prog} ${args} ${target} ${progArgs}%*\r\n`;
252
+ }
253
+ return cmd;
254
+ }
255
+ /**
256
+ * Generate the content of a shim for (Ba)sh in, for example, Cygwin and MSYS(2).
257
+ *
258
+ * @param src Path to the executable or script.
259
+ * @param to Path to the shim to be created.
260
+ * It is highly recommended to end with `.sh` or to contain no extension.
261
+ * @param opts Options.
262
+ * @return The content of shim.
263
+ */
264
+ function generateShShim(src, to, opts) {
265
+ let shTarget = path.relative(path.dirname(to), src);
266
+ let shProg = opts.prog && opts.prog.split('\\').join('/');
267
+ let shLongProg;
268
+ shTarget = shTarget.split('\\').join('/');
269
+ const quotedPathToTarget = path.isAbsolute(shTarget) ? `"${shTarget}"` : `"$basedir/${shTarget}"`;
270
+ let args = opts.args || '';
271
+ const shNodePath = normalizePathEnvVar(opts.nodePath).posix;
272
+ if (!shProg) {
273
+ shProg = quotedPathToTarget;
274
+ args = '';
275
+ shTarget = '';
276
+ }
277
+ else if (opts.prog === 'node' && opts.nodeExecPath) {
278
+ shProg = `"${opts.nodeExecPath}"`;
279
+ shTarget = quotedPathToTarget;
280
+ }
281
+ else {
282
+ shLongProg = `"$basedir/${opts.prog}"`;
283
+ shTarget = quotedPathToTarget;
284
+ }
285
+ let progArgs = opts.progArgs ? `${opts.progArgs.join(` `)} ` : '';
286
+ // #!/bin/sh
287
+ // basedir=`dirname "$0"`
288
+ //
289
+ // case `uname` in
290
+ // *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
291
+ // esac
292
+ //
293
+ // export NODE_PATH="<nodepath>"
294
+ //
295
+ // if [ -x "$basedir/node.exe" ]; then
296
+ // exec "$basedir/node.exe" "$basedir/node_modules/npm/bin/npm-cli.js" "$@"
297
+ // else
298
+ // exec node "$basedir/node_modules/npm/bin/npm-cli.js" "$@"
299
+ // fi
300
+ let sh = `\
301
+ #!/bin/sh
302
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')")
303
+
304
+ case \`uname\` in
305
+ *CYGWIN*) basedir=\`cygpath -w "$basedir"\`;;
306
+ esac
307
+
308
+ `;
309
+ if (opts.nodePath) {
310
+ sh += `\
311
+ if [ -z "$NODE_PATH" ]; then
312
+ export NODE_PATH="${shNodePath}"
313
+ else
314
+ export NODE_PATH="$NODE_PATH:${shNodePath}"
315
+ fi
316
+ `;
317
+ }
318
+ if (shLongProg) {
319
+ sh += `\
320
+ if [ -x ${shLongProg} ]; then
321
+ exec ${shLongProg} ${args} ${shTarget} ${progArgs}"$@"
322
+ else
323
+ exec ${shProg} ${args} ${shTarget} ${progArgs}"$@"
324
+ fi
325
+ `;
326
+ }
327
+ else {
328
+ sh += `\
329
+ ${shProg} ${args} ${shTarget} ${progArgs}"$@"
330
+ exit $?
331
+ `;
332
+ }
333
+ return sh;
334
+ }
335
+ /**
336
+ * Generate the content of a shim for PowerShell.
337
+ *
338
+ * @param src Path to the executable or script.
339
+ * @param to Path to the shim to be created.
340
+ * It is highly recommended to end with `.ps1`.
341
+ * @param opts Options.
342
+ * @return The content of shim.
343
+ */
344
+ function generatePwshShim(src, to, opts) {
345
+ let shTarget = path.relative(path.dirname(to), src);
346
+ const shProg = opts.prog && opts.prog.split('\\').join('/');
347
+ let pwshProg = shProg && `"${shProg}$exe"`;
348
+ let pwshLongProg;
349
+ shTarget = shTarget.split('\\').join('/');
350
+ const quotedPathToTarget = path.isAbsolute(shTarget) ? `"${shTarget}"` : `"$basedir/${shTarget}"`;
351
+ let args = opts.args || '';
352
+ let normalizedPathEnvVar = normalizePathEnvVar(opts.nodePath);
353
+ const nodePath = normalizedPathEnvVar.win32;
354
+ const shNodePath = normalizedPathEnvVar.posix;
355
+ if (!pwshProg) {
356
+ pwshProg = quotedPathToTarget;
357
+ args = '';
358
+ shTarget = '';
359
+ }
360
+ else if (opts.prog === 'node' && opts.nodeExecPath) {
361
+ pwshProg = `"${opts.nodeExecPath}"`;
362
+ shTarget = quotedPathToTarget;
363
+ }
364
+ else {
365
+ pwshLongProg = `"$basedir/${opts.prog}$exe"`;
366
+ shTarget = quotedPathToTarget;
367
+ }
368
+ let progArgs = opts.progArgs ? `${opts.progArgs.join(` `)} ` : '';
369
+ // #!/usr/bin/env pwsh
370
+ // $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
371
+ //
372
+ // $ret=0
373
+ // $exe = ""
374
+ // if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
375
+ // # Fix case when both the Windows and Linux builds of Node
376
+ // # are installed in the same directory
377
+ // $exe = ".exe"
378
+ // }
379
+ // if (Test-Path "$basedir/node") {
380
+ // # Support pipeline input
381
+ // if ($MyInvocation.ExpectingInput) {
382
+ // $input | & "$basedir/node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args
383
+ // } else {
384
+ // & "$basedir/node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args
385
+ // }
386
+ // $ret=$LASTEXITCODE
387
+ // } else {
388
+ // # Support pipeline input
389
+ // if ($MyInvocation.ExpectingInput) {
390
+ // $input | & "node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args
391
+ // } else {
392
+ // & "node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args
393
+ // }
394
+ // $ret=$LASTEXITCODE
395
+ // }
396
+ // exit $ret
397
+ let pwsh = `\
398
+ #!/usr/bin/env pwsh
399
+ $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
400
+
401
+ $exe=""
402
+ ${opts.nodePath ? `\
403
+ $pathsep=":"
404
+ $env_node_path=$env:NODE_PATH
405
+ $new_node_path="${nodePath}"
406
+ ` : ''}\
407
+ if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
408
+ # Fix case when both the Windows and Linux builds of Node
409
+ # are installed in the same directory
410
+ $exe=".exe"
411
+ ${opts.nodePath ? ' $pathsep=";"\n' : ''}\
412
+ }`;
413
+ if (opts.nodePath) {
414
+ pwsh += `\
415
+ else {
416
+ $new_node_path="${shNodePath}"
417
+ }
418
+ if ([string]::IsNullOrEmpty($env_node_path)) {
419
+ $env:NODE_PATH=$new_node_path
420
+ } else {
421
+ $env:NODE_PATH="$env_node_path$pathsep$new_node_path"
422
+ }
423
+ `;
424
+ }
425
+ if (pwshLongProg) {
426
+ pwsh += `
427
+ $ret=0
428
+ if (Test-Path ${pwshLongProg}) {
429
+ # Support pipeline input
430
+ if ($MyInvocation.ExpectingInput) {
431
+ $input | & ${pwshLongProg} ${args} ${shTarget} ${progArgs}$args
432
+ } else {
433
+ & ${pwshLongProg} ${args} ${shTarget} ${progArgs}$args
434
+ }
435
+ $ret=$LASTEXITCODE
436
+ } else {
437
+ # Support pipeline input
438
+ if ($MyInvocation.ExpectingInput) {
439
+ $input | & ${pwshProg} ${args} ${shTarget} ${progArgs}$args
440
+ } else {
441
+ & ${pwshProg} ${args} ${shTarget} ${progArgs}$args
442
+ }
443
+ $ret=$LASTEXITCODE
444
+ }
445
+ ${opts.nodePath ? '$env:NODE_PATH=$env_node_path\n' : ''}\
446
+ exit $ret
447
+ `;
448
+ }
449
+ else {
450
+ pwsh += `
451
+ # Support pipeline input
452
+ if ($MyInvocation.ExpectingInput) {
453
+ $input | & ${pwshProg} ${args} ${shTarget} ${progArgs}$args
454
+ } else {
455
+ & ${pwshProg} ${args} ${shTarget} ${progArgs}$args
456
+ }
457
+ ${opts.nodePath ? '$env:NODE_PATH=$env_node_path\n' : ''}\
458
+ exit $LASTEXITCODE
459
+ `;
460
+ }
461
+ return pwsh;
462
+ }
463
+ /**
464
+ * Chmod just created shim and make it executable
465
+ *
466
+ * @param to Path to shim.
467
+ */
468
+ function chmodShim(to, opts) {
469
+ return opts.fs_.chmod(to, 0o755);
470
+ }
471
+ function normalizePathEnvVar(nodePath) {
472
+ if (!nodePath) {
473
+ return {
474
+ win32: '',
475
+ posix: ''
476
+ };
477
+ }
478
+ let split = (typeof nodePath === 'string' ? nodePath.split(path.delimiter) : Array.from(nodePath));
479
+ let result = {};
480
+ for (let i = 0; i < split.length; i++) {
481
+ const win32 = split[i].split('/').join('\\');
482
+ const posix = isWindows() ? split[i].split('\\').join('/').replace(/^([^:\\/]*):/, (_, $1) => `/mnt/${$1.toLowerCase()}`) : split[i];
483
+ result.win32 = result.win32 ? `${result.win32};${win32}` : win32;
484
+ result.posix = result.posix ? `${result.posix}:${posix}` : posix;
485
+ result[i] = { win32, posix };
486
+ }
487
+ return result;
488
+ }
489
+ module.exports = cmdShim;
478
490
  //# sourceMappingURL=index.js.map