diff --git a/src/common.js b/src/common.js index 141cb578..23eb319e 100644 --- a/src/common.js +++ b/src/common.js @@ -98,6 +98,7 @@ function setup(env) { if (typeof formatter === 'function') { const val = args[index]; match = formatter.call(self, val); + match = String(match).replace(/%/g, '%%'); // Now we need to remove `args[index]` since it's inlined in the `format` args.splice(index, 1); diff --git a/test.js b/test.js index a1d6f633..c16c51ee 100644 --- a/test.js +++ b/test.js @@ -43,6 +43,19 @@ describe('debug', () => { assert.deepStrictEqual(messages.length, 3); }); + it('escapes percent signs returned by custom formatters', () => { + const log = debug('test'); + log.enabled = true; + debug.formatters.x = () => '%j'; + + const messages = []; + log.log = (...args) => messages.push(args); + log('%x', null, 1); + + assert(messages[0][0].includes('%%j')); + delete debug.formatters.x; + }); + describe('extend namespace', () => { it('should extend namespace', () => { const log = debug('foo'); diff --git a/test.node.js b/test.node.js index 4cc3c051..a68a8744 100644 --- a/test.node.js +++ b/test.node.js @@ -36,5 +36,22 @@ describe('debug node', () => { assert.deepStrictEqual(util.formatWithOptions.getCall(0).args[0], options); stdErrWriteStub.restore(); }); + + it('does not interpret percent signs in formatter output', () => { + debug.enable('*'); + const options = { + hideDate: true, + colors: false + }; + Object.assign(debug.inspectOpts, options); + const stdErrWriteStub = sinon.stub(process.stderr, 'write'); + const log = debug('percent'); + log('%o', {'%j': '%j %j %%'}, 1, 2, 3); + assert.strictEqual( + stdErrWriteStub.firstCall.firstArg, + 'percent { \'%j\': \'%j %j %%\' } 1 2 3\n' + ); + stdErrWriteStub.restore(); + }); }); });