@vltpkg/cmd-shim 0.0.0-9

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/LICENSE ADDED
@@ -0,0 +1,89 @@
1
+ This is a fork and combination of "cmd-shim" and "read-cmd-shim".
2
+
3
+
4
+ "cmd-shim" is used under the terms of the following license:
5
+ ------------------------------------------------------------
6
+ The ISC License
7
+
8
+ Copyright (c) npm, Inc. and Contributors
9
+
10
+ Permission to use, copy, modify, and/or distribute this software for any
11
+ purpose with or without fee is hereby granted, provided that the above
12
+ copyright notice and this permission notice appear in all copies.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
17
+ SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
20
+ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21
+ ------------------------------------------------------------
22
+
23
+ "read-cmd-shim" is used under the terms of the following license:
24
+ ------------------------------------------------------------
25
+ Copyright (c) 2015, Rebecca Turner <me@re-becca.org>
26
+
27
+ Permission to use, copy, modify, and/or distribute this software for any
28
+ purpose with or without fee is hereby granted, provided that the above
29
+ copyright notice and this permission notice appear in all copies.
30
+
31
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
32
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
33
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
34
+ SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
35
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
36
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
37
+ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
38
+ ------------------------------------------------------------
39
+
40
+
41
+ All additions and changes in this fork are released under the following
42
+ license:
43
+ ------------------------------------------------------------
44
+ Copyright (c) vlt technology, Inc.
45
+
46
+ Redistribution and use in source and binary forms, with or without
47
+ modification, are permitted provided that the following conditions are met:
48
+
49
+ 1. Redistributions of source code must retain the above copyright notice, this
50
+ list of conditions and the following disclaimer.
51
+ 2. Redistributions in binary form must reproduce the above copyright notice,
52
+ this list of conditions and the following disclaimer in the documentation
53
+ and/or other materials provided with the distribution.
54
+
55
+ Subject to the terms and conditions of this license, each copyright holder and
56
+ contributor hereby grants to those receiving rights under this license a
57
+ perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
58
+ (except for failure to satisfy the conditions of this license) patent license
59
+ to make, have made, use, offer to sell, sell, import, and otherwise transfer
60
+ this software, where such license applies only to those patent claims, already
61
+ acquired or hereafter acquired, licensable by such copyright holder or
62
+ contributor that are necessarily infringed by:
63
+
64
+ (a) their Contribution(s) (the licensed copyrights of copyright holders and
65
+ non-copyrightable additions of contributors, in source or binary form) alone;
66
+ or
67
+ (b) combination of their Contribution(s) with the work of authorship to which
68
+ such Contribution(s) was added by such copyright holder or contributor, if, at
69
+ the time the Contribution is added, such addition causes such combination to be
70
+ necessarily infringed. The patent license shall not apply to any other
71
+ combinations which include the Contribution.
72
+
73
+ Except as expressly stated above, no rights or licenses from any copyright
74
+ holder or contributor is granted under this license, whether expressly, by
75
+ implication, estoppel or otherwise.
76
+
77
+ DISCLAIMER
78
+
79
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
80
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
81
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
82
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
83
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
84
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
85
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
86
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
87
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
88
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
89
+ ------------------------------------------------------------
package/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # @vltpkg/cmd-shim
2
+
3
+ This is a module for writing `.cmd` and `.ps1` shims for executables
4
+ on Windows, since shebangs are not supported on Windows, and thus
5
+ symlinks are not sufficient for running executables.
6
+
7
+ **[Usage](#usage)** · **[Note](#note)**
8
+
9
+ ## Overview
10
+
11
+ This module doesn't do anything on non-Windows platforms.
12
+
13
+ On Windows platforms, it parses the `#!` (shebang) line of a script,
14
+ and figures out how to write the appropriate `.cmd` shim for it.
15
+
16
+ The behavior is just like creating a symlink, and it'll clobber
17
+ anything in its way.
18
+
19
+ It can also be used to determine which package a shim points to
20
+ (assuming it was created by vlt).
21
+
22
+ ## Usage
23
+
24
+ ```js
25
+ import { cmdShim, findSource } from '@vltpkg/cmd-shim'
26
+
27
+ const binFile = 'node_modules/some-pkg/bin/foo.js'
28
+ const target = 'node_modules/.bin/foo'
29
+
30
+ await cmdShim(binFile, target)
31
+ // now the file is there
32
+ assert(statSync(target).isFile())
33
+
34
+ // prints: 'node_modules/some-pkg'
35
+ console.error(await findSource(target))
36
+ ```
@@ -0,0 +1,5 @@
1
+ import type { RollbackRemove } from '@vltpkg/rollback-remove';
2
+ export { findCmdShim, findCmdShimIfExists, readCmdShim, readCmdShimIfExists, } from './read.ts';
3
+ export declare const cmdShimIfExists: (from: string, to: string, remover: RollbackRemove) => Promise<void>;
4
+ export declare const cmdShim: (from: string, to: string, remover: RollbackRemove) => Promise<void>;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAG7D,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,WAAW,EACX,mBAAmB,GACpB,MAAM,WAAW,CAAA;AAKlB,eAAO,MAAM,eAAe,SACpB,MAAM,MACR,MAAM,WACD,cAAc,kBAQxB,CAAA;AAED,eAAO,MAAM,OAAO,SACZ,MAAM,MACR,MAAM,WACD,cAAc,kBAYxB,CAAA"}
@@ -0,0 +1,192 @@
1
+ // On windows, create a .cmd file.
2
+ // Read the #! in the file to see what it uses. The vast majority
3
+ // of the time, this will be either:
4
+ // "#!/usr/bin/env <prog> <args...>"
5
+ // or:
6
+ // "#!<prog> <args...>"
7
+ //
8
+ // Write a binroot/pkg.bin + ".cmd" file that has this line in it:
9
+ // @<prog> <args...> %dp0%<target> %*
10
+ import { chmod, mkdir, readFile, stat, writeFile, } from 'node:fs/promises';
11
+ import { error } from '@vltpkg/error-cause';
12
+ import { dirname, relative } from 'node:path';
13
+ import { convertToSetCommands } from "./to-batch-syntax.js";
14
+ export { findCmdShim, findCmdShimIfExists, readCmdShim, readCmdShimIfExists, } from "./read.js";
15
+ const shebangExpr = /^#!\s*(?:\/usr\/bin\/env\s+(?:-S\s+)?((?:[^ \t=]+=[^ \t=]+\s+)*))?([^ \t]+)(.*)$/;
16
+ export const cmdShimIfExists = async (from, to, remover) => {
17
+ try {
18
+ await stat(from);
19
+ }
20
+ catch {
21
+ return;
22
+ }
23
+ await writeShim(from, to, remover);
24
+ };
25
+ export const cmdShim = async (from, to, remover) => {
26
+ try {
27
+ await stat(from);
28
+ await writeShim(from, to, remover);
29
+ }
30
+ catch (er) {
31
+ throw error('Could not shim to executable file', {
32
+ path: from,
33
+ target: to,
34
+ cause: er,
35
+ });
36
+ }
37
+ };
38
+ const writeShim = async (from, to, remover) => {
39
+ await Promise.all([
40
+ remover.rm(to),
41
+ remover.rm(to + '.cmd'),
42
+ remover.rm(to + '.ps1'),
43
+ remover.rm(to + '.pwsh'),
44
+ ]);
45
+ // make a cmd file and a sh script
46
+ // First, check if the bin is a #! of some sort.
47
+ // If not, then assume it's something that'll be compiled, or some other
48
+ // sort of script, and just call it directly.
49
+ await mkdir(dirname(to), { recursive: true });
50
+ const data = await readFile(from, 'utf8').catch(() => '');
51
+ if (!data) {
52
+ return await writeShim_(from, to);
53
+ }
54
+ const firstLine = data.trim().split(/\r*\n/)[0];
55
+ const shebang = firstLine?.match(shebangExpr) ?? undefined;
56
+ return writeShim_(from, to, shebang?.[2], shebang?.[3], shebang?.[1]);
57
+ };
58
+ const writeShim_ = async (from, to, prog, args, variables) => {
59
+ let shTarget = relative(dirname(to), from);
60
+ let target = shTarget.split('/').join('\\');
61
+ let longProg;
62
+ let shProg = prog?.split('\\').join('/');
63
+ let shLongProg;
64
+ let pwshProg = shProg && `"${shProg}$exe"`;
65
+ let pwshLongProg;
66
+ shTarget = shTarget.split('\\').join('/');
67
+ args = args || '';
68
+ variables = variables || '';
69
+ if (!prog) {
70
+ prog = `"%dp0%\\${target}"`;
71
+ shProg = `"$basedir/${shTarget}"`;
72
+ pwshProg = shProg;
73
+ args = '';
74
+ target = '';
75
+ shTarget = '';
76
+ }
77
+ else {
78
+ longProg = `"%dp0%\\${prog}.exe"`;
79
+ shLongProg = `"$basedir/${prog}"`;
80
+ pwshLongProg = `"$basedir/${prog}$exe"`;
81
+ target = `"%dp0%\\${target}"`;
82
+ shTarget = `"$basedir/${shTarget}"`;
83
+ }
84
+ // Subroutine trick to fix https://github.com/npm/cmd-shim/issues/10
85
+ // and https://github.com/npm/cli/issues/969
86
+ const head = '@ECHO off\r\n' +
87
+ 'GOTO start\r\n' +
88
+ ':find_dp0\r\n' +
89
+ 'SET dp0=%~dp0\r\n' +
90
+ 'EXIT /b\r\n' +
91
+ ':start\r\n' +
92
+ 'SETLOCAL\r\n' +
93
+ 'CALL :find_dp0\r\n';
94
+ let cmd;
95
+ if (longProg) {
96
+ /* c8 ignore next */
97
+ shLongProg = (shLongProg ?? '').trim();
98
+ args = args.trim();
99
+ const variablesBatch = convertToSetCommands(variables);
100
+ cmd =
101
+ `${head}${variablesBatch}\r\n` +
102
+ `IF EXIST ${longProg} (\r\n` +
103
+ ` SET "_prog=${longProg.replace(/(^")|("$)/g, '')}"\r\n` +
104
+ ') ELSE (\r\n' +
105
+ ` SET "_prog=${prog.replace(/(^")|("$)/g, '')}"\r\n` +
106
+ ' SET PATHEXT=%PATHEXT:;.JS;=;%\r\n' +
107
+ ')\r\n' +
108
+ '\r\n' +
109
+ // prevent "Terminate Batch Job? (Y/n)" message
110
+ // https://github.com/npm/cli/issues/969#issuecomment-737496588
111
+ 'endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & ' +
112
+ `"%_prog%" ${args} ${target} %*\r\n`;
113
+ }
114
+ else {
115
+ cmd = `${head}${prog} ${args} ${target} %*\r\n`;
116
+ }
117
+ let sh = '#!/bin/sh\n' +
118
+ `basedir=$(dirname "$(echo "$0" | sed -e 's,\\\\,/,g')")\n` +
119
+ '\n' +
120
+ 'case `uname` in\n' +
121
+ ' *CYGWIN*|*MINGW*|*MSYS*)\n' +
122
+ ' if command -v cygpath > /dev/null 2>&1; then\n' +
123
+ ' basedir=`cygpath -w "$basedir"`\n' +
124
+ ' fi\n' +
125
+ ' ;;\n' +
126
+ 'esac\n' +
127
+ '\n';
128
+ if (shLongProg) {
129
+ sh +=
130
+ `if [ -x ${shLongProg} ]; then\n` +
131
+ ` exec ${variables}${shLongProg} ${args} ${shTarget} "$@"\n` +
132
+ 'else \n' +
133
+ ` exec ${variables}${shProg} ${args} ${shTarget} "$@"\n` +
134
+ 'fi\n';
135
+ }
136
+ else {
137
+ sh += `exec ${shProg} ${args} ${shTarget} "$@"\n`;
138
+ }
139
+ let pwsh = '#!/usr/bin/env pwsh\n' +
140
+ '$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent\n' +
141
+ '\n' +
142
+ '$exe=""\n' +
143
+ 'if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {\n' +
144
+ ' # Fix case when both the Windows and Linux builds of Node\n' +
145
+ ' # are installed in the same directory\n' +
146
+ ' $exe=".exe"\n' +
147
+ '}\n';
148
+ if (shLongProg) {
149
+ pwsh +=
150
+ '$ret=0\n' +
151
+ `if (Test-Path ${pwshLongProg}) {\n` +
152
+ ' # Support pipeline input\n' +
153
+ ' if ($MyInvocation.ExpectingInput) {\n' +
154
+ ` $input | & ${pwshLongProg} ${args} ${shTarget} $args\n` +
155
+ ' } else {\n' +
156
+ ` & ${pwshLongProg} ${args} ${shTarget} $args\n` +
157
+ ' }\n' +
158
+ ' $ret=$LASTEXITCODE\n' +
159
+ '} else {\n' +
160
+ ' # Support pipeline input\n' +
161
+ ' if ($MyInvocation.ExpectingInput) {\n' +
162
+ ` $input | & ${pwshProg} ${args} ${shTarget} $args\n` +
163
+ ' } else {\n' +
164
+ ` & ${pwshProg} ${args} ${shTarget} $args\n` +
165
+ ' }\n' +
166
+ ' $ret=$LASTEXITCODE\n' +
167
+ '}\n' +
168
+ 'exit $ret\n';
169
+ }
170
+ else {
171
+ pwsh +=
172
+ '# Support pipeline input\n' +
173
+ 'if ($MyInvocation.ExpectingInput) {\n' +
174
+ ` $input | & ${pwshProg} ${args} ${shTarget} $args\n` +
175
+ '} else {\n' +
176
+ ` & ${pwshProg} ${args} ${shTarget} $args\n` +
177
+ '}\n' +
178
+ 'exit $LASTEXITCODE\n';
179
+ }
180
+ await Promise.all([
181
+ writeFile(to + '.cmd', cmd, 'utf8'),
182
+ writeFile(to + '.ps1', pwsh, 'utf8'),
183
+ writeFile(to, sh, 'utf8'),
184
+ ]);
185
+ await chmodShim(to);
186
+ };
187
+ const chmodShim = async (to) => await Promise.all([
188
+ chmod(to, 0o755),
189
+ chmod(to + '.cmd', 0o755),
190
+ chmod(to + '.ps1', 0o755),
191
+ ]);
192
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// On windows, create a .cmd file.\n// Read the #! in the file to see what it uses. The vast majority\n// of the time, this will be either:\n// \"#!/usr/bin/env <prog> <args...>\"\n// or:\n// \"#!<prog> <args...>\"\n//\n// Write a binroot/pkg.bin + \".cmd\" file that has this line in it:\n// @<prog> <args...> %dp0%<target> %*\n\nimport {\n chmod,\n mkdir,\n readFile,\n stat,\n writeFile,\n} from 'node:fs/promises'\n\nimport { error } from '@vltpkg/error-cause'\nimport type { RollbackRemove } from '@vltpkg/rollback-remove'\nimport { dirname, relative } from 'node:path'\nimport { convertToSetCommands } from './to-batch-syntax.ts'\nexport {\n findCmdShim,\n findCmdShimIfExists,\n readCmdShim,\n readCmdShimIfExists,\n} from './read.ts'\n\nconst shebangExpr =\n /^#!\\s*(?:\\/usr\\/bin\\/env\\s+(?:-S\\s+)?((?:[^ \\t=]+=[^ \\t=]+\\s+)*))?([^ \\t]+)(.*)$/\n\nexport const cmdShimIfExists = async (\n from: string,\n to: string,\n remover: RollbackRemove,\n) => {\n try {\n await stat(from)\n } catch {\n return\n }\n await writeShim(from, to, remover)\n}\n\nexport const cmdShim = async (\n from: string,\n to: string,\n remover: RollbackRemove,\n) => {\n try {\n await stat(from)\n await writeShim(from, to, remover)\n } catch (er) {\n throw error('Could not shim to executable file', {\n path: from,\n target: to,\n cause: er,\n })\n }\n}\n\nconst writeShim = async (\n from: string,\n to: string,\n remover: RollbackRemove,\n) => {\n await Promise.all([\n remover.rm(to),\n remover.rm(to + '.cmd'),\n remover.rm(to + '.ps1'),\n remover.rm(to + '.pwsh'),\n ])\n // make a cmd file and a sh script\n // First, check if the bin is a #! of some sort.\n // If not, then assume it's something that'll be compiled, or some other\n // sort of script, and just call it directly.\n await mkdir(dirname(to), { recursive: true })\n const data = await readFile(from, 'utf8').catch(() => '')\n if (!data) {\n return await writeShim_(from, to)\n }\n const firstLine = data.trim().split(/\\r*\\n/)[0]\n const shebang = firstLine?.match(shebangExpr) ?? undefined\n return writeShim_(\n from,\n to,\n shebang?.[2],\n shebang?.[3],\n shebang?.[1],\n )\n}\n\nconst writeShim_ = async (\n from: string,\n to: string,\n prog?: string,\n args?: string,\n variables?: string,\n) => {\n let shTarget = relative(dirname(to), from)\n let target = shTarget.split('/').join('\\\\')\n let longProg\n let shProg = prog?.split('\\\\').join('/')\n let shLongProg\n let pwshProg = shProg && `\"${shProg}$exe\"`\n let pwshLongProg\n shTarget = shTarget.split('\\\\').join('/')\n args = args || ''\n variables = variables || ''\n if (!prog) {\n prog = `\"%dp0%\\\\${target}\"`\n shProg = `\"$basedir/${shTarget}\"`\n pwshProg = shProg\n args = ''\n target = ''\n shTarget = ''\n } else {\n longProg = `\"%dp0%\\\\${prog}.exe\"`\n shLongProg = `\"$basedir/${prog}\"`\n pwshLongProg = `\"$basedir/${prog}$exe\"`\n target = `\"%dp0%\\\\${target}\"`\n shTarget = `\"$basedir/${shTarget}\"`\n }\n\n // Subroutine trick to fix https://github.com/npm/cmd-shim/issues/10\n // and https://github.com/npm/cli/issues/969\n const head =\n '@ECHO off\\r\\n' +\n 'GOTO start\\r\\n' +\n ':find_dp0\\r\\n' +\n 'SET dp0=%~dp0\\r\\n' +\n 'EXIT /b\\r\\n' +\n ':start\\r\\n' +\n 'SETLOCAL\\r\\n' +\n 'CALL :find_dp0\\r\\n'\n\n let cmd\n if (longProg) {\n /* c8 ignore next */\n shLongProg = (shLongProg ?? '').trim()\n args = args.trim()\n const variablesBatch = convertToSetCommands(variables)\n cmd =\n `${head}${variablesBatch}\\r\\n` +\n `IF EXIST ${longProg} (\\r\\n` +\n ` SET \"_prog=${longProg.replace(/(^\")|(\"$)/g, '')}\"\\r\\n` +\n ') ELSE (\\r\\n' +\n ` SET \"_prog=${prog.replace(/(^\")|(\"$)/g, '')}\"\\r\\n` +\n ' SET PATHEXT=%PATHEXT:;.JS;=;%\\r\\n' +\n ')\\r\\n' +\n '\\r\\n' +\n // prevent \"Terminate Batch Job? (Y/n)\" message\n // https://github.com/npm/cli/issues/969#issuecomment-737496588\n 'endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & ' +\n `\"%_prog%\" ${args} ${target} %*\\r\\n`\n } else {\n cmd = `${head}${prog} ${args} ${target} %*\\r\\n`\n }\n\n let sh =\n '#!/bin/sh\\n' +\n `basedir=$(dirname \"$(echo \"$0\" | sed -e 's,\\\\\\\\,/,g')\")\\n` +\n '\\n' +\n 'case `uname` in\\n' +\n ' *CYGWIN*|*MINGW*|*MSYS*)\\n' +\n ' if command -v cygpath > /dev/null 2>&1; then\\n' +\n ' basedir=`cygpath -w \"$basedir\"`\\n' +\n ' fi\\n' +\n ' ;;\\n' +\n 'esac\\n' +\n '\\n'\n\n if (shLongProg) {\n sh +=\n `if [ -x ${shLongProg} ]; then\\n` +\n ` exec ${variables}${shLongProg} ${args} ${shTarget} \"$@\"\\n` +\n 'else \\n' +\n ` exec ${variables}${shProg} ${args} ${shTarget} \"$@\"\\n` +\n 'fi\\n'\n } else {\n sh += `exec ${shProg} ${args} ${shTarget} \"$@\"\\n`\n }\n\n let pwsh =\n '#!/usr/bin/env pwsh\\n' +\n '$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent\\n' +\n '\\n' +\n '$exe=\"\"\\n' +\n 'if ($PSVersionTable.PSVersion -lt \"6.0\" -or $IsWindows) {\\n' +\n ' # Fix case when both the Windows and Linux builds of Node\\n' +\n ' # are installed in the same directory\\n' +\n ' $exe=\".exe\"\\n' +\n '}\\n'\n\n if (shLongProg) {\n pwsh +=\n '$ret=0\\n' +\n `if (Test-Path ${pwshLongProg}) {\\n` +\n ' # Support pipeline input\\n' +\n ' if ($MyInvocation.ExpectingInput) {\\n' +\n ` $input | & ${pwshLongProg} ${args} ${shTarget} $args\\n` +\n ' } else {\\n' +\n ` & ${pwshLongProg} ${args} ${shTarget} $args\\n` +\n ' }\\n' +\n ' $ret=$LASTEXITCODE\\n' +\n '} else {\\n' +\n ' # Support pipeline input\\n' +\n ' if ($MyInvocation.ExpectingInput) {\\n' +\n ` $input | & ${pwshProg} ${args} ${shTarget} $args\\n` +\n ' } else {\\n' +\n ` & ${pwshProg} ${args} ${shTarget} $args\\n` +\n ' }\\n' +\n ' $ret=$LASTEXITCODE\\n' +\n '}\\n' +\n 'exit $ret\\n'\n } else {\n pwsh +=\n '# Support pipeline input\\n' +\n 'if ($MyInvocation.ExpectingInput) {\\n' +\n ` $input | & ${pwshProg} ${args} ${shTarget} $args\\n` +\n '} else {\\n' +\n ` & ${pwshProg} ${args} ${shTarget} $args\\n` +\n '}\\n' +\n 'exit $LASTEXITCODE\\n'\n }\n\n await Promise.all([\n writeFile(to + '.cmd', cmd, 'utf8'),\n writeFile(to + '.ps1', pwsh, 'utf8'),\n writeFile(to, sh, 'utf8'),\n ])\n await chmodShim(to)\n}\n\nconst chmodShim = async (to: string) =>\n await Promise.all([\n chmod(to, 0o755),\n chmod(to + '.cmd', 0o755),\n chmod(to + '.ps1', 0o755),\n ])\n"]}
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,2 @@
1
+ export declare const paths: (path: string) => string[];
2
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/paths.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,KAAK,SAEP,MAAM,aAMW,CAAA"}
@@ -0,0 +1,12 @@
1
+ // On non-windows platforms, we only look for symlinks, not shims
2
+ /* c8 ignore start */
3
+ export const paths = process.platform === 'win32' ?
4
+ (path) => [
5
+ path + '.cmd',
6
+ path + '.ps1',
7
+ path,
8
+ path + '.pwsh',
9
+ ]
10
+ : (path) => [path];
11
+ /* c8 ignore stop */
12
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/paths.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,qBAAqB;AACrB,MAAM,CAAC,MAAM,KAAK,GAChB,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;IAC5B,CAAC,IAAY,EAAE,EAAE,CAAC;QAChB,IAAI,GAAG,MAAM;QACb,IAAI,GAAG,MAAM;QACb,IAAI;QACJ,IAAI,GAAG,OAAO;KACf;IACH,CAAC,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;AAC5B,oBAAoB","sourcesContent":["// On non-windows platforms, we only look for symlinks, not shims\n/* c8 ignore start */\nexport const paths =\n process.platform === 'win32' ?\n (path: string) => [\n path + '.cmd',\n path + '.ps1',\n path,\n path + '.pwsh',\n ]\n : (path: string) => [path]\n/* c8 ignore stop */\n"]}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Find a matching cmd, cygwin, powershell, or symlink executable,
3
+ * and return the matching file and the target it references in a
4
+ * `[string,string]` tuple if found, or undefined if not.
5
+ */
6
+ export declare const findCmdShimIfExists: (path: string) => Promise<undefined | [string, string]>;
7
+ /**
8
+ * Find a matching cmd, cygwin, powershell, or symlink executable,
9
+ * and return the matching file and the target it references in a
10
+ * `[string,string]` tuple if found, or throw if not found.
11
+ */
12
+ export declare const findCmdShim: (path: string) => Promise<[string, string]>;
13
+ /**
14
+ * Read the specified executable shim if it exists, returning the
15
+ * target it references. Return undefined if it does not exist
16
+ * or cannot be dereferenced.
17
+ */
18
+ export declare const readCmdShimIfExists: (path: string) => Promise<string | undefined>;
19
+ /**
20
+ * Read the specified executable shim, returning the target
21
+ * it references. Raise an error if it does not exist or cannot be read.
22
+ */
23
+ export declare const readCmdShim: (path: string) => Promise<string>;
24
+ //# sourceMappingURL=read.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/read.ts"],"names":[],"mappings":"AAoCA;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,SACxB,MAAM,KACX,OAAO,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAMtC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,WAAW,SAAgB,MAAM,8BAQ7C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,SAAgB,MAAM,gCAUrD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,SAAgB,MAAM,oBAmB7C,CAAA"}
@@ -0,0 +1,92 @@
1
+ import { error } from '@vltpkg/error-cause';
2
+ import { lstat, readFile, readlink } from 'node:fs/promises';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { paths } from "./paths.js";
5
+ const extractPath = (path, cmdshimContents) => {
6
+ if (/\.cmd$/i.test(path)) {
7
+ return extractPathFromCmd(cmdshimContents);
8
+ }
9
+ else if (/\.(ps1|pwsh)$/i.test(path)) {
10
+ return extractPathFromPowershell(cmdshimContents);
11
+ }
12
+ else {
13
+ return extractPathFromCygwin(cmdshimContents);
14
+ }
15
+ };
16
+ const extractPathFromPowershell = (cmdshimContents) => {
17
+ const matches = /"[$]basedir\/([^"]+?)"\s+[$]args/.exec(cmdshimContents);
18
+ return matches?.[1]?.replace(/\\/g, '/');
19
+ };
20
+ const extractPathFromCmd = (cmdshimContents) => {
21
+ const matches = /"%(?:~dp0|dp0%)\\([^"]+?)"\s+%[*]/.exec(cmdshimContents);
22
+ return matches?.[1]?.replace(/\\/g, '/');
23
+ };
24
+ const extractPathFromCygwin = (cmdshimContents) => {
25
+ const matches = /"[$]basedir\/([^"]+?)"\s+"[$]@"/.exec(cmdshimContents);
26
+ return matches?.[1]?.replace(/\\/g, '/');
27
+ };
28
+ /**
29
+ * Find a matching cmd, cygwin, powershell, or symlink executable,
30
+ * and return the matching file and the target it references in a
31
+ * `[string,string]` tuple if found, or undefined if not.
32
+ */
33
+ export const findCmdShimIfExists = async (path) => {
34
+ path = path.replace(/\.(cmd|pwsh|ps1)$/i, '');
35
+ for (const p of paths(path)) {
36
+ const result = await readCmdShimIfExists(p);
37
+ if (result)
38
+ return [p, result];
39
+ }
40
+ };
41
+ /**
42
+ * Find a matching cmd, cygwin, powershell, or symlink executable,
43
+ * and return the matching file and the target it references in a
44
+ * `[string,string]` tuple if found, or throw if not found.
45
+ */
46
+ export const findCmdShim = async (path) => {
47
+ const result = await findCmdShimIfExists(path);
48
+ if (result)
49
+ return result;
50
+ throw error('Could not find matching cmd shim', { path }, findCmdShim);
51
+ };
52
+ /**
53
+ * Read the specified executable shim if it exists, returning the
54
+ * target it references. Return undefined if it does not exist
55
+ * or cannot be dereferenced.
56
+ */
57
+ export const readCmdShimIfExists = async (path) => {
58
+ try {
59
+ const st = await lstat(path);
60
+ if (st.isSymbolicLink()) {
61
+ return resolve(dirname(path), await readlink(path));
62
+ }
63
+ const contents = await readFile(path);
64
+ const destination = extractPath(path, contents.toString());
65
+ if (destination)
66
+ return resolve(dirname(path), destination);
67
+ }
68
+ catch { }
69
+ };
70
+ /**
71
+ * Read the specified executable shim, returning the target
72
+ * it references. Raise an error if it does not exist or cannot be read.
73
+ */
74
+ export const readCmdShim = async (path) => {
75
+ try {
76
+ const st = await lstat(path);
77
+ if (st.isSymbolicLink()) {
78
+ return resolve(dirname(path), await readlink(path));
79
+ }
80
+ // create a new error to capture the stack trace from this point,
81
+ // instead of getting some opaque stack into node's internals
82
+ const contents = await readFile(path);
83
+ const destination = extractPath(path, contents.toString());
84
+ if (destination)
85
+ return resolve(dirname(path), destination);
86
+ }
87
+ catch (er) {
88
+ throw error('Could not read shim', { path, cause: er }, readCmdShim);
89
+ }
90
+ throw error('Not a valid cmd shim', { path }, readCmdShim);
91
+ };
92
+ //# sourceMappingURL=read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/read.ts"],"names":[],"mappings":"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","sourcesContent":["import { error } from '@vltpkg/error-cause'\nimport { lstat, readFile, readlink } from 'node:fs/promises'\nimport { dirname, resolve } from 'node:path'\nimport { paths } from './paths.ts'\n\nconst extractPath = (path: string, cmdshimContents: string) => {\n if (/\\.cmd$/i.test(path)) {\n return extractPathFromCmd(cmdshimContents)\n } else if (/\\.(ps1|pwsh)$/i.test(path)) {\n return extractPathFromPowershell(cmdshimContents)\n } else {\n return extractPathFromCygwin(cmdshimContents)\n }\n}\n\nconst extractPathFromPowershell = (cmdshimContents: string) => {\n const matches = /\"[$]basedir\\/([^\"]+?)\"\\s+[$]args/.exec(\n cmdshimContents,\n )\n return matches?.[1]?.replace(/\\\\/g, '/')\n}\n\nconst extractPathFromCmd = (cmdshimContents: string) => {\n const matches = /\"%(?:~dp0|dp0%)\\\\([^\"]+?)\"\\s+%[*]/.exec(\n cmdshimContents,\n )\n return matches?.[1]?.replace(/\\\\/g, '/')\n}\n\nconst extractPathFromCygwin = (cmdshimContents: string) => {\n const matches = /\"[$]basedir\\/([^\"]+?)\"\\s+\"[$]@\"/.exec(\n cmdshimContents,\n )\n return matches?.[1]?.replace(/\\\\/g, '/')\n}\n\n/**\n * Find a matching cmd, cygwin, powershell, or symlink executable,\n * and return the matching file and the target it references in a\n * `[string,string]` tuple if found, or undefined if not.\n */\nexport const findCmdShimIfExists = async (\n path: string,\n): Promise<undefined | [string, string]> => {\n path = path.replace(/\\.(cmd|pwsh|ps1)$/i, '')\n for (const p of paths(path)) {\n const result = await readCmdShimIfExists(p)\n if (result) return [p, result]\n }\n}\n\n/**\n * Find a matching cmd, cygwin, powershell, or symlink executable,\n * and return the matching file and the target it references in a\n * `[string,string]` tuple if found, or throw if not found.\n */\nexport const findCmdShim = async (path: string) => {\n const result = await findCmdShimIfExists(path)\n if (result) return result\n throw error(\n 'Could not find matching cmd shim',\n { path },\n findCmdShim,\n )\n}\n\n/**\n * Read the specified executable shim if it exists, returning the\n * target it references. Return undefined if it does not exist\n * or cannot be dereferenced.\n */\nexport const readCmdShimIfExists = async (path: string) => {\n try {\n const st = await lstat(path)\n if (st.isSymbolicLink()) {\n return resolve(dirname(path), await readlink(path))\n }\n const contents = await readFile(path)\n const destination = extractPath(path, contents.toString())\n if (destination) return resolve(dirname(path), destination)\n } catch {}\n}\n\n/**\n * Read the specified executable shim, returning the target\n * it references. Raise an error if it does not exist or cannot be read.\n */\nexport const readCmdShim = async (path: string) => {\n try {\n const st = await lstat(path)\n if (st.isSymbolicLink()) {\n return resolve(dirname(path), await readlink(path))\n }\n // create a new error to capture the stack trace from this point,\n // instead of getting some opaque stack into node's internals\n const contents = await readFile(path)\n const destination = extractPath(path, contents.toString())\n if (destination) return resolve(dirname(path), destination)\n } catch (er) {\n throw error(\n 'Could not read shim',\n { path, cause: er },\n readCmdShim,\n )\n }\n throw error('Not a valid cmd shim', { path }, readCmdShim)\n}\n"]}
@@ -0,0 +1,4 @@
1
+ export declare const convertToSetCommand: (key: string, value: string) => string;
2
+ export declare const convertToSetCommands: (variableString: string) => string;
3
+ export declare const replaceDollarWithPercentPair: (value: string) => string;
4
+ //# sourceMappingURL=to-batch-syntax.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-batch-syntax.d.ts","sourceRoot":"","sources":["../../src/to-batch-syntax.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,QAAS,MAAM,SAAS,MAAM,WACd,CAAA;AAiBhD,eAAO,MAAM,oBAAoB,mBAAoB,MAAM,WAG9C,CAAA;AAEb,eAAO,MAAM,4BAA4B,UAAW,MAAM,WAezD,CAAA"}
@@ -0,0 +1,28 @@
1
+ export const convertToSetCommand = (key, value) => convertToSetCommand_(key.trim(), value.trim());
2
+ const convertToSetCommand_ = (key, value) => key && value ?
3
+ `@SET ${key}=${replaceDollarWithPercentPair(value)}\r\n`
4
+ : '';
5
+ const extractVariableValuePairs = (declarations) => declarations.reduce((pairs, declaration) => {
6
+ const [key = '', val = ''] = declaration.split('=');
7
+ pairs[key] = val;
8
+ return pairs;
9
+ }, {});
10
+ export const convertToSetCommands = (variableString) => Object.entries(extractVariableValuePairs(variableString.split(' ')))
11
+ .map(([key, val]) => convertToSetCommand(key, val))
12
+ .join('');
13
+ export const replaceDollarWithPercentPair = (value) => {
14
+ const dollarExpressions = /\$\{?([^$@#?\- \t{}:]+)\}?/g;
15
+ let result = '';
16
+ let startIndex = 0;
17
+ do {
18
+ const match = dollarExpressions.exec(value);
19
+ if (match) {
20
+ const betweenMatches = value.substring(startIndex, match.index) || '';
21
+ result += betweenMatches + '%' + String(match[1]) + '%';
22
+ startIndex = dollarExpressions.lastIndex;
23
+ }
24
+ } while (dollarExpressions.lastIndex > 0);
25
+ result += value.slice(startIndex);
26
+ return result;
27
+ };
28
+ //# sourceMappingURL=to-batch-syntax.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-batch-syntax.js","sourceRoot":"","sources":["../../src/to-batch-syntax.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,GAAW,EAAE,KAAa,EAAE,EAAE,CAChE,oBAAoB,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;AAEhD,MAAM,oBAAoB,GAAG,CAAC,GAAW,EAAE,KAAa,EAAE,EAAE,CAC1D,GAAG,IAAI,KAAK,CAAC,CAAC;IACZ,QAAQ,GAAG,IAAI,4BAA4B,CAAC,KAAK,CAAC,MAAM;IAC1D,CAAC,CAAC,EAAE,CAAA;AAEN,MAAM,yBAAyB,GAAG,CAAC,YAAsB,EAAE,EAAE,CAC3D,YAAY,CAAC,MAAM,CACjB,CAAC,KAA6B,EAAE,WAAW,EAAE,EAAE;IAC7C,MAAM,CAAC,GAAG,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACnD,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,CAAA;IAChB,OAAO,KAAK,CAAA;AACd,CAAC,EACD,EAAE,CACH,CAAA;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,cAAsB,EAAE,EAAE,CAC7D,MAAM,CAAC,OAAO,CAAC,yBAAyB,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;KACjE,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,mBAAmB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;KAClD,IAAI,CAAC,EAAE,CAAC,CAAA;AAEb,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,KAAa,EAAE,EAAE;IAC5D,MAAM,iBAAiB,GAAG,6BAA6B,CAAA;IACvD,IAAI,MAAM,GAAG,EAAE,CAAA;IACf,IAAI,UAAU,GAAG,CAAC,CAAA;IAClB,GAAG,CAAC;QACF,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAC3C,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,cAAc,GAClB,KAAK,CAAC,SAAS,CAAC,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAA;YAChD,MAAM,IAAI,cAAc,GAAG,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAA;YACvD,UAAU,GAAG,iBAAiB,CAAC,SAAS,CAAA;QAC1C,CAAC;IACH,CAAC,QAAQ,iBAAiB,CAAC,SAAS,GAAG,CAAC,EAAC;IACzC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;IACjC,OAAO,MAAM,CAAA;AACf,CAAC,CAAA","sourcesContent":["export const convertToSetCommand = (key: string, value: string) =>\n convertToSetCommand_(key.trim(), value.trim())\n\nconst convertToSetCommand_ = (key: string, value: string) =>\n key && value ?\n `@SET ${key}=${replaceDollarWithPercentPair(value)}\\r\\n`\n : ''\n\nconst extractVariableValuePairs = (declarations: string[]) =>\n declarations.reduce(\n (pairs: Record<string, string>, declaration) => {\n const [key = '', val = ''] = declaration.split('=')\n pairs[key] = val\n return pairs\n },\n {},\n )\n\nexport const convertToSetCommands = (variableString: string) =>\n Object.entries(extractVariableValuePairs(variableString.split(' ')))\n .map(([key, val]) => convertToSetCommand(key, val))\n .join('')\n\nexport const replaceDollarWithPercentPair = (value: string) => {\n const dollarExpressions = /\\$\\{?([^$@#?\\- \\t{}:]+)\\}?/g\n let result = ''\n let startIndex = 0\n do {\n const match = dollarExpressions.exec(value)\n if (match) {\n const betweenMatches =\n value.substring(startIndex, match.index) || ''\n result += betweenMatches + '%' + String(match[1]) + '%'\n startIndex = dollarExpressions.lastIndex\n }\n } while (dollarExpressions.lastIndex > 0)\n result += value.slice(startIndex)\n return result\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@vltpkg/cmd-shim",
3
+ "description": "Executable script shims for Windows",
4
+ "version": "0.0.0-9",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/vltpkg/vltpkg.git",
8
+ "directory": "src/cmd-shim"
9
+ },
10
+ "tshy": {
11
+ "selfLink": false,
12
+ "liveDev": true,
13
+ "dialects": [
14
+ "esm"
15
+ ],
16
+ "exports": {
17
+ "./package.json": "./package.json",
18
+ ".": "./src/index.ts"
19
+ }
20
+ },
21
+ "dependencies": {
22
+ "@vltpkg/error-cause": "0.0.0-9",
23
+ "@vltpkg/rollback-remove": "0.0.0-9"
24
+ },
25
+ "devDependencies": {
26
+ "@eslint/js": "^9.20.0",
27
+ "@types/eslint__js": "^8.42.3",
28
+ "@types/node": "^22.13.1",
29
+ "eslint": "^9.20.0",
30
+ "prettier": "^3.4.2",
31
+ "tap": "^21.1.0",
32
+ "tshy": "^3.0.2",
33
+ "typedoc": "0.27.6",
34
+ "typescript": "5.7.3",
35
+ "typescript-eslint": "^8.23.0"
36
+ },
37
+ "license": "BSD-2-Clause-Patent",
38
+ "engines": {
39
+ "node": ">=22"
40
+ },
41
+ "tap": {
42
+ "extends": "../../tap-config.yaml"
43
+ },
44
+ "prettier": "../../.prettierrc.js",
45
+ "module": "./dist/esm/index.js",
46
+ "type": "module",
47
+ "exports": {
48
+ "./package.json": "./package.json",
49
+ ".": {
50
+ "import": {
51
+ "types": "./dist/esm/index.d.ts",
52
+ "default": "./dist/esm/index.js"
53
+ }
54
+ }
55
+ },
56
+ "files": [
57
+ "dist"
58
+ ],
59
+ "scripts": {
60
+ "format": "prettier --write . --log-level warn --ignore-path ../../.prettierignore --cache",
61
+ "format:check": "prettier --check . --ignore-path ../../.prettierignore --cache",
62
+ "lint": "eslint . --fix",
63
+ "lint:check": "eslint .",
64
+ "snap": "tap",
65
+ "test": "tap",
66
+ "posttest": "tsc --noEmit",
67
+ "typecheck": "tsc --noEmit"
68
+ }
69
+ }