@arcgis/node-toolkit 5.2.0-next.51 → 5.2.0-next.53

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/README.md CHANGED
@@ -8,5 +8,5 @@ It is not intended to be used directly, but rather used as a dependency by other
8
8
 
9
9
  ## License
10
10
 
11
- This package is licensed under the terms described in the `LICENSE.md` file, located in the root of the package, and at https://js.arcgis.com/5.1/LICENSE.txt.
12
- For third party notices, see https://js.arcgis.com/5.1/third-party-notices.txt.
11
+ This package is licensed under the terms described in the `LICENSE.md` file, located in the root of the package, and at https://js.arcgis.com/5.2/LICENSE.txt.
12
+ For third party notices, see https://js.arcgis.com/5.2/third-party-notices.txt.
package/dist/file.d.ts CHANGED
@@ -1,55 +1,9 @@
1
- import { ExecSyncOptionsWithStringEncoding, SpawnOptions, SpawnSyncOptionsWithStringEncoding } from 'node:child_process';
2
1
  /**
3
2
  * Asynchronously check if a file or directory exists.
4
3
  * Using un-promisified version because promises version creates exceptions
5
4
  * which interferes with debugging when "Pause on caught exceptions" is enabled
6
5
  */
7
6
  export declare const existsAsync: (file: string) => Promise<boolean>;
8
- /**
9
- * Synchronously execute a shell command and return the output.
10
- *
11
- * Prefer {@link sp} for most use cases. {@link sh} goes through a shell,
12
- * which requires careful escaping and can mis-handle dynamic input with spaces
13
- * or shell metacharacters.
14
- *
15
- * Use {@link sh} only when shell features are intentionally required, such as
16
- * pipes, redirects, `&&`, or command substitution.
17
- */
18
- export declare function sh(command: string, options?: Partial<ExecSyncOptionsWithStringEncoding>): string;
19
- /**
20
- * Asynchronously execute a shell command and return the output.
21
- *
22
- * Prefer {@link asyncSp} for most use cases. {@link asyncSh} executes through
23
- * a shell, so callers must handle shell escaping and quoting correctly.
24
- *
25
- * Use {@link asyncSh} only when shell syntax is intentionally needed.
26
- */
27
- export declare function asyncSh(command: string, options?: Partial<ExecSyncOptionsWithStringEncoding>): Promise<string>;
28
- /**
29
- * Synchronously execute a command without shell interpolation and return the output.
30
- *
31
- * This is usually safer than {@link sh} because arguments are passed as
32
- * discrete tokens, which avoids most shell escaping issues and command
33
- * injection pitfalls.
34
- *
35
- * Example:
36
- * `sp("git", ["show", `${baseRef}:pnpm-workspace.yaml`])`
37
- *
38
- * Avoid {@link sp} only when you explicitly need shell features.
39
- */
40
- export declare function sp(command: string, args: string[], options?: Partial<SpawnSyncOptionsWithStringEncoding>): string;
41
- /**
42
- * Asynchronously execute a command without shell interpolation and return the output.
43
- *
44
- * This is usually safer than {@link asyncSh} because arguments are not parsed
45
- * by a shell.
46
- *
47
- * Example:
48
- * `await asyncSp("pnpm", ["--filter=@arcgis/map-components", "list", "@arcgis/core", "--json"])`
49
- *
50
- * Prefer {@link asyncSp} by default for external command execution.
51
- */
52
- export declare function asyncSp(command: string, args: string[], options?: Partial<SpawnOptions>): Promise<string>;
53
7
  /**
54
8
  * Create a file with the specified content if it does not already exist.
55
9
  */
package/dist/file.js CHANGED
@@ -2,111 +2,10 @@ import { access, existsSync } from "node:fs";
2
2
  import { constants, mkdir, writeFile } from "node:fs/promises";
3
3
  import { dirname, resolve, sep, join } from "path";
4
4
  import { fileURLToPath } from "node:url";
