fix: don't double-process %% before a format specifier#1040
Open
spokodev wants to merge 1 commit into
Open
Conversation
`debug` ran its own format pass that collapsed every `%%` to `%`, then
handed the result to `util.formatWithOptions` (Node) / `console.*`
(browser), which collapse `%%` again. For an even number of leading `%`
before a real specifier this re-collapse shifted the specifier boundary:
debug('%%%s', 'X') // logged "%s X", util.format gives "%X"
debug('100%%%d done', 50) // logged "100%d done 50", expected "100%50 done"
Leave `%%` untouched when the downstream logger still has arguments (it
performs the single, correct collapse), and only collapse it ourselves
when no arguments remain, since those loggers keep `%%` verbatim with
zero args. Custom formatters, `%s`/`%d`/`%j` and lone `%%` all stay
correct.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A literal
%%immediately followed by a real format specifier is mis-rendered. The README statesdebuguses printf-style formatting and delegates%s/%d/%jto Node'sutil.format, soutil.formatis the source of truth:The output should match
util.format. It only breaks for an even number of leading%before a specifier; odd counts (e.g.%%s) were already correct.Root cause
src/common.jsrunsdebug's own format pass before the result reaches the downstream logger:That collapsed string is then passed to
util.formatWithOptions(Node) /console.*(browser), which collapse%%again. The escape is processed twice. For'%%%s', the first pass emits'%' + '%s'='%%s', andutil.format('%%s', 'X')is'%s X': the specifier boundary has shifted.The reason
debugcollapses at all is that these loggers only collapse%%when at least one argument is present:So with zero remaining arguments
debughas to collapse%%itself, otherwise it would leak as%%.Fix
Decide per-call whether the downstream logger will still have arguments:
%%verbatim and let the logger perform the single, correct collapse.%%→%ourselves, as before.The remaining-argument count is the original argument count minus those consumed by
debug's own custom formatters (%o/%O/registered), computed before the pass so a%%appearing before a custom formatter is decided correctly.This keeps
%s/%d/%j, custom formatters, separated%% %s, zero-arg100%% done, and%%inside custom-formatter output all correct.Relationship to #1028 / #770
Both rewrite the custom-formatter output branch so a
%produced by%o/%Ois not re-interpreted (issue #766). They leave the%%escape branch untouched. Applying #1028 on top of current HEAD,debug('%%%s', 'X')still logs%s X: a separate defect in the%%branch, which is what this PR addresses.Tests
Added a node test group in
test.node.jsassertingutil.formatparity for%%before a specifier, plus the preserved behaviors (zero-arg collapse, custom formatters, formatter-output verbatim). The parity tests fail onmasterand pass with the fix.test:node(20) andtest:browser(14) are green.