or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-klaw

File system walker with Readable stream interface for recursively traversing directories and their contents

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/klaw@4.1.x

To install, run

npx @tessl/cli install tessl/npm-klaw@4.1.0

index.mddocs/

Klaw

Klaw is a Node.js file system walker that provides a Readable stream interface for recursively traversing directories and their contents. It enables developers to walk through file systems asynchronously, emitting objects containing file paths and fs.Stats for each discovered item (files, directories, symlinks).

Package Information

  • Package Name: klaw
  • Package Type: npm
  • Language: JavaScript
  • Installation: npm install klaw
  • Minimum Node.js: 14.14.0

Core Imports

const klaw = require('klaw');

For ES modules:

import klaw from 'klaw';

Basic Usage

const klaw = require('klaw');

// Simple traversal collecting all paths
const items = [];
klaw('/some/directory')
  .on('data', item => {
    items.push(item.path);
    console.log(item.path, item.stats.isDirectory() ? 'DIR' : 'FILE');
  })
  .on('end', () => console.log('Done!', items.length, 'items'));

// Using readable stream pattern
klaw('/some/directory')
  .on('readable', function () {
    let item;
    while ((item = this.read())) {
      // Process each item
      console.log(item.path);
    }
  });

// Modern async iteration
for await (const item of klaw('/some/directory')) {
  console.log(item.path, item.stats.size);
}

Architecture

Klaw is built around a simple, focused architecture:

  • Walker Class: Internal Readable stream that implements the recursive traversal logic
  • Factory Function: Main exported function that creates Walker instances with configuration
  • Stream Interface: Full Node.js Readable stream compatibility with object mode
  • Async Processing: Non-blocking file system operations using Node.js fs callbacks
  • Configurable Strategy: Pluggable traversal order, filtering, and depth control

Capabilities

File System Walking

Core functionality for recursively traversing directory structures and emitting file system items.

/**
 * Creates a file system walker that recursively traverses directories
 * @param {string|URL} directory - Root directory to walk (string path or file URL)
 * @param {WalkerOptions} [options] - Configuration options for traversal behavior
 * @returns {Walker} Readable stream in object mode emitting file system items
 */
function klaw(directory, options);

interface WalkerOptions {
  /** Method for processing directory contents: 'shift' (breadth-first) or 'pop' (depth-first) */
  queueMethod?: 'shift' | 'pop';
  /** Function to sort directory contents before processing */
  pathSorter?: (a: string, b: string) => number;
  /** Function to filter which paths to include in traversal */
  filter?: (path: string) => boolean;
  /** Maximum recursion depth (-1 for unlimited) */
  depthLimit?: number;
  /** Whether to follow symlinks (false) or treat as items (true) */
  preserveSymlinks?: boolean;
  /** Custom file system implementation (defaults to Node.js fs) */
  fs?: FileSystemInterface;
  /** Additional Readable stream options (objectMode is always true) */
  [key: string]: any;
}

interface FileSystemInterface {
  stat: (path: string, callback: (err: Error | null, stats: fs.Stats) => void) => void;
  lstat: (path: string, callback: (err: Error | null, stats: fs.Stats) => void) => void;
  readdir: (path: string, callback: (err: Error | null, files: string[]) => void) => void;
}

interface WalkerItem {
  /** Full path to the file or directory */
  path: string;
  /** Node.js fs.Stats object containing file system metadata */
  stats: fs.Stats;
}

Stream Events:

  • data: Emitted for each file system item with WalkerItem object
  • end: Emitted when traversal is complete
  • error: Emitted on file system errors, receives (error, item) parameters
  • readable: Standard Readable stream event for pull-based consumption

Usage Examples:

const klaw = require('klaw');
const path = require('path');

// Basic traversal with data events
klaw('/home/user/documents')
  .on('data', item => {
    if (item.stats.isFile()) {
      console.log('File:', item.path, 'Size:', item.stats.size);
    }
  })
  .on('error', (err, item) => {
    console.error('Error processing:', item.path, err.message);
  })
  .on('end', () => console.log('Traversal complete'));

// Filtering hidden files and directories
const filterFunc = item => {
  const basename = path.basename(item);
  return basename === '.' || basename[0] !== '.';
};

klaw('/some/directory', { filter: filterFunc })
  .on('data', item => {
    // Only non-hidden items reach here
    console.log(item.path);
  });