5
- import { execSync, exec, spawnSync, spawn } from "node:child_process";
6
- import { styleText } from "node:util";
7
5
  const existsAsync = async (file) => (
8
6
  //#endregion existsAsync
9
7
  await new Promise((resolve2) => access(file, constants.F_OK, (error) => resolve2(!error)))
10
8
  );
11
- function sh(command, options = {}) {
12
- try {
13
- const normalizedOptions = { encoding: "utf8", ...options };
14
- return execSync(command.trim(), normalizedOptions).trim();
15
- } catch (error) {
16
- makeExecErrorReadable(error);
17
- throw error;
18
- }
19
- }
20
- async function asyncSh(command, options = {}) {
21
- const normalizedOptions = { encoding: "utf8", ...options };
22
- return await new Promise((resolve2, reject) => {
23
- exec(command.trim(), normalizedOptions, (error, stdout, stderr) => {
24
- if (error) {
25
- makeExecErrorReadable(error);
26
- reject(error);
27
- return;
28
- }
29
- resolve2(stdout.trim() || stderr.trim());
30
- });
31
- });
32
- }
33
- function sp(command, args, options = {}) {
34
- validatePnpmUsage(command, options);
35
- const normalizedOptions = { encoding: "utf8", ...options };
36
- const result = spawnSync(command, args, normalizedOptions);
37
- if (result.error) {
38
- throw result.error;
39
- }
40
- const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
41
- const exitCode = result.status ?? 0;
42
- if (exitCode !== 0) {
43
- throw new Error(
44
- `Command failed with exit code ${String(exitCode)}: ${command} ${args.join(" ")}
45
- ${output}`.trim()
46
- );
47
- }
48
- return output;
49
- }
50
- async function asyncSp(command, args, options = {}) {
51
- validatePnpmUsage(command, options);
52
- const child = spawn(command, args, options);
53
- const stdoutChunks = [];
54
- const stderrChunks = [];
55
- child.stdout?.on(
56
- "data",
57
- (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk))
58
- );
59
- child.stderr?.on(
60
- "data",
61
- (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk))
62
- );
63
- return await new Promise((resolve2, reject) => {
64
- child.on("error", (error) => reject(error));
65
- child.on("close", (code) => {
66
- const output = `${stdoutChunks.join("")}${stderrChunks.join("")}`.trim();
67
- const exitCode = code ?? 0;
68
- if (exitCode !== 0) {
69
- reject(
70
- new Error(
71
- `Command failed with exit code ${String(exitCode)}: ${command} ${args.join(" ")}
72
- ${output}`.trim()
73
- )
74
- );
75
- return;
76
- }
77
- resolve2(output);
78
- });
79
- });
80
- }
81
- function validatePnpmUsage(command, options) {
82
- if (command === "pnpm" && // Don't error for explicit shell:false to permit system-dependent handling
83
- !("shell" in options)) {
84
- throw Error(
85
- 'If invoking pnpm using sp/asyncSp, must provide {shell:true} or {shell:process.platform==="win32"} option and manually handle shell escaping'
86
- );
87
- }
88
- }
89
- function makeExecErrorReadable(error) {
90
- if (error instanceof Error && error.stack && "output" in error && Array.isArray(error.output) && "status" in error) {
91
- const stackIndex = error.stack.indexOf("\n at ");
92
- if (stackIndex !== -1) {
93
- const output = error.output.filter(Boolean).join("\n").trim();
94
- const newHeader = `${styleText("red", error.message)} (exit code: ${String(error.status)})
95
- ${output}`;
96
- const oldStackFrames = error.stack.substring(stackIndex);
97
- error.stack = `Error: ${newHeader}${oldStackFrames}`;
98
- }
99
- Object.defineProperties(error, {
100
- output: { enumerable: false },
101
- stdout: { enumerable: false },
102
- stderr: { enumerable: false },
103
- signal: { enumerable: false },
104
- status: { enumerable: false },
105
- pid: { enumerable: false },
106
- stdio: { enumerable: false }
107
- });
108
- }
109
- }
110
9
  async function createFileIfNotExists(filePath, content) {
111
10
  await mkdir(dirname(filePath), { recursive: true });
112
11
  if (!await existsAsync(filePath)) {
@@ -144,11 +43,7 @@ async function asyncFindPath(target, startDirectory = process.cwd()) {
144
43
  }
145
44
  export {
146
45
  asyncFindPath,
147
- asyncSh,
148
- asyncSp,
149
46
  createFileIfNotExists,
150
47
  existsAsync,
151
- findPath,
152
- sh,
153
- sp
48
+ findPath
154
49
  };
@@ -0,0 +1,76 @@
1
+ import { ExecSyncOptionsWithStringEncoding, SpawnSyncOptionsWithStringEncoding, SpawnOptions } from 'node:child_process';
2
+ type SyncProcessOptions = Omit<SpawnSyncOptionsWithStringEncoding, "stdio">;
3
+ type AsyncProcessOptions = Omit<SpawnOptions, "stdio"> & {
4
+ input?: NonNullable<SpawnSyncOptionsWithStringEncoding["input"]>;
5
+ };
6
+ /**
7
+ * Synchronously execute a shell command and return the output.
8
+ *
9
+ * Prefer {@link runCommandSync} for command-only execution, or {@link collectOutputSync}
10
+ * when you need to capture stdout. {@link sh} goes through a shell, which
11
+ * requires careful escaping and can mis-handle dynamic input with spaces or
12
+ * shell metacharacters.
13
+ *
14
+ * Use {@link sh} only when shell features are intentionally required, such as
15
+ * pipes, redirects, `&&`, or command substitution.
16
+ *
17
+ * Example (shell features required):
18
+ * `sh("pnpm list | grep @arcgis/core")`
19
+ */
20
+ export declare function sh(command: string, options?: Partial<ExecSyncOptionsWithStringEncoding>): string;
21
+ /**
22
+ * Asynchronously execute a shell command and return the output.
23
+ *
24
+ * Prefer {@link runCommand} for command-only execution, or {@link collectOutput}
25
+ * when you need to capture stdout. {@link asyncSh} executes through a shell,
26
+ * so callers must handle shell escaping and quoting correctly.
27
+ *
28
+ * Use {@link asyncSh} only when shell syntax is intentionally needed.
29
+ *
30
+ * Example (shell features required):
31
+ * `await asyncSh("pnpm list | grep @arcgis/core")`
32
+ */
33
+ export declare function asyncSh(command: string, options?: Partial<ExecSyncOptionsWithStringEncoding>): Promise<string>;
34
+ /**
35
+ * Synchronously execute a command without shell interpolation and return the output.
36
+ *
37
+ * This is usually safer than {@link sh} because arguments are passed as
38
+ * discrete tokens, which avoids most shell escaping issues and command
39
+ * injection pitfalls.
40
+ *
41
+ * Example:
42
+ * `sp("git", ["show", `${baseRef}:pnpm-workspace.yaml`])`
43
+ *
44
+ * Avoid {@link sp} only when you explicitly need shell features.
45
+ * @deprecated Use {@link collectOutputSync} or {@link runCommandSync} instead.
46
+ */
47
+ export declare function sp(command: string, args: string[], options?: Partial<SpawnSyncOptionsWithStringEncoding>): string;
48
+ /**
49
+ * Synchronously execute a command and stream output to the current process.
50
+ * Note 1:
51
+ * `pnpm` command will be automatically invoked with `shell: process.platform === "win32"`
52
+ * if the `shell` option is not explicitly provided.
53
+ * Note 2:
54
+ * If the `input` option is provided, it will be piped to the child process's stdin.
55
+ */
56
+ export declare function runCommandSync(command: string, args: string[], options?: Partial<SyncProcessOptions>): void;
57
+ /**
58
+ * Asynchronously execute a command and stream output to the current process.
59
+ * Note 1:
60
+ * `pnpm` command will be automatically invoked with `shell: process.platform === "win32"`
61
+ * if the `shell` option is not explicitly provided.
62
+ * Note 2:
63
+ * If the `input` option is provided, it will be piped to the child process's stdin.
64
+ */
65
+ export declare function runCommand(command: string, args: string[], options?: Partial<AsyncProcessOptions>): Promise<void>;
66
+ /**
67
+ * Synchronously execute a command and collect stdout while streaming stderr.
68
+ * Note that `pnpm` command will be automatically invoked with `shell: process.platform === "win32"`.
69
+ */
70
+ export declare function collectOutputSync(command: string, args: string[], options?: Partial<SyncProcessOptions>): string;
71
+ /**
72
+ * Asynchronously execute a command and collect stdout while streaming stderr.
73
+ * Note that `pnpm` command will be automatically invoked with `shell: process.platform === "win32"`.
74
+ */
75
+ export declare function collectOutput(command: string, args: string[], options?: Partial<AsyncProcessOptions>): Promise<string>;
76
+ export {};
package/dist/shell.js ADDED
@@ -0,0 +1,154 @@
1
+ import { exec, spawn, spawnSync, execSync } from "node:child_process";
2
+ import { styleText } from "node:util";
3
+ function sh(command, options = {}) {
4
+ try {
5
+ const normalizedOptions = { encoding: "utf8", ...options };
6
+ return execSync(command.trim(), normalizedOptions).trim();
7
+ } catch (error) {
8
+ makeExecErrorReadable(error);
9
+ throw error;
10
+ }
11
+ }
12
+ async function asyncSh(command, options = {}) {
13
+ const normalizedOptions = { encoding: "utf8", ...options };
14
+ return await new Promise((resolve, reject) => {
15
+ exec(command.trim(), normalizedOptions, (error, stdout, stderr) => {
16
+ if (error) {
17
+ makeExecErrorReadable(error);
18
+ reject(error);
19
+ return;
20
+ }
21
+ resolve(stdout.trim() || stderr.trim());
22
+ });
23
+ });
24
+ }
25
+ function makeExecErrorReadable(error) {
26
+ if (error instanceof Error && error.stack && "output" in error && Array.isArray(error.output) && "status" in error) {
27
+ const stackIndex = error.stack.indexOf("\n at ");
28
+ if (stackIndex !== -1) {
29
+ const output = error.output.filter(Boolean).join("\n").trim();
30
+ const newHeader = `${styleText("red", error.message)} (exit code: ${String(error.status)})
31
+ ${output}`;
32
+ const oldStackFrames = error.stack.substring(stackIndex);
33
+ error.stack = `Error: ${newHeader}${oldStackFrames}`;
34
+ }
35
+ Object.defineProperties(error, {
36
+ output: { enumerable: false },
37
+ stdout: { enumerable: false },
38
+ stderr: { enumerable: false },
39
+ signal: { enumerable: false },
40
+ status: { enumerable: false },
41
+ pid: { enumerable: false },
42
+ stdio: { enumerable: false }
43
+ });
44
+ }
45
+ }
46
+ function sp(command, args, options = {}) {
47
+ if (process.platform === "win32" && command === "pnpm" && // Allow explicit shell config including shell:false for environment-specific handling.
48
+ !("shell" in options)) {
49
+ throw Error(
50
+ 'If invoking pnpm on Windows, provide { shell: true } or { shell: process.platform === "win32" } and handle shell escaping.'
51
+ );
52
+ }
53
+ const normalizedOptions = { encoding: "utf8", ...options };
54
+ const result = spawnSync(command, args, normalizedOptions);
55
+ if (result.error) {
56
+ throw result.error;
57
+ }
58
+ const sep = result.stdout && result.stderr ? "\n" : "";
59
+ const output = `${result.stdout ?? ""}${sep}${result.stderr ?? ""}`.trim();
60
+ const exitCode = result.status ?? 0;
61
+ if (exitCode !== 0) {
62
+ throw makeSpawnError(command, args, exitCode, result.signal, output);
63
+ }
64
+ return output;
65
+ }
66
+ function runCommandSync(command, args, options = {}) {
67
+ const fixedOptions = fixPnpmUsage(command, options);
68
+ const { input } = fixedOptions;
69
+ const stdio = input === void 0 ? "inherit" : ["pipe", "inherit", "inherit"];
70
+ const result = spawnSync(command, args, { ...fixedOptions, stdio });
71
+ assertSyncResultSuccess(command, args, result);
72
+ }
73
+ async function runCommand(command, args, options = {}) {
74
+ const fixedOptions = fixPnpmUsage(command, options);
75
+ const { input } = fixedOptions;
76
+ const stdio = input === void 0 ? "inherit" : ["pipe", "inherit", "inherit"];
77
+ const child = spawn(command, args, { ...fixedOptions, stdio });
78
+ if (input !== void 0) {
79
+ child.stdin?.end(input);
80
+ }
81
+ await new Promise((resolve, reject) => {
82
+ child.on("error", (error) => reject(error));
83
+ child.on("close", (code, signal) => {
84
+ if (code !== 0) {
85
+ reject(makeSpawnError(command, args, code, signal, ""));
86
+ return;
87
+ }
88
+ resolve();
89
+ });
90
+ });
91
+ }
92
+ function collectOutputSync(command, args, options = {}) {
93
+ const fixedOptions = fixPnpmUsage(command, options);
94
+ const result = spawnSync(command, args, {
95
+ encoding: "utf8",
96
+ ...fixedOptions,
97
+ stdio: ["ignore", "pipe", "inherit"]
98
+ });
99
+ assertSyncResultSuccess(command, args, result);
100
+ return (result.stdout ?? "").trim();
101
+ }
102
+ async function collectOutput(command, args, options = {}) {
103
+ const fixedOptions = fixPnpmUsage(command, options);
104
+ const child = spawn(command, args, {
105
+ ...fixedOptions,
106
+ stdio: ["ignore", "pipe", "inherit"]
107
+ });
108
+ const stdoutChunks = [];
109
+ child.stdout?.on(
110
+ "data",
111
+ (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk))
112
+ );
113
+ await new Promise((resolve, reject) => {
114
+ child.on("error", (error) => reject(error));
115
+ child.on("close", (code, signal) => {
116
+ if (code !== 0) {
117
+ reject(makeSpawnError(command, args, code, signal, ""));
118
+ return;
119
+ }
120
+ resolve();
121
+ });
122
+ });
123
+ return stdoutChunks.join("").trim();
124
+ }
125
+ function fixPnpmUsage(command, options) {
126
+ if (process.platform === "win32" && command === "pnpm") {
127
+ const fixedOptions = { shell: process.platform === "win32", ...options };
128
+ return fixedOptions;
129
+ }
130
+ return options;
131
+ }
132
+ function assertSyncResultSuccess(command, args, result) {
133
+ if (result.error) {
134
+ throw result.error;
135
+ }
136
+ if (result.status !== 0) {
137
+ throw makeSpawnError(command, args, result.status, result.signal, String(result.stderr ?? "").trim());
138
+ }
139
+ }
140
+ function makeSpawnError(command, args, code, signal, output) {
141
+ const commandText = `${command} ${args.join(" ")}`.trim();
142
+ const statusText = code !== null ? `exit code ${String(code)}` : `signal ${signal ?? "unknown"}`;
143
+ return new Error(`Command failed with ${statusText}: ${commandText}
144
+ ${output}`.trim());
145
+ }
146
+ export {
147
+ asyncSh,
148
+ collectOutput,
149
+ collectOutputSync,
150
+ runCommand,
151
+ runCommandSync,
152
+ sh,
153
+ sp
154
+ };
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@arcgis/node-toolkit",
3
- "version": "5.2.0-next.51",
3
+ "version": "5.2.0-next.53",
4
4
  "description": "Collection of common internal build-time patterns and utilities for ArcGIS Maps SDK for JavaScript components.",
5
5
  "homepage": "https://developers.arcgis.com/javascript/latest/",
6
6
  "type": "module",
7
7
  "exports": {
8
8
  "./file": "./dist/file.js",
9
+ "./shell": "./dist/shell.js",
9
10
  "./glob": "./dist/glob.js",
10
11
  "./path": "./dist/path.js",
11
12
  "./packageJson": "./dist/packageJson.js",