@gjsify/fs 0.0.3 → 0.1.0
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 -34
- 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 -40570
- 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/callback.ts
CHANGED
|
@@ -1,14 +1,104 @@
|
|
|
1
|
+
// Reference: Node.js lib/fs.js (callback API)
|
|
2
|
+
// Reimplemented for GJS using Gio.File async operations
|
|
3
|
+
|
|
4
|
+
import GLib from '@girs/glib-2.0';
|
|
5
|
+
import Gio from '@girs/gio-2.0';
|
|
1
6
|
import { open as openP, rm as rmP } from './promises.js'
|
|
2
|
-
import {
|
|
3
|
-
import { PathLike, OpenMode, Mode, ReadPosition, ReadAsyncOptions, NoParamCallback, RmOptions } from 'fs';
|
|
7
|
+
import { PathLike, OpenMode, Mode, ReadPosition, ReadAsyncOptions, NoParamCallback, RmOptions, RmDirOptions, MakeDirectoryOptions } from 'node:fs';
|
|
4
8
|
import { FileHandle } from './file-handle.js';
|
|
5
|
-
import { Buffer } from 'buffer';
|
|
9
|
+
import { Buffer } from 'node:buffer';
|
|
10
|
+
import { Stats, BigIntStats, STAT_ATTRIBUTES } from './stats.js';
|
|
11
|
+
import { createNodeError } from './errors.js';
|
|
12
|
+
import { realpathSync, readdirSync, renameSync, copyFileSync, accessSync, appendFileSync, readlinkSync, truncateSync, chmodSync, chownSync, mkdirSync, rmdirSync, readFileSync, writeFileSync } from './sync.js';
|
|
13
|
+
// encoding helpers available if needed in future
|
|
14
|
+
|
|
15
|
+
import type { OpenFlags } from './types/index.js';
|
|
16
|
+
|
|
17
|
+
// --- helpers ---
|
|
18
|
+
|
|
19
|
+
function parseOptsCb(optionsOrCallback: unknown, maybeCallback?: Function): { options: Record<string, unknown>; callback: Function } {
|
|
20
|
+
return typeof optionsOrCallback === 'function'
|
|
21
|
+
? { options: {}, callback: optionsOrCallback }
|
|
22
|
+
: { options: (optionsOrCallback ?? {}) as Record<string, unknown>, callback: maybeCallback! };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function statImpl(path: PathLike, flags: Gio.FileQueryInfoFlags, syscall: string, options: Record<string, unknown>, callback: Function): void {
|
|
26
|
+
const file = Gio.File.new_for_path(path.toString());
|
|
27
|
+
file.query_info_async(STAT_ATTRIBUTES, flags, GLib.PRIORITY_DEFAULT, null, (_s: Gio.File, res: Gio.AsyncResult) => {
|
|
28
|
+
try {
|
|
29
|
+
const info = file.query_info_finish(res);
|
|
30
|
+
callback(null, options?.bigint ? new BigIntStats(info, path) : new Stats(info, path));
|
|
31
|
+
} catch (err: unknown) {
|
|
32
|
+
callback(createNodeError(err, syscall, path));
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// --- stat / lstat ---
|
|
38
|
+
|
|
39
|
+
export function stat(path: PathLike, callback: (err: NodeJS.ErrnoException | null, stats: Stats) => void): void;
|
|
40
|
+
export function stat(path: PathLike, options: { bigint?: boolean }, callback: (err: NodeJS.ErrnoException | null, stats: Stats | BigIntStats) => void): void;
|
|
41
|
+
export function stat(path: PathLike, optionsOrCallback: { bigint?: boolean } | ((err: NodeJS.ErrnoException | null, stats: Stats) => void), maybeCallback?: Function): void {
|
|
42
|
+
const { options, callback } = parseOptsCb(optionsOrCallback, maybeCallback);
|
|
43
|
+
statImpl(path, Gio.FileQueryInfoFlags.NONE, 'stat', options, callback);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function lstat(path: PathLike, callback: (err: NodeJS.ErrnoException | null, stats: Stats) => void): void;
|
|
47
|
+
export function lstat(path: PathLike, options: { bigint?: boolean }, callback: (err: NodeJS.ErrnoException | null, stats: Stats | BigIntStats) => void): void;
|
|
48
|
+
export function lstat(path: PathLike, optionsOrCallback: { bigint?: boolean } | ((err: NodeJS.ErrnoException | null, stats: Stats) => void), maybeCallback?: Function): void {
|
|
49
|
+
const { options, callback } = parseOptsCb(optionsOrCallback, maybeCallback);
|
|
50
|
+
statImpl(path, Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, 'lstat', options, callback);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// --- readdir ---
|
|
54
|
+
|
|
55
|
+
export function readdir(path: PathLike, callback: (err: NodeJS.ErrnoException | null, files: string[]) => void): void;
|
|
56
|
+
export function readdir(path: PathLike, options: { withFileTypes?: boolean; encoding?: string; recursive?: boolean }, callback: (err: NodeJS.ErrnoException | null, files: string[] | unknown[]) => void): void;
|
|
57
|
+
export function readdir(path: PathLike, optionsOrCallback: { withFileTypes?: boolean; encoding?: string; recursive?: boolean } | ((err: NodeJS.ErrnoException | null, files: string[]) => void), maybeCallback?: Function): void {
|
|
58
|
+
const { options, callback } = parseOptsCb(optionsOrCallback, maybeCallback);
|
|
59
|
+
Promise.resolve().then(() => {
|
|
60
|
+
try {
|
|
61
|
+
callback(null, readdirSync(path, options as { withFileTypes?: boolean; encoding?: string; recursive?: boolean }));
|
|
62
|
+
} catch (err: unknown) {
|
|
63
|
+
callback(createNodeError(err, 'readdir', path));
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// --- realpath ---
|
|
69
|
+
|
|
70
|
+
export function realpath(path: PathLike, callback: (err: NodeJS.ErrnoException | null, resolvedPath: string) => void): void;
|
|
71
|
+
export function realpath(path: PathLike, options: { encoding?: BufferEncoding }, callback: (err: NodeJS.ErrnoException | null, resolvedPath: string) => void): void;
|
|
72
|
+
export function realpath(path: PathLike, optionsOrCallback: { encoding?: BufferEncoding } | ((err: NodeJS.ErrnoException | null, resolvedPath: string) => void), maybeCallback?: Function): void {
|
|
73
|
+
const { callback } = parseOptsCb(optionsOrCallback, maybeCallback);
|
|
74
|
+
Promise.resolve().then(() => {
|
|
75
|
+
try {
|
|
76
|
+
callback(null, realpathSync(path));
|
|
77
|
+
} catch (err: unknown) {
|
|
78
|
+
callback(err as NodeJS.ErrnoException);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
6
82
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
export
|
|
11
|
-
export
|
|
83
|
+
// --- symlink ---
|
|
84
|
+
|
|
85
|
+
export function symlink(target: PathLike, path: PathLike, callback: NoParamCallback): void;
|
|
86
|
+
export function symlink(target: PathLike, path: PathLike, type: string | null, callback: NoParamCallback): void;
|
|
87
|
+
export function symlink(target: PathLike, path: PathLike, typeOrCallback: string | null | NoParamCallback, maybeCallback?: NoParamCallback): void {
|
|
88
|
+
const callback: NoParamCallback = typeof typeOrCallback === 'function' ? typeOrCallback : maybeCallback!;
|
|
89
|
+
if (typeof callback !== 'function') {
|
|
90
|
+
throw new TypeError('Callback must be a function. Received ' + typeof callback);
|
|
91
|
+
}
|
|
92
|
+
const file = Gio.File.new_for_path(path.toString());
|
|
93
|
+
file.make_symbolic_link_async(target.toString(), GLib.PRIORITY_DEFAULT, null, (_s: Gio.File, res: Gio.AsyncResult) => {
|
|
94
|
+
try {
|
|
95
|
+
file.make_symbolic_link_finish(res);
|
|
96
|
+
callback(null);
|
|
97
|
+
} catch (err: unknown) {
|
|
98
|
+
callback(createNodeError(err, 'symlink', target, path));
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
12
102
|
|
|
13
103
|
type OpenCallback = (err: NodeJS.ErrnoException | null, fd: number) => void;
|
|
14
104
|
|
|
@@ -47,29 +137,29 @@ export function open(path: PathLike, flags: OpenMode | undefined, callback: Open
|
|
|
47
137
|
*/
|
|
48
138
|
export function open(path: PathLike, callback: OpenCallback): void;
|
|
49
139
|
|
|
50
|
-
export function open(path: PathLike, ...args:
|
|
51
|
-
let flags:
|
|
140
|
+
export function open(path: PathLike, ...args: (OpenMode | Mode | OpenCallback | undefined | null)[]): void {
|
|
141
|
+
let flags: OpenMode | undefined;
|
|
52
142
|
let mode: Mode | undefined | null;
|
|
53
143
|
let callback: OpenCallback
|
|
54
144
|
|
|
55
145
|
switch (args.length) {
|
|
56
146
|
case 1:
|
|
57
|
-
callback = args[0]
|
|
147
|
+
callback = args[0] as OpenCallback
|
|
58
148
|
break;
|
|
59
149
|
case 2:
|
|
60
|
-
flags = args[0]
|
|
61
|
-
callback = args[1]
|
|
150
|
+
flags = args[0] as OpenMode | undefined
|
|
151
|
+
callback = args[1] as OpenCallback
|
|
62
152
|
break;
|
|
63
153
|
case 3:
|
|
64
|
-
flags = args[0]
|
|
65
|
-
mode = args[1]
|
|
66
|
-
callback = args[2]
|
|
154
|
+
flags = args[0] as OpenMode | undefined
|
|
155
|
+
mode = args[1] as Mode | undefined | null
|
|
156
|
+
callback = args[2] as OpenCallback
|
|
67
157
|
break;
|
|
68
158
|
default:
|
|
69
159
|
break;
|
|
70
160
|
}
|
|
71
161
|
|
|
72
|
-
openP(path, flags as
|
|
162
|
+
openP(path, flags as OpenFlags | undefined, mode)
|
|
73
163
|
.then((fileHandle) => {
|
|
74
164
|
callback(null, fileHandle.fd);
|
|
75
165
|
})
|
|
@@ -167,14 +257,16 @@ export function write(fd: number, string: string, position: number | undefined |
|
|
|
167
257
|
*/
|
|
168
258
|
export function write(fd: number, string: string, callback: WriteStrCallback): void;
|
|
169
259
|
|
|
170
|
-
export function write<TBuffer extends NodeJS.ArrayBufferView>(fd: number, data: string | TBuffer, ...args:
|
|
260
|
+
export function write<TBuffer extends NodeJS.ArrayBufferView>(fd: number, data: string | TBuffer, ...args: (number | string | BufferEncoding | WriteStrCallback | WriteBufCallback | undefined | null)[]): void {
|
|
171
261
|
|
|
172
262
|
const fileHandle = FileHandle.getInstance(fd);
|
|
173
|
-
|
|
263
|
+
|
|
174
264
|
if (typeof data === 'string') {
|
|
175
|
-
const callback
|
|
265
|
+
const callback = args.pop() as WriteStrCallback;
|
|
266
|
+
const position = args[0] as number | undefined | null;
|
|
267
|
+
const encoding = args[1] as BufferEncoding | undefined | null;
|
|
176
268
|
|
|
177
|
-
fileHandle.write(data,
|
|
269
|
+
fileHandle.write(data, position, encoding)
|
|
178
270
|
.then((res) => {
|
|
179
271
|
callback(null, res.bytesWritten, res.buffer);
|
|
180
272
|
})
|
|
@@ -185,10 +277,10 @@ export function write<TBuffer extends NodeJS.ArrayBufferView>(fd: number, data:
|
|
|
185
277
|
return;
|
|
186
278
|
}
|
|
187
279
|
|
|
188
|
-
const callback
|
|
189
|
-
const offset
|
|
190
|
-
const length
|
|
191
|
-
const position
|
|
280
|
+
const callback = args[args.length -1] as WriteBufCallback;
|
|
281
|
+
const offset = args[0] as number | undefined;
|
|
282
|
+
const length = args[1] as number | undefined;
|
|
283
|
+
const position = args[2] as number | undefined;
|
|
192
284
|
|
|
193
285
|
fileHandle.write(data, offset, length, position)
|
|
194
286
|
.then((res) => {
|
|
@@ -281,33 +373,32 @@ export function read<TBuffer extends NodeJS.ArrayBufferView>(
|
|
|
281
373
|
): void;
|
|
282
374
|
export function read(fd: number, callback: ReadCallback): void;
|
|
283
375
|
|
|
284
|
-
export function read(fd: number, ...args:
|
|
376
|
+
export function read(fd: number, ...args: unknown[]): void {
|
|
285
377
|
|
|
286
378
|
const fileHandle = FileHandle.getInstance(fd);
|
|
287
379
|
|
|
288
|
-
const callback: ReadCallback = args[args.length -1];
|
|
289
|
-
const err = new Error(warnNotImplemented('fs.read'));
|
|
290
|
-
callback(err, 0, Buffer.from(''));
|
|
380
|
+
const callback: ReadCallback = args[args.length -1] as ReadCallback;
|
|
291
381
|
|
|
292
382
|
let buffer: NodeJS.ArrayBufferView | undefined;
|
|
293
383
|
let offset: number | null | undefined;
|
|
294
384
|
let length: number | null | undefined;
|
|
295
385
|
let position: ReadPosition | null | undefined;
|
|
296
386
|
|
|
297
|
-
if (
|
|
298
|
-
|
|
387
|
+
if (args.length <= 1) {
|
|
388
|
+
// read(fd, callback) — use defaults
|
|
389
|
+
} else if (typeof args[0] === 'object' && !ArrayBuffer.isView(args[0])) {
|
|
390
|
+
const options = args[0] as ReadAsyncOptions<NodeJS.ArrayBufferView>;
|
|
299
391
|
buffer = options.buffer;
|
|
300
392
|
offset = options.offset;
|
|
301
393
|
length = options.length;
|
|
302
394
|
position = options.position;
|
|
303
395
|
} else {
|
|
304
|
-
buffer = args[0];
|
|
305
|
-
offset = args[1];
|
|
306
|
-
length = args[2];
|
|
307
|
-
position = args[3];
|
|
396
|
+
buffer = args[0] as NodeJS.ArrayBufferView | undefined;
|
|
397
|
+
offset = args[1] as number | null | undefined;
|
|
398
|
+
length = args[2] as number | null | undefined;
|
|
399
|
+
position = args[3] as ReadPosition | null | undefined;
|
|
308
400
|
}
|
|
309
401
|
|
|
310
|
-
|
|
311
402
|
fileHandle.read(buffer, offset, length, position)
|
|
312
403
|
.then((res) => {
|
|
313
404
|
callback(null, res.bytesRead, res.buffer);
|
|
@@ -343,13 +434,13 @@ export function close(fd: number, callback?: NoParamCallback): void {
|
|
|
343
434
|
export function rm(path: PathLike, callback: NoParamCallback): void;
|
|
344
435
|
export function rm(path: PathLike, options: RmOptions, callback: NoParamCallback): void;
|
|
345
436
|
|
|
346
|
-
export function rm(path: PathLike, ...args:
|
|
437
|
+
export function rm(path: PathLike, ...args: (RmOptions | NoParamCallback)[]): void {
|
|
347
438
|
|
|
348
439
|
let options: RmOptions = {};
|
|
349
|
-
let callback: NoParamCallback = args[args.length -1];
|
|
440
|
+
let callback: NoParamCallback = args[args.length -1] as NoParamCallback;
|
|
350
441
|
|
|
351
442
|
if (args.length >= 2) {
|
|
352
|
-
options = args[0];
|
|
443
|
+
options = args[0] as RmOptions;
|
|
353
444
|
}
|
|
354
445
|
|
|
355
446
|
rmP(path, options)
|
|
@@ -359,4 +450,226 @@ export function rm(path: PathLike, ...args: any[]): void {
|
|
|
359
450
|
.catch((err) => {
|
|
360
451
|
callback(err);
|
|
361
452
|
});
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// --- rename ---
|
|
456
|
+
|
|
457
|
+
export function rename(oldPath: PathLike, newPath: PathLike, callback: NoParamCallback): void {
|
|
458
|
+
Promise.resolve().then(() => {
|
|
459
|
+
try {
|
|
460
|
+
renameSync(oldPath, newPath);
|
|
461
|
+
callback(null);
|
|
462
|
+
} catch (err: unknown) {
|
|
463
|
+
callback(err as NodeJS.ErrnoException);
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// --- copyFile ---
|
|
469
|
+
|
|
470
|
+
export function copyFile(src: PathLike, dest: PathLike, callback: NoParamCallback): void;
|
|
471
|
+
export function copyFile(src: PathLike, dest: PathLike, mode: number, callback: NoParamCallback): void;
|
|
472
|
+
export function copyFile(src: PathLike, dest: PathLike, modeOrCb: number | NoParamCallback, maybeCb?: NoParamCallback): void {
|
|
473
|
+
const mode = typeof modeOrCb === 'function' ? 0 : modeOrCb;
|
|
474
|
+
const callback = typeof modeOrCb === 'function' ? modeOrCb : maybeCb!;
|
|
475
|
+
Promise.resolve().then(() => {
|
|
476
|
+
try {
|
|
477
|
+
copyFileSync(src, dest, mode);
|
|
478
|
+
callback(null);
|
|
479
|
+
} catch (err: unknown) {
|
|
480
|
+
callback(err as NodeJS.ErrnoException);
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// --- access ---
|
|
486
|
+
|
|
487
|
+
export function access(path: PathLike, callback: NoParamCallback): void;
|
|
488
|
+
export function access(path: PathLike, mode: number, callback: NoParamCallback): void;
|
|
489
|
+
export function access(path: PathLike, modeOrCb: number | NoParamCallback, maybeCb?: NoParamCallback): void {
|
|
490
|
+
const mode = typeof modeOrCb === 'function' ? undefined : modeOrCb;
|
|
491
|
+
const callback = typeof modeOrCb === 'function' ? modeOrCb : maybeCb!;
|
|
492
|
+
Promise.resolve().then(() => {
|
|
493
|
+
try {
|
|
494
|
+
accessSync(path, mode);
|
|
495
|
+
callback(null);
|
|
496
|
+
} catch (err: unknown) {
|
|
497
|
+
callback(err as NodeJS.ErrnoException);
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// --- appendFile ---
|
|
503
|
+
|
|
504
|
+
export function appendFile(path: PathLike, data: string | Uint8Array, callback: NoParamCallback): void;
|
|
505
|
+
export function appendFile(path: PathLike, data: string | Uint8Array, options: { encoding?: string; mode?: number; flag?: string } | string, callback: NoParamCallback): void;
|
|
506
|
+
export function appendFile(path: PathLike, data: string | Uint8Array, optsOrCb: { encoding?: string; mode?: number; flag?: string } | string | NoParamCallback, maybeCb?: NoParamCallback): void {
|
|
507
|
+
const callback = typeof optsOrCb === 'function' ? optsOrCb : maybeCb!;
|
|
508
|
+
const options = typeof optsOrCb === 'function' ? undefined : optsOrCb;
|
|
509
|
+
Promise.resolve().then(() => {
|
|
510
|
+
try {
|
|
511
|
+
appendFileSync(path, data, options);
|
|
512
|
+
callback(null);
|
|
513
|
+
} catch (err: unknown) {
|
|
514
|
+
callback(err as NodeJS.ErrnoException);
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// --- readlink ---
|
|
520
|
+
|
|
521
|
+
export function readlink(path: PathLike, callback: (err: NodeJS.ErrnoException | null, linkString: string) => void): void;
|
|
522
|
+
export function readlink(path: PathLike, options: { encoding?: string } | string, callback: (err: NodeJS.ErrnoException | null, linkString: string | Buffer) => void): void;
|
|
523
|
+
export function readlink(path: PathLike, optsOrCb: { encoding?: string } | string | ((err: NodeJS.ErrnoException | null, linkString: string | Buffer) => void), maybeCb?: (err: NodeJS.ErrnoException | null, linkString: string | Buffer) => void): void {
|
|
524
|
+
const callback = typeof optsOrCb === 'function' ? optsOrCb : maybeCb!;
|
|
525
|
+
const options = typeof optsOrCb === 'function' ? undefined : optsOrCb;
|
|
526
|
+
Promise.resolve().then(() => {
|
|
527
|
+
try {
|
|
528
|
+
callback(null, readlinkSync(path, options));
|
|
529
|
+
} catch (err: unknown) {
|
|
530
|
+
callback(err as NodeJS.ErrnoException, '');
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// --- truncate ---
|
|
536
|
+
|
|
537
|
+
export function truncate(path: PathLike, callback: NoParamCallback): void;
|
|
538
|
+
export function truncate(path: PathLike, len: number, callback: NoParamCallback): void;
|
|
539
|
+
export function truncate(path: PathLike, lenOrCb: number | NoParamCallback, maybeCb?: NoParamCallback): void {
|
|
540
|
+
const len = typeof lenOrCb === 'function' ? 0 : lenOrCb;
|
|
541
|
+
const callback = typeof lenOrCb === 'function' ? lenOrCb : maybeCb!;
|
|
542
|
+
Promise.resolve().then(() => {
|
|
543
|
+
try {
|
|
544
|
+
truncateSync(path, len);
|
|
545
|
+
callback(null);
|
|
546
|
+
} catch (err: unknown) {
|
|
547
|
+
callback(err as NodeJS.ErrnoException);
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// --- chmod ---
|
|
553
|
+
|
|
554
|
+
export function chmod(path: PathLike, mode: Mode, callback: NoParamCallback): void {
|
|
555
|
+
Promise.resolve().then(() => {
|
|
556
|
+
try {
|
|
557
|
+
chmodSync(path, mode);
|
|
558
|
+
callback(null);
|
|
559
|
+
} catch (err: unknown) {
|
|
560
|
+
callback(err as NodeJS.ErrnoException);
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// --- chown ---
|
|
566
|
+
|
|
567
|
+
export function chown(path: PathLike, uid: number, gid: number, callback: NoParamCallback): void {
|
|
568
|
+
Promise.resolve().then(() => {
|
|
569
|
+
try {
|
|
570
|
+
chownSync(path, uid, gid);
|
|
571
|
+
callback(null);
|
|
572
|
+
} catch (err: unknown) {
|
|
573
|
+
callback(err as NodeJS.ErrnoException);
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// --- mkdir (callback) ---
|
|
579
|
+
|
|
580
|
+
export function mkdir(path: PathLike, callback: NoParamCallback): void;
|
|
581
|
+
export function mkdir(path: PathLike, options: MakeDirectoryOptions | Mode, callback: NoParamCallback): void;
|
|
582
|
+
export function mkdir(path: PathLike, optsOrCb: MakeDirectoryOptions | Mode | NoParamCallback, maybeCb?: NoParamCallback): void {
|
|
583
|
+
const callback = typeof optsOrCb === 'function' ? optsOrCb : maybeCb!;
|
|
584
|
+
const options = typeof optsOrCb === 'function' ? undefined : optsOrCb;
|
|
585
|
+
Promise.resolve().then(() => {
|
|
586
|
+
try {
|
|
587
|
+
mkdirSync(path, options as MakeDirectoryOptions | Mode | undefined);
|
|
588
|
+
callback(null);
|
|
589
|
+
} catch (err: unknown) {
|
|
590
|
+
callback(err as NodeJS.ErrnoException);
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// --- rmdir (callback) ---
|
|
596
|
+
|
|
597
|
+
export function rmdir(path: PathLike, callback: NoParamCallback): void;
|
|
598
|
+
export function rmdir(path: PathLike, options: RmDirOptions, callback: NoParamCallback): void;
|
|
599
|
+
export function rmdir(path: PathLike, optsOrCb: RmDirOptions | NoParamCallback, maybeCb?: NoParamCallback): void {
|
|
600
|
+
const callback = typeof optsOrCb === 'function' ? optsOrCb : maybeCb!;
|
|
601
|
+
const options = typeof optsOrCb === 'function' ? undefined : optsOrCb;
|
|
602
|
+
Promise.resolve().then(() => {
|
|
603
|
+
try {
|
|
604
|
+
rmdirSync(path, options);
|
|
605
|
+
callback(null);
|
|
606
|
+
} catch (err: unknown) {
|
|
607
|
+
callback(err as NodeJS.ErrnoException);
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// --- readFile (callback) ---
|
|
613
|
+
|
|
614
|
+
export function readFile(path: PathLike, callback: (err: NodeJS.ErrnoException | null, data: Buffer) => void): void;
|
|
615
|
+
export function readFile(path: PathLike, options: { encoding?: string; flag?: string } | string, callback: (err: NodeJS.ErrnoException | null, data: string | Buffer) => void): void;
|
|
616
|
+
export function readFile(path: PathLike, optsOrCb: { encoding?: string; flag?: string } | string | ((err: NodeJS.ErrnoException | null, data: Buffer) => void), maybeCb?: (err: NodeJS.ErrnoException | null, data: string | Buffer | null) => void): void {
|
|
617
|
+
const callback = typeof optsOrCb === 'function' ? optsOrCb : maybeCb!;
|
|
618
|
+
const options = typeof optsOrCb === 'function' ? undefined : optsOrCb;
|
|
619
|
+
Promise.resolve().then(() => {
|
|
620
|
+
try {
|
|
621
|
+
const readOpts = typeof options === 'string' ? { encoding: options as string | null, flag: 'r' } : { encoding: (options?.encoding ?? null) as string | null, flag: options?.flag ?? 'r' };
|
|
622
|
+
callback(null, readFileSync(path.toString(), readOpts) as unknown as Buffer);
|
|
623
|
+
} catch (err: unknown) {
|
|
624
|
+
callback(err as NodeJS.ErrnoException, null as unknown as Buffer);
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// --- writeFile (callback) ---
|
|
630
|
+
|
|
631
|
+
export function writeFile(path: PathLike, data: string | Uint8Array, callback: NoParamCallback): void;
|
|
632
|
+
export function writeFile(path: PathLike, data: string | Uint8Array, options: { encoding?: string; mode?: number; flag?: string } | string, callback: NoParamCallback): void;
|
|
633
|
+
export function writeFile(path: PathLike, data: string | Uint8Array, optsOrCb: { encoding?: string; mode?: number; flag?: string } | string | NoParamCallback, maybeCb?: NoParamCallback): void {
|
|
634
|
+
const callback = typeof optsOrCb === 'function' ? optsOrCb : maybeCb!;
|
|
635
|
+
Promise.resolve().then(() => {
|
|
636
|
+
try {
|
|
637
|
+
writeFileSync(path.toString(), data);
|
|
638
|
+
callback(null);
|
|
639
|
+
} catch (err: unknown) {
|
|
640
|
+
callback(err as NodeJS.ErrnoException);
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// --- link (callback) ---
|
|
646
|
+
|
|
647
|
+
export function link(existingPath: PathLike, newPath: PathLike, callback: NoParamCallback): void {
|
|
648
|
+
Promise.resolve().then(() => {
|
|
649
|
+
try {
|
|
650
|
+
const result = GLib.spawn_command_line_sync(`ln ${existingPath.toString()} ${newPath.toString()}`);
|
|
651
|
+
if (!result[0]) {
|
|
652
|
+
throw Object.assign(new Error(`EPERM: operation not permitted, link '${existingPath}' -> '${newPath}'`), {
|
|
653
|
+
code: 'EPERM', errno: -1, syscall: 'link',
|
|
654
|
+
path: existingPath.toString(), dest: newPath.toString()
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
callback(null);
|
|
658
|
+
} catch (err: unknown) {
|
|
659
|
+
callback(err as NodeJS.ErrnoException);
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// --- unlink (callback) ---
|
|
665
|
+
|
|
666
|
+
export function unlink(path: PathLike, callback: NoParamCallback): void {
|
|
667
|
+
Promise.resolve().then(() => {
|
|
668
|
+
try {
|
|
669
|
+
GLib.unlink(path.toString());
|
|
670
|
+
callback(null);
|
|
671
|
+
} catch (err: unknown) {
|
|
672
|
+
callback(err as NodeJS.ErrnoException);
|
|
673
|
+
}
|
|
674
|
+
});
|
|
362
675
|
}
|
package/src/dirent.ts
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
|
+
// Reference: Node.js lib/internal/fs/utils.js (Dirent class)
|
|
2
|
+
// Reimplemented for GJS using Gio.FileInfo
|
|
3
|
+
|
|
1
4
|
import Gio from '@girs/gio-2.0';
|
|
2
|
-
import { basename } from 'path';
|
|
5
|
+
import { basename, dirname } from 'node:path';
|
|
6
|
+
|
|
7
|
+
import type { Dirent as OriginalDirent } from 'node:fs'; // Types from @types/node
|
|
3
8
|
|
|
4
|
-
|
|
9
|
+
// POSIX file type constants from stat mode bits (S_IFMT mask = 0o170000)
|
|
10
|
+
const S_IFMT = 0o170000;
|
|
11
|
+
const S_IFSOCK = 0o140000;
|
|
12
|
+
const S_IFLNK = 0o120000;
|
|
13
|
+
const S_IFREG = 0o100000;
|
|
14
|
+
const S_IFBLK = 0o060000;
|
|
15
|
+
const S_IFDIR = 0o040000;
|
|
16
|
+
const S_IFCHR = 0o020000;
|
|
17
|
+
const S_IFIFO = 0o010000;
|
|
5
18
|
|
|
6
19
|
/**
|
|
7
20
|
* A representation of a directory entry, which can be a file or a subdirectory
|
|
@@ -21,6 +34,12 @@ export class Dirent implements OriginalDirent {
|
|
|
21
34
|
*/
|
|
22
35
|
name: string;
|
|
23
36
|
|
|
37
|
+
/**
|
|
38
|
+
* The path to the parent directory of the file this `fs.Dirent` object refers to.
|
|
39
|
+
* @since v20.12.0, v18.20.0
|
|
40
|
+
*/
|
|
41
|
+
parentPath: string;
|
|
42
|
+
|
|
24
43
|
private _isFile = false;
|
|
25
44
|
private _isDirectory = false;
|
|
26
45
|
private _isBlockDevice = false;
|
|
@@ -33,11 +52,12 @@ export class Dirent implements OriginalDirent {
|
|
|
33
52
|
protected _file: Gio.File;
|
|
34
53
|
|
|
35
54
|
/** This is not part of node.fs and is used internal by gjsify */
|
|
36
|
-
constructor(path: string, filename?: string) {
|
|
55
|
+
constructor(path: string, filename?: string, fileType?: Gio.FileType) {
|
|
37
56
|
if (!filename) filename = basename(path);
|
|
38
57
|
this.name = filename;
|
|
58
|
+
this.parentPath = dirname(path);
|
|
39
59
|
this._file = Gio.File.new_for_path(path);
|
|
40
|
-
const type = this._file.query_file_type(Gio.FileQueryInfoFlags.NONE, null);
|
|
60
|
+
const type = fileType ?? this._file.query_file_type(Gio.FileQueryInfoFlags.NONE, null);
|
|
41
61
|
|
|
42
62
|
switch (type) {
|
|
43
63
|
case Gio.FileType.DIRECTORY:
|
|
@@ -54,15 +74,43 @@ export class Dirent implements OriginalDirent {
|
|
|
54
74
|
break;
|
|
55
75
|
case Gio.FileType.SPECIAL:
|
|
56
76
|
// File is a "special" file, such as a socket, fifo, block device, or character device.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// TODO: this._isSocket =
|
|
60
|
-
// TODO: this._isFifo =
|
|
77
|
+
// Use unix::mode from Gio.FileInfo to distinguish the exact type via POSIX S_IFMT bits.
|
|
78
|
+
this._classifySpecialFile(path);
|
|
61
79
|
break;
|
|
62
80
|
}
|
|
63
81
|
|
|
64
82
|
}
|
|
65
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Classify a SPECIAL file type using the unix::mode attribute from Gio.FileInfo.
|
|
86
|
+
* Falls back to marking nothing if the mode attribute is unavailable.
|
|
87
|
+
*/
|
|
88
|
+
private _classifySpecialFile(path: string): void {
|
|
89
|
+
try {
|
|
90
|
+
const file = Gio.File.new_for_path(path);
|
|
91
|
+
const info = file.query_info('unix::mode', Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
|
|
92
|
+
const mode = info.get_attribute_uint32('unix::mode');
|
|
93
|
+
if (mode === 0) return;
|
|
94
|
+
const fmt = mode & S_IFMT;
|
|
95
|
+
switch (fmt) {
|
|
96
|
+
case S_IFBLK:
|
|
97
|
+
this._isBlockDevice = true;
|
|
98
|
+
break;
|
|
99
|
+
case S_IFCHR:
|
|
100
|
+
this._isCharacterDevice = true;
|
|
101
|
+
break;
|
|
102
|
+
case S_IFSOCK:
|
|
103
|
+
this._isSocket = true;
|
|
104
|
+
break;
|
|
105
|
+
case S_IFIFO:
|
|
106
|
+
this._isFIFO = true;
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
} catch {
|
|
110
|
+
// If we can't query the mode (e.g. permission denied), leave all flags as false
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
66
114
|
/**
|
|
67
115
|
* Returns `true` if the `fs.Dirent` object describes a regular file.
|
|
68
116
|
* @since v10.10.0
|
package/src/encoding.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Adapted from Deno (refs/deno/ext/node/polyfills/_fs/_fs_mkdtemp.ts)
|
|
3
|
+
// Copyright (c) 2018-2026 the Deno authors. MIT license.
|
|
4
|
+
// Modifications: Extracted encoding helpers, adapted for GJS
|
|
5
|
+
|
|
6
|
+
import { Buffer } from 'node:buffer';
|
|
2
7
|
|
|
3
8
|
import type { ReadOptions } from './types/index.js';
|
|
4
|
-
import type { ObjectEncodingOptions, BufferEncodingOption } from 'fs'; // Types from @types/node
|
|
9
|
+
import type { ObjectEncodingOptions, BufferEncodingOption } from 'node:fs'; // Types from @types/node
|
|
5
10
|
|
|
6
11
|
export function getEncodingFromOptions(options: ReadOptions | ObjectEncodingOptions | BufferEncodingOption= { encoding: null, flag: 'r' }, defaultEncoding: null | BufferEncoding | "buffer" = 'utf8'): BufferEncoding | 'buffer' {
|
|
7
12
|
if (options === null) {
|