// Depth-limited traversal
klaw('/deep/directory', { depthLimit: 2 })
  .on('data', item => {
    console.log(item.path);
  });

// Custom sorting (alphabetical)
klaw('/directory', { 
  pathSorter: (a, b) => a.localeCompare(b) 
})
  .on('data', item => {
    console.log(item.path);
  });

// File URL support
const { pathToFileURL } = require('url');
klaw(pathToFileURL('/some/directory'))
  .on('data', item => {
    console.log(item.path);
  });

Stream Processing Integration

Since klaw returns a standard Node.js Readable stream, it integrates seamlessly with stream processing libraries.

Example with through2 for advanced filtering:

const klaw = require('klaw');
const through2 = require('through2');
const path = require('path');

// Filter to only include .js files
const jsFilesOnly = through2.obj(function (item, enc, next) {
  if (!item.stats.isDirectory() && path.extname(item.path) === '.js') {
    this.push(item);
  }
  next();
});

klaw('/project/src')
  .pipe(jsFilesOnly)
  .on('data', item => {
    console.log('JavaScript file:', item.path);
  });

// Aggregate file sizes by extension
let totalSizes = {};
const aggregateByExtension = through2.obj(function (item, enc, next) {
  if (item.stats.isFile()) {
    const ext = path.extname(item.path) || 'no-extension';
    totalSizes[ext] = (totalSizes[ext] || 0) + item.stats.size;
  }
  this.push(item);
  next();
});

klaw('/project')
  .pipe(aggregateByExtension)
  .on('end', () => {
    console.log('File sizes by extension:', totalSizes);
  });

Error Handling

Klaw provides comprehensive error handling for file system operations.

// Error event signature
walker.on('error', (error: Error, item: WalkerItem) => void);

Error Handling Examples:

const klaw = require('klaw');

klaw('/some/directory')
  .on('data', item => {
    console.log(item.path);
  })
  .on('error', (err, item) => {
    // Handle individual file/directory errors
    console.error(`Error accessing ${item.path}:`, err.message);
    
    // Common error types:
    // - ENOENT: File or directory doesn't exist
    // - EACCES: Permission denied
    // - ENOTDIR: Expected directory but found file
  })
  .on('end', () => {
    console.log('Traversal completed despite errors');
  });

// Propagating errors through streams
const through2 = require('through2');

const transform = through2.obj(function (item, enc, next) {
  // Process items
  this.push(item);
  next();
});

klaw('/directory')
  .on('error', err => transform.emit('error', err)) // Forward errors
  .pipe(transform)
  .on('error', err => {
    console.error('Stream error:', err.message);
  });

Advanced Configuration

Klaw supports advanced configuration options for customizing traversal behavior.

Queue Method Configuration:

// Breadth-first traversal (default)
klaw('/directory', { queueMethod: 'shift' })
  .on('data', item => console.log(item.path));

// Depth-first traversal  
klaw('/directory', { queueMethod: 'pop' })
  .on('data', item => console.log(item.path));

Symlink Handling:

// Default: follow symlinks and traverse target
klaw('/directory', { preserveSymlinks: false })
  .on('data', item => {
    // item.path shows target path for symlinks
    console.log(item.path);
  });

// Treat symlinks as items themselves
klaw('/directory', { preserveSymlinks: true })
  .on('data', item => {
    if (item.stats.isSymbolicLink()) {
      console.log('Symlink:', item.path);
    }
  });

Custom File System:

// Using mock-fs for testing
const mockFs = require('mock-fs');

klaw('/virtual/directory', { fs: mockFs })
  .on('data', item => {
    console.log('Virtual file:', item.path);
  });

Types

/**
 * Walker class extends Node.js Readable stream
 * Internal class, not directly exported
 */
class Walker extends Readable {
  constructor(directory: string | URL, options?: WalkerOptions);
  
  /** Standard Readable stream methods */
  read(size?: number): WalkerItem | null;
  pipe<T extends NodeJS.WritableStream>(destination: T, options?: object): T;
  
  /** Event emitter methods for stream events */
  on(event: 'data', listener: (item: WalkerItem) => void): this;
  on(event: 'end', listener: () => void): this;
  on(event: 'error', listener: (error: Error, item: WalkerItem) => void): this;
  on(event: 'readable', listener: () => void): this;
}