Node.js File System Directory Walker
The `walk` package for Node.js provides a comprehensive utility for traversing file system directories, drawing inspiration from Python's `os.walk` function. It primarily operates asynchronously using the EventEmitter pattern, allowing for event-driven processing of files and directories as they are discovered. The library, currently at version 2.3.15, also includes a synchronous counterpart. Key features include built-in flow control and an optimization strategy that minimizes the number of open file descriptors, making it suitable for environments with traditional hard disks. However, it's important to note that the package was initially developed during the Node.js v0.x era and has remained largely unchanged for approximately a decade. Consequently, its API design reflects an older Node.js paradigm. The author now explicitly recommends considering `@root/walk` as a more modern, simpler, and faster alternative for new projects, indicating that `walk` is largely in a maintenance or superseded state rather than active development.
Common errors
-
SyntaxError: Cannot use import statement outside a module
cause Attempting to use `import walk from 'walk';` or `import { walk } from 'walk';` in a Node.js environment configured for CommonJS, or in an ESM file without proper CJS interop.fixIf in CommonJS, use `const walk = require('walk');`. If in ESM, consider using `@root/walk` or explicitly configuring Node.js to handle CommonJS imports within an ESM context (e.g., using a `.cjs` file extension or `package.json` `exports` field). -
TypeError: walker.on is not a function
cause This error occurs when attempting to attach event listeners (`.on()`) directly to the result of `walk.walkSync()`. The synchronous walker's primary event mechanism is through `options.listeners`, not the `EventEmitter` interface returned by `walk.walkSync` (which doesn't return an EventEmitter).fixFor `walkSync`, pass event handlers via the `options.listeners` object instead of calling `.on()` on the returned value. Example: `walk.walkSync(path, { listeners: { file: (r, s, n) => { /*...*/ n(); } } });`
Warnings
- deprecated The package author explicitly recommends migrating to `@root/walk` for new projects due to its simpler, faster, and more modern design. While `walk` remains functional, it is considered superseded.
- gotcha This package was developed for CommonJS environments (`require`). Attempting to use `import` statements directly in an ESM module without specific Node.js loader configuration for CJS interop will result in errors.
- gotcha The `walkSync` function, despite using synchronous `fs` methods, still uses `EventEmitter` internally which relies on `process.nextTick()`. For truly synchronous, blocking behavior where control flow should not yield, `options.listeners` must be used instead of `.on()` event handlers.
- gotcha For asynchronous `walk` operations, it is crucial to call `next()` in every `file`, `directory`, and `errors` event handler. Failing to call `next()` will halt the directory traversal indefinitely, as it acts as a flow control mechanism.
Install
-
npm install walk -
yarn add walk -
pnpm add walk
Imports
- walk (module object)
import walkModule from 'walk';
const walkModule = require('walk'); - walk (asynchronous function)
import { walk } from 'walk';const { walk } = require('walk'); - walkSync (synchronous function)
import { walkSync } from 'walk';const { walkSync } = require('walk');
Quickstart
const walk = require('walk');
const fs = require('fs');
const path = require('path');
const testDir = path.join(__dirname, 'temp_walk_dir');
const subDir = path.join(testDir, 'sub_dir');
const file1 = path.join(testDir, 'file1.txt');
const file2 = path.join(subDir, 'file2.txt');
// Setup: Create a temporary directory structure
fs.mkdirSync(testDir, { recursive: true });
fs.mkdirSync(subDir, { recursive: true });
fs.writeFileSync(file1, 'content for file1');
fs.writeFileSync(file2, 'content for file2');
console.log(`Starting walk in: ${testDir}`);
let filesFound = [];
let directoriesFound = [];
let errorsEncountered = [];
const options = {};
const walker = walk.walk(testDir, options);
walker.on('file', function (root, fileStats, next) {
filesFound.push(path.join(root, fileStats.name));
// console.log(`Found file: ${path.join(root, fileStats.name)}`);
next(); // Crucial to call next() to continue the walk
});
walker.on('directory', function (root, dirStats, next) {
directoriesFound.push(path.join(root, dirStats.name));
// console.log(`Found directory: ${path.join(root, dirStats.name)}`);
next(); // Crucial to call next() to continue the walk
});
walker.on('errors', function (root, nodeStatsArray, next) {
nodeStatsArray.forEach(nodeStats => {
errorsEncountered.push({ path: path.join(root, nodeStats.name), error: nodeStats.error ? nodeStats.error.message : 'Unknown error' });
});
console.error(`Errors encountered in ${root}:`, errorsEncountered);
next(); // Crucial to call next() to continue the walk
});
walker.on('end', function () {
console.log('\n--- Walk Complete ---');
console.log('Files found:', filesFound.sort());
console.log('Directories found:', directoriesFound.sort());
if (errorsEncountered.length > 0) {
console.error('Final errors summary:', errorsEncountered);
}
// Cleanup: Remove the temporary directory
fs.rmSync(testDir, { recursive: true, force: true });
console.log('\nTemporary directory cleaned up.');
});