CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-walkdir

Directory traversal library that walks file system trees with multiple interface options including callback, event emitter, synchronous, and promise-based patterns.

Pending
Overview
Eval results
Files

events-control.mddocs/

Event System & Control

Event emitter interface providing comprehensive file system events and flow control methods for directory traversal operations.

Capabilities

WalkEmitter Interface

The emitter returned by walkdir provides fine-grained control over the traversal process.

/**
 * Extended EventEmitter with walkdir-specific control methods
 */
interface WalkEmitter extends EventEmitter {
  /** Stop the walk immediately and prevent further events */
  end(): void;
  /** Alias for end() - stop the walk immediately */
  stop(): void;
  /** Pause event emission while keeping traversal active */
  pause(): void;
  /** Resume event emission after pause */
  resume(): void;
  /** Ignore specific paths during traversal */
  ignore(paths: string | string[]): void;
}

Usage Examples:

const walkdir = require('walkdir');

const emitter = walkdir('./my-directory');

// Stop after finding 100 files
let fileCount = 0;
emitter.on('file', (path, stat) => {
  fileCount++;
  if (fileCount >= 100) {
    emitter.end(); // Stop immediately
  }
});

// Pause and resume based on system load
emitter.on('directory', (path, stat) => {
  if (isSystemBusy()) {
    emitter.pause();
    setTimeout(() => emitter.resume(), 1000);
  }
});

// Ignore specific directories dynamically
emitter.on('directory', (path, stat) => {
  if (path.includes('node_modules') || path.includes('.git')) {
    emitter.ignore(path); // Skip this directory and all children
  }
});

Core Events

Events emitted for different types of file system objects found during traversal.

/**
 * All events follow the pattern: (path: string, stat: fs.Stats, depth: number)
 */

/** Emitted for every file system object found */
emitter.on('path', (path, stat, depth) => {});

/** Emitted only for regular files */
emitter.on('file', (path, stat, depth) => {});

/** Emitted only for directories */
emitter.on('directory', (path, stat, depth) => {});

/** Emitted only for symbolic links */
emitter.on('link', (path, stat, depth) => {});

/** Emitted for the initial target directory (if it's a directory) */
emitter.on('targetdirectory', (path, stat, depth) => {});

/** Emitted when a directory is empty */
emitter.on('empty', (path, stat, depth) => {});

/** Emitted when traversal completes successfully */
emitter.on('end', () => {});

Usage Examples:

const emitter = walkdir('./project');

// Process different file types
emitter.on('file', (path, stat, depth) => {
  if (path.endsWith('.js')) {
    console.log('JavaScript file:', path, 'at depth', depth);
  }
});

emitter.on('directory', (path, stat, depth) => {
  console.log('Directory:', path, 'contains', stat.size, 'entries');
});

emitter.on('link', (path, stat, depth) => {
  console.log('Symbolic link found:', path);
});

// Handle completion
emitter.on('end', () => {
  console.log('Directory traversal completed');
});

// Track progress
let totalItems = 0;
emitter.on('path', () => {
  totalItems++;
  if (totalItems % 100 === 0) {
    console.log(`Processed ${totalItems} items...`);
  }
});

Special Device Events

Events for special file system objects like sockets, fifos, and devices.

/** Emitted for socket descriptors */
emitter.on('socket', (path, stat, depth) => {});

/** Emitted for FIFO/named pipes */
emitter.on('fifo', (path, stat, depth) => {});

/** Emitted for block devices */
emitter.on('blockdevice', (path, stat, depth) => {});

/** Emitted for character devices */
emitter.on('characterdevice', (path, stat, depth) => {});

Usage Example:

const emitter = walkdir('/dev');

emitter.on('blockdevice', (path, stat) => {
  console.log('Block device (disk):', path);
});

emitter.on('characterdevice', (path, stat) => {
  console.log('Character device (terminal/tty):', path);
});

emitter.on('socket', (path, stat) => {
  console.log('Socket found:', path);
});

Error Events

Error handling events for various failure scenarios during traversal.

