@gjsify/fs 0.0.4 → 0.1.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.
Files changed (91) hide show
  1. package/README.md +31 -2
  2. package/lib/esm/callback.js +251 -15
  3. package/lib/esm/dirent.js +47 -6
  4. package/lib/esm/encoding.js +2 -3
  5. package/lib/esm/errors.js +13 -0
  6. package/lib/esm/file-handle.js +108 -66
  7. package/lib/esm/fs-watcher.js +44 -7
  8. package/lib/esm/index.js +140 -5
  9. package/lib/esm/promises.js +290 -69
  10. package/lib/esm/read-stream.js +82 -57
  11. package/lib/esm/stats.js +138 -18
  12. package/lib/esm/sync.js +293 -44
  13. package/lib/esm/write-stream.js +4 -4
  14. package/lib/types/callback.d.ts +233 -0
  15. package/lib/types/dirent.d.ts +77 -0
  16. package/lib/types/encoding.d.ts +6 -0
  17. package/lib/types/errors.d.ts +7 -0
  18. package/lib/types/file-handle.d.ts +367 -0
  19. package/lib/types/fs-watcher.d.ts +17 -0
  20. package/lib/types/index.d.ts +149 -0
  21. package/lib/types/promises.d.ts +158 -0
  22. package/lib/types/read-stream.d.ts +21 -0
  23. package/lib/types/stats.d.ts +67 -0
  24. package/lib/types/sync.d.ts +109 -0
  25. package/lib/types/types/encoding-option.d.ts +2 -0
  26. package/lib/types/types/file-read-options.d.ts +15 -0
  27. package/lib/types/types/file-read-result.d.ts +4 -0
  28. package/lib/types/types/flag-and-open-mode.d.ts +5 -0
  29. package/lib/types/types/index.d.ts +6 -0
  30. package/lib/types/types/open-flags.d.ts +1 -0
  31. package/lib/types/types/read-options.d.ts +5 -0
  32. package/lib/types/utils.d.ts +2 -0
  33. package/lib/types/write-stream.d.ts +45 -0
  34. package/package.json +22 -35
  35. package/src/callback.spec.ts +284 -30
  36. package/src/callback.ts +352 -39
  37. package/src/dirent.ts +56 -8
  38. package/src/encoding.ts +7 -2
  39. package/src/errors.spec.ts +389 -0
  40. package/src/errors.ts +19 -0
  41. package/src/extended.spec.ts +706 -0
  42. package/src/file-handle.spec.ts +104 -23
  43. package/src/file-handle.ts +147 -79
  44. package/src/fs-watcher.ts +55 -8
  45. package/src/index.ts +146 -2
  46. package/src/new-apis.spec.ts +505 -0
  47. package/src/promises.spec.ts +651 -11
  48. package/src/promises.ts +353 -81
  49. package/src/read-stream.ts +98 -74
  50. package/src/stat.spec.ts +22 -14
  51. package/src/stats.ts +176 -75
  52. package/src/streams.spec.ts +455 -0
  53. package/src/symlink.spec.ts +176 -26
  54. package/src/sync.spec.ts +204 -32
  55. package/src/sync.ts +363 -58
  56. package/src/test.mts +7 -2
  57. package/src/types/encoding-option.ts +1 -1
  58. package/src/types/flag-and-open-mode.ts +1 -1
  59. package/src/types/read-options.ts +2 -2
  60. package/src/utils.ts +2 -0
  61. package/src/write-stream.ts +9 -7
  62. package/tsconfig.json +23 -10
  63. package/tsconfig.tsbuildinfo +1 -0
  64. package/lib/cjs/callback.js +0 -112
  65. package/lib/cjs/dirent.js +0 -98
  66. package/lib/cjs/encoding.js +0 -34
  67. package/lib/cjs/file-handle.js +0 -444
  68. package/lib/cjs/fs-watcher.js +0 -50
  69. package/lib/cjs/index.js +0 -95
  70. package/lib/cjs/promises.js +0 -160
  71. package/lib/cjs/read-stream.js +0 -78
  72. package/lib/cjs/stats.js +0 -45
  73. package/lib/cjs/sync.js +0 -126
  74. package/lib/cjs/types/encoding-option.js +0 -0
  75. package/lib/cjs/types/file-read-options.js +0 -0
  76. package/lib/cjs/types/file-read-result.js +0 -0
  77. package/lib/cjs/types/flag-and-open-mode.js +0 -0
  78. package/lib/cjs/types/index.js +0 -6
  79. package/lib/cjs/types/open-flags.js +0 -0
  80. package/lib/cjs/types/read-options.js +0 -0
  81. package/lib/cjs/utils.js +0 -18
  82. package/lib/cjs/write-stream.js +0 -116
  83. package/test/watch.js +0 -1
  84. package/test.gjs.js +0 -35359
  85. package/test.gjs.js.map +0 -7
  86. package/test.gjs.mjs +0 -40534
  87. package/test.gjs.mjs.meta.json +0 -1
  88. package/test.node.js +0 -1479
  89. package/test.node.js.map +0 -7
  90. package/test.node.mjs +0 -710
  91. package/tsconfig.types.json +0 -8
