@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/README.md +40 -4
- package/dist/features/fs-watcher.d.ts +25 -1
- package/dist/features/fs-watcher.d.ts.map +1 -1
- package/dist/features/fs-watcher.js +100 -10
- package/dist/features/fs-watcher.js.map +1 -1
- package/dist/utils/cp.d.ts +16 -16
- package/dist/utils/cp.d.ts.map +1 -1
- package/dist/utils/cp.js +41 -44
- package/dist/utils/cp.js.map +1 -1
- package/dist/utils/fs.d.ts +4 -4
- package/dist/utils/fs.d.ts.map +1 -1
- package/dist/utils/fs.js +10 -11
- package/dist/utils/fs.js.map +1 -1
- package/docs/cpx.md +173 -0
- package/docs/fs-watcher.md +2 -2
- package/docs/pathx.md +21 -22
- package/package.json +3 -3
- package/src/features/fs-watcher.ts +124 -13
- package/src/utils/cp.ts +67 -65
- package/src/utils/fs.ts +13 -11
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
|
|
56
|
+
//#region spawn types
|
|
57
57
|
|
|
58
|
-
export interface
|
|
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
|
|
98
|
+
//#region SpawnProcess
|
|
95
99
|
|
|
96
|
-
export class
|
|
100
|
+
export class SpawnProcess implements PromiseLike<SpawnResult> {
|
|
97
101
|
private readonly _process: ChildProcess;
|
|
98
|
-
private readonly _promise: Promise<
|
|
102
|
+
private readonly _promise: Promise<SpawnResult>;
|
|
99
103
|
|
|
100
|
-
constructor(cp: ChildProcess, promise: Promise<
|
|
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 =
|
|
110
|
-
onfulfilled?: ((value:
|
|
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<
|
|
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
|
|
133
|
+
//#region spawn / spawnSync
|
|
130
134
|
|
|
131
|
-
export function
|
|
132
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
163
|
-
|
|
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:
|
|
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(
|
|
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
|
|
175
|
+
return new SpawnProcess(cp, promise);
|
|
180
176
|
}
|
|
181
177
|
|
|
182
|
-
export function
|
|
183
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
* 파일을
|
|
235
|
+
* 파일을 Uint8Array로 읽는다.
|
|
236
236
|
* @param targetPath - 읽을 파일 경로
|
|
237
237
|
*/
|
|
238
|
-
export function
|
|
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
|
-
* 파일을
|
|
247
|
+
* 파일을 Uint8Array로 읽는다 (비동기).
|
|
248
248
|
* @param targetPath - 읽을 파일 경로
|
|
249
249
|
*/
|
|
250
|
-
export async function
|
|
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
|
|
543
|
+
const dirs: string[] = [];
|
|
544
544
|
|
|
545
545
|
let current = fromPath;
|
|
546
546
|
while (current) {
|
|
547
|
-
|
|
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
|
-
|
|
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
|