cross-spawn-esm 1.1.3 → 1.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/dist/index.d.ts +45 -38
- package/dist/index.js +51 -51
- package/package.json +8 -6
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
import { ChildProcess, SpawnOptions, SpawnSyncOptions, SpawnSyncReturns } from "node:child_process";
|
|
2
2
|
import { NonSharedBuffer } from "node:buffer";
|
|
3
|
+
//#region src/utils/escapes.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Remove line breaks (`\r` and `\n`) from the given string. Without this, an argument containing a newline could terminate the enclosing
|
|
6
|
+
* command early and inject an arbitrary second command into the shell.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* // Without escaping, cmd.exe would run two commands:
|
|
10
|
+
* // node -e "console.log('ok')
|
|
11
|
+
* // calc.exe"
|
|
12
|
+
* escapeLineBreaks("console.log('ok')\ncalc.exe");
|
|
13
|
+
* //=> "console.log('ok')calc.exe"
|
|
14
|
+
*/
|
|
15
|
+
declare function escapeLineBreaks(arg: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Escape Windows `cmd.exe` shell meta characters (``/([()\][%!^"`<>&|;, *?])/g``) by prefixing each with `^`. Without this, a meta character in a command or argument would be interpreted
|
|
18
|
+
* by the shell, breaking the command or enabling arbitrary command execution.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // Without escaping, cmd.exe would treat the spaces and `&` as separators:
|
|
22
|
+
* escapeMetaChars('notepad "a & b.txt"');
|
|
23
|
+
* //=> 'notepad^ ^"a^ ^&^ b.txt^"'
|
|
24
|
+
*/
|
|
25
|
+
declare function escapeMetaChars(arg: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Escapes a command name for safe use in a Windows `cmd.exe` invocation.
|
|
28
|
+
*/
|
|
29
|
+
declare function escapeCommand(arg: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Escape a single argument into a fully self-contained token for a Windows
|
|
32
|
+
* `cmd.exe` command line.
|
|
33
|
+
*/
|
|
34
|
+
declare function escapeArgument(arg: unknown, doubleEscapeMetaChars?: boolean): string;
|
|
35
|
+
//#endregion
|
|
3
36
|
//#region src/utils/resolve-command.d.ts
|
|
4
37
|
interface ParsedCommand {
|
|
5
38
|
command: string;
|
|
@@ -32,18 +65,25 @@ declare function resolveCommandAttempt(parsed: ParsedCommand, withoutPathExt?: b
|
|
|
32
65
|
declare function resolveCommand(parsed: ParsedCommand): string | null;
|
|
33
66
|
//#endregion
|
|
34
67
|
//#region src/enoent.d.ts
|
|
68
|
+
interface SpawnError extends NodeJS.ErrnoException {
|
|
69
|
+
code: string;
|
|
70
|
+
errno: number;
|
|
71
|
+
path: string;
|
|
72
|
+
syscall: string;
|
|
73
|
+
spawnargs: ReadonlyArray<string>;
|
|
74
|
+
}
|
|
35
75
|
/**
|
|
36
76
|
* Builds an ENOENT error for a command that could not be found. Creates
|
|
37
77
|
* an error whose shape matches what Node's own `spawn`/`spawnSync` would produce
|
|
38
78
|
* when it fails to locate the command file. This makes the error interchangeable
|
|
39
79
|
* with a real ENOENT from Node.
|
|
40
80
|
*/
|
|
41
|
-
declare function notFoundError(original: ParsedCommand["original"], syscall: "spawn" | "spawnSync"):
|
|
81
|
+
declare function notFoundError(original: ParsedCommand["original"], syscall: "spawn" | "spawnSync"): SpawnError;
|
|
42
82
|
/**
|
|
43
83
|
* Detects the Windows-only fake ENOENT that `cmd.exe` produces. On POSIX or
|
|
44
84
|
* for any other exit code the error is not applicable, so `null` is returned.
|
|
45
85
|
*/
|
|
46
|
-
declare function verifyENOENT(status: number | null, parsed: ParsedCommand, syscall: "spawn" | "spawnSync"):
|
|
86
|
+
declare function verifyENOENT(status: number | null, parsed: ParsedCommand, syscall: "spawn" | "spawnSync"): SpawnError | null;
|
|
47
87
|
/**
|
|
48
88
|
* Hooks into a `ChildProcess` `exit` event so that a missing command is
|
|
49
89
|
* surfaced as an `error` event instead of silently exiting with code `1`.
|
|
@@ -98,39 +138,6 @@ declare function readShebang(file: string): string | null;
|
|
|
98
138
|
*/
|
|
99
139
|
declare function detectShebang(parsed: ParsedCommand): string | null;
|
|
100
140
|
//#endregion
|
|
101
|
-
//#region src/utils/escapes.d.ts
|
|
102
|
-
/**
|
|
103
|
-
* Remove line breaks (`\r` and `\n`) from the given string. Without this, an argument containing a newline could terminate the enclosing
|
|
104
|
-
* command early and inject an arbitrary second command into the shell.
|
|
105
|
-
*
|
|
106
|
-
* @example
|
|
107
|
-
* // Without escaping, cmd.exe would run two commands:
|
|
108
|
-
* // node -e "console.log('ok')
|
|
109
|
-
* // calc.exe"
|
|
110
|
-
* escapeLineBreaks("console.log('ok')\ncalc.exe");
|
|
111
|
-
* //=> "console.log('ok')calc.exe"
|
|
112
|
-
*/
|
|
113
|
-
declare function escapeLineBreaks(arg: string): string;
|
|
114
|
-
/**
|
|
115
|
-
* Escape Windows `cmd.exe` shell meta characters (``/([()\][%!^"`<>&|;, *?])/g``) by prefixing each with `^`. Without this, a meta character in a command or argument would be interpreted
|
|
116
|
-
* by the shell, breaking the command or enabling arbitrary command execution.
|
|
117
|
-
*
|
|
118
|
-
* @example
|
|
119
|
-
* // Without escaping, cmd.exe would treat the spaces and `&` as separators:
|
|
120
|
-
* escapeMetaChars('notepad "a & b.txt"');
|
|
121
|
-
* //=> 'notepad^ ^"a^ ^&^ b.txt^"'
|
|
122
|
-
*/
|
|
123
|
-
declare function escapeMetaChars(arg: string): string;
|
|
124
|
-
/**
|
|
125
|
-
* Escapes a command name for safe use in a Windows `cmd.exe` invocation.
|
|
126
|
-
*/
|
|
127
|
-
declare function escapeCommand(arg: string): string;
|
|
128
|
-
/**
|
|
129
|
-
* Escape a single argument into a fully self-contained token for a Windows
|
|
130
|
-
* `cmd.exe` command line.
|
|
131
|
-
*/
|
|
132
|
-
declare function escapeArgument(arg: unknown, doubleEscapeMetaChars?: boolean): string;
|
|
133
|
-
//#endregion
|
|
134
141
|
//#region src/utils/path-key.d.ts
|
|
135
142
|
interface PathKeyOptions {
|
|
136
143
|
/**
|
|
@@ -139,14 +146,14 @@ interface PathKeyOptions {
|
|
|
139
146
|
*
|
|
140
147
|
* @see https://nodejs.org/api/process.html#process_process_env
|
|
141
148
|
*/
|
|
142
|
-
env?:
|
|
149
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
143
150
|
/**
|
|
144
151
|
* Get the PATH key for a specific platform.
|
|
145
152
|
* @default process.platform
|
|
146
153
|
*
|
|
147
154
|
* @see https://nodejs.org/api/process.html#process_process_platform
|
|
148
155
|
*/
|
|
149
|
-
platform?: NodeJS.Platform;
|
|
156
|
+
readonly platform?: NodeJS.Platform;
|
|
150
157
|
}
|
|
151
158
|
/**
|
|
152
159
|
* Get the [PATH] environment variable key cross-platform.
|
|
@@ -227,4 +234,4 @@ export declare const _utils: {
|
|
|
227
234
|
pathKey: typeof pathKey;
|
|
228
235
|
};
|
|
229
236
|
//#endregion
|
|
230
|
-
export type { ParsedCommand, PathKeyOptions };
|
|
237
|
+
export type { ParsedCommand, PathKeyOptions, SpawnError };
|
package/dist/index.js
CHANGED
|
@@ -12,9 +12,51 @@ const metaCharsRegExp = /([()\][%!^"`<>&|;, *?])/g;
|
|
|
12
12
|
const executableRegExp = /\.(?:exe)$/i;
|
|
13
13
|
const cmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;
|
|
14
14
|
//#endregion
|
|
15
|
-
//#region src/utils/
|
|
16
|
-
function
|
|
17
|
-
return
|
|
15
|
+
//#region src/utils/escapes.ts
|
|
16
|
+
function escapeLineBreaks(arg) {
|
|
17
|
+
return arg.replace(lineBreaksRegExp, "");
|
|
18
|
+
}
|
|
19
|
+
function escapeMetaChars(arg) {
|
|
20
|
+
return arg.replace(metaCharsRegExp, "^$1");
|
|
21
|
+
}
|
|
22
|
+
function escapeCommand(arg) {
|
|
23
|
+
return escapeMetaChars(escapeLineBreaks(arg));
|
|
24
|
+
}
|
|
25
|
+
function escapeArgument(arg, doubleEscapeMetaChars) {
|
|
26
|
+
let argStr = `${arg}`;
|
|
27
|
+
argStr = escapeLineBreaks(argStr);
|
|
28
|
+
argStr = argStr.replace(/(?=(\\+?)?)\1"/g, "$1$1\\\"");
|
|
29
|
+
argStr = argStr.replace(/(?=(\\+?)?)\1$/, "$1$1");
|
|
30
|
+
argStr = escapeMetaChars(`"${argStr}"`);
|
|
31
|
+
if (doubleEscapeMetaChars) argStr = escapeMetaChars(argStr);
|
|
32
|
+
return argStr;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/enoent.ts
|
|
36
|
+
function notFoundError(original, syscall) {
|
|
37
|
+
return Object.assign(/* @__PURE__ */ new Error(`${syscall} ${original.command} ENOENT`), {
|
|
38
|
+
code: "ENOENT",
|
|
39
|
+
errno: -2,
|
|
40
|
+
syscall: `${syscall} ${original.command}`,
|
|
41
|
+
path: original.command,
|
|
42
|
+
spawnargs: original.args
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function verifyENOENT(status, parsed, syscall) {
|
|
46
|
+
if (isWin && status === 1 && !parsed.file) return notFoundError(parsed.original, syscall);
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
function hookChildProcess(cp, parsed) {
|
|
50
|
+
if (!isWin) return;
|
|
51
|
+
const originalEmit = cp.emit;
|
|
52
|
+
cp.emit = function(name, ...args) {
|
|
53
|
+
if (name === "exit") {
|
|
54
|
+
const status = args[0];
|
|
55
|
+
const err = verifyENOENT(status, parsed, "spawn");
|
|
56
|
+
if (err) return originalEmit.call(cp, "error", err);
|
|
57
|
+
}
|
|
58
|
+
return originalEmit.apply(cp, [name, ...args]);
|
|
59
|
+
};
|
|
18
60
|
}
|
|
19
61
|
//#endregion
|
|
20
62
|
//#region src/utils/isexe.ts
|
|
@@ -54,7 +96,7 @@ const pathSeparatorRegExp = new RegExp(`[${path.posix.sep}${path.sep === path.po
|
|
|
54
96
|
function whichSync(command, { cwd = _cwd, path: optPath = _env.PATH, pathExt: optPathExt = _env.PATHEXT } = {}) {
|
|
55
97
|
const delimiter = path.delimiter;
|
|
56
98
|
const dirs = command.match(pathSeparatorRegExp) ? [""] : [...isWin ? [cwd] : [], ...(optPath ?? "").split(delimiter)];
|
|
57
|
-
const pathExtExe = isWin ? optPathExt
|
|
99
|
+
const pathExtExe = isWin ? optPathExt || [
|
|
58
100
|
".EXE",
|
|
59
101
|
".CMD",
|
|
60
102
|
".BAT",
|
|
@@ -72,6 +114,11 @@ function whichSync(command, { cwd = _cwd, path: optPath = _env.PATH, pathExt: op
|
|
|
72
114
|
return null;
|
|
73
115
|
}
|
|
74
116
|
//#endregion
|
|
117
|
+
//#region src/utils/path-key.ts
|
|
118
|
+
function pathKey({ env = _env, platform = _platform } = {}) {
|
|
119
|
+
return platform === "win32" ? Object.keys(env).reverse().find((key) => key.toUpperCase() === "PATH") ?? "Path" : "PATH";
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
75
122
|
//#region src/utils/resolve-command.ts
|
|
76
123
|
function resolveCommandAttempt(parsed, withoutPathExt) {
|
|
77
124
|
const env = parsed.options.env ?? _env;
|
|
@@ -85,33 +132,6 @@ function resolveCommand(parsed) {
|
|
|
85
132
|
return resolveCommandAttempt(parsed) ?? (isWin ? resolveCommandAttempt(parsed, true) : null);
|
|
86
133
|
}
|
|
87
134
|
//#endregion
|
|
88
|
-
//#region src/enoent.ts
|
|
89
|
-
function notFoundError(original, syscall) {
|
|
90
|
-
return Object.assign(/* @__PURE__ */ new Error(`${syscall} ${original.command} ENOENT`), {
|
|
91
|
-
code: "ENOENT",
|
|
92
|
-
errno: -2,
|
|
93
|
-
syscall: `${syscall} ${original.command}`,
|
|
94
|
-
path: original.command,
|
|
95
|
-
spawnargs: original.args
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
function verifyENOENT(status, parsed, syscall) {
|
|
99
|
-
if (isWin && status === 1 && !parsed.file) return notFoundError(parsed.original, syscall);
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
function hookChildProcess(cp, parsed) {
|
|
103
|
-
if (!isWin) return;
|
|
104
|
-
const originalEmit = cp.emit;
|
|
105
|
-
cp.emit = function(name, ...args) {
|
|
106
|
-
if (name === "exit") {
|
|
107
|
-
const status = args[0];
|
|
108
|
-
const err = verifyENOENT(status, parsed, "spawn");
|
|
109
|
-
if (err) return originalEmit.call(cp, "error", err);
|
|
110
|
-
}
|
|
111
|
-
return originalEmit.apply(cp, [name, ...args]);
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
//#endregion
|
|
115
135
|
//#region src/utils/shebang.ts
|
|
116
136
|
function shebangCommand(string) {
|
|
117
137
|
const match = string.match(shebangRegExp);
|
|
@@ -144,26 +164,6 @@ function detectShebang(parsed) {
|
|
|
144
164
|
return parsed.file;
|
|
145
165
|
}
|
|
146
166
|
//#endregion
|
|
147
|
-
//#region src/utils/escapes.ts
|
|
148
|
-
function escapeLineBreaks(arg) {
|
|
149
|
-
return arg.replace(lineBreaksRegExp, "");
|
|
150
|
-
}
|
|
151
|
-
function escapeMetaChars(arg) {
|
|
152
|
-
return arg.replace(metaCharsRegExp, "^$1");
|
|
153
|
-
}
|
|
154
|
-
function escapeCommand(arg) {
|
|
155
|
-
return escapeMetaChars(escapeLineBreaks(arg));
|
|
156
|
-
}
|
|
157
|
-
function escapeArgument(arg, doubleEscapeMetaChars) {
|
|
158
|
-
let argStr = `${arg}`;
|
|
159
|
-
argStr = escapeLineBreaks(argStr);
|
|
160
|
-
argStr = argStr.replace(/(?=(\\+?)?)\1"/g, "$1$1\\\"");
|
|
161
|
-
argStr = argStr.replace(/(?=(\\+?)?)\1$/, "$1$1");
|
|
162
|
-
argStr = escapeMetaChars(`"${argStr}"`);
|
|
163
|
-
if (doubleEscapeMetaChars) argStr = escapeMetaChars(argStr);
|
|
164
|
-
return argStr;
|
|
165
|
-
}
|
|
166
|
-
//#endregion
|
|
167
167
|
//#region src/parse.ts
|
|
168
168
|
function parseNonShell(parsed) {
|
|
169
169
|
if (!isWin) return parsed;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cross-spawn-esm",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.2.0",
|
|
5
5
|
"description": "ESM version of cross-spawn package",
|
|
6
6
|
"author": "Timur Bikmuhametov <tim.bic22@gmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -35,17 +35,18 @@
|
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@timbic/eslint-config": "1.2.2",
|
|
37
37
|
"@timbic/prettier-config": "1.2.3",
|
|
38
|
-
"@types/node": "26.6.
|
|
38
|
+
"@types/node": "26.6.4",
|
|
39
|
+
"@vitest/coverage-v8": "5.0.3",
|
|
39
40
|
"bumpp": "12.3.0",
|
|
40
|
-
"eslint": "10.
|
|
41
|
+
"eslint": "10.12.0",
|
|
41
42
|
"husky": "9.1.7",
|
|
42
|
-
"lint-staged": "17.
|
|
43
|
+
"lint-staged": "17.6.0",
|
|
43
44
|
"mkdirp": "3.0.1",
|
|
44
|
-
"prettier": "3.9.
|
|
45
|
+
"prettier": "3.9.9",
|
|
45
46
|
"rimraf": "6.1.3",
|
|
46
47
|
"tsdown": "0.23.0",
|
|
47
48
|
"typescript": "6.0.3",
|
|
48
|
-
"vitest": "5.0.
|
|
49
|
+
"vitest": "5.0.3"
|
|
49
50
|
},
|
|
50
51
|
"lint-staged": {
|
|
51
52
|
"*/**/*.{js,ts,md,json}": [
|
|
@@ -63,6 +64,7 @@
|
|
|
63
64
|
"typecheck": "tsc --noEmit",
|
|
64
65
|
"test": "vitest run",
|
|
65
66
|
"test:watch": "vitest",
|
|
67
|
+
"test:coverage": "vitest run --coverage",
|
|
66
68
|
"release": "bumpp"
|
|
67
69
|
}
|
|
68
70
|
}
|