@simplysm/core-node 13.0.100 → 14.0.1
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/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/lib/worker-dev-proxy.js +15 -0
- package/package.json +8 -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/README.md +0 -112
- package/docs/features.md +0 -91
- package/docs/fs.md +0 -309
- package/docs/path.md +0 -120
- package/docs/worker.md +0 -168
- 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/tests/utils/fs.spec.ts
DELETED
|
@@ -1,705 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
import os from "os";
|
|
5
|
-
import {
|
|
6
|
-
existsSync,
|
|
7
|
-
exists,
|
|
8
|
-
mkdirSync,
|
|
9
|
-
mkdir,
|
|
10
|
-
rmSync,
|
|
11
|
-
rm,
|
|
12
|
-
copySync,
|
|
13
|
-
copy,
|
|
14
|
-
readSync,
|
|
15
|
-
read,
|
|
16
|
-
readBufferSync,
|
|
17
|
-
readBuffer,
|
|
18
|
-
readJsonSync,
|
|
19
|
-
readJson,
|
|
20
|
-
writeSync,
|
|
21
|
-
write,
|
|
22
|
-
writeJsonSync,
|
|
23
|
-
writeJson,
|
|
24
|
-
readdirSync,
|
|
25
|
-
readdir,
|
|
26
|
-
statSync,
|
|
27
|
-
stat,
|
|
28
|
-
lstatSync,
|
|
29
|
-
lstat,
|
|
30
|
-
globSync,
|
|
31
|
-
glob,
|
|
32
|
-
clearEmptyDirectory,
|
|
33
|
-
findAllParentChildPathsSync,
|
|
34
|
-
findAllParentChildPaths,
|
|
35
|
-
} from "../../src/utils/fs";
|
|
36
|
-
import { SdError } from "@simplysm/core-common";
|
|
37
|
-
|
|
38
|
-
describe("fs functions", () => {
|
|
39
|
-
const testDir = path.join(os.tmpdir(), "fs-utils-test-" + Date.now());
|
|
40
|
-
|
|
41
|
-
beforeEach(() => {
|
|
42
|
-
fs.mkdirSync(testDir, { recursive: true });
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
afterEach(() => {
|
|
46
|
-
fs.rmSync(testDir, { recursive: true, force: true });
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
//#region exists
|
|
50
|
-
|
|
51
|
-
describe("existsSync", () => {
|
|
52
|
-
it("returns true for existing file", () => {
|
|
53
|
-
const filePath = path.join(testDir, "test.txt");
|
|
54
|
-
fs.writeFileSync(filePath, "test");
|
|
55
|
-
|
|
56
|
-
expect(existsSync(filePath)).toBe(true);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("returns false for nonexistent file", () => {
|
|
60
|
-
const filePath = path.join(testDir, "nonexistent.txt");
|
|
61
|
-
expect(existsSync(filePath)).toBe(false);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe("exists", () => {
|
|
67
|
-
it("returns true for existing file", async () => {
|
|
68
|
-
const filePath = path.join(testDir, "test.txt");
|
|
69
|
-
fs.writeFileSync(filePath, "test");
|
|
70
|
-
|
|
71
|
-
expect(await exists(filePath)).toBe(true);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("returns false for nonexistent file", async () => {
|
|
75
|
-
const filePath = path.join(testDir, "nonexistent.txt");
|
|
76
|
-
expect(await exists(filePath)).toBe(false);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
//#endregion
|
|
82
|
-
|
|
83
|
-
//#region mkdir
|
|
84
|
-
|
|
85
|
-
describe("mkdirSync", () => {
|
|
86
|
-
it("creates directory", () => {
|
|
87
|
-
const dirPath = path.join(testDir, "newdir");
|
|
88
|
-
mkdirSync(dirPath);
|
|
89
|
-
|
|
90
|
-
expect(fs.existsSync(dirPath)).toBe(true);
|
|
91
|
-
expect(fs.statSync(dirPath).isDirectory()).toBe(true);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("creates nested directories (recursive)", () => {
|
|
95
|
-
const dirPath = path.join(testDir, "a/b/c");
|
|
96
|
-
mkdirSync(dirPath);
|
|
97
|
-
|
|
98
|
-
expect(fs.existsSync(dirPath)).toBe(true);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("passes without error for existing directory", () => {
|
|
102
|
-
expect(() => mkdirSync(testDir)).not.toThrow();
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
describe("mkdir", () => {
|
|
107
|
-
it("creates directory asynchronously", async () => {
|
|
108
|
-
const dirPath = path.join(testDir, "asyncdir");
|
|
109
|
-
await mkdir(dirPath);
|
|
110
|
-
|
|
111
|
-
expect(fs.existsSync(dirPath)).toBe(true);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
//#endregion
|
|
116
|
-
|
|
117
|
-
//#region rm
|
|
118
|
-
|
|
119
|
-
describe("rmSync", () => {
|
|
120
|
-
it("deletes file", () => {
|
|
121
|
-
const filePath = path.join(testDir, "todelete.txt");
|
|
122
|
-
fs.writeFileSync(filePath, "test");
|
|
123
|
-
|
|
124
|
-
rmSync(filePath);
|
|
125
|
-
|
|
126
|
-
expect(fs.existsSync(filePath)).toBe(false);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it("deletes directory (recursive)", () => {
|
|
130
|
-
const dirPath = path.join(testDir, "todelete");
|
|
131
|
-
fs.mkdirSync(dirPath);
|
|
132
|
-
fs.writeFileSync(path.join(dirPath, "file.txt"), "test");
|
|
133
|
-
|
|
134
|
-
rmSync(dirPath);
|
|
135
|
-
|
|
136
|
-
expect(fs.existsSync(dirPath)).toBe(false);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("passes without error for nonexistent path", () => {
|
|
140
|
-
expect(() => rmSync(path.join(testDir, "nonexistent"))).not.toThrow();
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe("rm", () => {
|
|
145
|
-
it("deletes file asynchronously", async () => {
|
|
146
|
-
const filePath = path.join(testDir, "asyncdelete.txt");
|
|
147
|
-
fs.writeFileSync(filePath, "test");
|
|
148
|
-
|
|
149
|
-
await rm(filePath);
|
|
150
|
-
|
|
151
|
-
expect(fs.existsSync(filePath)).toBe(false);
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
//#endregion
|
|
156
|
-
|
|
157
|
-
//#region read/write
|
|
158
|
-
|
|
159
|
-
describe("readSync", () => {
|
|
160
|
-
it("reads file content as UTF-8 string", () => {
|
|
161
|
-
const filePath = path.join(testDir, "read.txt");
|
|
162
|
-
fs.writeFileSync(filePath, "Hello, World!");
|
|
163
|
-
|
|
164
|
-
const content = readSync(filePath);
|
|
165
|
-
|
|
166
|
-
expect(content).toBe("Hello, World!");
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
describe("read", () => {
|
|
172
|
-
it("reads file asynchronously", async () => {
|
|
173
|
-
const filePath = path.join(testDir, "asyncread.txt");
|
|
174
|
-
fs.writeFileSync(filePath, "async content");
|
|
175
|
-
|
|
176
|
-
const content = await read(filePath);
|
|
177
|
-
|
|
178
|
-
expect(content).toBe("async content");
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
describe("readBufferSync", () => {
|
|
183
|
-
it("reads file as Buffer", () => {
|
|
184
|
-
const filePath = path.join(testDir, "buffer.txt");
|
|
185
|
-
fs.writeFileSync(filePath, "buffer content");
|
|
186
|
-
|
|
187
|
-
const buffer = readBufferSync(filePath);
|
|
188
|
-
|
|
189
|
-
expect(buffer instanceof Uint8Array).toBe(true);
|
|
190
|
-
expect(buffer.toString()).toBe("buffer content");
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
describe("readBuffer", () => {
|
|
195
|
-
it("reads file as Buffer asynchronously", async () => {
|
|
196
|
-
const filePath = path.join(testDir, "asyncbuffer.txt");
|
|
197
|
-
fs.writeFileSync(filePath, "async buffer content");
|
|
198
|
-
|
|
199
|
-
const buffer = await readBuffer(filePath);
|
|
200
|
-
|
|
201
|
-
expect(buffer instanceof Uint8Array).toBe(true);
|
|
202
|
-
expect(buffer.toString()).toBe("async buffer content");
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
describe("writeSync", () => {
|
|
207
|
-
it("writes string to file", () => {
|
|
208
|
-
const filePath = path.join(testDir, "write.txt");
|
|
209
|
-
|
|
210
|
-
writeSync(filePath, "written content");
|
|
211
|
-
|
|
212
|
-
expect(fs.readFileSync(filePath, "utf-8")).toBe("written content");
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it("writes Buffer to file", () => {
|
|
216
|
-
const filePath = path.join(testDir, "buffer-write.bin");
|
|
217
|
-
const buffer = new Uint8Array([0x00, 0x01, 0x02, 0xff]);
|
|
218
|
-
|
|
219
|
-
writeSync(filePath, buffer);
|
|
220
|
-
|
|
221
|
-
expect(new Uint8Array(fs.readFileSync(filePath))).toEqual(buffer);
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it("auto-creates parent directory if missing", () => {
|
|
225
|
-
const filePath = path.join(testDir, "sub/dir/write.txt");
|
|
226
|
-
|
|
227
|
-
writeSync(filePath, "nested content");
|
|
228
|
-
|
|
229
|
-
expect(fs.readFileSync(filePath, "utf-8")).toBe("nested content");
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
describe("write", () => {
|
|
234
|
-
it("writes file asynchronously", async () => {
|
|
235
|
-
const filePath = path.join(testDir, "asyncwrite.txt");
|
|
236
|
-
|
|
237
|
-
await write(filePath, "async written");
|
|
238
|
-
|
|
239
|
-
expect(fs.readFileSync(filePath, "utf-8")).toBe("async written");
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
//#endregion
|
|
244
|
-
|
|
245
|
-
//#region JSON
|
|
246
|
-
|
|
247
|
-
describe("readJsonSync", () => {
|
|
248
|
-
it("reads JSON file", () => {
|
|
249
|
-
const filePath = path.join(testDir, "data.json");
|
|
250
|
-
fs.writeFileSync(filePath, '{"name": "test", "value": 42}');
|
|
251
|
-
|
|
252
|
-
const data = readJsonSync<{ name: string; value: number }>(filePath);
|
|
253
|
-
|
|
254
|
-
expect(data).toEqual({ name: "test", value: 42 });
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it("includes truncated content when reading invalid JSON with over 500 characters", () => {
|
|
258
|
-
const filePath = path.join(testDir, "long-invalid.json");
|
|
259
|
-
const longContent = "{ invalid " + "x".repeat(600) + " }";
|
|
260
|
-
fs.writeFileSync(filePath, longContent);
|
|
261
|
-
|
|
262
|
-
try {
|
|
263
|
-
readJsonSync(filePath);
|
|
264
|
-
expect.fail("Should throw error");
|
|
265
|
-
} catch (err) {
|
|
266
|
-
expect((err as Error).message).toContain("...(truncated)");
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
describe("writeJsonSync", () => {
|
|
272
|
-
it("writes JSON file", () => {
|
|
273
|
-
const filePath = path.join(testDir, "output.json");
|
|
274
|
-
const data = { name: "test", value: 42 };
|
|
275
|
-
|
|
276
|
-
writeJsonSync(filePath, data);
|
|
277
|
-
|
|
278
|
-
const content = JSON.parse(fs.readFileSync(filePath, "utf-8")) as unknown;
|
|
279
|
-
expect(content).toEqual(data);
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
it("writes JSON file with formatting", () => {
|
|
283
|
-
const filePath = path.join(testDir, "formatted.json");
|
|
284
|
-
const data = { name: "test" };
|
|
285
|
-
|
|
286
|
-
writeJsonSync(filePath, data, { space: 2 });
|
|
287
|
-
|
|
288
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
289
|
-
expect(content).toContain("\n");
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
it("writes JSON file with replacer option", () => {
|
|
293
|
-
const filePath = path.join(testDir, "replaced.json");
|
|
294
|
-
const data = { name: "test", secret: "hidden" };
|
|
295
|
-
|
|
296
|
-
writeJsonSync(filePath, data, {
|
|
297
|
-
replacer: (_key, value) =>
|
|
298
|
-
typeof value === "string" && value === "hidden" ? undefined : value,
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
const content = JSON.parse(fs.readFileSync(filePath, "utf-8")) as Record<string, unknown>;
|
|
302
|
-
expect(content).toEqual({ name: "test" });
|
|
303
|
-
expect(content["secret"]).toBeUndefined();
|
|
304
|
-
});
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
describe("readJson", () => {
|
|
308
|
-
it("reads JSON file asynchronously", async () => {
|
|
309
|
-
const filePath = path.join(testDir, "asyncdata.json");
|
|
310
|
-
fs.writeFileSync(filePath, '{"name": "async", "value": 100}');
|
|
311
|
-
|
|
312
|
-
const data = await readJson<{ name: string; value: number }>(filePath);
|
|
313
|
-
|
|
314
|
-
expect(data).toEqual({ name: "async", value: 100 });
|
|
315
|
-
});
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
describe("writeJson", () => {
|
|
319
|
-
it("writes JSON file asynchronously", async () => {
|
|
320
|
-
const filePath = path.join(testDir, "asyncoutput.json");
|
|
321
|
-
const data = { name: "async", value: 100 };
|
|
322
|
-
|
|
323
|
-
await writeJson(filePath, data);
|
|
324
|
-
|
|
325
|
-
const content = JSON.parse(fs.readFileSync(filePath, "utf-8")) as unknown;
|
|
326
|
-
expect(content).toEqual(data);
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
//#endregion
|
|
331
|
-
|
|
332
|
-
//#region copy
|
|
333
|
-
|
|
334
|
-
describe("copySync", () => {
|
|
335
|
-
it("copies file", () => {
|
|
336
|
-
const source = path.join(testDir, "source.txt");
|
|
337
|
-
const target = path.join(testDir, "target.txt");
|
|
338
|
-
fs.writeFileSync(source, "source content");
|
|
339
|
-
|
|
340
|
-
copySync(source, target);
|
|
341
|
-
|
|
342
|
-
expect(fs.readFileSync(target, "utf-8")).toBe("source content");
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
it("copies directory (recursive)", () => {
|
|
346
|
-
const sourceDir = path.join(testDir, "sourceDir");
|
|
347
|
-
const targetDir = path.join(testDir, "targetDir");
|
|
348
|
-
fs.mkdirSync(sourceDir);
|
|
349
|
-
fs.writeFileSync(path.join(sourceDir, "file.txt"), "content");
|
|
350
|
-
fs.mkdirSync(path.join(sourceDir, "sub"));
|
|
351
|
-
fs.writeFileSync(path.join(sourceDir, "sub/nested.txt"), "nested");
|
|
352
|
-
|
|
353
|
-
copySync(sourceDir, targetDir);
|
|
354
|
-
|
|
355
|
-
expect(fs.existsSync(path.join(targetDir, "file.txt"))).toBe(true);
|
|
356
|
-
expect(fs.existsSync(path.join(targetDir, "sub/nested.txt"))).toBe(true);
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
it("ignores nonexistent source", () => {
|
|
360
|
-
const source = path.join(testDir, "nonexistent");
|
|
361
|
-
const target = path.join(testDir, "target");
|
|
362
|
-
|
|
363
|
-
expect(() => copySync(source, target)).not.toThrow();
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
it("selectively copies with filter option", () => {
|
|
367
|
-
const sourceDir = path.join(testDir, "filterSource");
|
|
368
|
-
const targetDir = path.join(testDir, "filterTarget");
|
|
369
|
-
fs.mkdirSync(sourceDir);
|
|
370
|
-
fs.writeFileSync(path.join(sourceDir, "include.txt"), "include");
|
|
371
|
-
fs.writeFileSync(path.join(sourceDir, "exclude.log"), "exclude");
|
|
372
|
-
|
|
373
|
-
copySync(sourceDir, targetDir, (p) => !p.endsWith(".log"));
|
|
374
|
-
|
|
375
|
-
expect(fs.existsSync(path.join(targetDir, "include.txt"))).toBe(true);
|
|
376
|
-
expect(fs.existsSync(path.join(targetDir, "exclude.log"))).toBe(false);
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
it("skips subdirectories and items when filter excludes directory", () => {
|
|
380
|
-
const sourceDir = path.join(testDir, "filterDirSource");
|
|
381
|
-
const targetDir = path.join(testDir, "filterDirTarget");
|
|
382
|
-
fs.mkdirSync(sourceDir);
|
|
383
|
-
fs.mkdirSync(path.join(sourceDir, "excluded"));
|
|
384
|
-
fs.mkdirSync(path.join(sourceDir, "included"));
|
|
385
|
-
fs.writeFileSync(path.join(sourceDir, "excluded", "nested.txt"), "nested");
|
|
386
|
-
fs.writeFileSync(path.join(sourceDir, "included", "nested.txt"), "nested");
|
|
387
|
-
|
|
388
|
-
copySync(sourceDir, targetDir, (p) => !p.includes("excluded"));
|
|
389
|
-
|
|
390
|
-
expect(fs.existsSync(path.join(targetDir, "excluded"))).toBe(false);
|
|
391
|
-
expect(fs.existsSync(path.join(targetDir, "excluded", "nested.txt"))).toBe(false);
|
|
392
|
-
expect(fs.existsSync(path.join(targetDir, "included"))).toBe(true);
|
|
393
|
-
expect(fs.existsSync(path.join(targetDir, "included", "nested.txt"))).toBe(true);
|
|
394
|
-
});
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
describe("copy", () => {
|
|
398
|
-
it("copies file asynchronously", async () => {
|
|
399
|
-
const source = path.join(testDir, "asyncSource.txt");
|
|
400
|
-
const target = path.join(testDir, "asyncTarget.txt");
|
|
401
|
-
fs.writeFileSync(source, "async source content");
|
|
402
|
-
|
|
403
|
-
await copy(source, target);
|
|
404
|
-
|
|
405
|
-
expect(fs.readFileSync(target, "utf-8")).toBe("async source content");
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
it("selectively copies with filter option asynchronously", async () => {
|
|
409
|
-
const sourceDir = path.join(testDir, "asyncFilterSource");
|
|
410
|
-
const targetDir = path.join(testDir, "asyncFilterTarget");
|
|
411
|
-
fs.mkdirSync(sourceDir);
|
|
412
|
-
fs.writeFileSync(path.join(sourceDir, "keep.ts"), "keep");
|
|
413
|
-
fs.writeFileSync(path.join(sourceDir, "skip.js"), "skip");
|
|
414
|
-
|
|
415
|
-
await copy(sourceDir, targetDir, (p) => p.endsWith(".ts"));
|
|
416
|
-
|
|
417
|
-
expect(fs.existsSync(path.join(targetDir, "keep.ts"))).toBe(true);
|
|
418
|
-
expect(fs.existsSync(path.join(targetDir, "skip.js"))).toBe(false);
|
|
419
|
-
});
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
//#endregion
|
|
423
|
-
|
|
424
|
-
//#region readdir
|
|
425
|
-
|
|
426
|
-
describe("readdirSync", () => {
|
|
427
|
-
it("reads directory contents", () => {
|
|
428
|
-
fs.writeFileSync(path.join(testDir, "file1.txt"), "");
|
|
429
|
-
fs.writeFileSync(path.join(testDir, "file2.txt"), "");
|
|
430
|
-
fs.mkdirSync(path.join(testDir, "subdir"));
|
|
431
|
-
|
|
432
|
-
const entries = readdirSync(testDir);
|
|
433
|
-
|
|
434
|
-
expect(entries).toContain("file1.txt");
|
|
435
|
-
expect(entries).toContain("file2.txt");
|
|
436
|
-
expect(entries).toContain("subdir");
|
|
437
|
-
});
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
describe("readdir", () => {
|
|
441
|
-
it("reads directory contents asynchronously", async () => {
|
|
442
|
-
fs.writeFileSync(path.join(testDir, "async1.txt"), "");
|
|
443
|
-
fs.writeFileSync(path.join(testDir, "async2.txt"), "");
|
|
444
|
-
|
|
445
|
-
const entries = await readdir(testDir);
|
|
446
|
-
|
|
447
|
-
expect(entries).toContain("async1.txt");
|
|
448
|
-
expect(entries).toContain("async2.txt");
|
|
449
|
-
});
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
//#endregion
|
|
453
|
-
|
|
454
|
-
//#region stat
|
|
455
|
-
|
|
456
|
-
describe("statSync", () => {
|
|
457
|
-
it("gets file information", () => {
|
|
458
|
-
const filePath = path.join(testDir, "statfile.txt");
|
|
459
|
-
fs.writeFileSync(filePath, "content");
|
|
460
|
-
|
|
461
|
-
const result = statSync(filePath);
|
|
462
|
-
|
|
463
|
-
expect(result.isFile()).toBe(true);
|
|
464
|
-
expect(result.size).toBeGreaterThan(0);
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
describe("stat", () => {
|
|
470
|
-
it("gets file information asynchronously", async () => {
|
|
471
|
-
const filePath = path.join(testDir, "asyncstatfile.txt");
|
|
472
|
-
fs.writeFileSync(filePath, "async content");
|
|
473
|
-
|
|
474
|
-
const result = await stat(filePath);
|
|
475
|
-
|
|
476
|
-
expect(result.isFile()).toBe(true);
|
|
477
|
-
expect(result.size).toBeGreaterThan(0);
|
|
478
|
-
});
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
describe("lstatSync", () => {
|
|
482
|
-
it("returns symbolic link information for symbolic links", () => {
|
|
483
|
-
const targetPath = path.join(testDir, "target.txt");
|
|
484
|
-
const linkPath = path.join(testDir, "link.txt");
|
|
485
|
-
fs.writeFileSync(targetPath, "target content");
|
|
486
|
-
fs.symlinkSync(targetPath, linkPath);
|
|
487
|
-
|
|
488
|
-
const lstatResult = lstatSync(linkPath);
|
|
489
|
-
const statResult = statSync(linkPath);
|
|
490
|
-
|
|
491
|
-
// lstat returns information about the symbolic link itself
|
|
492
|
-
expect(lstatResult.isSymbolicLink()).toBe(true);
|
|
493
|
-
expect(lstatResult.isFile()).toBe(false);
|
|
494
|
-
|
|
495
|
-
// stat returns information about the target of the link
|
|
496
|
-
expect(statResult.isSymbolicLink()).toBe(false);
|
|
497
|
-
expect(statResult.isFile()).toBe(true);
|
|
498
|
-
});
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
describe("lstat", () => {
|
|
502
|
-
it("returns symbolic link information asynchronously", async () => {
|
|
503
|
-
const targetPath = path.join(testDir, "async-target.txt");
|
|
504
|
-
const linkPath = path.join(testDir, "async-link.txt");
|
|
505
|
-
fs.writeFileSync(targetPath, "target content");
|
|
506
|
-
fs.symlinkSync(targetPath, linkPath);
|
|
507
|
-
|
|
508
|
-
const lstatResult = await lstat(linkPath);
|
|
509
|
-
const statResult = await stat(linkPath);
|
|
510
|
-
|
|
511
|
-
// lstat returns information about the symbolic link itself
|
|
512
|
-
expect(lstatResult.isSymbolicLink()).toBe(true);
|
|
513
|
-
expect(lstatResult.isFile()).toBe(false);
|
|
514
|
-
|
|
515
|
-
// stat returns information about the target of the link
|
|
516
|
-
expect(statResult.isSymbolicLink()).toBe(false);
|
|
517
|
-
expect(statResult.isFile()).toBe(true);
|
|
518
|
-
});
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
//#endregion
|
|
522
|
-
|
|
523
|
-
//#region glob
|
|
524
|
-
|
|
525
|
-
describe("globSync", () => {
|
|
526
|
-
it("searches files by glob pattern", () => {
|
|
527
|
-
fs.writeFileSync(path.join(testDir, "a.txt"), "");
|
|
528
|
-
fs.writeFileSync(path.join(testDir, "b.txt"), "");
|
|
529
|
-
fs.writeFileSync(path.join(testDir, "c.js"), "");
|
|
530
|
-
|
|
531
|
-
const txtFiles = globSync(path.join(testDir, "*.txt"));
|
|
532
|
-
|
|
533
|
-
expect(txtFiles.length).toBe(2);
|
|
534
|
-
expect(txtFiles.some((f) => f.endsWith("a.txt"))).toBe(true);
|
|
535
|
-
expect(txtFiles.some((f) => f.endsWith("b.txt"))).toBe(true);
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
it("searches nested directories", () => {
|
|
539
|
-
fs.mkdirSync(path.join(testDir, "nested"));
|
|
540
|
-
fs.writeFileSync(path.join(testDir, "nested/deep.txt"), "");
|
|
541
|
-
|
|
542
|
-
const files = globSync(path.join(testDir, "**/*.txt"));
|
|
543
|
-
|
|
544
|
-
expect(files.some((f) => f.endsWith("deep.txt"))).toBe(true);
|
|
545
|
-
});
|
|
546
|
-
|
|
547
|
-
it("includes hidden files with dot: true option", () => {
|
|
548
|
-
fs.writeFileSync(path.join(testDir, ".hidden"), "");
|
|
549
|
-
fs.writeFileSync(path.join(testDir, "visible"), "");
|
|
550
|
-
|
|
551
|
-
const withoutDot = globSync(path.join(testDir, "*"));
|
|
552
|
-
const withDot = globSync(path.join(testDir, "*"), { dot: true });
|
|
553
|
-
|
|
554
|
-
expect(withoutDot.some((f) => f.endsWith(".hidden"))).toBe(false);
|
|
555
|
-
expect(withDot.some((f) => f.endsWith(".hidden"))).toBe(true);
|
|
556
|
-
});
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
describe("glob", () => {
|
|
560
|
-
it("searches files asynchronously by glob pattern", async () => {
|
|
561
|
-
fs.writeFileSync(path.join(testDir, "async.txt"), "");
|
|
562
|
-
|
|
563
|
-
const files = await glob(path.join(testDir, "*.txt"));
|
|
564
|
-
|
|
565
|
-
expect(files.length).toBeGreaterThan(0);
|
|
566
|
-
});
|
|
567
|
-
});
|
|
568
|
-
|
|
569
|
-
//#endregion
|
|
570
|
-
|
|
571
|
-
//#region clearEmptyDirectoryAsync
|
|
572
|
-
|
|
573
|
-
describe("clearEmptyDirectory", () => {
|
|
574
|
-
it("recursively deletes empty directories", async () => {
|
|
575
|
-
const emptyDir = path.join(testDir, "empty/nested/deep");
|
|
576
|
-
fs.mkdirSync(emptyDir, { recursive: true });
|
|
577
|
-
|
|
578
|
-
await clearEmptyDirectory(path.join(testDir, "empty"));
|
|
579
|
-
|
|
580
|
-
expect(fs.existsSync(path.join(testDir, "empty"))).toBe(false);
|
|
581
|
-
});
|
|
582
|
-
|
|
583
|
-
it("keeps directories with files", async () => {
|
|
584
|
-
const dirWithFile = path.join(testDir, "notempty");
|
|
585
|
-
fs.mkdirSync(dirWithFile);
|
|
586
|
-
fs.writeFileSync(path.join(dirWithFile, "file.txt"), "content");
|
|
587
|
-
|
|
588
|
-
await clearEmptyDirectory(dirWithFile);
|
|
589
|
-
|
|
590
|
-
expect(fs.existsSync(dirWithFile)).toBe(true);
|
|
591
|
-
});
|
|
592
|
-
});
|
|
593
|
-
|
|
594
|
-
//#endregion
|
|
595
|
-
|
|
596
|
-
//#region findAllParentChildPaths
|
|
597
|
-
|
|
598
|
-
describe("findAllParentChildPathsSync", () => {
|
|
599
|
-
it("finds specific file in parent directories", () => {
|
|
600
|
-
const deepDir = path.join(testDir, "a/b/c");
|
|
601
|
-
fs.mkdirSync(deepDir, { recursive: true });
|
|
602
|
-
fs.writeFileSync(path.join(testDir, "marker.txt"), "");
|
|
603
|
-
fs.writeFileSync(path.join(testDir, "a/marker.txt"), "");
|
|
604
|
-
|
|
605
|
-
const results = findAllParentChildPathsSync("marker.txt", deepDir, testDir);
|
|
606
|
-
|
|
607
|
-
expect(results.length).toBe(2);
|
|
608
|
-
});
|
|
609
|
-
|
|
610
|
-
it("returns empty array when no matching file is found", () => {
|
|
611
|
-
const deepDir = path.join(testDir, "a/b/c");
|
|
612
|
-
fs.mkdirSync(deepDir, { recursive: true });
|
|
613
|
-
|
|
614
|
-
const results = findAllParentChildPathsSync("nonexistent-file.txt", deepDir, testDir);
|
|
615
|
-
|
|
616
|
-
expect(results).toEqual([]);
|
|
617
|
-
});
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
describe("findAllParentChildPaths", () => {
|
|
621
|
-
it("finds specific file in parent directories asynchronously", async () => {
|
|
622
|
-
const deepDir = path.join(testDir, "x/y/z");
|
|
623
|
-
fs.mkdirSync(deepDir, { recursive: true });
|
|
624
|
-
fs.writeFileSync(path.join(testDir, "config.json"), "");
|
|
625
|
-
fs.writeFileSync(path.join(testDir, "x/config.json"), "");
|
|
626
|
-
|
|
627
|
-
const results = await findAllParentChildPaths("config.json", deepDir, testDir);
|
|
628
|
-
|
|
629
|
-
expect(results.length).toBe(2);
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
it("returns empty array asynchronously when no matching file is found", async () => {
|
|
633
|
-
const deepDir = path.join(testDir, "x/y/z");
|
|
634
|
-
fs.mkdirSync(deepDir, { recursive: true });
|
|
635
|
-
|
|
636
|
-
const results = await findAllParentChildPaths("nonexistent-file.txt", deepDir, testDir);
|
|
637
|
-
|
|
638
|
-
expect(results).toEqual([]);
|
|
639
|
-
});
|
|
640
|
-
});
|
|
641
|
-
|
|
642
|
-
//#endregion
|
|
643
|
-
|
|
644
|
-
//#region Error Cases
|
|
645
|
-
|
|
646
|
-
describe("error cases", () => {
|
|
647
|
-
it("includes path information in SdError when reading nonexistent file", () => {
|
|
648
|
-
const filePath = path.join(testDir, "nonexistent.txt");
|
|
649
|
-
expect(() => readSync(filePath)).toThrow(SdError);
|
|
650
|
-
try {
|
|
651
|
-
readSync(filePath);
|
|
652
|
-
} catch (err) {
|
|
653
|
-
expect((err as Error).message).toContain(filePath);
|
|
654
|
-
}
|
|
655
|
-
});
|
|
656
|
-
|
|
657
|
-
it("includes path information in SdError when reading nonexistent file asynchronously", async () => {
|
|
658
|
-
const filePath = path.join(testDir, "nonexistent.txt");
|
|
659
|
-
await expect(read(filePath)).rejects.toThrow(SdError);
|
|
660
|
-
try {
|
|
661
|
-
await read(filePath);
|
|
662
|
-
} catch (err) {
|
|
663
|
-
expect((err as Error).message).toContain(filePath);
|
|
664
|
-
}
|
|
665
|
-
});
|
|
666
|
-
|
|
667
|
-
it("throws error when reading nonexistent directory", () => {
|
|
668
|
-
expect(() => readdirSync(path.join(testDir, "nonexistent"))).toThrow();
|
|
669
|
-
});
|
|
670
|
-
|
|
671
|
-
it("throws error when stat nonexistent file", () => {
|
|
672
|
-
expect(() => statSync(path.join(testDir, "nonexistent.txt"))).toThrow();
|
|
673
|
-
});
|
|
674
|
-
|
|
675
|
-
it("includes path and content information in SdError when reading invalid JSON", () => {
|
|
676
|
-
const filePath = path.join(testDir, "invalid.json");
|
|
677
|
-
const content = "{ invalid json }";
|
|
678
|
-
fs.writeFileSync(filePath, content);
|
|
679
|
-
|
|
680
|
-
expect(() => readJsonSync(filePath)).toThrow(SdError);
|
|
681
|
-
try {
|
|
682
|
-
readJsonSync(filePath);
|
|
683
|
-
} catch (err) {
|
|
684
|
-
expect((err as Error).message).toContain(filePath);
|
|
685
|
-
expect((err as Error).message).toContain(content);
|
|
686
|
-
}
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
it("includes path and content information in SdError when reading invalid JSON asynchronously", async () => {
|
|
690
|
-
const filePath = path.join(testDir, "invalid-async.json");
|
|
691
|
-
const content = "{ invalid json }";
|
|
692
|
-
fs.writeFileSync(filePath, content);
|
|
693
|
-
|
|
694
|
-
await expect(readJson(filePath)).rejects.toThrow(SdError);
|
|
695
|
-
try {
|
|
696
|
-
await readJson(filePath);
|
|
697
|
-
} catch (err) {
|
|
698
|
-
expect((err as Error).message).toContain(filePath);
|
|
699
|
-
expect((err as Error).message).toContain(content);
|
|
700
|
-
}
|
|
701
|
-
});
|
|
702
|
-
});
|
|
703
|
-
|
|
704
|
-
//#endregion
|
|
705
|
-
});
|