Lazy-evaluation wrapper for debug — skip string building entirely when logging is disabled.
The debug module always evaluates its arguments, even when the namespace is disabled. If you're building expensive strings — JSON serialization, large object inspection, string concatenation — you pay the cost even when nobody's reading the output.
debug-fabulous wraps debug with lazy evaluation. Pass a function instead of a string, and it only runs when the namespace is actually enabled. When logging is off, the call is a no-op — no string allocation, no concatenation, no wasted cycles.
This matters at scale. Libraries like gulp-sourcemaps (700K+ weekly downloads) use debug-fabulous as a transitive dependency for exactly this reason.
npm install debug-fabulousconst debugFab = require('debug-fabulous');
const debug = debugFab()('my-app');
// Lazy evaluation — the function only runs if 'my-app' is enabled
debug(() => 'user object: ' + JSON.stringify(largeUserObject));
// Plain strings still work
debug('server started on port %d', 3000);Create hierarchical namespaces without string juggling:
const debugFab = require('debug-fabulous');
const debug = debugFab()('my-app');
const dbDebug = debug.spawn('db'); // my-app:db
const queryDebug = dbDebug.spawn('query'); // my-app:db:query
dbDebug('connected');
queryDebug(() => `SELECT took ${ms}ms, returned ${rows.length} rows`);If you just need hierarchical debuggers without the factory:
const { spawnable } = require('debug-fabulous');
const debug = spawnable('my-app');
const child = debug.spawn('worker'); // my-app:worker
child('processing job %d', jobId);debug-fabulous is written in TypeScript and ships type declarations.
import debugFab, { spawnable } from 'debug-fabulous';
const debug = debugFab()('my-app');
// Lazy eval with type safety
debug(() => `processed ${items.length} items`);
// Spawn children
const child = debug.spawn('worker');
child('ready');
// Return an array for format strings
debug(() => ['found %d results in %dms', count, elapsed]);Returns a wrapped debug factory with lazy evaluation and namespace caching.
debugApi(optional) — a customdebuginstance. Defaults torequire('debug').
The returned factory has the same API as debug (enable(), disable(), load(), save(), etc.) plus lazy evaluation support.
Pass a function instead of a string. It's only called when the namespace is enabled:
// Function returns a string
debug(() => expensiveStringOperation());
// Function returns [formatter, ...args] array
debug(() => ['user %s performed %d actions', userName, count]);Creates a child debugger under the current namespace:
const root = debug('app'); // app
const db = root.spawn('db'); // app:db
const cache = db.spawn('cache'); // app:db:cacheStandalone function that creates a spawnable debugger directly:
const { spawnable } = require('debug-fabulous');
const debug = spawnable('app');- Namespace caching —
Map-based memoization means repeateddebug('same-ns')calls return the same instance instantly. - Singleton no-op — Disabled namespaces get a shared no-op function instead of allocating per-instance.
- Lazy closures — When you pass a function, it's never invoked if the namespace is disabled. No string allocation, no concatenation, no
JSON.stringify().
If you find this project useful, consider sponsoring @nmccready to support ongoing maintenance and development. ❤️
MIT — Nicholas McCready and contributors.