mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
chore[react-devtools]: extract some utils into separate modules to unify implementations (#30597)
Stacked on https://github.com/facebook/react/pull/30596. See [this
commit](4ba5e784bb).
Moving `formatWithStyles` and `formatConsoleArguments` to its own
modules, so that we can finally have a single implementation for these
and stop inlining them in RDT global hook object.
This commit is contained in:
72
packages/react-devtools-shared/src/backend/utils/formatConsoleArguments.js
vendored
Normal file
72
packages/react-devtools-shared/src/backend/utils/formatConsoleArguments.js
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
// Do not add / import anything to this file.
|
||||
// This function could be used from multiple places, including hook.
|
||||
|
||||
// Skips CSS and object arguments, inlines other in the first argument as a template string
|
||||
export default function formatConsoleArguments(
|
||||
maybeMessage: any,
|
||||
...inputArgs: $ReadOnlyArray<any>
|
||||
): $ReadOnlyArray<any> {
|
||||
if (inputArgs.length === 0 || typeof maybeMessage !== 'string') {
|
||||
return [maybeMessage, ...inputArgs];
|
||||
}
|
||||
|
||||
const args = inputArgs.slice();
|
||||
|
||||
let template = '';
|
||||
let argumentsPointer = 0;
|
||||
for (let i = 0; i < maybeMessage.length; ++i) {
|
||||
const currentChar = maybeMessage[i];
|
||||
if (currentChar !== '%') {
|
||||
template += currentChar;
|
||||
continue;
|
||||
}
|
||||
|
||||
const nextChar = maybeMessage[i + 1];
|
||||
++i;
|
||||
|
||||
// Only keep CSS and objects, inline other arguments
|
||||
switch (nextChar) {
|
||||
case 'c':
|
||||
case 'O':
|
||||
case 'o': {
|
||||
++argumentsPointer;
|
||||
template += `%${nextChar}`;
|
||||
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
case 'i': {
|
||||
const [arg] = args.splice(argumentsPointer, 1);
|
||||
template += parseInt(arg, 10).toString();
|
||||
|
||||
break;
|
||||
}
|
||||
case 'f': {
|
||||
const [arg] = args.splice(argumentsPointer, 1);
|
||||
template += parseFloat(arg).toString();
|
||||
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
const [arg] = args.splice(argumentsPointer, 1);
|
||||
template += arg.toString();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
template += `%${nextChar}`;
|
||||
}
|
||||
}
|
||||
|
||||
return [template, ...args];
|
||||
}
|
||||
67
packages/react-devtools-shared/src/backend/utils/formatWithStyles.js
vendored
Normal file
67
packages/react-devtools-shared/src/backend/utils/formatWithStyles.js
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
// Do not add / import anything to this file.
|
||||
// This function could be used from multiple places, including hook.
|
||||
|
||||
// Formats an array of args with a style for console methods, using
|
||||
// the following algorithm:
|
||||
// 1. The first param is a string that contains %c
|
||||
// - Bail out and return the args without modifying the styles.
|
||||
// We don't want to affect styles that the developer deliberately set.
|
||||
// 2. The first param is a string that doesn't contain %c but contains
|
||||
// string formatting
|
||||
// - [`%c${args[0]}`, style, ...args.slice(1)]
|
||||
// - Note: we assume that the string formatting that the developer uses
|
||||
// is correct.
|
||||
// 3. The first param is a string that doesn't contain string formatting
|
||||
// OR is not a string
|
||||
// - Create a formatting string where:
|
||||
// boolean, string, symbol -> %s
|
||||
// number -> %f OR %i depending on if it's an int or float
|
||||
// default -> %o
|
||||
export default function formatWithStyles(
|
||||
inputArgs: $ReadOnlyArray<any>,
|
||||
style?: string,
|
||||
): $ReadOnlyArray<any> {
|
||||
if (
|
||||
inputArgs === undefined ||
|
||||
inputArgs === null ||
|
||||
inputArgs.length === 0 ||
|
||||
// Matches any of %c but not %%c
|
||||
(typeof inputArgs[0] === 'string' && inputArgs[0].match(/([^%]|^)(%c)/g)) ||
|
||||
style === undefined
|
||||
) {
|
||||
return inputArgs;
|
||||
}
|
||||
|
||||
// Matches any of %(o|O|d|i|s|f), but not %%(o|O|d|i|s|f)
|
||||
const REGEXP = /([^%]|^)((%%)*)(%([oOdisf]))/g;
|
||||
if (typeof inputArgs[0] === 'string' && inputArgs[0].match(REGEXP)) {
|
||||
return [`%c${inputArgs[0]}`, style, ...inputArgs.slice(1)];
|
||||
} else {
|
||||
const firstArg = inputArgs.reduce((formatStr, elem, i) => {
|
||||
if (i > 0) {
|
||||
formatStr += ' ';
|
||||
}
|
||||
switch (typeof elem) {
|
||||
case 'string':
|
||||
case 'boolean':
|
||||
case 'symbol':
|
||||
return (formatStr += '%s');
|
||||
case 'number':
|
||||
const formatting = Number.isInteger(elem) ? '%i' : '%f';
|
||||
return (formatStr += formatting);
|
||||
default:
|
||||
return (formatStr += '%o');
|
||||
}
|
||||
}, '%c');
|
||||
return [firstArg, style, ...inputArgs];
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,15 @@
|
||||
*/
|
||||
|
||||
import {compareVersions} from 'compare-versions';
|
||||
import {dehydrate} from '../hydration';
|
||||
import {dehydrate} from 'react-devtools-shared/src/hydration';
|
||||
import isArray from 'shared/isArray';
|
||||
|
||||
import type {Source} from 'react-devtools-shared/src/shared/types';
|
||||
import type {DehydratedData} from 'react-devtools-shared/src/frontend/types';
|
||||
|
||||
export {default as formatWithStyles} from './formatWithStyles';
|
||||
export {default as formatConsoleArguments} from './formatConsoleArguments';
|
||||
|
||||
// TODO: update this to the first React version that has a corresponding DevTools backend
|
||||
const FIRST_DEVTOOLS_BACKEND_LOCKSTEP_VER = '999.9.9';
|
||||
export function hasAssignedBackend(version?: string): boolean {
|
||||
@@ -164,125 +167,6 @@ export function serializeToString(data: any): string {
|
||||
);
|
||||
}
|
||||
|
||||
// NOTE: KEEP IN SYNC with src/hook.js
|
||||
// Formats an array of args with a style for console methods, using
|
||||
// the following algorithm:
|
||||
// 1. The first param is a string that contains %c
|
||||
// - Bail out and return the args without modifying the styles.
|
||||
// We don't want to affect styles that the developer deliberately set.
|
||||
// 2. The first param is a string that doesn't contain %c but contains
|
||||
// string formatting
|
||||
// - [`%c${args[0]}`, style, ...args.slice(1)]
|
||||
// - Note: we assume that the string formatting that the developer uses
|
||||
// is correct.
|
||||
// 3. The first param is a string that doesn't contain string formatting
|
||||
// OR is not a string
|
||||
// - Create a formatting string where:
|
||||
// boolean, string, symbol -> %s
|
||||
// number -> %f OR %i depending on if it's an int or float
|
||||
// default -> %o
|
||||
export function formatWithStyles(
|
||||
inputArgs: $ReadOnlyArray<any>,
|
||||
style?: string,
|
||||
): $ReadOnlyArray<any> {
|
||||
if (
|
||||
inputArgs === undefined ||
|
||||
inputArgs === null ||
|
||||
inputArgs.length === 0 ||
|
||||
// Matches any of %c but not %%c
|
||||
(typeof inputArgs[0] === 'string' && inputArgs[0].match(/([^%]|^)(%c)/g)) ||
|
||||
style === undefined
|
||||
) {
|
||||
return inputArgs;
|
||||
}
|
||||
|
||||
// Matches any of %(o|O|d|i|s|f), but not %%(o|O|d|i|s|f)
|
||||
const REGEXP = /([^%]|^)((%%)*)(%([oOdisf]))/g;
|
||||
if (typeof inputArgs[0] === 'string' && inputArgs[0].match(REGEXP)) {
|
||||
return [`%c${inputArgs[0]}`, style, ...inputArgs.slice(1)];
|
||||
} else {
|
||||
const firstArg = inputArgs.reduce((formatStr, elem, i) => {
|
||||
if (i > 0) {
|
||||
formatStr += ' ';
|
||||
}
|
||||
switch (typeof elem) {
|
||||
case 'string':
|
||||
case 'boolean':
|
||||
case 'symbol':
|
||||
return (formatStr += '%s');
|
||||
case 'number':
|
||||
const formatting = Number.isInteger(elem) ? '%i' : '%f';
|
||||
return (formatStr += formatting);
|
||||
default:
|
||||
return (formatStr += '%o');
|
||||
}
|
||||
}, '%c');
|
||||
return [firstArg, style, ...inputArgs];
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: KEEP IN SYNC with src/hook.js
|
||||
// Skips CSS and object arguments, inlines other in the first argument as a template string
|
||||
export function formatConsoleArguments(
|
||||
maybeMessage: any,
|
||||
...inputArgs: $ReadOnlyArray<any>
|
||||
): $ReadOnlyArray<any> {
|
||||
if (inputArgs.length === 0 || typeof maybeMessage !== 'string') {
|
||||
return [maybeMessage, ...inputArgs];
|
||||
}
|
||||
|
||||
const args = inputArgs.slice();
|
||||
|
||||
let template = '';
|
||||
let argumentsPointer = 0;
|
||||
for (let i = 0; i < maybeMessage.length; ++i) {
|
||||
const currentChar = maybeMessage[i];
|
||||
if (currentChar !== '%') {
|
||||
template += currentChar;
|
||||
continue;
|
||||
}
|
||||
|
||||
const nextChar = maybeMessage[i + 1];
|
||||
++i;
|
||||
|
||||
// Only keep CSS and objects, inline other arguments
|
||||
switch (nextChar) {
|
||||
case 'c':
|
||||
case 'O':
|
||||
case 'o': {
|
||||
++argumentsPointer;
|
||||
template += `%${nextChar}`;
|
||||
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
case 'i': {
|
||||
const [arg] = args.splice(argumentsPointer, 1);
|
||||
template += parseInt(arg, 10).toString();
|
||||
|
||||
break;
|
||||
}
|
||||
case 'f': {
|
||||
const [arg] = args.splice(argumentsPointer, 1);
|
||||
template += parseFloat(arg).toString();
|
||||
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
const [arg] = args.splice(argumentsPointer, 1);
|
||||
template += arg.toString();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
template += `%${nextChar}`;
|
||||
}
|
||||
}
|
||||
|
||||
return [template, ...args];
|
||||
}
|
||||
|
||||
// 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
|
||||
103
packages/react-devtools-shared/src/hook.js
vendored
103
packages/react-devtools-shared/src/hook.js
vendored
@@ -24,6 +24,8 @@ import {
|
||||
ANSI_STYLE_DIMMING_TEMPLATE_WITH_COMPONENT_STACK,
|
||||
} from 'react-devtools-shared/src/constants';
|
||||
import attachRenderer from './attachRenderer';
|
||||
import formatConsoleArguments from 'react-devtools-shared/src/backend/utils/formatConsoleArguments';
|
||||
import formatWithStyles from 'react-devtools-shared/src/backend/utils/formatWithStyles';
|
||||
|
||||
// React's custom built component stack strings match "\s{4}in"
|
||||
// Chrome's prefix matches "\s{4}at"
|
||||
@@ -190,100 +192,6 @@ export function installHook(
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
// NOTE: KEEP IN SYNC with src/backend/utils.js
|
||||
function formatWithStyles(inputArgs: Array<any>, style?: string): Array<any> {
|
||||
if (
|
||||
inputArgs === undefined ||
|
||||
inputArgs === null ||
|
||||
inputArgs.length === 0 ||
|
||||
// Matches any of %c but not %%c
|
||||
(typeof inputArgs[0] === 'string' &&
|
||||
inputArgs[0].match(/([^%]|^)(%c)/g)) ||
|
||||
style === undefined
|
||||
) {
|
||||
return inputArgs;
|
||||
}
|
||||
|
||||
// Matches any of %(o|O|d|i|s|f), but not %%(o|O|d|i|s|f)
|
||||
const REGEXP = /([^%]|^)((%%)*)(%([oOdisf]))/g;
|
||||
if (typeof inputArgs[0] === 'string' && inputArgs[0].match(REGEXP)) {
|
||||
return [`%c${inputArgs[0]}`, style, ...inputArgs.slice(1)];
|
||||
} else {
|
||||
const firstArg = inputArgs.reduce((formatStr, elem, i) => {
|
||||
if (i > 0) {
|
||||
formatStr += ' ';
|
||||
}
|
||||
switch (typeof elem) {
|
||||
case 'string':
|
||||
case 'boolean':
|
||||
case 'symbol':
|
||||
return (formatStr += '%s');
|
||||
case 'number':
|
||||
const formatting = Number.isInteger(elem) ? '%i' : '%f';
|
||||
return (formatStr += formatting);
|
||||
default:
|
||||
return (formatStr += '%o');
|
||||
}
|
||||
}, '%c');
|
||||
return [firstArg, style, ...inputArgs];
|
||||
}
|
||||
}
|
||||
// NOTE: KEEP IN SYNC with src/backend/utils.js
|
||||
function formatConsoleArguments(
|
||||
maybeMessage: any,
|
||||
...inputArgs: $ReadOnlyArray<any>
|
||||
): $ReadOnlyArray<any> {
|
||||
if (inputArgs.length === 0 || typeof maybeMessage !== 'string') {
|
||||
return [maybeMessage, ...inputArgs];
|
||||
}
|
||||
|
||||
const args = inputArgs.slice();
|
||||
|
||||
let template = '';
|
||||
let argumentsPointer = 0;
|
||||
for (let i = 0; i < maybeMessage.length; ++i) {
|
||||
const currentChar = maybeMessage[i];
|
||||
if (currentChar !== '%') {
|
||||
template += currentChar;
|
||||
continue;
|
||||
}
|
||||
|
||||
const nextChar = maybeMessage[i + 1];
|
||||
++i;
|
||||
|
||||
// Only keep CSS and objects, inline other arguments
|
||||
switch (nextChar) {
|
||||
case 'c':
|
||||
case 'O':
|
||||
case 'o': {
|
||||
++argumentsPointer;
|
||||
template += `%${nextChar}`;
|
||||
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
case 'i': {
|
||||
const [arg] = args.splice(argumentsPointer, 1);
|
||||
template += parseInt(arg, 10).toString();
|
||||
|
||||
break;
|
||||
}
|
||||
case 'f': {
|
||||
const [arg] = args.splice(argumentsPointer, 1);
|
||||
template += parseFloat(arg).toString();
|
||||
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
const [arg] = args.splice(argumentsPointer, 1);
|
||||
template += arg.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [template, ...args];
|
||||
}
|
||||
|
||||
let uidCounter = 0;
|
||||
function inject(renderer: ReactRenderer): number {
|
||||
const id = ++uidCounter;
|
||||
@@ -658,13 +566,16 @@ export function installHook(
|
||||
// Dim the text color of the double logs if we're not hiding them.
|
||||
// Firefox doesn't support ANSI escape sequences
|
||||
if (__IS_FIREFOX__) {
|
||||
const argsWithCSSStyles = formatWithStyles(
|
||||
let argsWithCSSStyles = formatWithStyles(
|
||||
args,
|
||||
FIREFOX_CONSOLE_DIMMING_COLOR,
|
||||
);
|
||||
|
||||
if (injectedComponentStackAsFakeError) {
|
||||
argsWithCSSStyles[0] = `${argsWithCSSStyles[0]} %o`;
|
||||
argsWithCSSStyles = [
|
||||
`${argsWithCSSStyles[0]} %o`,
|
||||
...argsWithCSSStyles.slice(1),
|
||||
];
|
||||
}
|
||||
|
||||
originalMethod(...argsWithCSSStyles);
|
||||
|
||||
Reference in New Issue
Block a user