-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
Bringing the debug Dependency In-House
Replacing the external debug npm package (v2.6.9) with stdlib-native equivalents to eliminate a supply-chain risk and align all production code with stdlib's quality standards (docs, tests, examples, benchmarks, backward compatibility).
Background
What debug v2.6.9 Does
A tiny JavaScript debugging utility modelled after Node.js core's debugging technique. Works in Node.js and web browsers.
More here: https://github.com/debug-js/debug
Its sole dependency
msv2.0.0: Converts milliseconds to/from human-readable strings (e.g.,1500→"1.5s"). Used only for the+1.5sdiff suffix in log output.
How stdlib uses it
- 680+ files across:
@stdlib/random/streams/*,@stdlib/plot/*,@stdlib/streams/*,@stdlib/math/base/special/*,@stdlib/fs/*,@stdlib/repl/*,@stdlib/_tools/*, and more. - Only the factory function is used:
var logger = require('debug'); var debug = logger('namespace');thendebug('message: %s.', val);. - No advanced API usage: none of the 680+ files call
enable(),disable(),enabled(), or register custom formatters.
Proposed Changes
The plan creates 2 new packages mirroring stdlib's decomposable architecture, with each package being independently consumable.
Component 1: @stdlib/time/ms
Replaces the
msnpm dependency (transitive viadebug).
A utility to convert between millisecond values and human-readable time strings.
Scope: Only the ms → string direction is needed by the debug logger (e.g., 1500 → "1.5s"). Optionally support string → ms for completeness.
@stdlib/time/ms/
├── lib/
│ ├── index.js # re-export main
│ └── main.js # ms(val) → string or number
├── test/
│ └── test.js
├── benchmark/
│ └── benchmark.js
├── docs/
│ ├── repl.txt
│ └── types/
│ ├── index.d.ts
│ └── test.ts
├── examples/
│ └── index.js
├── README.md
└── package.json
API:
var ms = require( '@stdlib/time/ms' );
ms( 1500 ); // => '1.5s'
ms( 60000 ); // => '1m'
ms( 3600000 ); // => '1h'
ms( 86400000 ); // => '1d'
ms( 500 ); // => '500ms'Component 2: @stdlib/console/debug
The core package — the direct replacement for
require('debug').
A namespace-based diagnostic logging utility. This is the main factory function that all 680+ files will switch to.
@stdlib/console/debug/
├── lib/
│ ├── index.js # re-export main
│ ├── main.js # createDebug(namespace) factory (core logic)
│ ├── enable.js # enable(namespaces) — parse glob patterns
│ ├── disable.js # disable()
│ ├── enabled.js # enabled(name) → boolean
│ ├── format_args.js # environment-specific argument formatting
│ ├── colors.js # ANSI color selection (Node.js)
│ └── defaults.js # default options
├── test/
│ ├── test.js # main export tests
│ ├── test.enable.js # enable/disable/enabled tests
│ ├── test.format.js # format string placeholder tests
│ └── test.colors.js # color selection tests
├── benchmark/
│ ├── benchmark.js # benchmark: logger creation
│ └── benchmark.disabled.js # benchmark: no-op when disabled (perf)
├── docs/
│ ├── repl.txt
│ └── types/
│ ├── index.d.ts
│ └── test.ts
├── examples/
│ └── index.js
├── README.md
└── package.json
API (drop-in compatible with current usage):
var logger = require( '@stdlib/console/debug' );
// Create a namespaced logger:
var debug = logger( 'sparkline:unicode:main' );
// Log messages (no-op when disabled):
debug( 'Creating an instance with config: %s.', JSON.stringify( opts ) );
debug( 'Current value: %s.', this._yMin );
// Programmatic control (used internally, not by consumers):
logger.enable( 'sparkline:*' );
logger.disable();
logger.enabled( 'sparkline:unicode:main' ); // => true/falseKey implementation details:
- Reads
DEBUGenv var (Node.js) orlocalStorage.debug(browser) on load - Supports glob patterns:
*matches any characters,-prefix excludes - When disabled (the default), the function is a no-op with zero cost (early return)
- Format placeholders:
%s(string),%d(number),%o(inspect compact),%O(inspect multiline),%j(JSON),%%(escaped %) - ANSI colors in Node.js via deterministic namespace hashing
- Appends
+Xmsdiff timestamp between successive calls - Output target:
stderrin Node.js,console.login browsers - No external dependencies (uses
@stdlib/time/msfor humanized diffs)
Component 3: Migration — Updating All Consumers
This is the mechanical bulk change, done after the new packages are created and verified.
[MODIFY] All 680+ files using require( 'debug' )
The change is a single-line replacement per file:
-var logger = require( 'debug' );
+var logger = require( '@stdlib/console/debug' );Migration strategy (phased to reduce risk):
| Phase | Scope | Files |
|---|---|---|
| Phase 1 | @stdlib/random/streams/* |
~200 files (all have identical debug.js pattern) |
| Phase 2 | @stdlib/plot/* and @stdlib/streams/* |
~150 files |
| Phase 3 | @stdlib/math/base/special/* |
~100 files |
| Phase 4 | @stdlib/fs/*, @stdlib/repl/*, @stdlib/_tools/*, and remaining |
~200+ files |
Each phase follows the same process:
- Run
find+sedto replacerequire( 'debug' )→require( '@stdlib/console/debug' ) - Run existing tests for the affected module area
- Verify with
DEBUG='*' node <example>that output matches current behavior
Uninstall the dependancy
After all phases are complete:
npm uninstall debug
Verification Plan
Automated Tests
1. Unit tests for @stdlib/time/ms
make TESTS_FILTER=".*/time/ms/.*" testTests should cover:
- Millisecond → string conversion for all ranges (ms, seconds, minutes, hours, days)
- Edge cases: 0, negative values, Infinity, NaN
- Type validation: non-numeric input throws
2. Unit tests for @stdlib/console/debug
make TESTS_FILTER=".*/console/debug/.*" testTests should cover:
- Factory returns a function
- Logger is a no-op when
DEBUGis unset - Logger outputs when
DEBUGmatches namespace enable()/disable()/enabled()work correctly- Glob matching:
*,-prefixexclusion, multiple comma-separated patterns - Format placeholders:
%s,%d,%o,%O,%j,%% - Color assignment is deterministic per namespace
- Diff timestamp is appended
3. Regression tests for migrated modules
Run the full test suite for each phase:
# Phase 1:
make TESTS_FILTER=".*/random/streams/.*" test
# Phase 2:
make TESTS_FILTER=".*/plot/.*" test
make TESTS_FILTER=".*/streams/.*" test
# Phase 3:
make TESTS_FILTER=".*/math/base/special/.*" test
# Phase 4: Full suite
make testManual Verification
After migration, verify that debug output works end-to-end:
-
Create a test script that uses
@stdlib/random/streams/t(or any migrated module) -
Run without
DEBUG:node test_script.js
→ Verify: no debug output appears (silent by default)
-
Run with
DEBUG:DEBUG='random:streams:*' node test_script.js→ Verify: colored namespace-prefixed debug messages appear on stderr, with
+Xmsdiff timestamps -
Run with exclusion:
DEBUG='*,-random:*' node test_script.js→ Verify: excluded namespaces produce no output