CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-memfs

In-memory file-system with Node's fs API providing virtual file systems for testing, mocking, and development purposes.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

helpers.mddocs/

Helper Classes and Utilities

File statistics, directory entries, and utility classes available through the fs object for working with file system metadata and operations.

Core Imports

import { fs } from "memfs";

Capabilities

File Statistics

Comprehensive file and directory statistics matching Node.js Stats interface. Accessible via the fs.Stats constructor or returned by stat operations.

/**
 * File statistics class providing metadata about files and directories
 * Accessible through fs.Stats constructor or returned by fs.statSync(), etc.
 */
interface Stats<T = number | bigint> {
  // File size and identification
  size: T;
  ino: T;
  mode: T;
  nlink: T;
  
  // Ownership and permissions  
  uid: T;
  gid: T;
  rdev: T;
  
  // Timestamps
  atime: Date;
  mtime: Date; 
  ctime: Date;
  birthtime: Date;
  
  // Timestamp numbers (for compatibility)
  atimeMs: T;
  mtimeMs: T;
  ctimeMs: T;
  birthtimeMs: T;
  
  // File type detection methods
  isFile(): boolean;
  isDirectory(): boolean;
  isSymbolicLink(): boolean;
  isSocket(): boolean;
  isFIFO(): boolean;
  isCharacterDevice(): boolean;
  isBlockDevice(): boolean;
  
  // BigInt support
  isBigIntStats(): this is Stats<bigint>;
}

/**
 * File system statistics - returned by fs.statfsSync()
 */
interface StatFs<T = number | bigint> {
  type: T;
  bsize: T; 
  blocks: T;
  bfree: T;
  bavail: T;
  files: T;
  ffree: T;
}

Usage Examples:

import { fs } from "memfs";

// Create test files
fs.writeFileSync('/data.txt', 'Hello World!');
fs.mkdirSync('/folder');
fs.symlinkSync('./data.txt', '/link.txt');

// Get file statistics
const fileStats = fs.statSync('/data.txt');
console.log('File size:', fileStats.size);
console.log('Is file:', fileStats.isFile()); // true
console.log('Is directory:', fileStats.isDirectory()); // false
console.log('Mode:', fileStats.mode.toString(8)); // octal permissions
console.log('Modified:', fileStats.mtime);

// Directory statistics
const dirStats = fs.statSync('/folder');
console.log('Directory size:', dirStats.size);
console.log('Is directory:', dirStats.isDirectory()); // true

// Symlink statistics (lstat vs stat)
const linkStats = fs.lstatSync('/link.txt'); // stats of the link itself
const targetStats = fs.statSync('/link.txt'); // stats of the target file
console.log('Link is symlink:', linkStats.isSymbolicLink()); // true
console.log('Target is file:', targetStats.isFile()); // true

// BigInt statistics
const bigintStats = fs.statSync('/data.txt', { bigint: true });
console.log('BigInt size:', typeof bigintStats.size); // 'bigint'
console.log('Is BigInt stats:', bigintStats.isBigIntStats()); // true

// File system statistics
const fsStats = fs.statfsSync('/');
console.log('Block size:', fsStats.bsize);
console.log('Total blocks:', fsStats.blocks);
console.log('Free blocks:', fsStats.bfree);

Directory Entries

Directory entry objects providing file type and name information.

/**
 * Directory entry interface representing items found in directory listings
 * Returned by fs.readdirSync() with withFileTypes: true option
 */
interface Dirent {
  // Entry name and path
  name: string;
  path?: string;
  
  // File type detection methods (same as Stats)
  isFile(): boolean;
  isDirectory(): boolean;
  isSymbolicLink(): boolean;
  isSocket(): boolean;
  isFIFO(): boolean;
  isCharacterDevice(): boolean;
  isBlockDevice(): boolean;
}

Usage Examples:

import { fs } from "memfs";

// Set up directory structure
fs.mkdirSync('/app');
fs.writeFileSync('/app/index.js', 'console.log("Hello");');
fs.writeFileSync('/app/package.json', '{"name": "app"}');
fs.mkdirSync('/app/src');
fs.symlinkSync('./index.js', '/app/main.js');

// Get directory entries with file types
const entries = fs.readdirSync('/app', { withFileTypes: true });

entries.forEach(entry => {
  console.log(`${entry.name}:`);
  
  if (entry.isFile()) {
    console.log('  Type: File');
  } else if (entry.isDirectory()) {
    console.log('  Type: Directory');
  } else if (entry.isSymbolicLink()) {
    console.log('  Type: Symbolic Link');
  }
  
  // Optional: check path property if available
  if (entry.path) {
    console.log('  Path:', entry.path);
  }
});

