@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.
- package/README.md +93 -79
- package/dist/features/fs-watcher.d.ts +21 -21
- package/dist/features/fs-watcher.d.ts.map +1 -1
- package/dist/features/fs-watcher.js +176 -114
- package/dist/features/fs-watcher.js.map +1 -6
- package/dist/index.js +6 -7
- package/dist/index.js.map +1 -6
- package/dist/utils/fs.d.ts +96 -96
- package/dist/utils/fs.d.ts.map +1 -1
- package/dist/utils/fs.js +437 -272
- package/dist/utils/fs.js.map +1 -6
- package/dist/utils/path.d.ts +22 -22
- package/dist/utils/path.js +103 -45
- package/dist/utils/path.js.map +1 -6
- package/dist/worker/create-worker.d.ts +3 -3
- package/dist/worker/create-worker.js +106 -81
- package/dist/worker/create-worker.js.map +1 -6
- package/dist/worker/types.d.ts +14 -14
- package/dist/worker/types.js +4 -1
- package/dist/worker/types.js.map +1 -6
- package/dist/worker/worker.d.ts +5 -5
- package/dist/worker/worker.js +168 -132
- package/dist/worker/worker.js.map +1 -6
- package/docs/fs-watcher.md +107 -0
- package/docs/fsx.md +287 -0
- package/docs/pathx.md +115 -0
- package/docs/worker.md +117 -62
- package/lib/worker-dev-proxy.js +15 -0
- package/package.json +9 -6
- package/src/features/fs-watcher.ts +53 -42
- package/src/index.ts +3 -3
- package/src/utils/fs.ts +111 -120
- package/src/utils/path.ts +26 -26
- package/src/worker/create-worker.ts +10 -10
- package/src/worker/types.ts +14 -14
- package/src/worker/worker.ts +29 -29
- package/docs/features.md +0 -91
- package/docs/fs.md +0 -309
- package/docs/path.md +0 -120
- package/tests/utils/fs-watcher.spec.ts +0 -286
- package/tests/utils/fs.spec.ts +0 -705
- package/tests/utils/path.spec.ts +0 -179
- package/tests/worker/fixtures/test-worker.ts +0 -35
- 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
|
-
|
|
8
|
-
|
|
7
|
+
//#region 존재 여부 확인
|
|
8
|
+
/**
|
|
9
|
+
* 파일 또는 디렉토리가 존재하는지 확인한다 (동기).
|
|
10
|
+
* @param targetPath - 확인할 경로
|
|
11
|
+
*/
|
|
12
|
+
export function existsSync(targetPath) {
|
|
13
|
+
return fs.existsSync(targetPath);
|
|
9
14
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
-
|
|
281
|
-
|
|
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
|