Professional Documents
Culture Documents
Utils
Utils
import type {
ComponentFilter,
ElementType,
BrowserTheme,
} from './frontend/types';
import type {LRUCache} from 'react-devtools-shared/src/frontend/types';
// $FlowFixMe[method-unbinding]
const hasOwnProperty = Object.prototype.hasOwnProperty;
// Mirror
https://github.com/facebook/react/blob/7c21bf72ace77094fd1910cc350a548287ef8350/
packages/shared/getComponentName.js#L27-L37
export function getWrappedDisplayName(
outerType: mixed,
innerType: any,
wrapperName: string,
fallbackName?: string,
): string {
const displayName = (outerType: any).displayName;
return (
displayName || `${wrapperName}(${getDisplayName(innerType, fallbackName)})`
);
}
cachedDisplayNames.set(type, displayName);
return displayName;
}
function surrogatePairToCodePoint(
charCode1: number,
charCode2: number,
): number {
return ((charCode1 & 0x3ff) << 10) + (charCode2 & 0x3ff) + 0x10000;
}
encodedStringCache.set(string, encoded);
return encoded;
}
let i = 2;
switch (operation) {
case TREE_OPERATION_ADD: {
const id = ((operations[i + 1]: any): number);
const type = ((operations[i + 2]: any): ElementType);
i += 3;
i++; // isStrictModeCompliant
i++; // supportsProfiling
i++; // supportsStrictMode
i++; // hasOwnerMetadata
} else {
const parentID = ((operations[i]: any): number);
i++;
i++; // ownerID
i++; // key
logs.push(
`Add node ${id} (${displayName || 'null'}) as child of ${parentID}`,
);
}
break;
}
case TREE_OPERATION_REMOVE: {
const removeLength = ((operations[i + 1]: any): number);
i += 2;
i += 3;
i += 4;
logs.push(
`Node ${id} has ${numErrors} errors and ${numWarnings} warnings`,
);
break;
default:
throw Error(`Unsupported Bridge operation "${operation}"`);
}
}
console.log(logs.join('\n '));
}
switch (type) {
case ElementTypeClass:
case ElementTypeForwardRef:
case ElementTypeFunction:
case ElementTypeMemo:
if (displayName.indexOf('(') >= 0) {
const matches = displayName.match(/[^()]+/g);
if (matches != null) {
displayName = matches.pop();
hocDisplayNames = matches;
}
}
break;
default:
break;
}
return null;
}, object);
}
/**
* Get a enhanced/artificial type string based on the object instance
*/
export function getDataType(data: Object): DataType {
if (data === null) {
return 'null';
} else if (data === undefined) {
return 'undefined';
}
if (isElement(data)) {
return 'react_element';
}
if (!isPlainObject(data)) {
return 'class_instance';
}
return 'object';
case 'string':
return 'string';
case 'symbol':
return 'symbol';
case 'undefined':
if (
// $FlowFixMe[method-unbinding]
Object.prototype.toString.call(data) === '[object HTMLAllCollection]'
) {
return 'html_all_collection';
}
return 'undefined';
default:
return 'unknown';
}
}
function truncateForDisplay(
string: string,
length: number = MAX_PREVIEW_STRING_LENGTH,
) {
if (string.length > length) {
return string.slice(0, length) + '…';
} else {
return string;
}
}
switch (type) {
case 'html_element':
return `<${truncateForDisplay(data.tagName.toLowerCase())} />`;
case 'function':
return truncateForDisplay(
`ƒ ${typeof data.name === 'function' ? '' : data.name}() {}`,
);
case 'string':
return `"${data}"`;
case 'bigint':
return truncateForDisplay(data.toString() + 'n');
case 'regexp':
return truncateForDisplay(data.toString());
case 'symbol':
return truncateForDisplay(data.toString());
case 'react_element':
return `<${truncateForDisplay(
getDisplayNameForReactElement(data) || 'Unknown',
)} />`;
case 'array_buffer':
return `ArrayBuffer(${data.byteLength})`;
case 'data_view':
return `DataView(${data.buffer.byteLength})`;
case 'array':
if (showFormattedValue) {
let formatted = '';
for (let i = 0; i < data.length; i++) {
if (i > 0) {
formatted += ', ';
}
formatted += formatDataForPreview(data[i], false);
if (formatted.length > MAX_PREVIEW_STRING_LENGTH) {
// Prevent doing a lot of unnecessary iteration...
break;
}
}
return `[${truncateForDisplay(formatted)}]`;
} else {
const length = hasOwnProperty.call(data, meta.size)
? data[meta.size]
: data.length;
return `Array(${length})`;
}
case 'typed_array':
const shortName = `${data.constructor.name}(${data.length})`;
if (showFormattedValue) {
let formatted = '';
for (let i = 0; i < data.length; i++) {
if (i > 0) {
formatted += ', ';
}
formatted += data[i];
if (formatted.length > MAX_PREVIEW_STRING_LENGTH) {
// Prevent doing a lot of unnecessary iteration...
break;
}
}
return `${shortName} [${truncateForDisplay(formatted)}]`;
} else {
return shortName;
}
case 'iterator':
const name = data.constructor.name;
if (showFormattedValue) {
// TRICKY
// Don't use [...spread] syntax for this purpose.
// This project uses @babel/plugin-transform-spread in "loose" mode which
only works with Array values.
// Other types (e.g. typed arrays, Sets) will not spread correctly.
const array = Array.from(data);
// TRICKY
// Browsers display Maps and Sets differently.
// To mimic their behavior, detect if we've been given an entries tuple.
// Map(2) {"abc" => 123, "def" => 123}
// Set(2) {"abc", 123}
if (isArray(entryOrEntries)) {
const key = formatDataForPreview(entryOrEntries[0], true);
const value = formatDataForPreview(entryOrEntries[1], false);
formatted += `${key} => ${value}`;
} else {
formatted += formatDataForPreview(entryOrEntries, false);
}
// Basically checking that the object only has Object in its prototype chain
export const isPlainObject = (object: Object): boolean => {
const objectPrototype = Object.getPrototypeOf(object);
if (!objectPrototype) return true;