Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Improve console dimming on second StrictMode render #24306

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/react-devtools-shared/src/backend/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ export function serializeToString(data: any): string {
// based on https://github.com/tmpfs/format-util/blob/0e62d430efb0a1c51448709abd3e2406c14d8401/format.js#L1
// based on https://developer.mozilla.org/en-US/docs/Web/API/console#Using_string_substitutions
// Implements s, d, i and f placeholders
// NOTE: KEEP IN SYNC with src/hook.js
export function format(
maybeMessage: any,
...inputArgs: $ReadOnlyArray<any>
Expand Down
102 changes: 57 additions & 45 deletions packages/react-devtools-shared/src/hook.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,55 +171,67 @@ export function installHook(target: any): DevToolsHook | null {
} catch (err) {}
}

// NOTE: KEEP IN SYNC with src/backend/utils.js
function format(
maybeMessage: any,
...inputArgs: $ReadOnlyArray<any>
): string {
const args = inputArgs.slice();

// Symbols cannot be concatenated with Strings.
let formatted = String(maybeMessage);

// If the first argument is a string, check for substitutions.
if (typeof maybeMessage === 'string') {
if (args.length) {
const REGEXP = /(%?)(%([jds]))/g;

formatted = formatted.replace(REGEXP, (match, escaped, ptn, flag) => {
let arg = args.shift();
switch (flag) {
case 's':
arg += '';
break;
case 'd':
case 'i':
arg = parseInt(arg, 10).toString();
break;
case 'f':
arg = parseFloat(arg).toString();
break;
}
if (!escaped) {
return arg;
}
args.unshift(arg);
return match;
});
inputArgs: $ReadOnlyArray<any>,
color: string
): $ReadOnlyArray<any> {
// The console APIs have two forms:
// 1) Placeholder form, where the first N+1 arguments are:
// - a string containing N placeholders
// - N placeholder values
// followed by any number of arbitrary values.
// 2) Non-placeholder form, where all arguments are arbitrary values.

// The goal is to colorize the placeholder string (if it exists) along with
// any additional arguments that are strings. Examples:
// ['A', 1, 'B'] => ['%c%s %o %s', 'color: X', 'A', 1, 'B']
// ['A %d', 1, 'B'] => ['%cA %d %c%s', 'color: X', 1, 'color: X', 'B']
// ['A%cB', 'color: blue', 'C'] => ['%cA%cB %c%s', 'color: X', 'color: blue', 'color: X', 'C']

// Determine whether the first arg is a string containing N placeholders.
const REGEXP = /(^|[^%])%[cfijdsoO]/g;
const numPlaceholders = typeof inputArgs[0] === 'string'
? (inputArgs[0].match(REGEXP) || []).length
: 0;

if (numPlaceholders === 0) {
let placeholderString = '%c';

for (let i = 0; i < inputArgs.length; i++) {
if (typeof inputArgs[i] === 'string') {
placeholderString += '%s ';
} else {
placeholderString += '%o ';
}
}
}

// Arguments that remain after formatting.
if (args.length) {
for (let i = 0; i < args.length; i++) {
formatted += ' ' + String(args[i]);
return [placeholderString.trimEnd(), `color: ${color}`, ...inputArgs];
} else {
const placeholderArgs = inputArgs.slice(1, numPlaceholders + 1);
const nonPlaceholderArgs = inputArgs.slice(numPlaceholders + 1);
let placeholderString = '%c' + inputArgs[0];

if (nonPlaceholderArgs.length > 0) {
// Apply the base color again in case it was overriden by the user string.
placeholderString += ' %c';
for (let i = 0; i < nonPlaceholderArgs.length; i++) {
if (typeof nonPlaceholderArgs[i] === 'string') {
placeholderString += '%s ';
} else {
placeholderString += '%o ';
}
}
return [
placeholderString.trimEnd(),
`color: ${color}`,
...placeholderArgs,
`color: ${color}`,
...nonPlaceholderArgs,
];
} else {
return [placeholderString, `color: ${color}`, ...placeholderArgs];
}
}

// Update escaped %% values.
formatted = formatted.replace(/%{2,2}/g, '%');

return String(formatted);
}

let unpatchFn = null;
Expand Down Expand Up @@ -291,7 +303,7 @@ export function installHook(target: any): DevToolsHook | null {
}

if (color) {
originalMethod(`%c${format(...args)}`, `color: ${color}`);
originalMethod(...format(args, color));
} else {
throw Error('Console color is not defined');
}
Expand Down