@simplysm/core-node 13.0.100 → 14.0.4

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.
Files changed (44) hide show
  1. package/README.md +93 -79
  2. package/dist/features/fs-watcher.d.ts +21 -21
  3. package/dist/features/fs-watcher.d.ts.map +1 -1
  4. package/dist/features/fs-watcher.js +176 -114
  5. package/dist/features/fs-watcher.js.map +1 -6
  6. package/dist/index.js +6 -7
  7. package/dist/index.js.map +1 -6
  8. package/dist/utils/fs.d.ts +96 -96
  9. package/dist/utils/fs.d.ts.map +1 -1
  10. package/dist/utils/fs.js +437 -272
  11. package/dist/utils/fs.js.map +1 -6
  12. package/dist/utils/path.d.ts +22 -22
  13. package/dist/utils/path.js +103 -45
  14. package/dist/utils/path.js.map +1 -6
  15. package/dist/worker/create-worker.d.ts +3 -3
  16. package/dist/worker/create-worker.js +106 -81
  17. package/dist/worker/create-worker.js.map +1 -6
  18. package/dist/worker/types.d.ts +14 -14
  19. package/dist/worker/types.js +4 -1
  20. package/dist/worker/types.js.map +1 -6
  21. package/dist/worker/worker.d.ts +5 -5
  22. package/dist/worker/worker.js +168 -132
  23. package/dist/worker/worker.js.map +1 -6
  24. package/docs/fs-watcher.md +107 -0
  25. package/docs/fsx.md +287 -0
  26. package/docs/pathx.md +115 -0
  27. package/docs/worker.md +117 -62
  28. package/lib/worker-dev-proxy.js +15 -0
  29. package/package.json +9 -6
  30. package/src/features/fs-watcher.ts +53 -42
  31. package/src/index.ts +3 -3
  32. package/src/utils/fs.ts +111 -120
  33. package/src/utils/path.ts +26 -26
  34. package/src/worker/create-worker.ts +10 -10
  35. package/src/worker/types.ts +14 -14
  36. package/src/worker/worker.ts +29 -29
  37. package/docs/features.md +0 -91
  38. package/docs/fs.md +0 -309
  39. package/docs/path.md +0 -120
  40. package/tests/utils/fs-watcher.spec.ts +0 -286
  41. package/tests/utils/fs.spec.ts +0 -705
  42. package/tests/utils/path.spec.ts +0 -179
  43. package/tests/worker/fixtures/test-worker.ts +0 -35
  44. package/tests/worker/sd-worker.spec.ts +0 -174
package/dist/utils/fs.js CHANGED
@@ -4,308 +4,473 @@ import os from "os";
4
4
  import { glob as globRaw, globSync as globRawSync } from "glob";
5
5
  import { json, SdError } from "@simplysm/core-common";
6
6
  import "@simplysm/core-common";
