@gjsify/fs 0.3.21 → 0.4.3

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 (69) hide show
  1. package/lib/esm/_virtual/_rolldown/runtime.js +1 -1
  2. package/lib/esm/callback.js +1 -1
  3. package/lib/esm/cp.js +1 -1
  4. package/lib/esm/dir.js +1 -1
  5. package/lib/esm/dirent.js +1 -1
  6. package/lib/esm/encoding.js +1 -1
  7. package/lib/esm/errors.js +1 -1
  8. package/lib/esm/fd-ops.js +1 -1
  9. package/lib/esm/file-handle.js +1 -1
  10. package/lib/esm/fs-watcher.js +1 -1
  11. package/lib/esm/glob.js +1 -1
  12. package/lib/esm/read-stream.js +1 -1
  13. package/lib/esm/stat-watcher.js +1 -1
  14. package/lib/esm/statfs.js +1 -1
  15. package/lib/esm/stats.js +1 -1
  16. package/lib/esm/sync.js +1 -1
  17. package/lib/esm/utils.js +1 -1
  18. package/lib/esm/utimes.js +1 -1
  19. package/lib/esm/write-stream.js +1 -1
  20. package/package.json +51 -48
  21. package/src/callback.spec.ts +0 -296
  22. package/src/callback.ts +0 -684
  23. package/src/cp.spec.ts +0 -181
  24. package/src/cp.ts +0 -328
  25. package/src/dir.spec.ts +0 -204
  26. package/src/dir.ts +0 -199
  27. package/src/dirent.ts +0 -165
  28. package/src/encoding.ts +0 -45
  29. package/src/errors.spec.ts +0 -389
  30. package/src/errors.ts +0 -19
  31. package/src/extended.spec.ts +0 -706
  32. package/src/fd-ops.spec.ts +0 -234
  33. package/src/fd-ops.ts +0 -251
  34. package/src/file-handle.spec.ts +0 -115
  35. package/src/file-handle.ts +0 -856
  36. package/src/fs-watcher.ts +0 -198
  37. package/src/glob.spec.ts +0 -201
  38. package/src/glob.ts +0 -205
  39. package/src/index.ts +0 -313
  40. package/src/new-apis.spec.ts +0 -505
  41. package/src/promises.spec.ts +0 -812
  42. package/src/promises.ts +0 -686
  43. package/src/read-stream.ts +0 -128
  44. package/src/stat-watcher.ts +0 -116
  45. package/src/stat.spec.ts +0 -87
  46. package/src/statfs.spec.ts +0 -67
  47. package/src/statfs.ts +0 -92
  48. package/src/stats.ts +0 -207
  49. package/src/streams.spec.ts +0 -513
  50. package/src/symlink.spec.ts +0 -188
  51. package/src/sync.spec.ts +0 -377
  52. package/src/sync.ts +0 -562
  53. package/src/test.mts +0 -27
  54. package/src/types/encoding-option.ts +0 -3
  55. package/src/types/file-read-options.ts +0 -15
  56. package/src/types/file-read-result.ts +0 -4
  57. package/src/types/flag-and-open-mode.ts +0 -6
  58. package/src/types/index.ts +0 -6
  59. package/src/types/open-flags.ts +0 -14
  60. package/src/types/read-options.ts +0 -9
  61. package/src/utils.ts +0 -31
  62. package/src/utimes.spec.ts +0 -113
  63. package/src/utimes.ts +0 -97
  64. package/src/watch.spec.ts +0 -171
  65. package/src/watchfile.spec.ts +0 -185
  66. package/src/write-stream.ts +0 -142
  67. package/test/file.txt +0 -1
  68. package/tsconfig.json +0 -29
  69. package/tsconfig.tsbuildinfo +0 -1