// Filter entries by type
const files = entries.filter(entry => entry.isFile());
const directories = entries.filter(entry => entry.isDirectory());
const symlinks = entries.filter(entry => entry.isSymbolicLink());

console.log('Files:', files.map(f => f.name));
console.log('Directories:', directories.map(d => d.name));
console.log('Symlinks:', symlinks.map(s => s.name));

// Recursive directory traversal
function listAllFiles(dirPath: string, prefix = '') {
  const entries = fs.readdirSync(dirPath, { withFileTypes: true });
  
  entries.forEach(entry => {
    const fullPath = `${dirPath}/${entry.name}`;
    console.log(`${prefix}${entry.name}`);
    
    if (entry.isDirectory()) {
      listAllFiles(fullPath, prefix + '  ');
    }
  });
}

listAllFiles('/app');

File Handles

Promise-based file handle interface for advanced file operations.

/**
 * File handle class for promise-based file operations
 */
export class FileHandle extends EventEmitter {
  // File descriptor
  readonly fd: number;
  
  // File operations
  appendFile(data: string | Buffer, options?: { encoding?: BufferEncoding; mode?: number; flag?: string }): Promise<void>;
  chmod(mode: string | number): Promise<void>;
  chown(uid: number, gid: number): Promise<void>;
  close(): Promise<void>;
  datasync(): Promise<void>;
  sync(): Promise<void>;
  
  // Reading operations
  read<T extends ArrayBufferView>(
    buffer: T,
    offset?: number,
    length?: number,
    position?: number
  ): Promise<{ bytesRead: number; buffer: T }>;
  
  readFile(options?: { encoding?: BufferEncoding | null; flag?: string }): Promise<string | Buffer>;
  
  readv(buffers: readonly ArrayBufferView[], position?: number): Promise<ReadVResult>;
  
  // File metadata
  stat(options?: { bigint?: boolean }): Promise<Stats>;
  truncate(len?: number): Promise<void>;
  utimes(atime: string | number | Date, mtime: string | number | Date): Promise<void>;
  
  // Writing operations
  write<T extends ArrayBufferView>(
    buffer: T,
    offset?: number,
    length?: number,
    position?: number
  ): Promise<{ bytesWritten: number; buffer: T }>;
  
  write(data: string, position?: number, encoding?: BufferEncoding): Promise<{ bytesWritten: number; buffer: string }>;
  
  writeFile(data: string | Buffer, options?: { encoding?: BufferEncoding; mode?: number; flag?: string }): Promise<void>;
  
  writev(buffers: readonly ArrayBufferView[], position?: number): Promise<WriteVResult>;
}

// Result types for vectored I/O
export interface ReadVResult {
  bytesRead: number;
  buffers: ArrayBufferView[];
}

export interface WriteVResult {
  bytesWritten: number;
  buffers: ArrayBufferView[];
}

Usage Examples:

import { fs } from "memfs";

async function fileHandleOperations() {
  // Open file handle
  const fileHandle = await fs.promises.open('/data.txt', 'w+');
  
  try {
    // Write data
    await fileHandle.writeFile('Hello World!\nSecond line\n');
    
    // Read data
    const content = await fileHandle.readFile('utf8');
    console.log('Content:', content);
    
    // Buffer-based operations
    const buffer = Buffer.alloc(5);
    const readResult = await fileHandle.read(buffer, 0, 5, 0);
    console.log('Read:', readResult.bytesRead, 'bytes:', buffer.toString());
    
    // Write buffer
    const writeBuffer = Buffer.from('New content');
    const writeResult = await fileHandle.write(writeBuffer, 0, writeBuffer.length, 0);
    console.log('Wrote:', writeResult.bytesWritten, 'bytes');
    
    // File metadata operations
    await fileHandle.chmod(0o644);
    await fileHandle.utimes(new Date(), new Date());
    
    const stats = await fileHandle.stat();
    console.log('File size:', stats.size);
    
    // Vectored I/O
    const buffers = [Buffer.from('Hello '), Buffer.from('World!')];
    const writevResult = await fileHandle.writev(buffers);
    console.log('Vectored write:', writevResult.bytesWritten, 'bytes');
    
    // Sync operations
    await fileHandle.datasync(); // Sync data only
    await fileHandle.sync(); // Sync data and metadata
    
  } finally {
    // Always close file handle
    await fileHandle.close();
  }
}