/** Emitted for fatal errors reading the target directory */
emitter.on('error', (error) => {});

/** Emitted for non-fatal errors reading nested paths */
emitter.on('fail', (path, error) => {});

/** Emitted when maximum depth is reached */
emitter.on('maxdepth', (path, stat, depth) => {});

Error Handling Examples:

const emitter = walkdir('./some-directory');

// Handle fatal errors (target directory issues)
emitter.on('error', (error) => {
  console.error('Fatal error - cannot read target directory:', error.message);
  process.exit(1);
});

// Handle non-fatal errors (permission issues, corrupt files, etc.)
emitter.on('fail', (path, error) => {
  console.warn('Could not access path:', path, '-', error.message);
  // Continue processing other paths
});

// Handle depth limits  
emitter.on('maxdepth', (path, stat, depth) => {
  console.log('Reached maximum depth at:', path, 'depth:', depth);
  // Note: traversal continues but won't go deeper into this path
});

// Graceful error handling
let errorCount = 0;
emitter.on('fail', (path, error) => {
  errorCount++;
  if (errorCount > 10) {
    console.warn('Too many errors, stopping traversal');
    emitter.end();
  }
});

Flow Control Methods

Methods for controlling the traversal process dynamically.

/**
 * Stop the walk immediately
 * No more events will be emitted after calling end()
 */
emitter.end();

/**
 * Alias for end() - stop the walk immediately
 * Same functionality as end()
 */
emitter.stop();

/**
 * Pause event emission
 * Traversal continues but events are queued until resume()
 */
emitter.pause();

/**
 * Resume event emission after pause
 * Queued events will be emitted in order
 */
emitter.resume();

/**
 * Ignore specific paths during traversal
 * Ignored directories will not be entered or have children processed
 * @param {string|string[]} paths - Path or array of paths to ignore
 */
emitter.ignore(paths);

Flow Control Examples:

const emitter = walkdir('./large-project');

// Conditional stopping
emitter.on('file', (path, stat) => {
  if (path.endsWith('.exe') && stat.size > 100 * 1024 * 1024) {
    console.log('Found large executable, stopping scan');
    emitter.end();
  }
});

// Throttling with pause/resume
let processedCount = 0;
emitter.on('path', (path, stat) => {
  processedCount++;
  
  // Pause every 1000 items to prevent overwhelming
  if (processedCount % 1000 === 0) {
    emitter.pause();
    setTimeout(() => {
      console.log(`Processed ${processedCount} items, resuming...`);
      emitter.resume();
    }, 100);
  }
});

// Dynamic path ignoring
const ignoredPaths = [];
emitter.on('directory', (path, stat) => {
  if (path.includes('cache') || path.includes('temp')) {
    ignoredPaths.push(path);
    emitter.ignore(path);
    console.log('Ignoring cache/temp directory:', path);
  }
});

// Bulk ignore patterns
emitter.on('targetdirectory', () => {
  // Ignore common build/cache directories upfront
  emitter.ignore([
    './node_modules',
    './.git',
    './build',
    './dist',
    './.cache'
  ]);
});

Event Chaining Patterns

Common patterns for combining events and control methods.

// Progress reporting with early termination
const emitter = walkdir('./project');
let startTime = Date.now();
let itemCount = 0;

emitter.on('path', (path, stat) => {
  itemCount++;
  
  // Progress every 500 items  
  if (itemCount % 500 === 0) {
    const elapsed = (Date.now() - startTime) / 1000;
    console.log(`${itemCount} items processed in ${elapsed}s`);
  }
  
  // Stop after 10 seconds
  if (Date.now() - startTime > 10000) {
    console.log('Time limit reached, stopping');
    emitter.end();
  }
});

emitter.on('end', () => {
  const elapsed = (Date.now() - startTime) / 1000;
  console.log(`Completed: ${itemCount} items in ${elapsed}s`);
});

Install with Tessl CLI

npx tessl i tessl/npm-walkdir

docs

async-operations.md

core-walking.md

events-control.md

index.md

options-configuration.md

sync-operations.md

tile.json