@@ -1,513 +0,0 @@
1
- // fs stream tests — createReadStream, createWriteStream, pipe integration
2
- // Reference: refs/node-test/parallel/test-fs-read-stream*.js, test-fs-write-stream*.js
3
-
4
- import { describe, it, expect } from '@gjsify/unit';
5
- import { createReadStream, createWriteStream, writeFileSync, readFileSync, mkdtempSync, rmSync, mkdirSync } from 'node:fs';
6
- import { join } from 'node:path';
7
- import { tmpdir } from 'node:os';
8
- import { Buffer } from 'node:buffer';
9
- import { Transform, PassThrough } from 'node:stream';
10
-
11
- let tmpDir: string;
12
-
13
- function setup(): void {
14
- tmpDir = mkdtempSync(join(tmpdir(), 'gjsify-fs-stream-'));
15
- }
16
-
17
- function cleanup(): void {
18
- try { rmSync(tmpDir, { recursive: true }); } catch {}
19
- }
20
-
21
- export default async () => {
22
- // ---- createReadStream ----
23
-
24
- await describe('fs.createReadStream', async () => {
25
- await it('should read a file and emit data + end events', async () => {
26
- setup();
27
- try {
28
- const filePath = join(tmpDir, 'read-test.txt');
29
- writeFileSync(filePath, 'hello world');
30
- const chunks: string[] = [];
31
- await new Promise<void>((resolve, reject) => {
32
- const stream = createReadStream(filePath, { encoding: 'utf8' });
33
- stream.on('data', (chunk) => chunks.push(chunk as string));
34
- stream.on('end', () => resolve());
35
- stream.on('error', reject);
36
- });
37
- expect(chunks.join('')).toBe('hello world');
38
- } finally {
39
- cleanup();
40
- }
41
- });
42
-
43
- await it('should read file as Buffer by default', async () => {
44
- setup();
45
- try {
46
- const filePath = join(tmpDir, 'buf-test.txt');
47
- writeFileSync(filePath, 'buffer data');
48
- const chunks: Buffer[] = [];
49
- await new Promise<void>((resolve, reject) => {
50
- const stream = createReadStream(filePath);
51
- stream.on('data', (chunk) => chunks.push(Buffer.from(chunk as Uint8Array)));
52
- stream.on('end', () => resolve());
53
- stream.on('error', reject);
54
- });
55
- const result = Buffer.concat(chunks).toString('utf8');
56
- expect(result).toBe('buffer data');
57
- } finally {
58
- cleanup();
59
- }
60
- });
61
-
62
- await it('should handle empty file', async () => {
63
- setup();
64
- try {
65
- const filePath = join(tmpDir, 'empty.txt');
66
- writeFileSync(filePath, '');
67
- const chunks: string[] = [];
68
- await new Promise<void>((resolve, reject) => {
69
- const stream = createReadStream(filePath, { encoding: 'utf8' });
70
- stream.on('data', (chunk) => chunks.push(chunk as string));
71
- stream.on('end', () => resolve());
72
- stream.on('error', reject);
73
- });
74
- expect(chunks.join('')).toBe('');
75
- } finally {
76
- cleanup();
77
- }
78
- });
79
-
80
- await it('should emit error for non-existent file', async () => {
81
- const err = await new Promise<Error>((resolve) => {
82
- const stream = createReadStream('/nonexistent_gjsify_test_12345');
83
- stream.on('error', (e) => resolve(e));
84
- });
85
- expect(err).toBeDefined();
86
- // GJS Gio errors have numeric codes; Node.js has string 'ENOENT'
87
- const code: unknown = (err as NodeJS.ErrnoException).code;
88
- expect(code === 'ENOENT' || code === 1 || (err as any).code !== undefined).toBeTruthy();
89
- });
90
-
91
- await it('should read file with start and end options', async () => {
92
- setup();
93
- try {
94
- const filePath = join(tmpDir, 'range-test.txt');
95
- writeFileSync(filePath, 'abcdefghij');
96
- const chunks: string[] = [];
97
- await new Promise<void>((resolve, reject) => {
98
- const stream = createReadStream(filePath, { encoding: 'utf8', start: 2, end: 6 });
99
- stream.on('data', (chunk) => chunks.push(chunk as string));
100
- stream.on('end', () => resolve());
101
- stream.on('error', reject);
102
- });
103
- // start=2 (index 'c'), end=6 (index 'g', inclusive)
104
- expect(chunks.join('')).toBe('cdefg');
105
- } finally {
106
- cleanup();
107
- }
108
- });
109
-
110
- await it('should read a larger file (64KB)', async () => {
111
- setup();
112
- try {
113
- const filePath = join(tmpDir, 'large.txt');
114
- const size = 64 * 1024;
115
- writeFileSync(filePath, 'A'.repeat(size));
116
- let totalLength = 0;
117
- await new Promise<void>((resolve, reject) => {
118
- const stream = createReadStream(filePath);
119
- stream.on('data', (chunk) => { totalLength += (chunk as Buffer).length; });
120
- stream.on('end', () => resolve());
121
- stream.on('error', reject);
122
- });
123
- expect(totalLength).toBe(size);
124
- } finally {
125
- cleanup();
126
- }
127
- });
128
-
129
- await it('should track bytesRead', async () => {
130
- setup();
131
- try {
132
- const filePath = join(tmpDir, 'bytes-read.txt');
133
- const content = 'hello bytes';
134
- writeFileSync(filePath, content);
135
- const stream = createReadStream(filePath);
136
- await new Promise<void>((resolve, reject) => {
137
- stream.on('data', () => {});
138
- stream.on('end', () => resolve());
139
- stream.on('error', reject);
140
- });
141
- expect(stream.bytesRead).toBe(Buffer.byteLength(content));
142
- } finally {
143
- cleanup();
144
- }
145
- });
146
-
147
- await it('should have path property', async () => {
148
- setup();
149
- try {
150
- const filePath = join(tmpDir, 'path-prop.txt');
151
- writeFileSync(filePath, 'test');
152
- const stream = createReadStream(filePath);
153
- expect(stream.path).toBe(filePath);
154
- await new Promise<void>((resolve) => {
155
- stream.on('data', () => {});
156
- stream.on('end', () => resolve());
157
- });
158
- } finally {
159
- cleanup();
160
- }
161
- });
162
-
163
- // Regression: ReadStream uses _construct() for async file opening.
164
- // Verifies that 'open' and 'ready' fire and that pending starts as true.
165
- await it('should emit open and ready events before data, pending starts true', async () => {
166
- setup();
167
- try {
168
- const filePath = join(tmpDir, 'open-ready.txt');
169
- writeFileSync(filePath, 'hello');
170
- const stream = createReadStream(filePath);
171
- expect(stream.pending).toBeTruthy();
172
- const events: string[] = [];
173
- let openFd: number | undefined;
174
- await new Promise<void>((resolve, reject) => {
175
- stream.on('open', (fd: number) => { openFd = fd; events.push('open'); });
176
- stream.on('ready', () => events.push('ready'));
177
- stream.on('data', () => { if (!events.includes('data')) events.push('data'); });
178
- stream.on('end', () => resolve());
179
- stream.on('error', reject);
180
- stream.resume();
181
- });
182
- expect(openFd).toBeDefined();
183
- // 'open' and 'ready' must have fired (file was opened)
184
- expect(events).toContain('open');
185
- expect(events).toContain('ready');
186
- // 'open' must fire before any data event
187
- const openIdx = events.indexOf('open');
188
- const dataIdx = events.indexOf('data');
189
- if (dataIdx !== -1) expect(openIdx).toBeLessThan(dataIdx);
190
- } finally {
191
- cleanup();
192
- }
193
- });
194
-
195
- // Regression: piping a ReadStream before the file is open (while pending=true)
196
- // must still deliver all data. Previously _pendingReadSize approach hung under
197
- // backpressure on GJS 1.88; _construct() fixes this properly.
198
- await it('should pipe correctly when pipe() is called before file opens', async () => {
199
- setup();
200
- try {
201
- const srcPath = join(tmpDir, 'pre-pipe-src.txt');
202
- const dstPath = join(tmpDir, 'pre-pipe-dst.txt');
203
- const size = 3 * 64 * 1024; // 3 × 64KB — forces multiple read chunks + backpressure
204
- writeFileSync(srcPath, 'X'.repeat(size));
205
- await new Promise<void>((resolve, reject) => {
206
- const src = createReadStream(srcPath);
207
- const dst = createWriteStream(dstPath);
208
- // pipe() is called immediately — src is still pending (file not open yet)
209
- expect(src.pending).toBeTruthy();
210
- src.on('error', reject);
211
- dst.on('error', reject);
212
- dst.on('finish', resolve);
213
- src.pipe(dst);
214
- });
215
- expect(readFileSync(dstPath, 'utf8').length).toBe(size);
216
- } finally {
217
- cleanup();
218
- }
219
- });
220
- });
221
-
222
- // ---- createWriteStream ----
223
-
224
- await describe('fs.createWriteStream', async () => {
225
- await it('should write data to a file', async () => {
226
- setup();
227
- try {
228
- const filePath = join(tmpDir, 'write-test.txt');
229
- await new Promise<void>((resolve, reject) => {
230
- const stream = createWriteStream(filePath);
231
- stream.on('error', reject);
232
- stream.write('hello ');
233
- stream.write('world');
234
- stream.end(() => resolve());
235
- });
236
- expect(readFileSync(filePath, 'utf8')).toBe('hello world');
237
- } finally {
238
- cleanup();
239
- }
240
- });
241
-
242
- await it('should emit finish event', async () => {
243
- setup();
244
- try {
245
- const filePath = join(tmpDir, 'finish-test.txt');
246
- const stream = createWriteStream(filePath);
247
- const finished = await new Promise<boolean>((resolve) => {
248
- stream.on('finish', () => resolve(true));
249
- stream.end('done');
250
- });
251
- expect(finished).toBe(true);
252
- expect(readFileSync(filePath, 'utf8')).toBe('done');
253
- } finally {
254
- cleanup();
255
- }
256
- });
257
-
258
- await it('should write Buffer data', async () => {
259
- setup();
260
- try {
261
- const filePath = join(tmpDir, 'buf-write.txt');
262
- await new Promise<void>((resolve, reject) => {
263
- const stream = createWriteStream(filePath);
264
- stream.on('error', reject);
265
- stream.write(Buffer.from('buffer '));
266
- stream.end(Buffer.from('content'));
267
- stream.on('finish', () => resolve());
268
- });
269
- expect(readFileSync(filePath, 'utf8')).toBe('buffer content');
270
- } finally {
271
- cleanup();
272
- }
273
- });
274
-
275
- await it('should write a large file (64KB)', async () => {
276
- setup();
277
- try {
278
- const filePath = join(tmpDir, 'large-write.txt');
279
- const chunkSize = 1024;
280
- const numChunks = 64;
281
- await new Promise<void>((resolve, reject) => {
282
- const stream = createWriteStream(filePath);
283
- stream.on('error', reject);
284
- for (let i = 0; i < numChunks; i++) {
285
- stream.write('B'.repeat(chunkSize));
286
- }
287
- stream.end(() => resolve());
288
- });
289
- const content = readFileSync(filePath, 'utf8');
290
- expect(content.length).toBe(chunkSize * numChunks);
291
- } finally {
292
- cleanup();
293
- }
294
- });
295
-
296
- await it('should have path property', async () => {
297
- setup();
298
- try {
299
- const filePath = join(tmpDir, 'path-prop-ws.txt');
300
- const stream = createWriteStream(filePath);
301
- expect(stream.path).toBe(filePath);
302
- await new Promise<void>((resolve, reject) => {
303
- stream.on('error', reject);
304
- stream.end('test', () => resolve());
305
- });
306
- } finally {
307
- cleanup();
308
- }
309
- });
310
-
311
- await it('should track bytesWritten', async () => {
312
- setup();
313
- try {
314
- const filePath = join(tmpDir, 'bytes-written.txt');
315
- const stream = createWriteStream(filePath);
316
- await new Promise<void>((resolve, reject) => {
317
- stream.on('error', reject);
318
- stream.write('hello');
319
- stream.end(' world', () => resolve());
320
- });
321
- expect(stream.bytesWritten).toBe(11); // "hello world"
322
- } finally {
323
- cleanup();
324
- }
325
- });
326
- });
327
-
328
- // ---- pipe: ReadStream → WriteStream ----
329
-
330
- await describe('fs pipe: createReadStream → createWriteStream', async () => {
331
- await it('should copy a file via pipe', async () => {
332
- setup();
333
- try {
334
- const srcPath = join(tmpDir, 'src.txt');
335
- const dstPath = join(tmpDir, 'dst.txt');
336
- writeFileSync(srcPath, 'pipe copy test');
337
- await new Promise<void>((resolve, reject) => {
338
- const src = createReadStream(srcPath);
339
- const dst = createWriteStream(dstPath);
340
- src.on('error', reject);
341
- dst.on('error', reject);
342
- dst.on('finish', () => resolve());
343
- src.pipe(dst);
344
- });
345
- expect(readFileSync(dstPath, 'utf8')).toBe('pipe copy test');
346
- } finally {
347
- cleanup();
348
- }
349
- });
350
-
351
- await it('should copy a large file via pipe', async () => {
352
- setup();
353
- try {
354
- const srcPath = join(tmpDir, 'large-src.txt');
355
- const dstPath = join(tmpDir, 'large-dst.txt');
356
- const size = 128 * 1024; // 128KB
357
- writeFileSync(srcPath, 'D'.repeat(size));
358
- await new Promise<void>((resolve, reject) => {
359
- const src = createReadStream(srcPath);
360
- const dst = createWriteStream(dstPath);
361
- src.on('error', reject);
362
- dst.on('error', reject);
363
- dst.on('finish', () => resolve());
364
- src.pipe(dst);
365
- });
366
- const result = readFileSync(dstPath, 'utf8');
367
- expect(result.length).toBe(size);
368
- } finally {
369
- cleanup();
370
- }
371
- });
372
-
373
- await it('should pipe through a Transform (uppercase)', async () => {
374
- setup();
375
- try {
376
- const srcPath = join(tmpDir, 'transform-src.txt');
377
- const dstPath = join(tmpDir, 'transform-dst.txt');
378
- writeFileSync(srcPath, 'hello transform');
379
- const upper = new Transform({
380
- transform(chunk, _enc, cb) {
381
- cb(null, chunk.toString().toUpperCase());
382
- },
383
- });
384
- await new Promise<void>((resolve, reject) => {
385
- const src = createReadStream(srcPath, { encoding: 'utf8' });
386
- const dst = createWriteStream(dstPath);
387
- dst.on('error', reject);
388
- dst.on('finish', () => resolve());
389
- src.pipe(upper).pipe(dst);
390
- });
391
- expect(readFileSync(dstPath, 'utf8')).toBe('HELLO TRANSFORM');
392
- } finally {
393
- cleanup();
394
- }
395
- });
396
-
397
- await it('should pipe through a PassThrough', async () => {
398
- setup();
399
- try {
400
- const srcPath = join(tmpDir, 'pt-src.txt');
401
- const dstPath = join(tmpDir, 'pt-dst.txt');
402
- writeFileSync(srcPath, 'passthrough test');
403
- const pt = new PassThrough();
404
- await new Promise<void>((resolve, reject) => {
405
- const src = createReadStream(srcPath);
406
- const dst = createWriteStream(dstPath);
407
- dst.on('error', reject);
408
- dst.on('finish', () => resolve());
409
- src.pipe(pt).pipe(dst);
410
- });
411
- expect(readFileSync(dstPath, 'utf8')).toBe('passthrough test');
412
- } finally {
413
- cleanup();
414
- }
415
- });
416
- });
417
-
418
- // ---- Multiple sequential reads ----
419
-
420
- await describe('fs streams: sequential operations', async () => {
421
- await it('should read the same file twice sequentially', async () => {
422
- setup();
423
- try {
424
- const filePath = join(tmpDir, 'multi-read.txt');
425
- writeFileSync(filePath, 'read me twice');
426
- const readOnce = (): Promise<string> => new Promise((resolve, reject) => {
427
- const chunks: string[] = [];
428
- const stream = createReadStream(filePath, { encoding: 'utf8' });
429
- stream.on('data', (chunk) => chunks.push(chunk as string));
430
- stream.on('end', () => resolve(chunks.join('')));
431
- stream.on('error', reject);
432
- });
433
- const r1 = await readOnce();
434
- const r2 = await readOnce();
435
- expect(r1).toBe('read me twice');
436
- expect(r2).toBe('read me twice');
437
- } finally {
438
- cleanup();
439
- }
440
- });
441
-
442
- await it('should write then read the same file', async () => {
443
- setup();
444
- try {
445
- const filePath = join(tmpDir, 'write-then-read.txt');
446
- // Write
447
- await new Promise<void>((resolve, reject) => {
448
- const ws = createWriteStream(filePath);
449
- ws.on('error', reject);
450
- ws.end('written content', () => resolve());
451
- });
452
- // Read
453
- const chunks: string[] = [];
454
- await new Promise<void>((resolve, reject) => {
455
- const rs = createReadStream(filePath, { encoding: 'utf8' });
456
- rs.on('data', (chunk) => chunks.push(chunk as string));
457
- rs.on('end', () => resolve());
458
- rs.on('error', reject);
459
- });
460
- expect(chunks.join('')).toBe('written content');
461
- } finally {
462
- cleanup();
463
- }
464
- });
465
- });
466
-
467
- // ---- Unicode and special content ----
468
-
469
- await describe('fs streams: unicode and binary', async () => {
470
- await it('should handle UTF-8 content with multibyte chars', async () => {
471
- setup();
472
- try {
473
- const filePath = join(tmpDir, 'unicode.txt');
474
- const content = 'Hallo Welt! 你好世界 🌍🎉';
475
- writeFileSync(filePath, content);
476
- const chunks: string[] = [];
477
- await new Promise<void>((resolve, reject) => {
478
- const stream = createReadStream(filePath, { encoding: 'utf8' });
479
- stream.on('data', (chunk) => chunks.push(chunk as string));
480
- stream.on('end', () => resolve());
481
- stream.on('error', reject);
482
- });
483
- expect(chunks.join('')).toBe(content);
484
- } finally {
485
- cleanup();
486
- }
487
- });
488
-
489
- await it('should handle binary data round-trip', async () => {
490
- setup();
491
- try {
492
- const filePath = join(tmpDir, 'binary.dat');
493
- const data = Buffer.from([0x00, 0x01, 0x80, 0xff, 0xfe, 0x7f, 0x00, 0x42]);
494
- writeFileSync(filePath, data);
495
- const chunks: Buffer[] = [];
496
- await new Promise<void>((resolve, reject) => {
497
- const stream = createReadStream(filePath);
498
- stream.on('data', (chunk) => chunks.push(Buffer.from(chunk as Uint8Array)));
499
- stream.on('end', () => resolve());
500
- stream.on('error', reject);
501
- });
502
- const result = Buffer.concat(chunks);
503
- expect(result.length).toBe(8);
504
- expect(result[0]).toBe(0x00);
505
- expect(result[2]).toBe(0x80);
506
- expect(result[3]).toBe(0xff);
507
- expect(result[7]).toBe(0x42);
508
- } finally {
509
- cleanup();
510
- }
511
- });
512
- });
513
- };
@@ -1,188 +0,0 @@
1
- // Ported from refs/node-test/parallel/test-fs-symlink.js,
2
- // test-fs-readlink.js, test-fs-realpath.js
3
- // Original: MIT license, Node.js contributors
4
- import { describe, it, expect } from '@gjsify/unit';
5
- import {
6
- symlink as symlinkCb,
7
- mkdtempSync,
8
- rmdirSync,
9
- unlinkSync,
10
- lstatSync,
11
- readlinkSync,
12
- realpathSync,
13
- writeFileSync,
14
- } from 'node:fs';
15
- import { symlink, readlink, realpath, unlink, rmdir, lstat, writeFile, mkdtemp } from 'node:fs/promises';
16
- import { join } from 'node:path';
17
- import { tmpdir } from 'node:os';
18
-
19
- export default async () => {
20
- await describe('fs.symlink (callback)', async () => {
21
-
22
- await it('should throw when no callback provided', async () => {
23
- expect(() => {
24
- // @ts-ignore
25
- symlinkCb('some/path', 'some/other/path', 'dir');
26
- }).toThrow(Error);
27
- });
28
-
29
- await it('should create a symlink pointing to a file', async () => {
30
- const dir = mkdtempSync(join(tmpdir(), 'fs-sym-cb-'));
31
- const target = join(dir, 'target.txt');
32
- const link = join(dir, 'link.txt');
33
- writeFileSync(target, 'symlink target');
34
-
35
- await new Promise<void>((resolve, reject) => {
36
- symlinkCb(target, link, (err) => {
37
- if (err) return reject(err);
38
- try {
39
- const s = lstatSync(link);
40
- expect(s.isSymbolicLink()).toBe(true);
41
- resolve();
42
- } catch (e) {
43
- reject(e);
44
- }
45
- });
46
- });
47
-
48
- unlinkSync(link);
49
- unlinkSync(target);
50
- rmdirSync(dir);
51
- });
52
- });
53
-
54
- await describe('fs.symlink (promise)', async () => {
55
- await it('should create a symlink pointing to a file', async () => {
56
- const dir = await mkdtemp(join(tmpdir(), 'fs-sym-p-'));
57
- const target = join(dir, 'target.txt');
58
- const link = join(dir, 'link.txt');
59
- await writeFile(target, 'symlink target content');
60
-
61
- await symlink(target, link);
62
-
63
- const s = lstatSync(link);
64
- expect(s.isSymbolicLink()).toBe(true);
65
-
66
- await unlink(link);
67
- await unlink(target);
68
- await rmdir(dir);
69
- });
70
-
71
- await it('should throw ENOENT when symlinking to non-existent target', async () => {
72
- const dir = await mkdtemp(join(tmpdir(), 'fs-sym-err-'));
73
- const link = join(dir, 'link.txt');
74
- // Creating a dangling symlink — target doesn't exist
75
- await symlink('/nonexistent/path/12345abc', link);
76
- const s = await lstat(link);
77
- expect(s.isSymbolicLink()).toBe(true);
78
- await unlink(link);
79
- await rmdir(dir);
80
- });
81
-
82
- await it('should throw EEXIST when symlink already exists', async () => {
83
- const dir = await mkdtemp(join(tmpdir(), 'fs-sym-exist-'));
84
- const target = join(dir, 'target.txt');
85
- const link = join(dir, 'link.txt');
86
- await writeFile(target, 'data');
87
- await symlink(target, link);
88
-
89
- let threw = false;
90
- try {
91
- await symlink(target, link);
92
- } catch (e: unknown) {
93
- threw = true;
94
- expect((e as NodeJS.ErrnoException).code).toBe('EEXIST');
95
- }
96
- expect(threw).toBe(true);
97
-
98
- await unlink(link);
99
- await unlink(target);
100
- await rmdir(dir);
101
- });
102
- });
103
-
104
- await describe('fs.readlink', async () => {
105
- await it('should read the symlink target synchronously', async () => {
106
- const dir = mkdtempSync(join(tmpdir(), 'fs-rl-'));
107
- const target = join(dir, 'target.txt');
108
- const link2 = join(dir, 'link2.txt');
109
- writeFileSync(target, 'data');
110
-
111
- await new Promise<void>(resolve => {
112
- symlinkCb(target, link2, () => {
113
- const dest = readlinkSync(link2);
114
- expect(dest).toBe(target);
115
- unlinkSync(link2);
116
- unlinkSync(target);
117
- rmdirSync(dir);
118
- resolve();
119
- });
120
- });
121
- });
122
-
123
- await it('should read the symlink target via promise', async () => {
124
- const dir = await mkdtemp(join(tmpdir(), 'fs-rl-p-'));
125
- const target = join(dir, 'target.txt');
126
- const link = join(dir, 'link.txt');
127
- await writeFile(target, 'data');
128
- await symlink(target, link);
129
-
130
- const dest = await readlink(link);
131
- expect(dest).toBe(target);
132
-
133
- await unlink(link);
134
- await unlink(target);
135
- await rmdir(dir);
136
- });
137
-
138
- await it('should throw ENOENT when reading non-existent symlink', async () => {
139
- let threw = false;
140
- try {
141
- await readlink('/nonexistent/path/no-link-xyz');
142
- } catch (e: unknown) {
143
- threw = true;
144
- expect((e as NodeJS.ErrnoException).code).toBe('ENOENT');
145
- }
146
- expect(threw).toBe(true);
147
- });
148
- });
149
-
150
- await describe('fs.realpath', async () => {
151
- await it('should resolve a symlink to real path (sync)', async () => {
152
- const dir = mkdtempSync(join(tmpdir(), 'fs-rp-'));
153
- const target = join(dir, 'realfile.txt');
154
- const link = join(dir, 'link.txt');
155
- writeFileSync(target, 'realpath test');
156
-
157
- await new Promise<void>(resolve => {
158
- symlinkCb(target, link, () => {
159
- const real = realpathSync(link);
160
- // realpath resolves symlinks to actual path
161
- expect(typeof real).toBe('string');
162
- expect(real.length).toBeGreaterThan(0);
163
- unlinkSync(link);
164
- resolve();
165
- });
166
- });
167
-
168
- unlinkSync(target);
169
- rmdirSync(dir);
170
- });
171
-
172
- await it('should resolve a symlink to real path (promise)', async () => {
173
- const dir = await mkdtemp(join(tmpdir(), 'fs-rp-p-'));
174
- const target = join(dir, 'realfile.txt');
175
- const link = join(dir, 'link.txt');
176
- await writeFile(target, 'realpath test');
177
- await symlink(target, link);
178
-
179
- const real = await realpath(link);
180
- expect(typeof real).toBe('string');
181
- expect(real.length).toBeGreaterThan(0);
182
-
183
- await unlink(link);
184
- await unlink(target);
185
- await rmdir(dir);
186
- });
187
- });
188
- };