// File handle with error handling
async function safeFileOperations() {
  let fileHandle;
  try {
    fileHandle = await fs.promises.open('/safe-file.txt', 'w');
    await fileHandle.writeFile('Safe content');
  } catch (error) {
    console.error('File operation failed:', error);
  } finally {
    if (fileHandle) {
      await fileHandle.close();
    }
  }
}

Directory Iterator

Directory class for iterating over directory contents.

/**
 * Directory iterator class for asynchronous directory traversal
 */
export class Dir {
  readonly path: string;
  
  /**
   * Read next directory entry asynchronously
   * @returns Promise resolving to next Dirent or null if end reached
   */
  read(): Promise<Dirent | null>;
  
  /**
   * Read next directory entry synchronously
   * @returns Next Dirent or null if end reached
   */
  readSync(): Dirent | null;
  
  /**
   * Close directory iterator asynchronously
   * @returns Promise that resolves when closed
   */
  close(): Promise<void>;
  
  /**
   * Close directory iterator synchronously
   */
  closeSync(): void;
  
  /**
   * Async iterator interface
   */
  [Symbol.asyncIterator](): AsyncIterableIterator<Dirent>;
}

Usage Examples:

import { fs } from "memfs";

async function directoryIteration() {
  // Set up directory
  fs.mkdirSync('/items');
  fs.writeFileSync('/items/file1.txt', 'content1');
  fs.writeFileSync('/items/file2.txt', 'content2');
  fs.mkdirSync('/items/subdir');
  
  // Open directory
  const dir = await fs.promises.opendir('/items');
  
  try {
    // Manual iteration
    let entry;
    while ((entry = await dir.read()) !== null) {
      console.log(`${entry.name}: ${entry.isFile() ? 'file' : 'directory'}`);
    }
  } finally {
    await dir.close();
  }
  
  // Async iterator (recommended)
  const dir2 = await fs.promises.opendir('/items');
  try {
    for await (const entry of dir2) {
      console.log(`Async: ${entry.name}`);
      
      if (entry.isFile()) {
        const content = fs.readFileSync(`/items/${entry.name}`, 'utf8');
        console.log(`  Content: ${content}`);
      }
    }
  } finally {
    await dir2.close();
  }
  
  // Synchronous directory iteration
  const dirSync = fs.opendirSync('/items');
  try {
    let entry;
    while ((entry = dirSync.readSync()) !== null) {
      console.log(`Sync: ${entry.name}`);
    }
  } finally {
    dirSync.closeSync();
  }
}

// Directory traversal with filtering
async function findFiles(dirPath: string, extension: string): Promise<string[]> {
  const results: string[] = [];
  const dir = await fs.promises.opendir(dirPath);
  
  try {
    for await (const entry of dir) {
      const fullPath = `${dirPath}/${entry.name}`;
      
      if (entry.isFile() && entry.name.endsWith(extension)) {
        results.push(fullPath);
      } else if (entry.isDirectory()) {
        // Recursive search
        const subdirResults = await findFiles(fullPath, extension);
        results.push(...subdirResults);
      }
    }
  } finally {
    await dir.close();
  }
  
  return results;
}

Tree Visualization

Utility functions for generating human-readable tree representations of file systems.

/**
 * Generate a tree representation of the file system
 * @param fs File system API to use
 * @param opts Tree generation options
 * @returns String representation of the file system tree
 */
export function toTreeSync(fs: FsSynchronousApi, opts?: ToTreeOptions): string;

/**
 * Options for tree generation
 */
export interface ToTreeOptions {
  /** Root directory to start from (default: '/') */
  dir?: string;
  /** Tab character or string for indentation (default: '') */
  tab?: string;
  /** Maximum depth to traverse (default: 10) */
  depth?: number;
  /** Path separator character (default: '/') */
  separator?: '/' | '\\';
}

Usage Examples:

import { fs, toTreeSync } from "memfs";

// Create sample file system
fs.mkdirSync('/project', { recursive: true });
fs.writeFileSync('/project/README.md', '# My Project');
fs.writeFileSync('/project/package.json', '{"name": "my-project"}');
fs.mkdirSync('/project/src');
fs.writeFileSync('/project/src/index.js', 'console.log("Hello");');
fs.writeFileSync('/project/src/utils.js', 'module.exports = {};');
fs.mkdirSync('/project/tests');
fs.writeFileSync('/project/tests/index.test.js', 'test("works", () => {});');
fs.symlinkSync('../README.md', '/project/src/README.md');

// Generate tree with default options
const tree = toTreeSync(fs);
console.log('Full file system tree:');
console.log(tree);

