0
# Helper Classes and Utilities
1
2
File statistics, directory entries, and utility classes available through the fs object for working with file system metadata and operations.
3
4
## Core Imports
5
6
```typescript
7
import { fs } from "memfs";
8
```
9
10
## Capabilities
11
12
### File Statistics
13
14
Comprehensive file and directory statistics matching Node.js Stats interface. Accessible via the fs.Stats constructor or returned by stat operations.
15
16
```typescript { .api }
17
/**
18
* File statistics class providing metadata about files and directories
19
* Accessible through fs.Stats constructor or returned by fs.statSync(), etc.
20
*/
21
interface Stats<T = number | bigint> {
22
// File size and identification
23
size: T;
24
ino: T;
25
mode: T;
26
nlink: T;
27
28
// Ownership and permissions
29
uid: T;
30
gid: T;
31
rdev: T;
32
33
// Timestamps
34
atime: Date;
35
mtime: Date;
36
ctime: Date;
37
birthtime: Date;
38
39
// Timestamp numbers (for compatibility)
40
atimeMs: T;
41
mtimeMs: T;
42
ctimeMs: T;
43
birthtimeMs: T;
44
45
// File type detection methods
46
isFile(): boolean;
47
isDirectory(): boolean;
48
isSymbolicLink(): boolean;
49
isSocket(): boolean;
50
isFIFO(): boolean;
51
isCharacterDevice(): boolean;
52
isBlockDevice(): boolean;
53
54
// BigInt support
55
isBigIntStats(): this is Stats<bigint>;
56
}
57
58
/**
59
* File system statistics - returned by fs.statfsSync()
60
*/
61
interface StatFs<T = number | bigint> {
62
type: T;
63
bsize: T;
64
blocks: T;
65
bfree: T;
66
bavail: T;
67
files: T;
68
ffree: T;
69
}
70
```
71
72
**Usage Examples:**
73
74
```typescript
75
import { fs } from "memfs";
76
77
// Create test files
78
fs.writeFileSync('/data.txt', 'Hello World!');
79
fs.mkdirSync('/folder');
80
fs.symlinkSync('./data.txt', '/link.txt');
81
82
// Get file statistics
83
const fileStats = fs.statSync('/data.txt');
84
console.log('File size:', fileStats.size);
85
console.log('Is file:', fileStats.isFile()); // true
86
console.log('Is directory:', fileStats.isDirectory()); // false
87
console.log('Mode:', fileStats.mode.toString(8)); // octal permissions
88
console.log('Modified:', fileStats.mtime);
89
90
// Directory statistics
91
const dirStats = fs.statSync('/folder');
92
console.log('Directory size:', dirStats.size);
93
console.log('Is directory:', dirStats.isDirectory()); // true
94
95
// Symlink statistics (lstat vs stat)
96
const linkStats = fs.lstatSync('/link.txt'); // stats of the link itself
97
const targetStats = fs.statSync('/link.txt'); // stats of the target file
98
console.log('Link is symlink:', linkStats.isSymbolicLink()); // true
99
console.log('Target is file:', targetStats.isFile()); // true
100
101
// BigInt statistics
102
const bigintStats = fs.statSync('/data.txt', { bigint: true });
103
console.log('BigInt size:', typeof bigintStats.size); // 'bigint'
104
console.log('Is BigInt stats:', bigintStats.isBigIntStats()); // true
105
106
// File system statistics
107
const fsStats = fs.statfsSync('/');
108
console.log('Block size:', fsStats.bsize);
109
console.log('Total blocks:', fsStats.blocks);
110
console.log('Free blocks:', fsStats.bfree);
111
```
112
113
### Directory Entries
114
115
Directory entry objects providing file type and name information.
116
117
```typescript { .api }
118
/**
119
* Directory entry interface representing items found in directory listings
120
* Returned by fs.readdirSync() with withFileTypes: true option
121
*/
122
interface Dirent {
123
// Entry name and path
124
name: string;
125
path?: string;
126
127
// File type detection methods (same as Stats)
128
isFile(): boolean;
129
isDirectory(): boolean;
130
isSymbolicLink(): boolean;
131
isSocket(): boolean;
132
isFIFO(): boolean;
133
isCharacterDevice(): boolean;
134
isBlockDevice(): boolean;
135
}
136
```
137
138
**Usage Examples:**
139
140
```typescript
141
import { fs } from "memfs";
142
143
// Set up directory structure
144
fs.mkdirSync('/app');
145
fs.writeFileSync('/app/index.js', 'console.log("Hello");');
146
fs.writeFileSync('/app/package.json', '{"name": "app"}');
147
fs.mkdirSync('/app/src');
148
fs.symlinkSync('./index.js', '/app/main.js');
149
150
// Get directory entries with file types
151
const entries = fs.readdirSync('/app', { withFileTypes: true });
152
153
entries.forEach(entry => {
154
console.log(`${entry.name}:`);
155
156
if (entry.isFile()) {
157
console.log(' Type: File');
158
} else if (entry.isDirectory()) {
159
console.log(' Type: Directory');
160
} else if (entry.isSymbolicLink()) {
161
console.log(' Type: Symbolic Link');
162
}
163
164
// Optional: check path property if available
165
if (entry.path) {
166
console.log(' Path:', entry.path);
167
}
168
});
169
170
// Filter entries by type
171
const files = entries.filter(entry => entry.isFile());
172
const directories = entries.filter(entry => entry.isDirectory());
173
const symlinks = entries.filter(entry => entry.isSymbolicLink());
174
175
console.log('Files:', files.map(f => f.name));
176
console.log('Directories:', directories.map(d => d.name));
177
console.log('Symlinks:', symlinks.map(s => s.name));
178
179
// Recursive directory traversal
180
function listAllFiles(dirPath: string, prefix = '') {
181
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
182
183
entries.forEach(entry => {
184
const fullPath = `${dirPath}/${entry.name}`;
185
console.log(`${prefix}${entry.name}`);
186
187
if (entry.isDirectory()) {
188
listAllFiles(fullPath, prefix + ' ');
189
}
190
});
191
}
192
193
listAllFiles('/app');
194
```
195
196
### File Handles
197
198
Promise-based file handle interface for advanced file operations.
199
200
```typescript { .api }
201
/**
202
* File handle class for promise-based file operations
203
*/
204
export class FileHandle extends EventEmitter {
205
// File descriptor
206
readonly fd: number;
207
208
// File operations
209
appendFile(data: string | Buffer, options?: { encoding?: BufferEncoding; mode?: number; flag?: string }): Promise<void>;
210
chmod(mode: string | number): Promise<void>;
211
chown(uid: number, gid: number): Promise<void>;
212
close(): Promise<void>;
213
datasync(): Promise<void>;
214
sync(): Promise<void>;
215
216
// Reading operations
217
read<T extends ArrayBufferView>(
218
buffer: T,
219
offset?: number,
220
length?: number,
221
position?: number
222
): Promise<{ bytesRead: number; buffer: T }>;
223
224
readFile(options?: { encoding?: BufferEncoding | null; flag?: string }): Promise<string | Buffer>;
225
226
readv(buffers: readonly ArrayBufferView[], position?: number): Promise<ReadVResult>;
227
228
// File metadata
229
stat(options?: { bigint?: boolean }): Promise<Stats>;
230
truncate(len?: number): Promise<void>;
231
utimes(atime: string | number | Date, mtime: string | number | Date): Promise<void>;
232
233
// Writing operations
234
write<T extends ArrayBufferView>(
235
buffer: T,
236
offset?: number,
237
length?: number,
238
position?: number
239
): Promise<{ bytesWritten: number; buffer: T }>;
240
241
write(data: string, position?: number, encoding?: BufferEncoding): Promise<{ bytesWritten: number; buffer: string }>;
242
243
writeFile(data: string | Buffer, options?: { encoding?: BufferEncoding; mode?: number; flag?: string }): Promise<void>;
244
245
writev(buffers: readonly ArrayBufferView[], position?: number): Promise<WriteVResult>;
246
}
247
248
// Result types for vectored I/O
249
export interface ReadVResult {
250
bytesRead: number;
251
buffers: ArrayBufferView[];
252
}
253
254
export interface WriteVResult {
255
bytesWritten: number;
256
buffers: ArrayBufferView[];
257
}
258
```
259
260
**Usage Examples:**
261
262
```typescript
263
import { fs } from "memfs";
264
265
async function fileHandleOperations() {
266
// Open file handle
267
const fileHandle = await fs.promises.open('/data.txt', 'w+');
268
269
try {
270
// Write data
271
await fileHandle.writeFile('Hello World!\nSecond line\n');
272
273
// Read data
274
const content = await fileHandle.readFile('utf8');
275
console.log('Content:', content);
276
277
// Buffer-based operations
278
const buffer = Buffer.alloc(5);
279
const readResult = await fileHandle.read(buffer, 0, 5, 0);
280
console.log('Read:', readResult.bytesRead, 'bytes:', buffer.toString());
281
282
// Write buffer
283
const writeBuffer = Buffer.from('New content');
284
const writeResult = await fileHandle.write(writeBuffer, 0, writeBuffer.length, 0);
285
console.log('Wrote:', writeResult.bytesWritten, 'bytes');
286
287
// File metadata operations
288
await fileHandle.chmod(0o644);
289
await fileHandle.utimes(new Date(), new Date());
290
291
const stats = await fileHandle.stat();
292
console.log('File size:', stats.size);
293
294
// Vectored I/O
295
const buffers = [Buffer.from('Hello '), Buffer.from('World!')];
296
const writevResult = await fileHandle.writev(buffers);
297
console.log('Vectored write:', writevResult.bytesWritten, 'bytes');
298
299
// Sync operations
300
await fileHandle.datasync(); // Sync data only
301
await fileHandle.sync(); // Sync data and metadata
302
303
} finally {
304
// Always close file handle
305
await fileHandle.close();
306
}
307
}
308
309
// File handle with error handling
310
async function safeFileOperations() {
311
let fileHandle;
312
try {
313
fileHandle = await fs.promises.open('/safe-file.txt', 'w');
314
await fileHandle.writeFile('Safe content');
315
} catch (error) {
316
console.error('File operation failed:', error);
317
} finally {
318
if (fileHandle) {
319
await fileHandle.close();
320
}
321
}
322
}
323
```
324
325
### Directory Iterator
326
327
Directory class for iterating over directory contents.
328
329
```typescript { .api }
330
/**
331
* Directory iterator class for asynchronous directory traversal
332
*/
333
export class Dir {
334
readonly path: string;
335
336
/**
337
* Read next directory entry asynchronously
338
* @returns Promise resolving to next Dirent or null if end reached
339
*/
340
read(): Promise<Dirent | null>;
341
342
/**
343
* Read next directory entry synchronously
344
* @returns Next Dirent or null if end reached
345
*/
346
readSync(): Dirent | null;
347
348
/**
349
* Close directory iterator asynchronously
350
* @returns Promise that resolves when closed
351
*/
352
close(): Promise<void>;
353
354
/**
355
* Close directory iterator synchronously
356
*/
357
closeSync(): void;
358
359
/**
360
* Async iterator interface
361
*/
362
[Symbol.asyncIterator](): AsyncIterableIterator<Dirent>;
363
}
364
```
365
366
**Usage Examples:**
367
368
```typescript
369
import { fs } from "memfs";
370
371
async function directoryIteration() {
372
// Set up directory
373
fs.mkdirSync('/items');
374
fs.writeFileSync('/items/file1.txt', 'content1');
375
fs.writeFileSync('/items/file2.txt', 'content2');
376
fs.mkdirSync('/items/subdir');
377
378
// Open directory
379
const dir = await fs.promises.opendir('/items');
380
381
try {
382
// Manual iteration
383
let entry;
384
while ((entry = await dir.read()) !== null) {
385
console.log(`${entry.name}: ${entry.isFile() ? 'file' : 'directory'}`);
386
}
387
} finally {
388
await dir.close();
389
}
390
391
// Async iterator (recommended)
392
const dir2 = await fs.promises.opendir('/items');
393
try {
394
for await (const entry of dir2) {
395
console.log(`Async: ${entry.name}`);
396
397
if (entry.isFile()) {
398
const content = fs.readFileSync(`/items/${entry.name}`, 'utf8');
399
console.log(` Content: ${content}`);
400
}
401
}
402
} finally {
403
await dir2.close();
404
}
405
406
// Synchronous directory iteration
407
const dirSync = fs.opendirSync('/items');
408
try {
409
let entry;
410
while ((entry = dirSync.readSync()) !== null) {
411
console.log(`Sync: ${entry.name}`);
412
}
413
} finally {
414
dirSync.closeSync();
415
}
416
}
417
418
// Directory traversal with filtering
419
async function findFiles(dirPath: string, extension: string): Promise<string[]> {
420
const results: string[] = [];
421
const dir = await fs.promises.opendir(dirPath);
422
423
try {
424
for await (const entry of dir) {
425
const fullPath = `${dirPath}/${entry.name}`;
426
427
if (entry.isFile() && entry.name.endsWith(extension)) {
428
results.push(fullPath);
429
} else if (entry.isDirectory()) {
430
// Recursive search
431
const subdirResults = await findFiles(fullPath, extension);
432
results.push(...subdirResults);
433
}
434
}
435
} finally {
436
await dir.close();
437
}
438
439
return results;
440
}
441
```
442
443
### Tree Visualization
444
445
Utility functions for generating human-readable tree representations of file systems.
446
447
```typescript { .api }
448
/**
449
* Generate a tree representation of the file system
450
* @param fs File system API to use
451
* @param opts Tree generation options
452
* @returns String representation of the file system tree
453
*/
454
export function toTreeSync(fs: FsSynchronousApi, opts?: ToTreeOptions): string;
455
456
/**
457
* Options for tree generation
458
*/
459
export interface ToTreeOptions {
460
/** Root directory to start from (default: '/') */
461
dir?: string;
462
/** Tab character or string for indentation (default: '') */
463
tab?: string;
464
/** Maximum depth to traverse (default: 10) */
465
depth?: number;
466
/** Path separator character (default: '/') */
467
separator?: '/' | '\\';
468
}
469
```
470
471
**Usage Examples:**
472
473
```typescript
474
import { fs, toTreeSync } from "memfs";
475
476
// Create sample file system
477
fs.mkdirSync('/project', { recursive: true });
478
fs.writeFileSync('/project/README.md', '# My Project');
479
fs.writeFileSync('/project/package.json', '{"name": "my-project"}');
480
fs.mkdirSync('/project/src');
481
fs.writeFileSync('/project/src/index.js', 'console.log("Hello");');
482
fs.writeFileSync('/project/src/utils.js', 'module.exports = {};');
483
fs.mkdirSync('/project/tests');
484
fs.writeFileSync('/project/tests/index.test.js', 'test("works", () => {});');
485
fs.symlinkSync('../README.md', '/project/src/README.md');
486
487
// Generate tree with default options
488
const tree = toTreeSync(fs);
489
console.log('Full file system tree:');
490
console.log(tree);
491
492
// Generate tree for specific directory
493
const projectTree = toTreeSync(fs, { dir: '/project' });
494
console.log('Project tree:');
495
console.log(projectTree);
496
497
// Customize tree appearance
498
const customTree = toTreeSync(fs, {
499
dir: '/project',
500
tab: ' ', // Use 2 spaces for indentation
501
depth: 2, // Limit depth to 2 levels
502
separator: '/' // Use forward slash
503
});
504
console.log('Custom tree:');
505
console.log(customTree);
506
507
// Tree with different indentation
508
const fancyTree = toTreeSync(fs, {
509
dir: '/project',
510
tab: '│ ', // Use box drawing characters
511
depth: 3
512
});
513
console.log('Fancy tree:');
514
console.log(fancyTree);
515
516
// Utility function to print directory structure
517
function printDirectoryStructure(path: string, maxDepth = 5) {
518
console.log(`Directory structure for ${path}:`);
519
console.log(toTreeSync(fs, {
520
dir: path,
521
tab: '├─ ',
522
depth: maxDepth
523
}));
524
}
525
526
printDirectoryStructure('/project');
527
528
// Compare two directory structures
529
function compareDirectories(path1: string, path2: string) {
530
const tree1 = toTreeSync(fs, { dir: path1, tab: ' ' });
531
const tree2 = toTreeSync(fs, { dir: path2, tab: ' ' });
532
533
console.log(`${path1}:`);
534
console.log(tree1);
535
console.log(`\n${path2}:`);
536
console.log(tree2);
537
}
538
```
539
540
### Stream Classes
541
542
Stream implementations for reading and writing files.
543
544
```typescript { .api }
545
/**
546
* Readable stream for file reading
547
*/
548
export class ReadStream extends Readable {
549
readonly path: string;
550
readonly fd: number;
551
readonly flags: string;
552
readonly mode: number;
553
readonly start?: number;
554
readonly end?: number;
555
readonly autoClose: boolean;
556
557
bytesRead: number;
558
pending: boolean;
559
560
// Readable stream interface
561
_read(size: number): void;
562
_destroy(error: Error | null, callback: (error?: Error | null) => void): void;
563
}
564
565
/**
566
* Writable stream for file writing
567
*/
568
export class WriteStream extends Writable {
569
readonly path: string;
570
readonly fd: number;
571
readonly flags: string;
572
readonly mode: number;
573
readonly start?: number;
574
readonly autoClose: boolean;
575
576
bytesWritten: number;
577
pending: boolean;
578
579
// Writable stream interface
580
_write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void): void;
581
_destroy(error: Error | null, callback: (error?: Error | null) => void): void;
582
}
583
```
584
585
**Usage Examples:**
586
587
```typescript
588
import { fs } from "memfs";
589
590
function streamOperations() {
591
// Create readable stream
592
fs.writeFileSync('/source.txt', 'Hello\nWorld\nFrom\nStream!');
593
const readStream = fs.createReadStream('/source.txt', { encoding: 'utf8' });
594
595
// Create writable stream
596
const writeStream = fs.createWriteStream('/destination.txt');
597
598
// Stream events
599
readStream.on('data', (chunk) => {
600
console.log('Read chunk:', chunk);
601
});
602
603
readStream.on('end', () => {
604
console.log('Reading finished');
605
});
606
607
writeStream.on('finish', () => {
608
console.log('Writing finished');
609
console.log('Bytes written:', writeStream.bytesWritten);
610
});
611
612
// Pipe streams
613
readStream.pipe(writeStream);
614
615
// Manual stream writing
616
const manualWriteStream = fs.createWriteStream('/manual.txt');
617
manualWriteStream.write('Line 1\n');
618
manualWriteStream.write('Line 2\n');
619
manualWriteStream.end('Final line\n');
620
621
// Stream with options
622
const rangeReadStream = fs.createReadStream('/source.txt', {
623
start: 5, // Start at byte 5
624
end: 10, // End at byte 10
625
encoding: 'utf8'
626
});
627
628
rangeReadStream.on('data', (chunk) => {
629
console.log('Range chunk:', chunk);
630
});
631
}
632
```
633
634
## Type Definitions
635
636
```typescript { .api }
637
// File system API interface for utilities
638
export interface FsSynchronousApi {
639
readdirSync(path: string, options?: any): string[] | Dirent[];
640
statSync(path: string, options?: any): Stats;
641
lstatSync(path: string, options?: any): Stats;
642
readFileSync(path: string, options?: any): string | Buffer;
643
readlinkSync(path: string, options?: any): string;
644
// ... other synchronous methods
645
}
646
647
// Event types for file handles and streams
648
export interface FileHandleEventMap {
649
'close': () => void;
650
'error': (error: Error) => void;
651
}
652
653
export interface StreamEventMap {
654
'data': (chunk: any) => void;
655
'end': () => void;
656
'error': (error: Error) => void;
657
'close': () => void;
658
'finish': () => void;
659
}
660
661
// Generic buffer types
662
export type BufferLike = ArrayBufferView | ArrayBuffer;
663
export type StringOrBuffer = string | Buffer;
664
```