@simplysm/core-node 14.0.11 → 14.0.13

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/src/utils/cp.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ChildProcess } from "child_process";
1
+ import type { ChildProcess, SpawnOptions, SpawnSyncOptions } from "child_process";
2
2
  import { execSync as cpExecSync, spawn as cpSpawn, spawnSync as cpSpawnSync } from "child_process";
3
3
  import { bytes } from "@simplysm/core-common";
4
4
 
@@ -53,19 +53,9 @@ export function getSystemEncoding(): string {
53
53
  return _cachedEncoding;
54
54
  }
55
55
 
56
- //#region exec types
56
+ //#region spawn types
57
57
 
58
- export interface ExecOptions {
59
- cwd?: string;
60
- env?: Record<string, string>;
61
- stdio?: "pipe" | "inherit";
62
- shell?: boolean;
63
- reject?: boolean;
64
- }
65
-
66
- export type ExecSyncOptions = Omit<ExecOptions, "reject">;
67
-
68
- export interface ExecResult {
58
+ export interface SpawnResult {
69
59
  stdout: string;
70
60
  stderr: string;
71
61
  exitCode: number;
@@ -73,6 +63,20 @@ export interface ExecResult {
73
63
 
74
64
  //#endregion
75
65
 
66
+ //#region resolveStdioPipe
67
+
68
+ export function resolveStdioPipe(
69
+ stdio: SpawnOptions["stdio"],
70
+ ): { stdout: boolean; stderr: boolean } {
71
+ if (Array.isArray(stdio)) {
72
+ return { stdout: stdio[1] === "pipe", stderr: stdio[2] === "pipe" };
73
+ }
74
+ const isPipe = stdio === "pipe" || stdio == null;
75
+ return { stdout: isPipe, stderr: isPipe };
76
+ }
77
+
78
+ //#endregion
79
+
76
80
  //#region decodeBytes
77
81
 
78
82
  export function decodeBytes(raw: Uint8Array, systemEncoding?: string): string {
@@ -91,13 +95,13 @@ export function decodeBytes(raw: Uint8Array, systemEncoding?: string): string {
91
95
 
92
96
  //#endregion
93
97
 
94
- //#region ExecProcess
98
+ //#region SpawnProcess
95
99
 
96
- export class ExecProcess implements PromiseLike<ExecResult> {
100
+ export class SpawnProcess implements PromiseLike<SpawnResult> {
97
101
  private readonly _process: ChildProcess;
98
- private readonly _promise: Promise<ExecResult>;
102
+ private readonly _promise: Promise<SpawnResult>;
99
103
 
100
- constructor(cp: ChildProcess, promise: Promise<ExecResult>) {
104
+ constructor(cp: ChildProcess, promise: Promise<SpawnResult>) {
101
105
  this._process = cp;
102
106
  this._promise = promise;
103
107
  }
@@ -106,8 +110,8 @@ export class ExecProcess implements PromiseLike<ExecResult> {
106
110
  return this._process.pid;
107
111
  }
108
112
 
109
- then<TResult1 = ExecResult, TResult2 = never>(
110
- onfulfilled?: ((value: ExecResult) => TResult1 | PromiseLike<TResult1>) | null,
113
+ then<TResult1 = SpawnResult, TResult2 = never>(
114
+ onfulfilled?: ((value: SpawnResult) => TResult1 | PromiseLike<TResult1>) | null,
111
115
  onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,
112
116
  ): Promise<TResult1 | TResult2> {
113
117
  return this._promise.then(onfulfilled, onrejected);
@@ -115,7 +119,7 @@ export class ExecProcess implements PromiseLike<ExecResult> {
115
119
 
116
120
  catch<TResult = never>(
117
121
  onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null,
118
- ): Promise<ExecResult | TResult> {
122
+ ): Promise<SpawnResult | TResult> {
119
123
  return this._promise.catch(onrejected);
120
124
  }
121
125
 
@@ -126,77 +130,75 @@ export class ExecProcess implements PromiseLike<ExecResult> {
126
130
 
127
131
  //#endregion
128
132
 
129
- //#region exec / execSync
133
+ //#region spawn / spawnSync
130
134
 
131
- export function exec(cmd: string, args: string[], options?: ExecOptions): ExecProcess {
132
- const isInherit = options?.stdio === "inherit";
135
+ export function spawn(
136
+ cmd: string,
137
+ args: string[],
138
+ options?: SpawnOptions & { reject?: boolean },
139
+ ): SpawnProcess {
140
+ const opts: SpawnOptions = { stdio: "pipe", ...options, env: { ...process.env, ...options?.env } };
133
141
 
134
- const cp = cpSpawn(cmd, args, {
135
- cwd: options?.cwd,
136
- env: options?.env != null ? { ...process.env, ...options.env } : undefined,
137
- stdio: isInherit ? "inherit" : "pipe",
138
- shell: options?.shell ?? false,
139
- });
142
+ const cp = cpSpawn(cmd, args, opts);
140
143
 
141
- const promise = new Promise<ExecResult>((resolve, reject) => {
144
+ const { stdout: stdoutIsPipe, stderr: stderrIsPipe } = resolveStdioPipe(opts.stdio);
145
+
146
+ const promise = new Promise<SpawnResult>((resolve, reject) => {
142
147
  cp.on("error", (err) => {
143
- reject(Object.assign(err, { stdout: "", stderr: "", exitCode: 1 }));
148
+ reject(err);
144
149
  });
145
150
 
146
- if (isInherit) {
147
- cp.on("close", (code, signal) => {
148
- const exitCode = code ?? (signal != null ? 1 : 0);
149
- const result: ExecResult = { stdout: "", stderr: "", exitCode };
150
- if (exitCode !== 0 && options.reject !== false) {
151
- reject(Object.assign(new Error(`Command failed: ${cmd} ${args.join(" ")}`), result));
152
- } else {
153
- resolve(result);
154
- }
155
- });
156
- return;
157
- }
158
-
159
151
  const stdoutChunks: Uint8Array[] = [];
160
152
  const stderrChunks: Uint8Array[] = [];
161
153
 
162
- cp.stdout!.on("data", (chunk: Uint8Array) => stdoutChunks.push(chunk));
163
- cp.stderr!.on("data", (chunk: Uint8Array) => stderrChunks.push(chunk));
154
+ if (stdoutIsPipe) {
155
+ cp.stdout!.on("data", (chunk: Uint8Array) => stdoutChunks.push(chunk));
156
+ }
157
+ if (stderrIsPipe) {
158
+ cp.stderr!.on("data", (chunk: Uint8Array) => stderrChunks.push(chunk));
159
+ }
164
160
 
165
161
  cp.on("close", (code, signal) => {
166
162
  const exitCode = code ?? (signal != null ? 1 : 0);
167
- const stdout = decodeBytes(bytes.concat(stdoutChunks));
168
- const stderr = decodeBytes(bytes.concat(stderrChunks));
169
- const result: ExecResult = { stdout, stderr, exitCode };
163
+ const stdout = stdoutIsPipe ? decodeBytes(bytes.concat(stdoutChunks)) : "";
164
+ const stderr = stderrIsPipe ? decodeBytes(bytes.concat(stderrChunks)) : "";
165
+ const result: SpawnResult = { stdout, stderr, exitCode };
170
166
 
171
167
  if (exitCode !== 0 && options?.reject !== false) {
172
- reject(Object.assign(new Error(`Command failed: ${cmd} ${args.join(" ")}`), result));
168
+ reject(new Error(`Command failed: ${cmd} ${args.join(" ")}`));
173
169
  } else {
174
170
  resolve(result);
175
171
  }
176
172
  });
177
173
  });
178
174
 
179
- return new ExecProcess(cp, promise);
175
+ return new SpawnProcess(cp, promise);
180
176
  }
181
177
 
182
- export function execSync(cmd: string, args: string[], options?: ExecSyncOptions): ExecResult {
183
- const isInherit = options?.stdio === "inherit";
178
+ export function spawnSync(
179
+ cmd: string,
180
+ args: string[],
181
+ options?: SpawnSyncOptions & { reject?: boolean },
182
+ ): SpawnResult {
183
+ const opts: SpawnSyncOptions = {
184
+ stdio: "pipe",
185
+ ...options,
186
+ env: { ...process.env, ...options?.env },
187
+ };
184
188
 
185
- const result = cpSpawnSync(cmd, args, {
186
- cwd: options?.cwd,
187
- env: options?.env != null ? { ...process.env, ...options.env } : undefined,
188
- stdio: isInherit ? "inherit" : "pipe",
189
- shell: options?.shell ?? false,
190
- });
189
+ const result = cpSpawnSync(cmd, args, opts);
191
190
 
192
- if (isInherit) {
193
- return { stdout: "", stderr: "", exitCode: result.status ?? 0 };
194
- }
191
+ const { stdout: stdoutIsPipe, stderr: stderrIsPipe } = resolveStdioPipe(opts.stdio);
195
192
 
196
- const stdout = decodeBytes(result.stdout as Uint8Array);
197
- const stderr = decodeBytes(result.stderr as Uint8Array);
193
+ const stdout = stdoutIsPipe ? decodeBytes(result.stdout as Uint8Array) : "";
194
+ const stderr = stderrIsPipe ? decodeBytes(result.stderr as Uint8Array) : "";
195
+ const exitCode = result.status ?? 0;
196
+
197
+ if (exitCode !== 0 && options?.reject !== false) {
198
+ throw new Error(`Command failed: ${cmd} ${args.join(" ")}`);
199
+ }
198
200
 
199
- return { stdout, stderr, exitCode: result.status ?? 0 };
201
+ return { stdout, stderr, exitCode };
200
202
  }
201
203
 
202
204
  //#endregion
package/src/utils/fs.ts CHANGED
@@ -232,24 +232,24 @@ export async function read(targetPath: string): Promise<string> {
232
232
  }
233
233
 
234
234
  /**
235
- * 파일을 Buffer로 읽는다.
235
+ * 파일을 Uint8Array로 읽는다.
236
236
  * @param targetPath - 읽을 파일 경로
237
237
  */
238
- export function readBufferSync(targetPath: string): Buffer {
238
+ export function readBytesSync(targetPath: string): Uint8Array {
239
239
  try {
240
- return fs.readFileSync(targetPath);
240
+ return new Uint8Array(fs.readFileSync(targetPath));
241
241
  } catch (err) {
242
242
  throw new SdError(err, targetPath);
243
243
  }
244
244
  }
245
245
 
246
246
  /**
247
- * 파일을 Buffer로 읽는다 (비동기).
247
+ * 파일을 Uint8Array로 읽는다 (비동기).
248
248
  * @param targetPath - 읽을 파일 경로
249
249
  */
250
- export async function readBuffer(targetPath: string): Promise<Buffer> {
250
+ export async function readBytes(targetPath: string): Promise<Uint8Array> {
251
251
  try {
252
- return await fs.promises.readFile(targetPath);
252
+ return new Uint8Array(await fs.promises.readFile(targetPath));
253
253
  } catch (err) {
254
254
  throw new SdError(err, targetPath);
255
255
  }
@@ -540,13 +540,11 @@ export async function findAllParentChildPaths(
540
540
  fromPath: string,
541
541
  rootPath?: string,
542
542
  ): Promise<string[]> {
543
- const resultPaths: string[] = [];
543
+ const dirs: string[] = [];
544
544
 
545
545
  let current = fromPath;
546
546
  while (current) {
547
- const potential = path.resolve(current, childGlob);
548
- const globResults = await glob(potential);
549
- resultPaths.push(...globResults);
547
+ dirs.push(current);
550
548
 
551
549
  if (current === rootPath) break;
552
550
 
@@ -555,7 +553,11 @@ export async function findAllParentChildPaths(
555
553
  current = next;
556
554
  }
557
555
 
558
- return resultPaths;
556
+ const results = await Promise.all(
557
+ dirs.map((dir) => glob(path.resolve(dir, childGlob))),
558
+ );
559
+
560
+ return results.flat();
559
561
  }
560
562
 
561
563
  //#endregion