// Generate tree for specific directory
const projectTree = toTreeSync(fs, { dir: '/project' });
console.log('Project tree:');
console.log(projectTree);

// Customize tree appearance
const customTree = toTreeSync(fs, {
  dir: '/project',
  tab: '  ',           // Use 2 spaces for indentation
  depth: 2,            // Limit depth to 2 levels
  separator: '/'       // Use forward slash
});
console.log('Custom tree:');
console.log(customTree);

// Tree with different indentation
const fancyTree = toTreeSync(fs, {
  dir: '/project',
  tab: '│ ',           // Use box drawing characters
  depth: 3
});
console.log('Fancy tree:');
console.log(fancyTree);

// Utility function to print directory structure
function printDirectoryStructure(path: string, maxDepth = 5) {
  console.log(`Directory structure for ${path}:`);
  console.log(toTreeSync(fs, {
    dir: path,
    tab: '├─ ',
    depth: maxDepth
  }));
}

printDirectoryStructure('/project');

// Compare two directory structures
function compareDirectories(path1: string, path2: string) {
  const tree1 = toTreeSync(fs, { dir: path1, tab: '  ' });
  const tree2 = toTreeSync(fs, { dir: path2, tab: '  ' });
  
  console.log(`${path1}:`);
  console.log(tree1);
  console.log(`\n${path2}:`);
  console.log(tree2);
}

Stream Classes

Stream implementations for reading and writing files.

/**
 * Readable stream for file reading
 */
export class ReadStream extends Readable {
  readonly path: string;
  readonly fd: number;
  readonly flags: string;
  readonly mode: number;
  readonly start?: number;
  readonly end?: number;
  readonly autoClose: boolean;
  
  bytesRead: number;
  pending: boolean;
  
  // Readable stream interface
  _read(size: number): void;
  _destroy(error: Error | null, callback: (error?: Error | null) => void): void;
}

/**
 * Writable stream for file writing
 */
export class WriteStream extends Writable {
  readonly path: string;
  readonly fd: number;
  readonly flags: string;
  readonly mode: number;
  readonly start?: number;
  readonly autoClose: boolean;
  
  bytesWritten: number;
  pending: boolean;
  
  // Writable stream interface
  _write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void): void;
  _destroy(error: Error | null, callback: (error?: Error | null) => void): void;
}

Usage Examples:

import { fs } from "memfs";

function streamOperations() {
  // Create readable stream
  fs.writeFileSync('/source.txt', 'Hello\nWorld\nFrom\nStream!');
  const readStream = fs.createReadStream('/source.txt', { encoding: 'utf8' });
  
  // Create writable stream
  const writeStream = fs.createWriteStream('/destination.txt');
  
  // Stream events
  readStream.on('data', (chunk) => {
    console.log('Read chunk:', chunk);
  });
  
  readStream.on('end', () => {
    console.log('Reading finished');
  });
  
  writeStream.on('finish', () => {
    console.log('Writing finished');
    console.log('Bytes written:', writeStream.bytesWritten);
  });
  
  // Pipe streams
  readStream.pipe(writeStream);
  
  // Manual stream writing
  const manualWriteStream = fs.createWriteStream('/manual.txt');
  manualWriteStream.write('Line 1\n');
  manualWriteStream.write('Line 2\n');
  manualWriteStream.end('Final line\n');
  
  // Stream with options
  const rangeReadStream = fs.createReadStream('/source.txt', {
    start: 5,    // Start at byte 5
    end: 10,     // End at byte 10
    encoding: 'utf8'
  });
  
  rangeReadStream.on('data', (chunk) => {
    console.log('Range chunk:', chunk);
  });
}

Type Definitions

// File system API interface for utilities
export interface FsSynchronousApi {
  readdirSync(path: string, options?: any): string[] | Dirent[];
  statSync(path: string, options?: any): Stats;
  lstatSync(path: string, options?: any): Stats;
  readFileSync(path: string, options?: any): string | Buffer;
  readlinkSync(path: string, options?: any): string;
  // ... other synchronous methods
}

// Event types for file handles and streams
export interface FileHandleEventMap {
  'close': () => void;
  'error': (error: Error) => void;
}

export interface StreamEventMap {
  'data': (chunk: any) => void;
  'end': () => void;
  'error': (error: Error) => void;
  'close': () => void;
  'finish': () => void;
}

// Generic buffer types
export type BufferLike = ArrayBufferView | ArrayBuffer;
export type StringOrBuffer = string | Buffer;

docs

core-filesystem.md

helpers.md

index.md

tile.json