7
- function existsSync(targetPath) {
8
- return fs.existsSync(targetPath);
7
+ //#region 존재 여부 확인
8
+ /**
9
+ * 파일 또는 디렉토리가 존재하는지 확인한다 (동기).
10
+ * @param targetPath - 확인할 경로
11
+ */
12
+ export function existsSync(targetPath) {
13
+ return fs.existsSync(targetPath);
9
14
  }
10
- async function exists(targetPath) {
11
- try {
12
- await fs.promises.access(targetPath);
13
- return true;
14
- } catch {
15
- return false;
16
- }
15
+ /**
16
+ * 파일 또는 디렉토리가 존재하는지 확인한다 (비동기).
17
+ * @param targetPath - 확인할 경로
18
+ */
19
+ export async function exists(targetPath) {
20
+ try {
21
+ await fs.promises.access(targetPath);
22
+ return true;
23
+ }
24
+ catch {
25
+ return false;
26
+ }
17
27
  }
18
- function mkdirSync(targetPath) {
19
- try {
20
- fs.mkdirSync(targetPath, { recursive: true });
21
- } catch (err) {
22
- throw new SdError(err, targetPath);
23
- }
28
+ //#endregion
29
+ //#region 디렉토리 생성
30
+ /**
31
+ * 디렉토리를 생성한다 (재귀적).
32
+ * @param targetPath - 생성할 디렉토리 경로
33
+ */
34
+ export function mkdirSync(targetPath) {
35
+ try {
36
+ fs.mkdirSync(targetPath, { recursive: true });
37
+ }
38
+ catch (err) {
39
+ throw new SdError(err, targetPath);
40
+ }
24
41
  }
25
- async function mkdir(targetPath) {
26
- try {
27
- await fs.promises.mkdir(targetPath, { recursive: true });
28
- } catch (err) {
29
- throw new SdError(err, targetPath);
30
- }
42
+ /**
43
+ * 디렉토리를 생성한다 (재귀적, 비동기).
44
+ * @param targetPath - 생성할 디렉토리 경로
45
+ */
46
+ export async function mkdir(targetPath) {
47
+ try {
48
+ await fs.promises.mkdir(targetPath, { recursive: true });
49
+ }
50
+ catch (err) {
51
+ throw new SdError(err, targetPath);
52
+ }
31
53
  }
32
- function rmSync(targetPath) {
33
- try {
34
- fs.rmSync(targetPath, { recursive: true, force: true });
35
- } catch (err) {
36
- throw new SdError(err, targetPath);
37
- }
54
+ //#endregion
55
+ //#region 삭제
56
+ /**
57
+ * 파일 또는 디렉토리를 삭제한다.
58
+ * @param targetPath - 삭제할 경로
59
+ * @remarks 동기 버전은 재시도 없이 즉시 실패한다. 파일 잠금 등 일시적 오류가 발생할 수 있는 경우 rm을 사용하라.
60
+ */
61
+ export function rmSync(targetPath) {
62
+ try {
63
+ fs.rmSync(targetPath, { recursive: true, force: true });
64
+ }
65
+ catch (err) {
66
+ throw new SdError(err, targetPath);
67
+ }
38
68
  }
39
- async function rm(targetPath) {
40
- try {
41
- await fs.promises.rm(targetPath, {
42
- recursive: true,
43
- force: true,
44
- retryDelay: 500,
45
- maxRetries: 6
46
- });
47
- } catch (err) {
48
- throw new SdError(err, targetPath);
49
- }
69
+ /**
70
+ * 파일 또는 디렉토리를 삭제한다 (비동기).
71
+ * @param targetPath - 삭제할 경로
72
+ * @remarks 비동기 버전은 파일 잠금 등 일시적 오류에 대해 최대 6회(500ms 간격) 재시도한다.
73
+ */
74
+ export async function rm(targetPath) {
75
+ try {
76
+ await fs.promises.rm(targetPath, {
77
+ recursive: true,
78
+ force: true,
79
+ retryDelay: 500,
80
+ maxRetries: 6,
81
+ });
82
+ }
83
+ catch (err) {
84
+ throw new SdError(err, targetPath);
85
+ }
50
86
  }
51
87
  function collectCopyEntries(sourcePath, targetPath, children, filter) {
52
- const entries = [];
53
- for (const childPath of children) {
54
- if (filter !== void 0 && !filter(childPath)) {
55
- continue;
56
- }
57
- const relativeChildPath = path.relative(sourcePath, childPath);
58
- const childTargetPath = path.resolve(targetPath, relativeChildPath);
59
- entries.push({ sourcePath: childPath, targetPath: childTargetPath });
60
- }
61
- return entries;
88
+ const entries = [];
89
+ for (const childPath of children) {
90
+ if (filter !== undefined && !filter(childPath)) {
91
+ continue;
92
+ }
93
+ const relativeChildPath = path.relative(sourcePath, childPath);
94
+ const childTargetPath = path.resolve(targetPath, relativeChildPath);
95
+ entries.push({ sourcePath: childPath, targetPath: childTargetPath });
96
+ }
97
+ return entries;
62
98
  }
63
- function copySync(sourcePath, targetPath, filter) {
64
- if (!existsSync(sourcePath)) {
65
- return;
66
- }
67
- let stats;
68
- try {
69
- stats = fs.lstatSync(sourcePath);
70
- } catch (err) {
71
- throw new SdError(err, sourcePath);
72
- }
73
- if (stats.isDirectory()) {
74
- mkdirSync(targetPath);
75
- const children = globSync(path.resolve(sourcePath, "*"), { dot: true });
76
- for (const entry of collectCopyEntries(sourcePath, targetPath, children, filter)) {
77
- copySync(entry.sourcePath, entry.targetPath, filter);
78
- }
79
- } else {
80
- mkdirSync(path.dirname(targetPath));
81
- try {
82
- fs.copyFileSync(sourcePath, targetPath);
83
- } catch (err) {
84
- throw new SdError(err, targetPath);
99
+ /**
100
+ * 파일 또는 디렉토리를 복사한다.
101
+ *
102
+ * sourcePath가 존재하지 않으면 아무 작업도 수행하지 않고 반환한다.
103
+ *
104
+ * @param sourcePath 복사할 원본 경로
105
+ * @param targetPath 복사 대상 경로
106
+ * @param filter 복사 여부를 결정하는 필터 함수.
107
+ * 각 파일/디렉토리의 **절대 경로**가 전달된다.
108
+ * true를 반환하면 복사, false를 반환하면 제외한다.
109
+ * **주의**: 최상위 sourcePath는 필터링 대상이 아니며,
110
+ * 필터 함수는 모든 하위 항목(직접 및 간접)에 재귀적으로 적용된다.
111
+ * 디렉토리에 대해 false를 반환하면 해당 디렉토리와 모든 내용을 건너뛴다.
112
+ */
113
+ export function copySync(sourcePath, targetPath, filter) {
114
+ if (!existsSync(sourcePath)) {
115
+ return;
116
+ }
117
+ const stats = lstatSync(sourcePath);
118
+ if (stats.isDirectory()) {
119
+ mkdirSync(targetPath);
120
+ const children = globSync(path.resolve(sourcePath, "*"), { dot: true });
121
+ for (const entry of collectCopyEntries(sourcePath, targetPath, children, filter)) {
122
+ copySync(entry.sourcePath, entry.targetPath, filter);
123
+ }
124
+ }
125
+ else {
126
+ mkdirSync(path.dirname(targetPath));
127
+ try {
128
+ fs.copyFileSync(sourcePath, targetPath);
129
+ }
130
+ catch (err) {
131
+ throw new SdError(err, targetPath);
132
+ }
85
133
  }
86
- }
87
134
  }
88
- async function copy(sourcePath, targetPath, filter) {
89
- if (!await exists(sourcePath)) {
90
- return;
91
- }
92
- let stats;
93
- try {
94
- stats = await fs.promises.lstat(sourcePath);
95
- } catch (err) {
96
- throw new SdError(err, sourcePath);
97
- }
98
- if (stats.isDirectory()) {
99
- await mkdir(targetPath);
100
- const children = await glob(path.resolve(sourcePath, "*"), { dot: true });
101
- await collectCopyEntries(sourcePath, targetPath, children, filter).parallelAsync(async (entry) => {
102
- await copy(entry.sourcePath, entry.targetPath, filter);
103
- });
104
- } else {
105
- await mkdir(path.dirname(targetPath));
106
- try {
107
- await fs.promises.copyFile(sourcePath, targetPath);
108
- } catch (err) {
109
- throw new SdError(err, targetPath);
135
+ /**
136
+ * 파일 또는 디렉토리를 복사한다 (비동기).
137
+ *
138
+ * sourcePath가 존재하지 않으면 아무 작업도 수행하지 않고 반환한다.
139
+ *
140
+ * @param sourcePath 복사할 원본 경로
141
+ * @param targetPath 복사 대상 경로
142
+ * @param filter 복사 여부를 결정하는 필터 함수.
143
+ * 각 파일/디렉토리의 **절대 경로**가 전달된다.
144
+ * true를 반환하면 복사, false를 반환하면 제외한다.
145
+ * **주의**: 최상위 sourcePath는 필터링 대상이 아니며,
146
+ * 필터 함수는 모든 하위 항목(직접 및 간접)에 재귀적으로 적용된다.
147
+ * 디렉토리에 대해 false를 반환하면 해당 디렉토리와 모든 내용을 건너뛴다.
148
+ */
149
+ export async function copy(sourcePath, targetPath, filter) {
150
+ if (!(await exists(sourcePath))) {
151
+ return;
152
+ }
153
+ const stats = await lstat(sourcePath);
154
+ if (stats.isDirectory()) {
155
+ await mkdir(targetPath);
156
+ const children = await glob(path.resolve(sourcePath, "*"), { dot: true });
157
+ await collectCopyEntries(sourcePath, targetPath, children, filter)
158
+ .parallelAsync(async (entry) => {
159
+ await copy(entry.sourcePath, entry.targetPath, filter);
160
+ });
161
+ }
162
+ else {
163
+ await mkdir(path.dirname(targetPath));
164
+ try {
165
+ await fs.promises.copyFile(sourcePath, targetPath);
166
+ }
167
+ catch (err) {
168
+ throw new SdError(err, targetPath);
169
+ }
110
170
  }
111
- }
112
171
  }
113
- function readSync(targetPath) {
114
- try {
115
- return fs.readFileSync(targetPath, "utf-8");
116
- } catch (err) {
117
- throw new SdError(err, targetPath);
118
- }
172
+ //#endregion
173
+ //#region 파일 읽기
174
+ /**
175
+ * 파일을 UTF-8 문자열로 읽는다.
176
+ * @param targetPath - 읽을 파일 경로
177
+ */
178
+ export function readSync(targetPath) {
179
+ try {
180
+ return fs.readFileSync(targetPath, "utf-8");
181
+ }
182
+ catch (err) {
183
+ throw new SdError(err, targetPath);
184
+ }
119
185
  }
120
- async function read(targetPath) {
121
- try {
122
- return await fs.promises.readFile(targetPath, "utf-8");
123
- } catch (err) {
124
- throw new SdError(err, targetPath);
125
- }
186
+ /**
187
+ * 파일을 UTF-8 문자열로 읽는다 (비동기).
188
+ * @param targetPath - 읽을 파일 경로
189
+ */
190
+ export async function read(targetPath) {
191
+ try {
192
+ return await fs.promises.readFile(targetPath, "utf-8");
193
+ }
194
+ catch (err) {
195
+ throw new SdError(err, targetPath);
196
+ }
126
197
  }
127
- function readBufferSync(targetPath) {
128
- try {
129
- return fs.readFileSync(targetPath);
130
- } catch (err) {
131
- throw new SdError(err, targetPath);
132
- }
198
+ /**
199
+ * 파일을 Buffer로 읽는다.
200
+ * @param targetPath - 읽을 파일 경로
201
+ */
202
+ export function readBufferSync(targetPath) {
203
+ try {
204
+ return fs.readFileSync(targetPath);
205
+ }
206
+ catch (err) {
207
+ throw new SdError(err, targetPath);
208
+ }
133
209
  }
134
- async function readBuffer(targetPath) {
135
- try {
136
- return await fs.promises.readFile(targetPath);
137
- } catch (err) {
138
- throw new SdError(err, targetPath);
139
- }
210
+ /**
211
+ * 파일을 Buffer로 읽는다 (비동기).
212
+ * @param targetPath - 읽을 파일 경로
213
+ */
214
+ export async function readBuffer(targetPath) {
215
+ try {
216
+ return await fs.promises.readFile(targetPath);
217
+ }
218
+ catch (err) {
219
+ throw new SdError(err, targetPath);
220
+ }
140
221
  }
141
- function readJsonSync(targetPath) {
142
- const contents = readSync(targetPath);
143
- try {
144
- return json.parse(contents);
145
- } catch (err) {
146
- const preview = contents.length > 500 ? contents.slice(0, 500) + "...(truncated)" : contents;
147
- throw new SdError(err, targetPath + os.EOL + preview);
148
- }
222
+ /**
223
+ * JSON 파일을 읽는다 (JsonConvert 사용).
224
+ * @param targetPath - 읽을 JSON 파일 경로
225
+ */
226
+ export function readJsonSync(targetPath) {
227
+ const contents = readSync(targetPath);
228
+ try {
229
+ return json.parse(contents);
230
+ }
231
+ catch (err) {
232
+ const preview = contents.length > 500 ? contents.slice(0, 500) + "...(truncated)" : contents;
233
+ throw new SdError(err, targetPath + os.EOL + preview);
234
+ }
149
235
  }
150
- async function readJson(targetPath) {
151
- const contents = await read(targetPath);
152
- try {
153
- return json.parse(contents);
154
- } catch (err) {
155
- const preview = contents.length > 500 ? contents.slice(0, 500) + "...(truncated)" : contents;
156
- throw new SdError(err, targetPath + os.EOL + preview);
157
- }
236
+ /**
237
+ * JSON 파일을 읽는다 (JsonConvert 사용, 비동기).
238
+ * @param targetPath - 읽을 JSON 파일 경로
239
+ */
240
+ export async function readJson(targetPath) {
241
+ const contents = await read(targetPath);
242
+ try {
243
+ return json.parse(contents);
244
+ }
245
+ catch (err) {
246
+ const preview = contents.length > 500 ? contents.slice(0, 500) + "...(truncated)" : contents;
247
+ throw new SdError(err, targetPath + os.EOL + preview);
248
+ }
158
249
  }
159
- function writeSync(targetPath, data) {
160
- mkdirSync(path.dirname(targetPath));
161
- try {
162
- fs.writeFileSync(targetPath, data, { flush: true });
163
- } catch (err) {
164
- throw new SdError(err, targetPath);
165
- }
250
+ //#endregion
251
+ //#region 파일 쓰기
252
+ /**
253
+ * 파일에 데이터를 쓴다 (상위 디렉토리 자동 생성).
254
+ * @param targetPath - 쓸 파일 경로
255
+ * @param data - 쓸 데이터 (문자열 또는 바이너리)
256
+ */
257
+ export function writeSync(targetPath, data) {
258
+ mkdirSync(path.dirname(targetPath));
259
+ try {
260
+ fs.writeFileSync(targetPath, data, { flush: true });
261
+ }
262
+ catch (err) {
263
+ throw new SdError(err, targetPath);
264
+ }
166
265
  }
167
- async function write(targetPath, data) {
168
- await mkdir(path.dirname(targetPath));
169
- try {
170
- await fs.promises.writeFile(targetPath, data, { flush: true });
171
- } catch (err) {
172
- throw new SdError(err, targetPath);
173
- }
266
+ /**
267
+ * 파일에 데이터를 쓴다 (상위 디렉토리 자동 생성, 비동기).
268
+ * @param targetPath - 쓸 파일 경로
269
+ * @param data - 데이터 (문자열 또는 바이너리)
270
+ */
271
+ export async function write(targetPath, data) {
272
+ await mkdir(path.dirname(targetPath));
273
+ try {
274
+ await fs.promises.writeFile(targetPath, data, { flush: true });
275
+ }
276
+ catch (err) {
277
+ throw new SdError(err, targetPath);
278
+ }
174
279
  }
175
- function writeJsonSync(targetPath, data, options) {
176
- const jsonStr = json.stringify(data, options);
177
- writeSync(targetPath, jsonStr);
280
+ /**
281
+ * JSON 파일에 데이터를 쓴다 (JsonConvert 사용).
282
+ * @param targetPath - 쓸 JSON 파일 경로
283
+ * @param data - 쓸 데이터
284
+ * @param options - JSON 직렬화 옵션
285
+ */
286
+ export function writeJsonSync(targetPath, data, options) {
287
+ const jsonStr = json.stringify(data, options);
288
+ writeSync(targetPath, jsonStr);
178
289
  }
179
- async function writeJson(targetPath, data, options) {
180
- const jsonStr = json.stringify(data, options);
181
- await write(targetPath, jsonStr);
290
+ /**
291
+ * JSON 파일에 데이터를 쓴다 (JsonConvert 사용, 비동기).
292
+ * @param targetPath - 쓸 JSON 파일 경로
293
+ * @param data - 쓸 데이터
294
+ * @param options - JSON 직렬화 옵션
295
+ */
296
+ export async function writeJson(targetPath, data, options) {
297
+ const jsonStr = json.stringify(data, options);
298
+ await write(targetPath, jsonStr);
182
299
  }
183
- function readdirSync(targetPath) {
184
- try {
185
- return fs.readdirSync(targetPath);
186
- } catch (err) {
187
- throw new SdError(err, targetPath);
188
- }
300
+ //#endregion
301
+ //#region 디렉토리 읽기
302
+ /**
303
+ * 디렉토리의 내용을 읽는다.
304
+ * @param targetPath - 읽을 디렉토리 경로
305
+ */
306
+ export function readdirSync(targetPath) {
307
+ try {
308
+ return fs.readdirSync(targetPath);
309
+ }
310
+ catch (err) {
311
+ throw new SdError(err, targetPath);
312
+ }
189
313
  }
190
- async function readdir(targetPath) {
191
- try {
192
- return await fs.promises.readdir(targetPath);
193
- } catch (err) {
194
- throw new SdError(err, targetPath);
195
- }
314
+ /**
315
+ * 디렉토리의 내용을 읽는다 (비동기).
316
+ * @param targetPath - 읽을 디렉토리 경로
317
+ */
318
+ export async function readdir(targetPath) {
319
+ try {
320
+ return await fs.promises.readdir(targetPath);
321
+ }
322
+ catch (err) {
323
+ throw new SdError(err, targetPath);
324
+ }
196
325
  }
197
- function statSync(targetPath) {
198
- try {
199
- return fs.statSync(targetPath);
200
- } catch (err) {
201
- throw new SdError(err, targetPath);
202
- }
326
+ //#endregion
327
+ //#region 파일 정보
328
+ /**
329
+ * 파일/디렉토리 정보를 가져온다 (심볼릭 링크를 따라감).
330
+ * @param targetPath - 정보를 조회할 경로
331
+ */
332
+ export function statSync(targetPath) {
333
+ try {
334
+ return fs.statSync(targetPath);
335
+ }
336
+ catch (err) {
337
+ throw new SdError(err, targetPath);
338
+ }
203
339
  }
204
- async function stat(targetPath) {
205
- try {
206
- return await fs.promises.stat(targetPath);
207
- } catch (err) {
208
- throw new SdError(err, targetPath);
209
- }
340
+ /**
341
+ * 파일/디렉토리 정보를 가져온다 (심볼릭 링크를 따라감, 비동기).
342
+ * @param targetPath - 정보를 조회할 경로
343
+ */
344
+ export async function stat(targetPath) {
345
+ try {
346
+ return await fs.promises.stat(targetPath);
347
+ }
348
+ catch (err) {
349
+ throw new SdError(err, targetPath);
350
+ }
210
351
  }
211
- function lstatSync(targetPath) {
212
- try {
213
- return fs.lstatSync(targetPath);
214
- } catch (err) {
215
- throw new SdError(err, targetPath);
216
- }
352
+ /**
353
+ * 파일/디렉토리 정보를 가져온다 (심볼릭 링크를 따라가지 않음).
354
+ * @param targetPath - 정보를 조회할 경로
355
+ */
356
+ export function lstatSync(targetPath) {
357
+ try {
358
+ return fs.lstatSync(targetPath);
359
+ }
360
+ catch (err) {
361
+ throw new SdError(err, targetPath);
362
+ }
217
363
  }
218
- async function lstat(targetPath) {
219
- try {
220
- return await fs.promises.lstat(targetPath);
221
- } catch (err) {
222
- throw new SdError(err, targetPath);
223
- }
364
+ /**
365
+ * 파일/디렉토리 정보를 가져온다 (심볼릭 링크를 따라가지 않음, 비동기).
366
+ * @param targetPath - 정보를 조회할 경로
367
+ */
368
+ export async function lstat(targetPath) {
369
+ try {
370
+ return await fs.promises.lstat(targetPath);
371
+ }
372
+ catch (err) {
373
+ throw new SdError(err, targetPath);
374
+ }
224
375
  }
225
- function globSync(pattern, options) {
226
- return globRawSync(pattern.replace(/\\/g, "/"), options ?? {}).map(
227
- (item) => path.resolve(item.toString())
228
- );
376
+ //#endregion
377
+ //#region Glob
378
+ /**
379
+ * Glob 패턴을 사용하여 파일을 검색한다.
380
+ * @param pattern - Glob 패턴 (예: "**\/*.ts")
381
+ * @param options - glob 옵션
382
+ * @returns 매칭된 파일의 절대 경로 배열
383
+ */
384
+ export function globSync(pattern, options) {
385
+ return globRawSync(pattern.replace(/\\/g, "/"), options ?? {}).map((item) => path.resolve(item.toString()));
229
386
  }
230
- async function glob(pattern, options) {
231
- return (await globRaw(pattern.replace(/\\/g, "/"), options ?? {})).map(
232
- (item) => path.resolve(item.toString())
233
- );
387
+ /**
388
+ * Glob 패턴을 사용하여 파일을 검색한다 (비동기).
389
+ * @param pattern - Glob 패턴 (예: "**\/*.ts")
390
+ * @param options - glob 옵션
391
+ * @returns 매칭된 파일의 절대 경로 배열
392
+ */
393
+ export async function glob(pattern, options) {
394
+ return (await globRaw(pattern.replace(/\\/g, "/"), options ?? {})).map((item) => path.resolve(item.toString()));
234
395
  }
235
- async function clearEmptyDirectory(dirPath) {
236
- if (!await exists(dirPath)) return;
237
- const childNames = await readdir(dirPath);
238
- let hasFiles = false;
239
- for (const childName of childNames) {
240
- const childPath = path.resolve(dirPath, childName);
241
- if ((await lstat(childPath)).isDirectory()) {
242
- await clearEmptyDirectory(childPath);
243
- } else {
244
- hasFiles = true;
245
- }
246
- }
247
- if (hasFiles) return;
248
- if ((await readdir(dirPath)).length === 0) {
249
- await rm(dirPath);
250
- }
396
+ //#endregion
397
+ //#region 유틸리티
398
+ /**
399
+ * 지정된 디렉토리 하위의 빈 디렉토리를 재귀적으로 검색하여 삭제한다.
400
+ * 모든 하위 디렉토리가 삭제되어 상위 디렉토리가 비게 되면, 해당 디렉토리도 삭제된다.
401
+ */
402
+ export async function clearEmptyDirectory(dirPath) {
403
+ if (!(await exists(dirPath)))
404
+ return;
405
+ const childNames = await readdir(dirPath);
406
+ let hasFiles = false;
407
+ for (const childName of childNames) {
408
+ const childPath = path.resolve(dirPath, childName);
409
+ const childStat = await lstat(childPath);
410
+ if (childStat.isDirectory()) {
411
+ await clearEmptyDirectory(childPath);
412
+ }
413
+ else {
414
+ hasFiles = true;
415
+ }
416
+ }
417
+ // 파일이 있으면 삭제 불가
418
+ if (hasFiles)
419
+ return;
420
+ // 파일이 없는 경우에만 다시 확인 (하위 디렉토리가 삭제되었을 수 있음)
421
+ if ((await readdir(dirPath)).length === 0) {
422
+ await rm(dirPath);
423
+ }
251
424
  }
252
- function findAllParentChildPathsSync(childGlob, fromPath, rootPath) {
253
- const resultPaths = [];
254
- let current = fromPath;
255
- while (current) {
256
- const potential = path.resolve(current, childGlob);
257
- const globResults = globSync(potential);
258
- resultPaths.push(...globResults);
259
- if (current === rootPath) break;
260
- const next = path.dirname(current);
261
- if (next === current) break;
262
- current = next;
263
- }
264
- return resultPaths;
425
+ /**
426
+ * 시작 경로에서 루트 방향으로 부모 디렉토리를 순회하며 glob 패턴에 매칭되는 파일을 검색한다.
427
+ * 디렉토리에서 childGlob 패턴에 매칭되는 모든 파일 경로를 수집한다.
428
+ * @param childGlob - 각 디렉토리에서 검색할 glob 패턴
429
+ * @param fromPath - 검색을 시작할 경로
430
+ * @param rootPath - 검색을 중단할 경로 (지정하지 않으면 파일 시스템 루트까지 검색).
431
+ * **주의**: fromPath는 rootPath의 하위 경로여야 한다.
432
+ * 그렇지 않으면 파일 시스템 루트까지 검색한다.
433
+ */
434
+ export function findAllParentChildPathsSync(childGlob, fromPath, rootPath) {
435
+ const resultPaths = [];
436
+ let current = fromPath;
437
+ while (current) {
438
+ const potential = path.resolve(current, childGlob);
439
+ const globResults = globSync(potential);
440
+ resultPaths.push(...globResults);
441
+ if (current === rootPath)
442
+ break;
443
+ const next = path.dirname(current);
444
+ if (next === current)
445
+ break;
446
+ current = next;
447
+ }
448
+ return resultPaths;
265
449
  }
266
- async function findAllParentChildPaths(childGlob, fromPath, rootPath) {
267
- const resultPaths = [];
268
- let current = fromPath;
269
- while (current) {
270
- const potential = path.resolve(current, childGlob);
271
- const globResults = await glob(potential);
272
- resultPaths.push(...globResults);
273
- if (current === rootPath) break;
274
- const next = path.dirname(current);
275
- if (next === current) break;
276
- current = next;
277
- }
278
- return resultPaths;
450
+ /**
451
+ * 시작 경로에서 루트 방향으로 부모 디렉토리를 순회하며 glob 패턴에 매칭되는 파일을 검색한다 (비동기).
452
+ * 디렉토리에서 childGlob 패턴에 매칭되는 모든 파일 경로를 수집한다.
453
+ * @param childGlob - 각 디렉토리에서 검색할 glob 패턴
454
+ * @param fromPath - 검색을 시작할 경로
455
+ * @param rootPath - 검색을 중단할 경로 (지정하지 않으면 파일 시스템 루트까지 검색).
456
+ * **주의**: fromPath는 rootPath의 하위 경로여야 한다.
457
+ * 그렇지 않으면 파일 시스템 루트까지 검색한다.
458
+ */
459
+ export async function findAllParentChildPaths(childGlob, fromPath, rootPath) {
460
+ const resultPaths = [];
461
+ let current = fromPath;
462
+ while (current) {
463
+ const potential = path.resolve(current, childGlob);
464
+ const globResults = await glob(potential);
465
+ resultPaths.push(...globResults);
466
+ if (current === rootPath)
467
+ break;
468
+ const next = path.dirname(current);
469
+ if (next === current)
470
+ break;
471
+ current = next;
472
+ }
473
+ return resultPaths;
279
474
  }
280
- export {
281
- clearEmptyDirectory,
282
- copy,
283
- copySync,
284
- exists,
285
- existsSync,
286
- findAllParentChildPaths,
287
- findAllParentChildPathsSync,
288
- glob,
289
- globSync,
290
- lstat,
291
- lstatSync,
292
- mkdir,
293
- mkdirSync,
294
- read,
295
- readBuffer,
296
- readBufferSync,
297
- readJson,
298
- readJsonSync,
299
- readSync,
300
- readdir,
301
- readdirSync,
302
- rm,
303
- rmSync,
304
- stat,
305
- statSync,
306
- write,
307
- writeJson,
308
- writeJsonSync,
309
- writeSync
310
- };
311
- //# sourceMappingURL=fs.js.map
475
+ //#endregion
476
+ //# sourceMappingURL=fs.js.map