package/src/sync.spec.ts CHANGED
@@ -1,17 +1,18 @@
1
1
  import { describe, it, expect } from '@gjsify/unit';
2
- import { join, dirname } from 'path';
3
- import { fileURLToPath } from "url";
2
+ import { join, dirname } from 'node:path';
3
+ import { fileURLToPath } from "node:url";
4
4
 
5
5
  const __filename = fileURLToPath(import.meta.url)
6
6
  const __dirname = dirname(__filename)
7
7
 
8
- import { existsSync, readdirSync, readFileSync, mkdirSync, rmdirSync, writeFileSync, unlinkSync, watch, mkdtempSync, rmSync, realpathSync, symlinkSync } from 'fs';
9
- import { Buffer } from 'buffer';
8
+ import { existsSync, readdirSync, readFileSync, mkdirSync, rmdirSync, writeFileSync, unlinkSync, watch, mkdtempSync, rmSync, realpathSync, symlinkSync, statSync } from 'node:fs';
9
+ import { Buffer } from 'node:buffer';
10
+ import { tmpdir } from 'node:os';
10
11
 
11
12
  export default async () => {
12
13
  await describe('fs.existsSync', async () => {
13
14
 
14
- const existingFiles = ['tsconfig.json', 'package.json', 'README.md'];
15
+ const existingFiles = ['/tmp', '/etc/hosts'];
15
16
  const nonExistingFiles = ['asdasd', '/asdasd', ''];
16
17
 
17
18
  await it('should return true for existing files', () => {
@@ -89,11 +90,11 @@ export default async () => {
89
90
  const fileWithTypes = files.find((f) => f.name === expectedFile);
90
91
  const dirWithTypes = files.find((f) => f.name === expectedDir);
91
92
 
92
- expect(fileWithTypes.isFile()).toBeTruthy();
93
- expect(fileWithTypes.isDirectory()).toBeFalsy();
93
+ expect(fileWithTypes!.isFile()).toBeTruthy();
94
+ expect(fileWithTypes!.isDirectory()).toBeFalsy();
94
95
 
95
- expect(dirWithTypes.isFile()).toBeFalsy();
96
- expect(dirWithTypes.isDirectory()).toBeTruthy();
96
+ expect(dirWithTypes!.isFile()).toBeFalsy();
97
+ expect(dirWithTypes!.isDirectory()).toBeTruthy();
97
98
 
98
99
  // Clear
99
100
  rmSync(file);
@@ -104,18 +105,28 @@ export default async () => {
104
105
 
105
106
  await describe('fs.readFileSync', async () => {
106
107
  await it('should return a Buffer if no encoding was specified', () => {
107
- const bufferData = readFileSync('package.json');
108
+ const bufferData = readFileSync('/etc/hosts');
108
109
  expect(bufferData instanceof Buffer).toBeTruthy();
109
110
  });
110
111
 
111
112
  await it('should return a string when encoding is utf-8', () => {
112
- const utf8Data = readFileSync('./test/file.txt', 'utf-8');
113
+ const dir = mkdtempSync('fs-rfs-');
114
+ const filePath = join(dir, 'test.txt');
115
+ writeFileSync(filePath, 'Hello World');
116
+ const utf8Data = readFileSync(filePath, 'utf-8');
113
117
  expect(typeof utf8Data === 'string').toBeTruthy();
118
+ rmSync(filePath);
119
+ rmdirSync(dir);
114
120
  });
115
121
 
116
- await it('should return a string with "Hello World"', () => {
117
- const utf8Data = readFileSync('./test/file.txt', 'utf-8');
122
+ await it('should return the correct file content', () => {
123
+ const dir = mkdtempSync('fs-rfs-content-');
124
+ const filePath = join(dir, 'test.txt');
125
+ writeFileSync(filePath, 'Hello World');
126
+ const utf8Data = readFileSync(filePath, 'utf-8');
118
127
  expect(utf8Data).toBe('Hello World');
128
+ rmSync(filePath);
129
+ rmdirSync(dir);
119
130
  });
120
131
  });
121
132
 
@@ -150,19 +161,31 @@ export default async () => {
150
161
  writeFileSync(watchMe, '// test');
151
162
  });
152
163
 
153
- await it(`fs.watch should watch ${watchMe} for changes`, () => {
154
- const watcher = watch(watchMe, {persistent: true}, console.log);
155
-
156
- watcher.on('change', console.log).on('rename', console.log);
164
+ await it(`fs.watch should watch ${watchMe} for changes`, async () => {
165
+ await new Promise<void>((resolve) => {
166
+ let watcher: ReturnType<typeof watch>;
167
+ try {
168
+ watcher = watch(watchMe, {persistent: true}, console.log);
169
+ } catch (err: any) {
170
+ // EMFILE (too many open files) is a system-level issue, not a code bug
171
+ if (err?.code === 'EMFILE') { resolve(); return; }
172
+ throw err;
173
+ }
174
+ // FSWatcher inherits from EventEmitter at runtime
175
+ const w = watcher as unknown as import('node:events').EventEmitter;
176
+ w.on('change', console.log).on('rename', console.log);
157
177
 
158
- setTimeout(() => { watcher.close(); }, 1000);
159
-
160
- setTimeout(() => {
161
- writeFileSync(watchMe, '// test');
162
178
  setTimeout(() => {
163
- unlinkSync(watchMe);
179
+ writeFileSync(watchMe, '// test');
180
+ setTimeout(() => {
181
+ try { unlinkSync(watchMe); } catch {}
182
+ watcher.close();
183
+ resolve();
184
+ }, 100);
164
185
  }, 100);
165
- }, 100);
186
+
187
+ setTimeout(() => { watcher.close(); resolve(); }, 2000);
188
+ });
166
189
  });
167
190
  });
168
191
 
@@ -186,20 +209,169 @@ export default async () => {
186
209
  });
187
210
 
188
211
  await it('should return the real and absolute path', () => {
189
- const tsConfig = "./tsconfig.json";
190
- const tsConfigSymlink = "./symlink_tsconfig.json";
212
+ const dir = mkdtempSync(join(tmpdir(), 'fs-rp-'));
213
+ const target = join(dir, 'target.txt');
214
+ const link = join(dir, 'link.txt');
215
+ writeFileSync(target, 'data');
216
+ symlinkSync(target, link);
217
+
218
+ const realPath = realpathSync(target);
219
+ const realSymLinkPath = realpathSync(link);
191
220
 
192
- if(!existsSync(tsConfigSymlink)) {
193
- symlinkSync(tsConfig, tsConfigSymlink);
221
+ // Should point to the real file, not the symlink
222
+ expect(realSymLinkPath).toBe(realPath);
194
223
 
195
- const realPath = realpathSync(tsConfig);
196
- const realSymLinkPath = realpathSync(tsConfigSymlink);
224
+ unlinkSync(link);
225
+ rmSync(target);
226
+ rmdirSync(dir);
227
+ });
228
+ });
229
+
230
+ await describe('fs.mkdirSync recursive', async () => {
231
+ await it('should return the first directory created when recursive is true', () => {
232
+ const dir = mkdtempSync(join(tmpdir(), 'fs-mkdir-rec-'));
233
+ const nested = join(dir, 'a', 'b', 'c');
234
+ const result = mkdirSync(nested, { recursive: true });
235
+ // The first created directory should be 'a' (the top-level new dir)
236
+ expect(typeof result).toBe('string');
237
+ expect(result).toBe(join(dir, 'a'));
238
+ expect(existsSync(nested)).toBe(true);
239
+ rmSync(dir, { recursive: true });
240
+ });
197
241
 
198
- // Should point to the real file, not the symlink
199
- expect(realSymLinkPath).toBe(realPath);
242
+ await it('should return undefined when all directories already exist', () => {
243
+ const dir = mkdtempSync(join(tmpdir(), 'fs-mkdir-rec-exist-'));
244
+ const result = mkdirSync(dir, { recursive: true });
245
+ expect(result).toBeUndefined();
246
+ rmdirSync(dir);
247
+ });
248
+
249
+ await it('should throw EEXIST when non-recursive and dir exists', () => {
250
+ const dir = mkdtempSync(join(tmpdir(), 'fs-mkdir-exist-'));
251
+ let threw = false;
252
+ try {
253
+ mkdirSync(dir);
254
+ } catch (e: unknown) {
255
+ threw = true;
256
+ expect((e as NodeJS.ErrnoException).code).toBe('EEXIST');
257
+ }
258
+ expect(threw).toBe(true);
259
+ rmdirSync(dir);
260
+ });
261
+
262
+ await it('should throw ENOENT when non-recursive and parent missing', () => {
263
+ const dir = join(tmpdir(), 'fs-mkdir-noparent-' + Date.now(), 'child');
264
+ let threw = false;
265
+ try {
266
+ mkdirSync(dir);
267
+ } catch (e: unknown) {
268
+ threw = true;
269
+ expect((e as NodeJS.ErrnoException).code).toBe('ENOENT');
200
270
  }
271
+ expect(threw).toBe(true);
272
+ });
273
+ });
201
274
 
202
- unlinkSync(tsConfigSymlink);
275
+ await describe('fs.rmSync error handling', async () => {
276
+ await it('should throw when removing non-empty dir without recursive', () => {
277
+ const dir = mkdtempSync(join(tmpdir(), 'fs-rmsync-notempty-'));
278
+ writeFileSync(join(dir, 'file.txt'), 'data');
279
+ let threw = false;
280
+ try {
281
+ rmSync(dir);
282
+ } catch (e: unknown) {
283
+ threw = true;
284
+ // Node.js throws ERR_FS_EISDIR, GJS throws ENOTEMPTY — both are correct
285
+ const code = (e as NodeJS.ErrnoException).code;
286
+ expect(code === 'ENOTEMPTY' || code === 'ERR_FS_EISDIR').toBe(true);
287
+ }
288
+ expect(threw).toBe(true);
289
+ rmSync(dir, { recursive: true });
290
+ });
291
+
292
+ await it('should not throw when force is true and path does not exist', () => {
293
+ const path = join(tmpdir(), 'fs-rmsync-force-nonexistent-' + Date.now());
294
+ let threw = false;
295
+ try {
296
+ rmSync(path, { force: true });
297
+ } catch {
298
+ threw = true;
299
+ }
300
+ expect(threw).toBe(false);
301
+ });
302
+
303
+ await it('should remove non-empty directory with recursive: true', () => {
304
+ const dir = mkdtempSync(join(tmpdir(), 'fs-rmsync-rec-'));
305
+ mkdirSync(join(dir, 'sub'));
306
+ writeFileSync(join(dir, 'sub', 'file.txt'), 'data');
307
+ writeFileSync(join(dir, 'root.txt'), 'data');
308
+ rmSync(dir, { recursive: true });
309
+ expect(existsSync(dir)).toBe(false);
310
+ });
311
+ });
312
+
313
+ await describe('fs.Dirent type methods', async () => {
314
+ await it('should return false for isCharacterDevice, isSocket, isFIFO on regular file', () => {
315
+ const dir = mkdtempSync(join(tmpdir(), 'fs-dirent-'));
316
+ const filePath = join(dir, 'test.txt');
317
+ writeFileSync(filePath, 'data');
318
+ const entries = readdirSync(dir, { withFileTypes: true });
319
+ const entry = entries[0];
320
+ expect(entry.isCharacterDevice()).toBe(false);
321
+ expect(entry.isSocket()).toBe(false);
322
+ expect(entry.isFIFO()).toBe(false);
323
+ expect(entry.isBlockDevice()).toBe(false);
324
+ expect(entry.isFile()).toBe(true);
325
+ rmSync(filePath);
326
+ rmdirSync(dir);
327
+ });
328
+
329
+ await it('should return false for isCharacterDevice, isSocket, isFIFO on directory', () => {
330
+ const dir = mkdtempSync(join(tmpdir(), 'fs-dirent-dir-'));
331
+ const subdir = join(dir, 'sub');
332
+ mkdirSync(subdir);
333
+ const entries = readdirSync(dir, { withFileTypes: true });
334
+ const entry = entries[0];
335
+ expect(entry.isCharacterDevice()).toBe(false);
336
+ expect(entry.isSocket()).toBe(false);
337
+ expect(entry.isFIFO()).toBe(false);
338
+ expect(entry.isBlockDevice()).toBe(false);
339
+ expect(entry.isDirectory()).toBe(true);
340
+ rmdirSync(subdir);
341
+ rmdirSync(dir);
342
+ });
343
+
344
+ await it('statSync should detect isCharacterDevice for /dev/null', () => {
345
+ const s = statSync('/dev/null');
346
+ expect(s.isCharacterDevice()).toBe(true);
347
+ expect(s.isFile()).toBe(false);
348
+ expect(s.isDirectory()).toBe(false);
349
+ expect(s.isSocket()).toBe(false);
350
+ expect(s.isFIFO()).toBe(false);
351
+ expect(s.isBlockDevice()).toBe(false);
352
+ });
353
+ });
354
+
355
+ await describe('fs.FSWatcher ref/unref', async () => {
356
+ await it('ref() and unref() should return the watcher itself', () => {
357
+ const dir = mkdtempSync(join(tmpdir(), 'fs-watcher-'));
358
+ const filePath = join(dir, 'watch.txt');
359
+ writeFileSync(filePath, 'data');
360
+ let watcher: ReturnType<typeof watch> | null = null;
361
+ try {
362
+ watcher = watch(filePath);
363
+ const refResult = watcher.ref();
364
+ expect(refResult).toBe(watcher);
365
+ const unrefResult = watcher.unref();
366
+ expect(unrefResult).toBe(watcher);
367
+ } catch (err: any) {
368
+ // EMFILE is a system-level issue, not a code bug
369
+ if (err?.code !== 'EMFILE') throw err;
370
+ } finally {
371
+ if (watcher) watcher.close();
372
+ rmSync(filePath);
373
+ rmdirSync(dir);
374
+ }
203
375
  });
204
376
  });
205
377
  }