Port complete

This commit is contained in:
Jorge Cabiedes Acosta
2025-05-13 16:05:41 -07:00
parent a75932b2ea
commit 76dddd1d57
9 changed files with 3078 additions and 444 deletions
@@ -369,7 +369,7 @@ server.tool(
text: z.string(),
},
async ({text}) => {
const componentTree = await parseReactComponentTree(text);
const componentTree = await parseReactComponentTree();
return {
content: [
@@ -1,13 +1,6 @@
import * as babel from '@babel/core';
import puppeteer from 'puppeteer';
import {readFileSync} from 'fs';
import * as path from 'path';
// @ts-ignore
import * as babelPresetTypescript from '@babel/preset-typescript';
// @ts-ignore
import * as babelPresetEnv from '@babel/preset-env';
// @ts-ignore
import * as babelPresetReact from '@babel/preset-react';
import extractComponentTreeFromDevTools from '../utils/reactDevTools/extractComponentTree';
// import {generateComponentTree} from '../utils/reactDevTools/reactDevTools';
function delay(time: number) {
return new Promise(resolve => {
@@ -15,7 +8,7 @@ function delay(time: number) {
});
}
export async function parseReactComponentTree(code: string): Promise<string> {
export async function parseReactComponentTree(): Promise<string> {
const browser = await puppeteer.connect({
browserURL: 'http://127.0.0.1:9222',
defaultViewport: null,
@@ -27,15 +20,23 @@ export async function parseReactComponentTree(code: string): Promise<string> {
for (const page of pages) {
const url = await page.url();
if (url.startsWith('https://react.dev')) {
if (url.startsWith('http://localhost:3000')) {
localhostPage = page;
break;
}
}
if (localhostPage) {
const devtoolsHook = await localhostPage.evaluate(getReactComponentTree);
console.log(devtoolsHook);
const devtoolsHook = await localhostPage.evaluate(
() => (window as any).__REACT_DEVTOOLS_GLOBAL_HOOK__,
);
try {
} catch (error) {
console.error(error);
}
extractComponentTreeFromDevTools(devtoolsHook);
return new Promise(resolve => resolve(JSON.stringify(devtoolsHook)));
} else {
@@ -43,36 +44,6 @@ export async function parseReactComponentTree(code: string): Promise<string> {
}
}
function getReactComponentTree() {
// Check if the React DevTools hook is available
const hook: any = (window as any).__REACT_DEVTOOLS_GLOBAL_HOOK__;
if (!hook) {
console.error(
'React DevTools hook is not available. Make sure React DevTools extension is installed.',
);
return null;
}
// Get the first renderer from the DevTools hook
const renderers: any = Array.from(hook.renderers.values());
// return renderers;
if (renderers.length === 0) {
console.error('No React renderers found.');
return null;
}
const rootFiber = Array.from(
(window as any).__REACT_DEVTOOLS_GLOBAL_HOOK__.getFiberRoots(1),
)[0];
//ROOT FIBER
if (!rootFiber) {
return 'error';
}
}
parseReactComponentTree('')
parseReactComponentTree()
.then(result => console.log(result))
.catch(error => console.error(error));
@@ -1,399 +0,0 @@
/**
* 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
*/
// Define ReactComponentInfo type directly to avoid import issues
type ReactComponentInfo = {
name: string;
[key: string]: any;
};
// Constants for instance types
const FIBER_INSTANCE = 0;
const FILTERED_FIBER_INSTANCE = 2;
const VIRTUAL_INSTANCE = 1;
// Only keeping the constants needed for tree traversal
// Fiber tags - only keeping the ones used in getDisplayNameForFiber
const FunctionComponent = 0;
const ClassComponent = 1;
const IndeterminateComponent = 2;
const HostRoot = 3;
const HostPortal = 4;
const HostComponent = 5;
const HostText = 6;
const Fragment = 7;
const ForwardRef = 11;
const Profiler = 12;
const SuspenseComponent = 13;
const MemoComponent = 14;
const SimpleMemoComponent = 15;
const LazyComponent = 16;
const IncompleteClassComponent = 17;
const SuspenseListComponent = 19;
const ScopeComponent = 21;
const OffscreenComponent = 22;
const LegacyHiddenComponent = 23;
const CacheComponent = 24;
const TracingMarkerComponent = 25;
const HostHoistable = 26;
const HostSingleton = 27;
const IncompleteFunctionComponent = 28;
const Throw = 29;
const ViewTransitionComponent = 30;
const ActivityComponent = 31;
// Symbol constants
const CONCURRENT_MODE_NUMBER = 0xeacf;
const CONCURRENT_MODE_SYMBOL_STRING = 'Symbol(react.concurrent_mode)';
const DEPRECATED_ASYNC_MODE_SYMBOL_STRING = 'Symbol(react.async_mode)';
const PROVIDER_NUMBER = 0xeacd;
const PROVIDER_SYMBOL_STRING = 'Symbol(react.provider)';
const CONTEXT_NUMBER = 0xeace;
const CONTEXT_SYMBOL_STRING = 'Symbol(react.context)';
const SERVER_CONTEXT_SYMBOL_STRING = 'Symbol(react.server_context)';
const CONSUMER_SYMBOL_STRING = 'Symbol(react.consumer)';
const STRICT_MODE_NUMBER = 0xeacc;
const STRICT_MODE_SYMBOL_STRING = 'Symbol(react.strict_mode)';
const PROFILER_NUMBER = 0xead2;
const PROFILER_SYMBOL_STRING = 'Symbol(react.profiler)';
const SCOPE_NUMBER = 0xead7;
const SCOPE_SYMBOL_STRING = 'Symbol(react.scope)';
// Define a string key for REACT_MEMO_CACHE_SENTINEL to fix type issues
const REACT_MEMO_CACHE_SENTINEL = 'REACT_MEMO_CACHE_SENTINEL';
// Type definitions
type Fiber = {
tag: number;
elementType: any;
type: any;
stateNode: any;
memoizedProps: any;
memoizedState: any;
updateQueue: any;
key: string | null;
_debugID?: number;
_debugOwner?: any;
_debugStack?: any;
child: Fiber | null;
sibling: Fiber | null;
};
type Source = {
fileName: string;
lineNumber: number;
columnNumber?: number;
};
type FiberInstance = {
kind: 0;
id: number;
parent: null | DevToolsInstance;
firstChild: null | DevToolsInstance;
nextSibling: null | DevToolsInstance;
source: null | string | Error | Source;
logCount: number;
treeBaseDuration: number;
data: Fiber;
};
type FilteredFiberInstance = {
kind: 2;
id: number;
parent: null | DevToolsInstance;
firstChild: null | DevToolsInstance;
nextSibling: null | DevToolsInstance;
source: null | string | Error | Source;
logCount: number;
treeBaseDuration: number;
data: Fiber;
};
type VirtualInstance = {
kind: 1;
id: number;
parent: null | DevToolsInstance;
firstChild: null | DevToolsInstance;
nextSibling: null | DevToolsInstance;
source: null | string | Error | Source;
logCount: number;
treeBaseDuration: number;
data: ReactComponentInfo;
};
type DevToolsInstance = FiberInstance | FilteredFiberInstance | VirtualInstance;
// Helper functions
function getUID(): number {
return Math.floor(Math.random() * 1000000);
}
function getTypeSymbol(type: any): symbol | number | string | null {
const symbolOrNumber =
typeof type === 'object' && type !== null ? type.$$typeof : null;
return symbolOrNumber;
}
function resolveFiberType(type: any): any {
return type.type || type;
}
function getDisplayName(type: any): string | null {
if (type == null) {
return null;
}
let displayName = null;
if (typeof type.displayName === 'string') {
displayName = type.displayName;
} else if (typeof type.name === 'string' && type.name !== '') {
displayName = type.name;
}
return displayName || 'Anonymous';
}
function getWrappedDisplayName(
outerType: any,
innerType: any,
wrapperName: string,
fallbackName: string,
): string {
const displayName = getDisplayName(innerType);
return displayName ? `${wrapperName}(${displayName})` : fallbackName;
}
// Instance creation functions
function createFiberInstance(fiber: Fiber): FiberInstance {
return {
kind: FIBER_INSTANCE,
id: getUID(),
parent: null,
firstChild: null,
nextSibling: null,
source: null,
logCount: 0,
treeBaseDuration: 0,
data: fiber,
};
}
function createFilteredFiberInstance(fiber: Fiber): FilteredFiberInstance {
return {
kind: FILTERED_FIBER_INSTANCE,
id: 0,
parent: null,
firstChild: null,
nextSibling: null,
source: null,
logCount: 0,
treeBaseDuration: 0,
data: fiber,
};
}
function createVirtualInstance(
debugEntry: ReactComponentInfo,
): VirtualInstance {
return {
kind: VIRTUAL_INSTANCE,
id: getUID(),
parent: null,
firstChild: null,
nextSibling: null,
source: null,
logCount: 0,
treeBaseDuration: 0,
data: debugEntry,
};
}
// Main functions for tree traversal
function getDisplayNameForFiber(
fiber: Fiber,
shouldSkipForgetCheck: boolean = false,
): string | null {
const {elementType, type, tag} = fiber;
let resolvedType = type;
if (typeof type === 'object' && type !== null) {
resolvedType = resolveFiberType(type);
}
let resolvedContext: any = null;
if (
!shouldSkipForgetCheck &&
(fiber.updateQueue?.memoCache != null ||
(Array.isArray(fiber.memoizedState?.memoizedState) &&
fiber.memoizedState.memoizedState[0]?.[REACT_MEMO_CACHE_SENTINEL]) ||
fiber.memoizedState?.memoizedState?.[REACT_MEMO_CACHE_SENTINEL])
) {
const displayNameWithoutForgetWrapper = getDisplayNameForFiber(fiber, true);
if (displayNameWithoutForgetWrapper == null) {
return null;
}
return `Forget(${displayNameWithoutForgetWrapper})`;
}
switch (tag) {
case ActivityComponent:
return 'Activity';
case CacheComponent:
return 'Cache';
case ClassComponent:
case IncompleteClassComponent:
case IncompleteFunctionComponent:
case FunctionComponent:
case IndeterminateComponent:
return getDisplayName(resolvedType);
case ForwardRef:
return getWrappedDisplayName(
elementType,
resolvedType,
'ForwardRef',
'Anonymous',
);
case HostRoot:
const fiberRoot = fiber.stateNode;
if (fiberRoot != null && fiberRoot._debugRootType !== null) {
return fiberRoot._debugRootType;
}
return null;
case HostComponent:
case HostSingleton:
case HostHoistable:
return type;
case HostPortal:
case HostText:
return null;
case Fragment:
return 'Fragment';
case LazyComponent:
return 'Lazy';
case MemoComponent:
case SimpleMemoComponent:
return getWrappedDisplayName(
elementType,
resolvedType,
'Memo',
'Anonymous',
);
case SuspenseComponent:
return 'Suspense';
case LegacyHiddenComponent:
return 'LegacyHidden';
case OffscreenComponent:
return 'Offscreen';
case ScopeComponent:
return 'Scope';
case SuspenseListComponent:
return 'SuspenseList';
case Profiler:
return 'Profiler';
case TracingMarkerComponent:
return 'TracingMarker';
case ViewTransitionComponent:
return 'ViewTransition';
case Throw:
return 'Error';
default:
const typeSymbol = getTypeSymbol(type);
switch (typeSymbol) {
case CONCURRENT_MODE_NUMBER:
case CONCURRENT_MODE_SYMBOL_STRING:
case DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
return null;
case PROVIDER_NUMBER:
case PROVIDER_SYMBOL_STRING:
resolvedContext = fiber.type._context || fiber.type.context;
return `${resolvedContext.displayName || 'Context'}.Provider`;
case CONTEXT_NUMBER:
case CONTEXT_SYMBOL_STRING:
case SERVER_CONTEXT_SYMBOL_STRING:
if (
fiber.type._context === undefined &&
fiber.type.Provider === fiber.type
) {
resolvedContext = fiber.type;
return `${resolvedContext.displayName || 'Context'}.Provider`;
}
resolvedContext = fiber.type._context || fiber.type;
return `${resolvedContext.displayName || 'Context'}.Consumer`;
case CONSUMER_SYMBOL_STRING:
resolvedContext = fiber.type._context;
return `${resolvedContext.displayName || 'Context'}.Consumer`;
case STRICT_MODE_NUMBER:
case STRICT_MODE_SYMBOL_STRING:
return null;
case PROFILER_NUMBER:
case PROFILER_SYMBOL_STRING:
return `Profiler(${fiber.memoizedProps.id})`;
case SCOPE_NUMBER:
case SCOPE_SYMBOL_STRING:
return 'Scope';
default:
return null;
}
}
}
function debugTree(instance: DevToolsInstance, indent: number = 0) {
const name =
(instance.kind !== VIRTUAL_INSTANCE
? getDisplayNameForFiber(instance.data)
: instance.data.name) || '';
console.log(
' '.repeat(indent) +
'- ' +
(instance.kind === FILTERED_FIBER_INSTANCE ? 0 : instance.id) +
' (' +
name +
')',
'parent',
instance.parent === null
? ' '
: instance.parent.kind === FILTERED_FIBER_INSTANCE
? 0
: instance.parent.id,
'next',
instance.nextSibling === null ? ' ' : instance.nextSibling.id,
);
let child = instance.firstChild;
while (child !== null) {
debugTree(child, indent + 1);
child = child.nextSibling;
}
}
// Function to insert a child into the tree
function insertChild(child: DevToolsInstance) {
if (child.parent !== null) {
throw new Error('Child already has a parent');
}
child.parent = null;
child.nextSibling = null;
return child;
}
// Export the functions needed for tree traversal
export {
debugTree,
getDisplayNameForFiber,
createFiberInstance,
createFilteredFiberInstance,
createVirtualInstance,
insertChild,
FIBER_INSTANCE,
FILTERED_FIBER_INSTANCE,
VIRTUAL_INSTANCE,
};
@@ -0,0 +1,310 @@
import {generateComponentTree} from './reactDevTools';
import {Fiber} from './reactDevToolsTypes';
/**
* Interface representing a node in the component tree
*/
export interface ComponentTreeNode {
id: number;
name: string;
type: string | number;
key: string | null;
children: ComponentTreeNode[];
props?: Record<string, any> | undefined;
state?: Record<string, any> | undefined;
fiber?: Fiber | undefined;
}
/**
* Extracts the component tree from the React DevTools implementation
* @param renderer The React renderer
* @param fiber The root fiber to start from
* @returns The component tree as a structured object
*/
export function extractComponentTree(
renderer: any,
fiber: Fiber,
): ComponentTreeNode | null {
const result = generateComponentTree(renderer, fiber, false);
const idToDevToolsInstanceMap = (result as any).idToDevToolsInstanceMap;
const currentRoot = (result as any).currentRoot;
if (!currentRoot || !idToDevToolsInstanceMap) {
console.error(
'Failed to extract component tree - internal structures not available',
);
return null;
}
return convertDevToolsInstanceToTreeNode(currentRoot);
/**
* Helper function to convert a DevTools instance to our tree node format
*/
function convertDevToolsInstanceToTreeNode(instance: any): ComponentTreeNode {
// Extract name based on instance kind
let name = 'Unknown';
let type: string | number = 'Unknown';
let key: string | null = null;
let props: Record<string, any> | undefined = undefined;
let state: Record<string, any> | undefined = undefined;
let fiberData: Fiber | undefined = undefined;
if (instance.kind === 0) {
// FIBER_INSTANCE
const fiber = instance.data;
fiberData = fiber;
// @ts-ignore - Accessing private function
const getDisplayNameForFiber = (result as any).getDisplayNameForFiber;
if (typeof getDisplayNameForFiber === 'function') {
name = getDisplayNameForFiber(fiber) || 'Unknown';
} else {
// Fallback to type name if available
name =
fiber.type?.displayName ||
fiber.type?.name ||
(typeof fiber.type === 'string' ? fiber.type : 'Unknown');
}
// Get element type
// @ts-ignore - Accessing private function
const getElementTypeForFiber = (result as any).getElementTypeForFiber;
if (typeof getElementTypeForFiber === 'function') {
type = getElementTypeForFiber(fiber);
}
// Get key
key = fiber.key !== null ? String(fiber.key) : null;
// Get props and state if available
if (fiber.memoizedProps) {
props = {...fiber.memoizedProps};
}
if (fiber.memoizedState) {
// For hooks, this might be complex, so we'll just indicate it exists
state =
typeof fiber.memoizedState === 'object'
? {...fiber.memoizedState}
: {value: fiber.memoizedState};
}
} else if (instance.kind === 1) {
// VIRTUAL_INSTANCE
name = instance.data.name || 'Virtual';
type = 'Virtual';
key = instance.data.key != null ? String(instance.data.key) : null;
// Add environment if it exists
if (instance.data.env) {
props = {env: instance.data.env};
}
} else if (instance.kind === 2) {
// FILTERED_FIBER_INSTANCE
name = 'Filtered';
type = 'Filtered';
}
// Create the node
const node: ComponentTreeNode = {
id: instance.id,
name,
type,
key,
children: [],
props,
state: state,
fiber: fiberData,
};
// Add children recursively
let child = instance.firstChild;
while (child) {
node.children.push(convertDevToolsInstanceToTreeNode(child));
child = child.nextSibling;
}
return node;
}
}
/**
* Formats the component tree as a string
* @param tree The component tree to format
* @param options Options for formatting
* @returns A formatted string representation of the component tree
*/
export function formatComponentTree(
tree: ComponentTreeNode | null,
options: {
maxDepth?: number;
showIds?: boolean;
showProps?: boolean;
showState?: boolean;
} = {},
): string {
if (!tree) {
return 'No component tree available';
}
const {
maxDepth = Infinity,
showIds = true,
showProps = false,
showState = false,
} = options;
const lines: string[] = ['React Component Tree:'];
function formatNode(node: ComponentTreeNode, depth: number = 0) {
if (depth > maxDepth) return;
const indent = ' '.repeat(depth);
let output = `${indent}${node.name}`;
if (node.key) {
output += ` key="${node.key}"`;
}
if (showIds) {
output += ` (id: ${node.id})`;
}
lines.push(output);
// Format props if requested
if (showProps && node.props) {
const propsStr = JSON.stringify(node.props, null, 2)
.split('\n')
.map(line => `${indent} ${line}`)
.join('\n');
lines.push(`${indent} Props: ${propsStr}`);
}
// Format state if requested
if (showState && node.state) {
const stateStr = JSON.stringify(node.state, null, 2)
.split('\n')
.map(line => `${indent} ${line}`)
.join('\n');
lines.push(`${indent} State: ${stateStr}`);
}
// Format children recursively
for (const child of node.children) {
formatNode(child, depth + 1);
}
}
formatNode(tree);
return lines.join('\n');
}
/**
* Prints the component tree to the console
* @param tree The component tree to print
* @param options Options for printing
*/
export function printExtractedComponentTree(
tree: ComponentTreeNode | null,
options: {
maxDepth?: number;
showIds?: boolean;
showProps?: boolean;
showState?: boolean;
} = {},
) {
const formattedTree = formatComponentTree(tree, options);
console.log(formattedTree);
}
/**
* Helper function to extract and format the component tree in one step
* @param renderer The React renderer
* @param fiber The root fiber
* @param options Options for formatting
* @returns An object containing the tree and its formatted string representation
*/
export function extractAndFormatComponentTree(
renderer: any,
fiber: Fiber,
options: {
maxDepth?: number;
showIds?: boolean;
showProps?: boolean;
showState?: boolean;
} = {},
) {
const tree = extractComponentTree(renderer, fiber);
const formattedTree = formatComponentTree(tree, options);
return {
tree,
formattedTree,
};
}
/**
* Helper function to extract and print the component tree in one step
* @param renderer The React renderer
* @param fiber The root fiber
* @param options Options for printing
* @returns The extracted component tree
*/
export function extractAndPrintComponentTree(
renderer: any,
fiber: Fiber,
options: {
maxDepth?: number;
showIds?: boolean;
showProps?: boolean;
showState?: boolean;
} = {},
) {
const {tree, formattedTree} = extractAndFormatComponentTree(
renderer,
fiber,
options,
);
console.log(formattedTree);
return tree;
}
/**
* Helper function to extract the component tree from the React DevTools hook
* @param options Options for formatting
* @returns An object containing the tree and its formatted string representation, or null if extraction fails
*/
export default function extractComponentTreeFromDevTools(hook: any) {
if (!hook) {
console.error(
'React DevTools hook is not available. Make sure React DevTools extension is installed.',
);
return null;
}
const renderers: any = Array.from(hook.renderers.values());
if (renderers.length === 0) {
console.error('No React renderers found.');
return null;
}
const fiberRoots: any = Array.from(
hook.getFiberRoots(renderers[0].rendererID),
);
if (fiberRoots.length === 0) {
console.error('No fiber roots found.');
return null;
}
const currentFiber = fiberRoots[0].current;
const options: any = {
maxDepth: 3,
showIds: true,
showProps: true,
showState: true,
};
return extractAndFormatComponentTree(renderers[0], currentFiber, options);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,22 @@
/**
* 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.
*/
// Flip this flag to true to enable verbose console debug logging.
export const __DEBUG__ = false;
// Tree operation constants
export const TREE_OPERATION_ADD = 1;
export const TREE_OPERATION_REMOVE = 2;
export const TREE_OPERATION_REORDER_CHILDREN = 3;
export const TREE_OPERATION_UPDATE_TREE_BASE_DURATION = 4;
export const TREE_OPERATION_UPDATE_ERRORS_OR_WARNINGS = 5;
export const TREE_OPERATION_REMOVE_ROOT = 6;
export const TREE_OPERATION_SET_SUBTREE_MODE = 7;
// Profiling constants
export const PROFILING_FLAG_BASIC_SUPPORT = 0b01;
export const PROFILING_FLAG_TIMELINE_SUPPORT = 0b10;
@@ -0,0 +1,32 @@
export const MEMO_NUMBER = 0xead3;
export const MEMO_SYMBOL_STRING = 'Symbol(react.memo)';
export const FORWARD_REF_NUMBER = 0xead0;
export const FORWARD_REF_SYMBOL_STRING = 'Symbol(react.forward_ref)';
export const REACT_MEMO_CACHE_SENTINEL: symbol = Symbol.for(
'react.memo_cache_sentinel',
);
export const CONCURRENT_MODE_NUMBER = 0xeacf;
export const CONCURRENT_MODE_SYMBOL_STRING = 'Symbol(react.concurrent_mode)';
export const CONTEXT_NUMBER = 0xeace;
export const CONTEXT_SYMBOL_STRING = 'Symbol(react.context)';
export const SERVER_CONTEXT_SYMBOL_STRING = 'Symbol(react.server_context)';
export const DEPRECATED_ASYNC_MODE_SYMBOL_STRING = 'Symbol(react.async_mode)';
export const PROVIDER_NUMBER = 0xeacd;
export const PROVIDER_SYMBOL_STRING = 'Symbol(react.provider)';
export const CONSUMER_SYMBOL_STRING = 'Symbol(react.consumer)';
export const STRICT_MODE_NUMBER = 0xeacc;
export const STRICT_MODE_SYMBOL_STRING = 'Symbol(react.strict_mode)';
export const PROFILER_NUMBER = 0xead2;
export const PROFILER_SYMBOL_STRING = 'Symbol(react.profiler)';
export const SCOPE_NUMBER = 0xead7;
export const SCOPE_SYMBOL_STRING = 'Symbol(react.scope)';
@@ -0,0 +1,305 @@
/**
* 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.
*/
// Types migrated from shared/ReactTypes.js
export type ReactCallSite = [
string, // function name
string, // file name
number, // line number
number, // column number
];
export type ReactStackTrace = Array<ReactCallSite>;
export type ReactComponentInfo = {
name: string;
env?: string;
key?: null | string;
owner?: null | ReactComponentInfo;
stack?: null | ReactStackTrace;
props?: null | {[name: string]: any};
// Stashed Data for the Specific Execution Environment. Not part of the transport protocol
debugStack?: null | Error;
debugTask?: null | any;
};
export type ReactDebugInfo = Array<
ReactComponentInfo | any // Simplified from the original which included other types
>;
// Types migrated from react-reconciler/src/ReactInternalTypes.js
export type WorkTag = number;
export type Lanes = number;
export type Lane = number;
export type TypeOfMode = number;
export type Flags = number;
// Migrated from react-devtools-shared/src/backend/types.js
export type WorkTagMap = {
CacheComponent: WorkTag;
ClassComponent: WorkTag;
ContextConsumer: WorkTag;
ContextProvider: WorkTag;
CoroutineComponent: WorkTag;
CoroutineHandlerPhase: WorkTag;
DehydratedSuspenseComponent: WorkTag;
ForwardRef: WorkTag;
Fragment: WorkTag;
FunctionComponent: WorkTag;
HostComponent: WorkTag;
HostPortal: WorkTag;
HostRoot: WorkTag;
HostHoistable: WorkTag;
HostSingleton: WorkTag;
HostText: WorkTag;
IncompleteClassComponent: WorkTag;
IncompleteFunctionComponent: WorkTag;
IndeterminateComponent: WorkTag;
LazyComponent: WorkTag;
LegacyHiddenComponent: WorkTag;
MemoComponent: WorkTag;
Mode: WorkTag;
OffscreenComponent: WorkTag;
Profiler: WorkTag;
ScopeComponent: WorkTag;
SimpleMemoComponent: WorkTag;
SuspenseComponent: WorkTag;
SuspenseListComponent: WorkTag;
TracingMarkerComponent: WorkTag;
YieldComponent: WorkTag;
Throw: WorkTag;
ViewTransitionComponent: WorkTag;
ActivityComponent: WorkTag;
};
// Dependencies for Fiber
export type Dependencies = {
lanes: Lanes;
firstContext: any | null;
};
// A simplified version of the Fiber type from react-reconciler/src/ReactInternalTypes.js
export type Fiber = {
// Tag identifying the type of fiber.
tag: WorkTag;
// Unique identifier of this child.
key: null | string;
// The value of element.type which is used to preserve the identity during
// reconciliation of this child.
elementType: any;
// The resolved function/class/ associated with this fiber.
type: any;
// The local state associated with this fiber.
stateNode: any;
// Remaining fields belong to Fiber
// The Fiber to return to after finishing processing this one.
return: Fiber | null;
// Singly Linked List Tree Structure.
child: Fiber | null;
sibling: Fiber | null;
index: number;
// The ref last used to attach this node.
ref: any;
refCleanup: null | (() => void);
// Input is the data coming into process this fiber. Arguments. Props.
pendingProps: any;
memoizedProps: any;
// A queue of state updates and callbacks.
updateQueue: any;
// The state used to create the output
memoizedState: any;
// Dependencies (contexts, events) for this fiber, if it has any
dependencies: Dependencies | null;
// Bitfield that describes properties about the fiber and its subtree.
mode: TypeOfMode;
// Effect
flags: Flags;
subtreeFlags: Flags;
deletions: Array<Fiber> | null;
lanes: Lanes;
childLanes: Lanes;
// This is a pooled version of a Fiber. Every fiber that gets updated will
// eventually have a pair. There are cases when we can clean up pairs to save
// memory if we need to.
alternate: Fiber | null;
// Time spent rendering this Fiber and its descendants for the current update.
actualDuration?: number;
// If the Fiber is currently active in the "render" phase,
// This marks the time at which the work began.
actualStartTime?: number;
// Duration of the most recent render time for this Fiber.
selfBaseDuration?: number;
// Sum of base times for all descendants of this Fiber.
treeBaseDuration?: number;
// DEV only fields
_debugInfo?: ReactDebugInfo | null;
_debugOwner?: ReactComponentInfo | Fiber | null;
_debugStack?: string | Error | null;
_debugTask?: any | null;
_debugNeedsRemount?: boolean;
_debugHookTypes?: Array<string> | null;
};
// A simplified version of the FiberRoot type from react-reconciler/src/ReactInternalTypes.js
export type FiberRoot = {
// The type of root (legacy, batched, concurrent, etc.)
tag: number;
// Any additional information from the host associated with this root.
containerInfo: any;
// Used only by persistent updates.
pendingChildren: any;
// The currently active root fiber. This is the mutable root of the tree.
current: Fiber;
// A linked list of all roots that have pending work scheduled on them.
next: FiberRoot | null;
// Other fields omitted for simplicity
};
// Types migrated from react-devtools-shared/src/backend/types.js
export type BundleType = 0 | 1; // 0 = PROD, 1 = DEV
export type HostInstance = any;
export type Source = {
fileName: string;
lineNumber: number;
columnNumber?: number;
};
export type ReactRenderer = {
version: string;
rendererPackageName: string;
bundleType: BundleType;
// 16.0+ - To be removed in future versions.
findFiberByHostInstance?: (hostInstance: HostInstance) => any | null;
// 16.9+
overrideHookState?: (
fiber: any,
id: number,
path: Array<string | number>,
value: any,
) => void;
// 17+
overrideHookStateDeletePath?: (
fiber: any,
id: number,
path: Array<string | number>,
) => void;
// 17+
overrideHookStateRenamePath?: (
fiber: any,
id: number,
oldPath: Array<string | number>,
newPath: Array<string | number>,
) => void;
// 16.7+
overrideProps?: (
fiber: any,
path: Array<string | number>,
value: any,
) => void;
// 17+
overridePropsDeletePath?: (fiber: any, path: Array<string | number>) => void;
// 17+
overridePropsRenamePath?: (
fiber: any,
oldPath: Array<string | number>,
newPath: Array<string | number>,
) => void;
// 16.9+
scheduleUpdate?: (fiber: any) => void;
setSuspenseHandler?: (shouldSuspend: (fiber: any) => boolean) => void;
// Only injected by React v16.8+ in order to support hooks inspection.
currentDispatcherRef?: any;
// Only injected by React v16.9+ in DEV mode.
// Enables DevTools to append owners-only component stack to error messages.
getCurrentFiber?: (() => any | null) | null;
// Only injected by React Flight Clients in DEV mode.
// Enables DevTools to append owners-only component stack to error messages from Server Components.
getCurrentComponentInfo?: () => any | null;
// 17.0.2+
reconcilerVersion?: string;
// Uniquely identifies React DOM v15.
ComponentTree?: any;
// Present for React DOM v12 (possibly earlier) through v15.
Mount?: any;
// Only injected by React v17.0.3+ in DEV mode
setErrorHandler?: (shouldError: (fiber: any) => boolean | undefined) => void;
// Intentionally opaque type to avoid coupling DevTools to different Fast Refresh versions.
scheduleRefresh?: Function;
// 18.0+
injectProfilingHooks?: (profilingHooks: any) => void;
getLaneLabelMap?: () => Map<number, string> | null;
};
export type ChangeDescription = {
context: Array<string> | boolean | null;
didHooksChange: boolean;
isFirstMount: boolean;
props: Array<string> | null;
state: Array<string> | null;
hooks?: Array<number> | null;
};
export type PathFrame = {
key: string | null;
index: number;
displayName: string | null;
};
export type SerializedElement = {
displayName: string | null;
id: number;
key: number | string | null;
type: ElementType;
};
// WARNING
// The values below are referenced by ComponentFilters (which are saved via localStorage).
// Do not change them or it will break previously saved user customizations.
// If new element types are added, use new numbers rather than re-ordering existing ones.
//
// Changing these types is also a backwards breaking change for the standalone shell,
// since the frontend and backend must share the same values-
// and the backend is embedded in certain environments (like React Native).
export const ElementTypeClass = 1;
export const ElementTypeContext = 2;
export const ElementTypeFunction = 5;
export const ElementTypeForwardRef = 6;
export const ElementTypeHostComponent = 7;
export const ElementTypeMemo = 8;
export const ElementTypeOtherOrUnknown = 9;
export const ElementTypeProfiler = 10;
export const ElementTypeRoot = 11;
export const ElementTypeSuspense = 12;
export const ElementTypeSuspenseList = 13;
export const ElementTypeTracingMarker = 14;
export const ElementTypeVirtual = 15;
export const ElementTypeViewTransition = 16;
export const ElementTypeActivity = 17;
// Different types of elements displayed in the Elements tree.
// These types may be used to visually distinguish types,
// or to enable/disable certain functionality.
export type ElementType =
| 1
| 2
| 5
| 6
| 7
| 8
| 9
| 10
| 11
| 12
| 13
| 14
| 15
| 16
| 17;
export const StrictMode = 1;
@@ -0,0 +1,608 @@
import {WorkTagMap} from './reactDevToolsTypes';
import {compareVersions} from 'compare-versions';
import {ReactComponentInfo} from './reactDevToolsTypes';
// Migrated from react-devtools-shared/src/backend/utils/index.js
export function gt(a: string = '', b: string = ''): boolean {
return compareVersions(a, b) === 1;
}
export function gte(a: string = '', b: string = ''): boolean {
return compareVersions(a, b) > -1;
}
// Migrated from react-devtools-shared/src/utils.js
const cachedDisplayNames: WeakMap<Function, string> = new WeakMap();
export function getWrappedDisplayName(
outerType: any,
innerType: any,
wrapperName: string,
fallbackName: string = 'Anonymous',
): string {
const displayName = outerType?.displayName;
return (
displayName || `${wrapperName}(${getDisplayName(innerType, fallbackName)})`
);
}
export function getDisplayName(
type: any,
fallbackName: string = 'Anonymous',
): string {
const nameFromCache = cachedDisplayNames.get(type);
if (nameFromCache != null) {
return nameFromCache;
}
let displayName = fallbackName;
// The displayName property is not guaranteed to be a string.
// It's only safe to use for our purposes if it's a string.
if (typeof type.displayName === 'string') {
displayName = type.displayName;
} else if (typeof type.name === 'string' && type.name !== '') {
displayName = type.name;
}
cachedDisplayNames.set(type, displayName);
return displayName;
}
// Migrated from react-devtools-shared/src/backend/shared/DevToolsServerComponentLogs.js
type ComponentLogs = {
errors: Map<string, number>;
errorsCount: number;
warnings: Map<string, number>;
warningsCount: number;
};
// This keeps it around as long as the ComponentInfo is alive which
// lets the Fiber get reparented/remounted and still observe the previous errors/warnings.
// Unless we explicitly clear the logs from a Fiber.
export const componentInfoToComponentLogsMap: WeakMap<
ReactComponentInfo,
ComponentLogs
> = new WeakMap();
// Migrated from shared/objectIs.js
/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
function isPolyfill(x: any, y: any) {
return (
(x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
);
}
export const is: (x: any, y: any) => boolean =
typeof Object.is === 'function' ? Object.is : isPolyfill;
// Migrated from shared/hasOwnProperty.js
export const hasOwnProperty = Object.prototype.hasOwnProperty;
import {
MEMO_NUMBER,
MEMO_SYMBOL_STRING,
FORWARD_REF_NUMBER,
FORWARD_REF_SYMBOL_STRING,
REACT_MEMO_CACHE_SENTINEL,
CONCURRENT_MODE_NUMBER,
CONCURRENT_MODE_SYMBOL_STRING,
DEPRECATED_ASYNC_MODE_SYMBOL_STRING,
PROVIDER_NUMBER,
PROVIDER_SYMBOL_STRING,
CONTEXT_NUMBER,
CONTEXT_SYMBOL_STRING,
SERVER_CONTEXT_SYMBOL_STRING,
CONSUMER_SYMBOL_STRING,
STRICT_MODE_NUMBER,
STRICT_MODE_SYMBOL_STRING,
PROFILER_NUMBER,
PROFILER_SYMBOL_STRING,
SCOPE_NUMBER,
SCOPE_SYMBOL_STRING,
} from './reactDevToolsSymbols';
import {Fiber} from './reactDevToolsTypes';
// Migrated from react-devtools-shared/src/utils.js
let uidCounter: number = 0;
export function getUID(): number {
return ++uidCounter;
}
export function utfEncodeString(string: string): Array<number> {
const encoded = [];
let i = 0;
let charCode;
while (i < string.length) {
charCode = string.charCodeAt(i);
// Handle multibyte unicode characters (like emoji).
if ((charCode & 0xf800) === 0xd800) {
encoded.push(surrogatePairToCodePoint(charCode, string.charCodeAt(++i)));
} else {
encoded.push(charCode);
}
++i;
}
return encoded;
}
function surrogatePairToCodePoint(
charCode1: number,
charCode2: number,
): number {
return ((charCode1 & 0x3ff) << 10) + (charCode2 & 0x3ff) + 0x10000;
}
// Migrated from react-reconciler/src/ReactFiberDevToolsHook.js
export function injectProfilingHooks(profilingHooks: any): void {
// This is a simplified version of the function from ReactFiberDevToolsHook.js
// We're only implementing the bare minimum needed for the current use case
}
type getDisplayNameForFiberType = (fiber: Fiber) => string | null;
type getTypeSymbolType = (type: any) => symbol | string | number;
type ReactPriorityLevelsType = {
ImmediatePriority: number;
UserBlockingPriority: number;
NormalPriority: number;
LowPriority: number;
IdlePriority: number;
NoPriority: number;
};
export function getInternalReactConstants(version: string): {
getDisplayNameForFiber: getDisplayNameForFiberType;
getTypeSymbol: getTypeSymbolType;
ReactPriorityLevels: ReactPriorityLevelsType;
ReactTypeOfWork: WorkTagMap;
StrictModeBits: number;
} {
// **********************************************************
// The section below is copied from files in React repo.
// Keep it in sync, and add version guards if it changes.
//
// Technically these priority levels are invalid for versions before 16.9,
// but 16.9 is the first version to report priority level to DevTools,
// so we can avoid checking for earlier versions and support pre-16.9 canary releases in the process.
let ReactPriorityLevels: ReactPriorityLevelsType = {
ImmediatePriority: 99,
UserBlockingPriority: 98,
NormalPriority: 97,
LowPriority: 96,
IdlePriority: 95,
NoPriority: 90,
};
if (gt(version, '17.0.2')) {
ReactPriorityLevels = {
ImmediatePriority: 1,
UserBlockingPriority: 2,
NormalPriority: 3,
LowPriority: 4,
IdlePriority: 5,
NoPriority: 0,
};
}
let StrictModeBits = 0;
if (gte(version, '18.0.0-alpha')) {
// 18+
StrictModeBits = 0b011000;
} else if (gte(version, '16.9.0')) {
// 16.9 - 17
StrictModeBits = 0b1;
} else if (gte(version, '16.3.0')) {
// 16.3 - 16.8
StrictModeBits = 0b10;
}
let ReactTypeOfWork: WorkTagMap = {} as WorkTagMap;
// **********************************************************
// The section below is copied from files in React repo.
// Keep it in sync, and add version guards if it changes.
//
// TODO Update the gt() check below to be gte() whichever the next version number is.
// Currently the version in Git is 17.0.2 (but that version has not been/may not end up being released).
if (gt(version, '17.0.1')) {
ReactTypeOfWork = {
CacheComponent: 24, // Experimental
ClassComponent: 1,
ContextConsumer: 9,
ContextProvider: 10,
CoroutineComponent: -1, // Removed
CoroutineHandlerPhase: -1, // Removed
DehydratedSuspenseComponent: 18, // Behind a flag
ForwardRef: 11,
Fragment: 7,
FunctionComponent: 0,
HostComponent: 5,
HostPortal: 4,
HostRoot: 3,
HostHoistable: 26, // In reality, 18.2+. But doesn't hurt to include it here
HostSingleton: 27, // Same as above
HostText: 6,
IncompleteClassComponent: 17,
IncompleteFunctionComponent: 28,
IndeterminateComponent: 2, // removed in 19.0.0
LazyComponent: 16,
LegacyHiddenComponent: 23,
MemoComponent: 14,
Mode: 8,
OffscreenComponent: 22, // Experimental
Profiler: 12,
ScopeComponent: 21, // Experimental
SimpleMemoComponent: 15,
SuspenseComponent: 13,
SuspenseListComponent: 19, // Experimental
TracingMarkerComponent: 25, // Experimental - This is technically in 18 but we don't
// want to fork again so we're adding it here instead
YieldComponent: -1, // Removed
Throw: 29,
ViewTransitionComponent: 30, // Experimental
ActivityComponent: 31,
};
} else if (gte(version, '17.0.0-alpha')) {
ReactTypeOfWork = {
CacheComponent: -1, // Doesn't exist yet
ClassComponent: 1,
ContextConsumer: 9,
ContextProvider: 10,
CoroutineComponent: -1, // Removed
CoroutineHandlerPhase: -1, // Removed
DehydratedSuspenseComponent: 18, // Behind a flag
ForwardRef: 11,
Fragment: 7,
FunctionComponent: 0,
HostComponent: 5,
HostPortal: 4,
HostRoot: 3,
HostHoistable: -1, // Doesn't exist yet
HostSingleton: -1, // Doesn't exist yet
HostText: 6,
IncompleteClassComponent: 17,
IncompleteFunctionComponent: -1, // Doesn't exist yet
IndeterminateComponent: 2,
LazyComponent: 16,
LegacyHiddenComponent: 24,
MemoComponent: 14,
Mode: 8,
OffscreenComponent: 23, // Experimental
Profiler: 12,
ScopeComponent: 21, // Experimental
SimpleMemoComponent: 15,
SuspenseComponent: 13,
SuspenseListComponent: 19, // Experimental
TracingMarkerComponent: -1, // Doesn't exist yet
YieldComponent: -1, // Removed
Throw: -1, // Doesn't exist yet
ViewTransitionComponent: -1, // Doesn't exist yet
ActivityComponent: -1, // Doesn't exist yet
};
} else if (gte(version, '16.6.0-beta.0')) {
ReactTypeOfWork = {
CacheComponent: -1, // Doesn't exist yet
ClassComponent: 1,
ContextConsumer: 9,
ContextProvider: 10,
CoroutineComponent: -1, // Removed
CoroutineHandlerPhase: -1, // Removed
DehydratedSuspenseComponent: 18, // Behind a flag
ForwardRef: 11,
Fragment: 7,
FunctionComponent: 0,
HostComponent: 5,
HostPortal: 4,
HostRoot: 3,
HostHoistable: -1, // Doesn't exist yet
HostSingleton: -1, // Doesn't exist yet
HostText: 6,
IncompleteClassComponent: 17,
IncompleteFunctionComponent: -1, // Doesn't exist yet
IndeterminateComponent: 2,
LazyComponent: 16,
LegacyHiddenComponent: -1,
MemoComponent: 14,
Mode: 8,
OffscreenComponent: -1, // Experimental
Profiler: 12,
ScopeComponent: -1, // Experimental
SimpleMemoComponent: 15,
SuspenseComponent: 13,
SuspenseListComponent: 19, // Experimental
TracingMarkerComponent: -1, // Doesn't exist yet
YieldComponent: -1, // Removed
Throw: -1, // Doesn't exist yet
ViewTransitionComponent: -1, // Doesn't exist yet
ActivityComponent: -1, // Doesn't exist yet
};
} else if (gte(version, '16.4.3-alpha')) {
ReactTypeOfWork = {
CacheComponent: -1, // Doesn't exist yet
ClassComponent: 2,
ContextConsumer: 11,
ContextProvider: 12,
CoroutineComponent: -1, // Removed
CoroutineHandlerPhase: -1, // Removed
DehydratedSuspenseComponent: -1, // Doesn't exist yet
ForwardRef: 13,
Fragment: 9,
FunctionComponent: 0,
HostComponent: 7,
HostPortal: 6,
HostRoot: 5,
HostHoistable: -1, // Doesn't exist yet
HostSingleton: -1, // Doesn't exist yet
HostText: 8,
IncompleteClassComponent: -1, // Doesn't exist yet
IncompleteFunctionComponent: -1, // Doesn't exist yet
IndeterminateComponent: 4,
LazyComponent: -1, // Doesn't exist yet
LegacyHiddenComponent: -1,
MemoComponent: -1, // Doesn't exist yet
Mode: 10,
OffscreenComponent: -1, // Experimental
Profiler: 15,
ScopeComponent: -1, // Experimental
SimpleMemoComponent: -1, // Doesn't exist yet
SuspenseComponent: 16,
SuspenseListComponent: -1, // Doesn't exist yet
TracingMarkerComponent: -1, // Doesn't exist yet
YieldComponent: -1, // Removed
Throw: -1, // Doesn't exist yet
ViewTransitionComponent: -1, // Doesn't exist yet
ActivityComponent: -1, // Doesn't exist yet
};
} else {
ReactTypeOfWork = {
CacheComponent: -1, // Doesn't exist yet
ClassComponent: 2,
ContextConsumer: 12,
ContextProvider: 13,
CoroutineComponent: 7,
CoroutineHandlerPhase: 8,
DehydratedSuspenseComponent: -1, // Doesn't exist yet
ForwardRef: 14,
Fragment: 10,
FunctionComponent: 1,
HostComponent: 5,
HostPortal: 4,
HostRoot: 3,
HostHoistable: -1, // Doesn't exist yet
HostSingleton: -1, // Doesn't exist yet
HostText: 6,
IncompleteClassComponent: -1, // Doesn't exist yet
IncompleteFunctionComponent: -1, // Doesn't exist yet
IndeterminateComponent: 0,
LazyComponent: -1, // Doesn't exist yet
LegacyHiddenComponent: -1,
MemoComponent: -1, // Doesn't exist yet
Mode: 11,
OffscreenComponent: -1, // Experimental
Profiler: 15,
ScopeComponent: -1, // Experimental
SimpleMemoComponent: -1, // Doesn't exist yet
SuspenseComponent: 16,
SuspenseListComponent: -1, // Doesn't exist yet
TracingMarkerComponent: -1, // Doesn't exist yet
YieldComponent: 9,
Throw: -1, // Doesn't exist yet
ViewTransitionComponent: -1, // Doesn't exist yet
ActivityComponent: -1, // Doesn't exist yet
};
}
// **********************************************************
// End of copied code.
// **********************************************************
function getTypeSymbol(type: any): symbol | string | number {
const symbolOrNumber =
typeof type === 'object' && type !== null ? type.$$typeof : type;
return typeof symbolOrNumber === 'symbol'
? symbolOrNumber.toString()
: symbolOrNumber;
}
const {
CacheComponent,
ClassComponent,
IncompleteClassComponent,
IncompleteFunctionComponent,
FunctionComponent,
IndeterminateComponent,
ForwardRef,
HostRoot,
HostHoistable,
HostSingleton,
HostComponent,
HostPortal,
HostText,
Fragment,
LazyComponent,
LegacyHiddenComponent,
MemoComponent,
OffscreenComponent,
Profiler,
ScopeComponent,
SimpleMemoComponent,
SuspenseComponent,
SuspenseListComponent,
TracingMarkerComponent,
Throw,
ViewTransitionComponent,
ActivityComponent,
} = ReactTypeOfWork;
// TODO: any return type might be wrong
function resolveFiberType(type: any): any {
const typeSymbol = getTypeSymbol(type);
switch (typeSymbol) {
case MEMO_NUMBER:
case MEMO_SYMBOL_STRING:
// recursively resolving memo type in case of memo(forwardRef(Component))
return resolveFiberType(type.type);
case FORWARD_REF_NUMBER:
case FORWARD_REF_SYMBOL_STRING:
return type.render;
default:
return type;
}
}
// NOTICE Keep in sync with shouldFilterFiber() and other get*ForFiber methods
function getDisplayNameForFiber(
fiber: Fiber,
shouldSkipForgetCheck: boolean = false,
): string | null {
const {elementType, type, tag} = fiber;
let resolvedType = type;
if (typeof type === 'object' && type !== null) {
resolvedType = resolveFiberType(type);
}
let resolvedContext: any = null;
if (
!shouldSkipForgetCheck &&
// $FlowFixMe[incompatible-type] fiber.updateQueue is mixed
(fiber.updateQueue?.memoCache != null ||
(Array.isArray(fiber.memoizedState?.memoizedState) &&
fiber.memoizedState.memoizedState[0]?.[REACT_MEMO_CACHE_SENTINEL]) ||
fiber.memoizedState?.memoizedState?.[REACT_MEMO_CACHE_SENTINEL])
) {
const displayNameWithoutForgetWrapper = getDisplayNameForFiber(
fiber,
true,
);
if (displayNameWithoutForgetWrapper == null) {
return null;
}
return `Forget(${displayNameWithoutForgetWrapper})`;
}
switch (tag) {
case ActivityComponent:
return 'Activity';
case CacheComponent:
return 'Cache';
case ClassComponent:
case IncompleteClassComponent:
case IncompleteFunctionComponent:
case FunctionComponent:
case IndeterminateComponent:
return getDisplayName(resolvedType);
case ForwardRef:
return getWrappedDisplayName(
elementType,
resolvedType,
'ForwardRef',
'Anonymous',
);
case HostRoot:
const fiberRoot = fiber.stateNode;
if (fiberRoot != null && fiberRoot._debugRootType !== null) {
return fiberRoot._debugRootType;
}
return null;
case HostComponent:
case HostSingleton:
case HostHoistable:
return type;
case HostPortal:
case HostText:
return null;
case Fragment:
return 'Fragment';
case LazyComponent:
// This display name will not be user visible.
// Once a Lazy component loads its inner component, React replaces the tag and type.
// This display name will only show up in console logs when DevTools DEBUG mode is on.
return 'Lazy';
case MemoComponent:
case SimpleMemoComponent:
// Display name in React does not use `Memo` as a wrapper but fallback name.
return getWrappedDisplayName(
elementType,
resolvedType,
'Memo',
'Anonymous',
);
case LegacyHiddenComponent:
return 'LegacyHidden';
case OffscreenComponent:
return 'Offscreen';
case ScopeComponent:
return 'Scope';
case SuspenseComponent:
return 'Suspense';
case SuspenseListComponent:
return 'SuspenseList';
case TracingMarkerComponent:
return 'TracingMarker';
case ViewTransitionComponent:
return 'ViewTransition';
case Throw:
return 'Throw';
default:
const typeSymbol = getTypeSymbol(type);
switch (typeSymbol) {
case CONCURRENT_MODE_NUMBER:
case CONCURRENT_MODE_SYMBOL_STRING:
case DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
return 'ConcurrentMode';
case PROVIDER_NUMBER:
case PROVIDER_SYMBOL_STRING:
// Grab the displayName from the type if it's available.
// If it doesn't have a displayName, we'll fall back to the generic "Context.Provider" name.
if (resolvedContext == null) {
resolvedContext = type._context || type;
}
return `${resolvedContext.displayName || 'Context'}.Provider`;
case CONTEXT_NUMBER:
case CONTEXT_SYMBOL_STRING:
case SERVER_CONTEXT_SYMBOL_STRING:
// Grab the displayName from the type if it's available.
// If it doesn't have a displayName, we'll fall back to the generic "Context.Consumer" name.
if (resolvedContext == null) {
resolvedContext = type._context || type;
}
return `${resolvedContext.displayName || 'Context'}.Consumer`;
case CONSUMER_SYMBOL_STRING:
// Grab the displayName from the type if it's available.
// If it doesn't have a displayName, we'll fall back to the generic "Context.Consumer" name.
if (resolvedContext == null) {
resolvedContext = type;
}
return `${resolvedContext.displayName || 'Context'}.Consumer`;
case STRICT_MODE_NUMBER:
case STRICT_MODE_SYMBOL_STRING:
return 'StrictMode';
case PROFILER_NUMBER:
case PROFILER_SYMBOL_STRING:
return `Profiler`;
case SCOPE_NUMBER:
case SCOPE_SYMBOL_STRING:
return 'Scope';
default:
// Unknown element type.
// This may mean a new element type that has not yet been added to DevTools.
return null;
}
}
}
return {
getDisplayNameForFiber,
getTypeSymbol,
ReactPriorityLevels,
ReactTypeOfWork,
StrictModeBits,
};
}