From e17172e9b6ca35ea1f95672fc4ad5013fc800545 Mon Sep 17 00:00:00 2001 From: Arron Zou Date: Fri, 14 Aug 2026 17:38:54 +0800 Subject: [PATCH] Merge namespaces in debug.enable() instead of replacing them enable() currently wipes names/skips, so DEBUG=foo followed by enable("bar") disables foo. Merge incoming namespaces with the existing set. Call disable() first when replacement is intended. Fixes #425 --- README.md | 14 ++++++++--- src/common.js | 68 +++++++++++++++++++++++++++++++++++++-------------- test.js | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 654d105a..eb737417 100644 --- a/README.md +++ b/README.md @@ -313,11 +313,19 @@ Usage : `enable(namespaces)` `namespaces` can include modes separated by a colon and wildcards. -Note that calling `enable()` completely overrides previously set DEBUG variable : +Calling `enable()` merges with previously set namespaces (including `DEBUG`) +instead of replacing them: ``` -$ DEBUG=foo node -e 'var dbg = require("debug"); dbg.enable("bar"); console.log(dbg.enabled("foo"))' -=> false +$ DEBUG=foo node -e 'var dbg = require("debug"); dbg.enable("bar"); console.log(dbg.enabled("foo"), dbg.enabled("bar"))' +=> true true +``` + +To replace the set entirely, disable first: + +```js +debug.disable(); +debug.enable('bar'); ``` `disable()` diff --git a/src/common.js b/src/common.js index 141cb578..9bc74b1a 100644 --- a/src/common.js +++ b/src/common.js @@ -153,32 +153,64 @@ function setup(env) { } /** - * Enables a debug mode by namespaces. This can include modes - * separated by a colon and wildcards. + * Parse a namespace list into individual tokens. * * @param {String} namespaces - * @api public + * @return {Array} + * @api private */ - function enable(namespaces) { - createDebug.save(namespaces); - createDebug.namespaces = namespaces; - - createDebug.names = []; - createDebug.skips = []; - - const split = (typeof namespaces === 'string' ? namespaces : '') + function parseNamespaces(namespaces) { + return (typeof namespaces === 'string' ? namespaces : '') .trim() .replace(/\s+/g, ',') .split(',') .filter(Boolean); + } + + /** + * Serialize the current names/skips into a namespace string. + * + * @return {String} + * @api private + */ + function formatNamespaces() { + return [ + ...createDebug.names, + ...createDebug.skips.map(namespace => '-' + namespace) + ].join(','); + } + + /** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * New namespaces are merged with those already enabled. + * Call `disable()` first to replace the set instead of extending it. + * + * @param {String} namespaces + * @api public + */ + function enable(namespaces) { + const split = parseNamespaces(namespaces); for (const ns of split) { if (ns[0] === '-') { - createDebug.skips.push(ns.slice(1)); + const name = ns.slice(1); + createDebug.names = createDebug.names.filter(existing => existing !== name); + if (!createDebug.skips.includes(name)) { + createDebug.skips.push(name); + } } else { - createDebug.names.push(ns); + createDebug.skips = createDebug.skips.filter(existing => existing !== ns); + if (!createDebug.names.includes(ns)) { + createDebug.names.push(ns); + } } } + + const merged = formatNamespaces(); + createDebug.save(merged); + createDebug.namespaces = merged; } /** @@ -231,11 +263,11 @@ function setup(env) { * @api public */ function disable() { - const namespaces = [ - ...createDebug.names, - ...createDebug.skips.map(namespace => '-' + namespace) - ].join(','); - createDebug.enable(''); + const namespaces = formatNamespaces(); + createDebug.names = []; + createDebug.skips = []; + createDebug.save(''); + createDebug.namespaces = ''; return namespaces; } diff --git a/test.js b/test.js index a1d6f633..dfcd15ac 100644 --- a/test.js +++ b/test.js @@ -4,6 +4,10 @@ const assert = require('assert'); const debug = require('./src'); describe('debug', () => { + beforeEach(() => { + debug.disable(); + }); + it('passes a basic sanity check', () => { const log = debug('test'); log.enabled = true; @@ -29,6 +33,68 @@ describe('debug', () => { assert.deepStrictEqual(debug('test:67890').enabled, false); }); + describe('enable() does not flush existing namespaces (#425)', () => { + it('merges subsequent enable() calls with previously enabled namespaces', () => { + debug.enable('foo'); + debug.enable('bar'); + + assert.deepStrictEqual(debug.enabled('foo'), true); + assert.deepStrictEqual(debug.enabled('bar'), true); + }); + + it('preserves namespaces from a prior enable() like DEBUG=foo then enable("bar")', () => { + debug.enable('foo'); + debug.enable('bar'); + + assert.deepStrictEqual(debug.enabled('foo'), true); + assert.deepStrictEqual(debug.enabled('bar'), true); + assert.deepStrictEqual(debug.disable(), 'foo,bar'); + }); + + it('does not drop earlier namespaces when a later module enables its own', () => { + debug.enable('my-module'); + debug.enable('my-dep-module'); + + assert.deepStrictEqual(debug.enabled('my-module'), true); + assert.deepStrictEqual(debug.enabled('my-dep-module'), true); + }); + + it('can still replace the set via disable() then enable()', () => { + debug.enable('foo'); + debug.disable(); + debug.enable('bar'); + + assert.deepStrictEqual(debug.enabled('foo'), false); + assert.deepStrictEqual(debug.enabled('bar'), true); + }); + + it('adds skip patterns without dropping other namespaces', () => { + debug.enable('foo'); + debug.enable('-foo'); + debug.enable('bar'); + + assert.deepStrictEqual(debug.enabled('foo'), false); + assert.deepStrictEqual(debug.enabled('bar'), true); + }); + + it('re-enabling a skipped namespace removes the skip', () => { + debug.enable('foo'); + debug.enable('-foo'); + assert.deepStrictEqual(debug.enabled('foo'), false); + + debug.enable('foo'); + assert.deepStrictEqual(debug.enabled('foo'), true); + }); + + it('does not duplicate namespaces when enable() is repeated', () => { + debug.enable('foo'); + debug.enable('foo'); + debug.enable('bar,foo'); + + assert.deepStrictEqual(debug.disable(), 'foo,bar'); + }); + }); + it('uses custom log function', () => { const log = debug('test'); log.enabled = true;