[react-devtools] Fix trailing percent sign in formatConsoleArguments (#36852)

## Summary

`formatConsoleArguments` (used by the DevTools backend to inline console
substitutions) walks the format string and inlines `%s`/`%d`/`%i`/`%f`
arguments while leaving `%c`/`%o`/`%O` in place. For each `%` it reads
the **next** character to decide what to do.

When the format string ends with a lone `%` — e.g.
`console.log('Progress 100%', value)` — the character after `%` is
`undefined`, and the `default` branch ran:

```js
template += `%${nextChar}`; // -> "%undefined"
```

So the function emitted the literal text `%undefined`:

```js
formatConsoleArguments('Progress 100%', 'extra');
// before: ['Progress 100%undefined', 'extra']
// after:  ['Progress 100%', 'extra']
```

Browsers render a trailing `%` in a console format string as a literal
percent sign, so this PR keeps it as `%` when there is no following
character.

## How did you test this change?

Added a `keeps a trailing percent sign` test to the existing
`formatConsoleArguments` suite in `utils-test.js` (covering both a bare
trailing `%` and one that follows another substitution). Since the
function is pure and import-free, I also verified the patched logic
against every existing case in that suite plus the new ones to confirm
there are no regressions.
This commit is contained in:
udit
2026-06-22 21:29:09 +01:00
committed by GitHub
parent c15b3bbd89
commit bb3548eff8
2 changed files with 15 additions and 1 deletions
@@ -501,5 +501,13 @@ function f() { }
formatConsoleArguments('This is the %s template', undefined),
).toEqual(['This is the undefined template']);
});
it('keeps a trailing percent sign', () => {
expect(formatConsoleArguments('Progress 100%', 'extra')).toEqual([
'Progress 100%',
'extra',
]);
expect(formatConsoleArguments('%s 100%', 'done')).toEqual(['done 100%']);
});
});
});
@@ -64,7 +64,13 @@ export default function formatConsoleArguments(
}
default:
template += `%${nextChar}`;
if (nextChar === undefined) {
// A trailing '%' with no following character. Keep it as a literal
// '%' rather than emitting the string 'undefined'.
template += '%';
} else {
template += `%${nextChar}`;
}
}
}