totalist
totalist is a highly optimized, minimalist (under 250 bytes gzipped) utility for recursively listing all files within a specified directory in Node.js. It enables developers to efficiently traverse file systems and apply a custom callback function to each file found, providing the relative path, absolute path, and the `fs.Stats` object directly. The package offers both asynchronous (default, since Node.js 8.x) and synchronous (since Node.js 6.x) modes to accommodate various application contexts and performance requirements. The current stable version is 3.0.1, which includes native ESM support via `exports` maps. Its primary differentiators are its minuscule footprint, direct integration with `fs.Stats` within the callback, and the avoidance of generating intermediate large file lists, making it suitable for performance-critical scenarios.
Common errors
-
TypeError: totalist is not a function
cause Attempting to use `require` for `totalist` in a pure ESM context, or using a CommonJS `require` call that doesn't correctly resolve the module's default export when `exports` are defined.fixFor ESM projects, use `import { totalist } from 'totalist';` or `import { totalist } from 'totalist/sync';`. If still encountering issues in a CommonJS context, ensure your Node.js version is compatible with `exports` maps or consider using `totalist` v2.x. -
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'size' of undefined
cause The asynchronous `totalist` function was called but not `await`ed, leading to subsequent code attempting to use data (like `stats.size`) before the callback had a chance to populate it.fixAlways `await` the asynchronous `totalist` call: `await totalist(dir, callback);`. Ensure the calling function is marked `async`. -
Error: Cannot find module 'totalist/sync'
cause This error typically indicates that the module resolution failed to locate the synchronous variant of totalist, possibly due to an incorrect path, a non-standard module resolution setup, or a problem with the installed package.fixVerify the package is installed correctly (`npm install totalist`). Ensure your import statement is exactly `import { totalist } from 'totalist/sync';` and your bundler/Node.js environment supports subpath exports.
Warnings
- breaking Version 3.0.0 introduced native ESM support via the `exports` mapping in `package.json`. This may cause breaking changes for Node.js versions 13.0 through 13.7 due to their experimental and rapidly evolving module resolution behavior. Users on these specific Node.js versions should upgrade to a later stable release (14+ recommended) or remain on `totalist` v2.x.
- gotcha The default `totalist` export is asynchronous and returns a Promise. It must be `await`ed or handled within a Promise chain to ensure all files are processed before subsequent code executes. Failing to `await` it will result in the function returning immediately without processing files, potentially leading to incorrect program state.
- gotcha There are two distinct modes: asynchronous (`totalist`) and synchronous (`totalist/sync`). Incorrectly importing the async version when synchronous behavior is expected, or vice-versa, can lead to unexpected execution flow or errors. The sync version requires `import { totalist } from 'totalist/sync';`.
Install
-
npm install totalist -
yarn add totalist -
pnpm add totalist
Imports
- totalist
const { totalist } = require('totalist');import { totalist } from 'totalist'; - totalist (sync)
const { totalist } = require('totalist/sync');import { totalist } from 'totalist/sync'; - fs.Stats type
import type { Stats } from 'fs';
Quickstart
import { totalist } from 'totalist/sync';
import { resolve } from 'path';
import { fileURLToPath } from 'url';
import { existsSync, mkdirSync, writeFileSync } from 'fs';
// Create a dummy directory for demonstration
const __dirname = fileURLToPath(new URL('.', import.meta.url));
const testDir = resolve(__dirname, 'temp_files');
if (!existsSync(testDir)) {
mkdirSync(testDir, { recursive: true });
}
writeFileSync(resolve(testDir, 'file1.js'), 'console.log("file1");');
writeFileSync(resolve(testDir, 'style.css'), 'body { color: red; }');
writeFileSync(resolve(testDir, 'another.js'), 'console.log("file2");');
mkdirSync(resolve(testDir, 'subdir'), { recursive: true });
writeFileSync(resolve(testDir, 'subdir', 'nested.txt'), 'nested content');
const scripts = new Set();
const styles = new Set();
const others = new Set();
try {
totalist(testDir, (name, abs, stats) => {
if (name.endsWith('.js')) {
scripts.add(abs);
} else if (name.endsWith('.css')) {
styles.add(abs);
} else {
others.add(abs);
}
console.log(`Processing: ${name} (size: ${stats.size} bytes)`);
});
console.log('\n--- Summary ---');
console.log('JavaScript files:', [...scripts]);
console.log('CSS files:', [...styles]);
console.log('Other files:', [...others]);
} catch (error) {
console.error('Error listing files:', error);
}
// Clean up dummy directory (optional, but good for examples)
// import { rmSync } from 'fs';
// rmSync(testDir, { recursive: true, force: true });