@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.
- package/README.md +31 -2
- package/lib/esm/callback.js +251 -15
- package/lib/esm/dirent.js +47 -6
- package/lib/esm/encoding.js +2 -3
- package/lib/esm/errors.js +13 -0
- package/lib/esm/file-handle.js +108 -66
- package/lib/esm/fs-watcher.js +44 -7
- package/lib/esm/index.js +140 -5
- package/lib/esm/promises.js +290 -69
- package/lib/esm/read-stream.js +82 -57
- package/lib/esm/stats.js +138 -18
- package/lib/esm/sync.js +293 -44
- package/lib/esm/write-stream.js +4 -4
- package/lib/types/callback.d.ts +233 -0
- package/lib/types/dirent.d.ts +77 -0
- package/lib/types/encoding.d.ts +6 -0
- package/lib/types/errors.d.ts +7 -0
- package/lib/types/file-handle.d.ts +367 -0
- package/lib/types/fs-watcher.d.ts +17 -0
- package/lib/types/index.d.ts +149 -0
- package/lib/types/promises.d.ts +158 -0
- package/lib/types/read-stream.d.ts +21 -0
- package/lib/types/stats.d.ts +67 -0
- package/lib/types/sync.d.ts +109 -0
- package/lib/types/types/encoding-option.d.ts +2 -0
- package/lib/types/types/file-read-options.d.ts +15 -0
- package/lib/types/types/file-read-result.d.ts +4 -0
- package/lib/types/types/flag-and-open-mode.d.ts +5 -0
- package/lib/types/types/index.d.ts +6 -0
- package/lib/types/types/open-flags.d.ts +1 -0
- package/lib/types/types/read-options.d.ts +5 -0
- package/lib/types/utils.d.ts +2 -0
- package/lib/types/write-stream.d.ts +45 -0
- package/package.json +22 -35
- package/src/callback.spec.ts +284 -30
- package/src/callback.ts +352 -39
- package/src/dirent.ts +56 -8
- package/src/encoding.ts +7 -2
- package/src/errors.spec.ts +389 -0
- package/src/errors.ts +19 -0
- package/src/extended.spec.ts +706 -0
- package/src/file-handle.spec.ts +104 -23
- package/src/file-handle.ts +147 -79
- package/src/fs-watcher.ts +55 -8
- package/src/index.ts +146 -2
- package/src/new-apis.spec.ts +505 -0
- package/src/promises.spec.ts +651 -11
- package/src/promises.ts +353 -81
- package/src/read-stream.ts +98 -74
- package/src/stat.spec.ts +22 -14
- package/src/stats.ts +176 -75
- package/src/streams.spec.ts +455 -0
- package/src/symlink.spec.ts +176 -26
- package/src/sync.spec.ts +204 -32
- package/src/sync.ts +363 -58
- package/src/test.mts +7 -2
- package/src/types/encoding-option.ts +1 -1
- package/src/types/flag-and-open-mode.ts +1 -1
- package/src/types/read-options.ts +2 -2
- package/src/utils.ts +2 -0
- package/src/write-stream.ts +9 -7
- package/tsconfig.json +23 -10
- package/tsconfig.tsbuildinfo +1 -0
- package/lib/cjs/callback.js +0 -112
- package/lib/cjs/dirent.js +0 -98
- package/lib/cjs/encoding.js +0 -34
- package/lib/cjs/file-handle.js +0 -444
- package/lib/cjs/fs-watcher.js +0 -50
- package/lib/cjs/index.js +0 -95
- package/lib/cjs/promises.js +0 -160
- package/lib/cjs/read-stream.js +0 -78
- package/lib/cjs/stats.js +0 -45
- package/lib/cjs/sync.js +0 -126
- package/lib/cjs/types/encoding-option.js +0 -0
- package/lib/cjs/types/file-read-options.js +0 -0
- package/lib/cjs/types/file-read-result.js +0 -0
- package/lib/cjs/types/flag-and-open-mode.js +0 -0
- package/lib/cjs/types/index.js +0 -6
- package/lib/cjs/types/open-flags.js +0 -0
- package/lib/cjs/types/read-options.js +0 -0
- package/lib/cjs/utils.js +0 -18
- package/lib/cjs/write-stream.js +0 -116
- package/test/watch.js +0 -1
- package/test.gjs.js +0 -35359
- package/test.gjs.js.map +0 -7
- package/test.gjs.mjs +0 -40534
- package/test.gjs.mjs.meta.json +0 -1
- package/test.node.js +0 -1479
- package/test.node.js.map +0 -7
- package/test.node.mjs +0 -710
- 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 = ['
|
|
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
|
|
93
|
-
expect(fileWithTypes
|
|
93
|
+
expect(fileWithTypes!.isFile()).toBeTruthy();
|
|
94
|
+
expect(fileWithTypes!.isDirectory()).toBeFalsy();
|
|
94
95
|
|
|
95
|
-
expect(dirWithTypes
|
|
96
|
-
expect(dirWithTypes
|
|
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('
|
|
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
|
|
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
|
|
117
|
-
const
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
179
|
+
writeFileSync(watchMe, '// test');
|
|
180
|
+
setTimeout(() => {
|
|
181
|
+
try { unlinkSync(watchMe); } catch {}
|
|
182
|
+
watcher.close();
|
|
183
|
+
resolve();
|
|
184
|
+
}, 100);
|
|
164
185
|
}, 100);
|
|
165
|
-
|
|
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
|
|
190
|
-
const
|
|
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
|
-
|
|
193
|
-
|
|
221
|
+
// Should point to the real file, not the symlink
|
|
222
|
+
expect(realSymLinkPath).toBe(realPath);
|
|
194
223
|
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
|
|
199
|
-
|
|
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
|
-
|
|
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
|
}
|