import type {
Thenable,
ReactComponentInfo,
ReactDebugInfo,
ReactAsyncInfo,
ReactIOInfo,
ReactStackTrace,
ReactCallSite,
Wakeable,
} from 'shared/ReactTypes';
import type {HooksNode, HooksTree} from 'react-debug-tools/src/ReactDebugHooks';
import {
ComponentFilterDisplayName,
ComponentFilterElementType,
ComponentFilterHOC,
ComponentFilterLocation,
ComponentFilterEnvironmentName,
ComponentFilterActivitySlice,
ElementTypeClass,
ElementTypeContext,
ElementTypeFunction,
ElementTypeForwardRef,
ElementTypeHostComponent,
ElementTypeMemo,
ElementTypeOtherOrUnknown,
ElementTypeProfiler,
ElementTypeRoot,
ElementTypeSuspense,
ElementTypeSuspenseList,
ElementTypeTracingMarker,
ElementTypeViewTransition,
ElementTypeActivity,
ElementTypeVirtual,
StrictMode,
} from 'react-devtools-shared/src/frontend/types';
import {
deletePathInObject,
getDisplayName,
getWrappedDisplayName,
getInObject,
getUID,
renamePathInObject,
setInObject,
utfEncodeString,
} from 'react-devtools-shared/src/utils';
import {
formatConsoleArgumentsToSingleString,
formatDurationToMicrosecondsGranularity,
gt,
gte,
serializeToString,
} from 'react-devtools-shared/src/backend/utils';
import {
extractLocationFromComponentStack,
extractLocationFromOwnerStack,
parseStackTrace,
} from 'react-devtools-shared/src/backend/utils/parseStackTrace';
import {
cleanForBridge,
copyWithDelete,
copyWithRename,
copyWithSet,
getEffectDurations,
} from '../utils';
import {
__DEBUG__,
PROFILING_FLAG_BASIC_SUPPORT,
PROFILING_FLAG_TIMELINE_SUPPORT,
PROFILING_FLAG_PERFORMANCE_TRACKS_SUPPORT,
TREE_OPERATION_ADD,
TREE_OPERATION_REMOVE,
TREE_OPERATION_REORDER_CHILDREN,
TREE_OPERATION_SET_SUBTREE_MODE,
TREE_OPERATION_UPDATE_ERRORS_OR_WARNINGS,
TREE_OPERATION_UPDATE_TREE_BASE_DURATION,
TREE_OPERATION_APPLIED_ACTIVITY_SLICE_CHANGE,
SUSPENSE_TREE_OPERATION_ADD,
SUSPENSE_TREE_OPERATION_REMOVE,
SUSPENSE_TREE_OPERATION_REORDER_CHILDREN,
SUSPENSE_TREE_OPERATION_RESIZE,
SUSPENSE_TREE_OPERATION_SUSPENDERS,
UNKNOWN_SUSPENDERS_NONE,
UNKNOWN_SUSPENDERS_REASON_PRODUCTION,
UNKNOWN_SUSPENDERS_REASON_OLD_VERSION,
UNKNOWN_SUSPENDERS_REASON_THROWN_PROMISE,
} from '../../constants';
import {inspectHooksOfFiber} from 'react-debug-tools';
import {
CONCURRENT_MODE_NUMBER,
CONCURRENT_MODE_SYMBOL_STRING,
DEPRECATED_ASYNC_MODE_SYMBOL_STRING,
PROVIDER_NUMBER,
PROVIDER_SYMBOL_STRING,
CONTEXT_NUMBER,
CONTEXT_SYMBOL_STRING,
CONSUMER_SYMBOL_STRING,
STRICT_MODE_NUMBER,
STRICT_MODE_SYMBOL_STRING,
PROFILER_NUMBER,
PROFILER_SYMBOL_STRING,
REACT_MEMO_CACHE_SENTINEL,
SCOPE_NUMBER,
SCOPE_SYMBOL_STRING,
FORWARD_REF_NUMBER,
FORWARD_REF_SYMBOL_STRING,
MEMO_NUMBER,
MEMO_SYMBOL_STRING,
SERVER_CONTEXT_SYMBOL_STRING,
LAZY_SYMBOL_STRING,
REACT_OPTIMISTIC_KEY,
} from '../shared/ReactSymbols';
import {enableStyleXFeatures} from 'react-devtools-feature-flags';
import {componentInfoToComponentLogsMap} from '../shared/DevToolsServerComponentLogs';
import is from 'shared/objectIs';
import {getIODescription} from 'shared/ReactIODescription';
import {
getStackByFiberInDevAndProd,
getOwnerStackByFiberInDev,
supportsOwnerStacks,
supportsConsoleTasks,
} from './DevToolsFiberComponentStack';
const toString = Object.prototype.toString;
function isError(object: mixed) {
return toString.call(object) === '[object Error]';
}
import {getStyleXData} from '../StyleX/utils';
import {createProfilingHooks} from '../profilingHooks';
import type {GetTimelineData, ToggleProfilingStatus} from '../profilingHooks';
import type {Fiber, FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
import type {
ChangeDescription,
CommitDataBackend,
DevToolsHook,
InspectedElement,
InspectedElementPayload,
InstanceAndStyle,
HostInstance,
PathFrame,
PathMatch,
ProfilingDataBackend,
ProfilingDataForRootBackend,
ReactRenderer,
RendererInterface,
SerializedElement,
SerializedAsyncInfo,
WorkTagMap,
CurrentDispatcherRef,
LegacyDispatcherRef,
ProfilingSettings,
} from '../types';
import type {
ComponentFilter,
ActivitySliceFilter,
ElementType,
Plugins,
} from 'react-devtools-shared/src/frontend/types';
import type {ReactFunctionLocation} from 'shared/ReactTypes';
import {getSourceLocationByFiber} from './DevToolsFiberComponentStack';
import {formatOwnerStack} from '../shared/DevToolsOwnerStack';
const FIBER_INSTANCE = 0;
const VIRTUAL_INSTANCE = 1;
const FILTERED_FIBER_INSTANCE = 2;
type FiberInstance = {
kind: 0,
id: number,
parent: null | DevToolsInstance,
firstChild: null | DevToolsInstance,
nextSibling: null | DevToolsInstance,
source: null | string | Error | ReactFunctionLocation,
logCount: number,
treeBaseDuration: number,
suspendedBy: null | Array<ReactAsyncInfo>,
suspenseNode: null | SuspenseNode,
data: Fiber,
};
function createFiberInstance(fiber: Fiber): FiberInstance {
return {
kind: FIBER_INSTANCE,
id: getUID(),
parent: null,
firstChild: null,
nextSibling: null,
source: null,
logCount: 0,
treeBaseDuration: 0,
suspendedBy: null,
suspenseNode: null,
data: fiber,
};
}
type FilteredFiberInstance = {
kind: 2,
parent: null | DevToolsInstance,
firstChild: null | DevToolsInstance,
nextSibling: null | DevToolsInstance,
source: null | string | Error | ReactFunctionLocation,
logCount: number,
treeBaseDuration: number,
suspendedBy: null | Array<ReactAsyncInfo>,
suspenseNode: null | SuspenseNode,
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,
suspendedBy: null,
suspenseNode: null,
data: fiber,
}: any);
}
type VirtualInstance = {
kind: 1,
id: number,
parent: null | DevToolsInstance,
firstChild: null | DevToolsInstance,
nextSibling: null | DevToolsInstance,
source: null | string | Error | ReactFunctionLocation,
logCount: number,
treeBaseDuration: number,
suspendedBy: null | Array<ReactAsyncInfo>,
suspenseNode: null,
data: ReactComponentInfo,
};
function createVirtualInstance(
debugEntry: ReactComponentInfo,
): VirtualInstance {
return {
kind: VIRTUAL_INSTANCE,
id: getUID(),
parent: null,
firstChild: null,
nextSibling: null,
source: null,
logCount: 0,
treeBaseDuration: 0,
suspendedBy: null,
suspenseNode: null,
data: debugEntry,
};
}
type DevToolsInstance = FiberInstance | VirtualInstance | FilteredFiberInstance;
type Rect = {x: number, y: number, width: number, height: number, ...};
type SuspenseNode = {
instance: FiberInstance | FilteredFiberInstance,
parent: null | SuspenseNode,
firstChild: null | SuspenseNode,
nextSibling: null | SuspenseNode,
rects: null | Array<Rect>,
suspendedBy: Map<ReactIOInfo, Set<DevToolsInstance>>,
environments: Map<string, number>,
endTime: number,
hasUniqueSuspenders: boolean,
hasUnknownSuspenders: boolean,
};
type UpdateFlags = number;
const NoUpdate = 0b000;
const ShouldResetChildren = 0b001;
const ShouldResetSuspenseChildren = 0b010;
const ShouldResetParentSuspenseChildren = 0b100;
function createSuspenseNode(
instance: FiberInstance | FilteredFiberInstance,
): SuspenseNode {
return (instance.suspenseNode = {
instance: instance,
parent: null,
firstChild: null,
nextSibling: null,
rects: null,
suspendedBy: new Map(),
environments: new Map(),
endTime: 0,
hasUniqueSuspenders: false,
hasUnknownSuspenders: false,
});
}
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 getDispatcherRef(renderer: {
+currentDispatcherRef?: LegacyDispatcherRef | CurrentDispatcherRef,
...
}): void | CurrentDispatcherRef {
if (renderer.currentDispatcherRef === undefined) {
return undefined;
}
const injectedRef = renderer.currentDispatcherRef;
if (
typeof injectedRef.H === 'undefined' &&
typeof injectedRef.current !== 'undefined'
) {
return {
get H() {
return (injectedRef: any).current;
},
set H(value) {
(injectedRef: any).current = value;
},
};
}
return (injectedRef: any);
}
function getFiberFlags(fiber: Fiber): number {
return fiber.flags !== undefined ? fiber.flags : (fiber: any).effectTag;
}
const getCurrentTime =
typeof performance === 'object' && typeof performance.now === 'function'
? () => performance.now()
: () => Date.now();
export function getInternalReactConstants(version: string): {
getDisplayNameForFiber: getDisplayNameForFiberType,
getTypeSymbol: getTypeSymbolType,
ReactPriorityLevels: ReactPriorityLevelsType,
ReactTypeOfWork: WorkTagMap,
StrictModeBits: number,
SuspenseyImagesMode: number,
} {
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')) {
StrictModeBits = 0b011000;
} else if (gte(version, '16.9.0')) {
StrictModeBits = 0b1;
} else if (gte(version, '16.3.0')) {
StrictModeBits = 0b10;
}
const SuspenseyImagesMode = 0b0100000;
let ReactTypeOfWork: WorkTagMap = ((null: any): WorkTagMap);
if (gt(version, '17.0.1')) {
ReactTypeOfWork = {
CacheComponent: 24,
ClassComponent: 1,
ContextConsumer: 9,
ContextProvider: 10,
CoroutineComponent: -1,
CoroutineHandlerPhase: -1,
DehydratedSuspenseComponent: 18,
ForwardRef: 11,
Fragment: 7,
FunctionComponent: 0,
HostComponent: 5,
HostPortal: 4,
HostRoot: 3,
HostHoistable: 26,
HostSingleton: 27,
HostText: 6,
IncompleteClassComponent: 17,
IncompleteFunctionComponent: 28,
IndeterminateComponent: 2,
LazyComponent: 16,
LegacyHiddenComponent: 23,
MemoComponent: 14,
Mode: 8,
OffscreenComponent: 22,
Profiler: 12,
ScopeComponent: 21,
SimpleMemoComponent: 15,
SuspenseComponent: 13,
SuspenseListComponent: 19,
TracingMarkerComponent: 25,
YieldComponent: -1,
Throw: 29,
ViewTransitionComponent: 30,
ActivityComponent: 31,
};
} else if (gte(version, '17.0.0-alpha')) {
ReactTypeOfWork = {
CacheComponent: -1,
ClassComponent: 1,
ContextConsumer: 9,
ContextProvider: 10,
CoroutineComponent: -1,
CoroutineHandlerPhase: -1,
DehydratedSuspenseComponent: 18,
ForwardRef: 11,
Fragment: 7,
FunctionComponent: 0,
HostComponent: 5,
HostPortal: 4,
HostRoot: 3,
HostHoistable: -1,
HostSingleton: -1,
HostText: 6,
IncompleteClassComponent: 17,
IncompleteFunctionComponent: -1,
IndeterminateComponent: 2,
LazyComponent: 16,
LegacyHiddenComponent: 24,
MemoComponent: 14,
Mode: 8,
OffscreenComponent: 23,
Profiler: 12,
ScopeComponent: 21,
SimpleMemoComponent: 15,
SuspenseComponent: 13,
SuspenseListComponent: 19,
TracingMarkerComponent: -1,
YieldComponent: -1,
Throw: -1,
ViewTransitionComponent: -1,
ActivityComponent: -1,
};
} else if (gte(version, '16.6.0-beta.0')) {
ReactTypeOfWork = {
CacheComponent: -1,
ClassComponent: 1,
ContextConsumer: 9,
ContextProvider: 10,
CoroutineComponent: -1,
CoroutineHandlerPhase: -1,
DehydratedSuspenseComponent: 18,
ForwardRef: 11,
Fragment: 7,
FunctionComponent: 0,
HostComponent: 5,
HostPortal: 4,
HostRoot: 3,
HostHoistable: -1,
HostSingleton: -1,
HostText: 6,
IncompleteClassComponent: 17,
IncompleteFunctionComponent: -1,
IndeterminateComponent: 2,
LazyComponent: 16,
LegacyHiddenComponent: -1,
MemoComponent: 14,
Mode: 8,
OffscreenComponent: -1,
Profiler: 12,
ScopeComponent: -1,
SimpleMemoComponent: 15,
SuspenseComponent: 13,
SuspenseListComponent: 19,
TracingMarkerComponent: -1,
YieldComponent: -1,
Throw: -1,
ViewTransitionComponent: -1,
ActivityComponent: -1,
};
} else if (gte(version, '16.4.3-alpha')) {
ReactTypeOfWork = {
CacheComponent: -1,
ClassComponent: 2,
ContextConsumer: 11,
ContextProvider: 12,
CoroutineComponent: -1,
CoroutineHandlerPhase: -1,
DehydratedSuspenseComponent: -1,
ForwardRef: 13,
Fragment: 9,
FunctionComponent: 0,
HostComponent: 7,
HostPortal: 6,
HostRoot: 5,
HostHoistable: -1,
HostSingleton: -1,
HostText: 8,
IncompleteClassComponent: -1,
IncompleteFunctionComponent: -1,
IndeterminateComponent: 4,
LazyComponent: -1,
LegacyHiddenComponent: -1,
MemoComponent: -1,
Mode: 10,
OffscreenComponent: -1,
Profiler: 15,
ScopeComponent: -1,
SimpleMemoComponent: -1,
SuspenseComponent: 16,
SuspenseListComponent: -1,
TracingMarkerComponent: -1,
YieldComponent: -1,
Throw: -1,
ViewTransitionComponent: -1,
ActivityComponent: -1,
};
} else {
ReactTypeOfWork = {
CacheComponent: -1,
ClassComponent: 2,
ContextConsumer: 12,
ContextProvider: 13,
CoroutineComponent: 7,
CoroutineHandlerPhase: 8,
DehydratedSuspenseComponent: -1,
ForwardRef: 14,
Fragment: 10,
FunctionComponent: 1,
HostComponent: 5,
HostPortal: 4,
HostRoot: 3,
HostHoistable: -1,
HostSingleton: -1,
HostText: 6,
IncompleteClassComponent: -1,
IncompleteFunctionComponent: -1,
IndeterminateComponent: 0,
LazyComponent: -1,
LegacyHiddenComponent: -1,
MemoComponent: -1,
Mode: 11,
OffscreenComponent: -1,
Profiler: 15,
ScopeComponent: -1,
SimpleMemoComponent: -1,
SuspenseComponent: 16,
SuspenseListComponent: -1,
TracingMarkerComponent: -1,
YieldComponent: 9,
Throw: -1,
ViewTransitionComponent: -1,
ActivityComponent: -1,
};
}
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;
function resolveFiberType(type: any): $FlowFixMe {
const typeSymbol = getTypeSymbol(type);
switch (typeSymbol) {
case MEMO_NUMBER:
case MEMO_SYMBOL_STRING:
return resolveFiberType(type.type);
case FORWARD_REF_NUMBER:
case FORWARD_REF_SYMBOL_STRING:
return type.render;
default:
return type;
}
}
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;
}
}
}
return {
getDisplayNameForFiber,
getTypeSymbol,
ReactPriorityLevels,
ReactTypeOfWork,
StrictModeBits,
SuspenseyImagesMode,
};
}
const knownEnvironmentNames: Set<string> = new Set();
const rootToFiberInstanceMap: Map<FiberRoot, FiberInstance> = new Map();
const idToDevToolsInstanceMap: Map<
FiberInstance['id'] | VirtualInstance['id'],
FiberInstance | VirtualInstance,
> = new Map();
let focusedActivityID: null | FiberInstance['id'] = null;
let focusedActivity: null | Fiber = null;
const idToSuspenseNodeMap: Map<FiberInstance['id'], SuspenseNode> = new Map();
const publicInstanceToDevToolsInstanceMap: Map<HostInstance, DevToolsInstance> =
new Map();
const hostResourceToDevToolsInstanceMap: Map<
HostInstance,
Set<DevToolsInstance>,
> = new Map();
function getPublicInstance(instance: HostInstance): HostInstance {
if (typeof instance === 'object' && instance !== null) {
if (typeof instance.canonical === 'object' && instance.canonical !== null) {
if (
typeof instance.canonical.publicInstance === 'object' &&
instance.canonical.publicInstance !== null
) {
return instance.canonical.publicInstance;
}
}
if (typeof instance._nativeTag === 'number') {
return instance._nativeTag;
}
}
return instance;
}
function getNativeTag(instance: HostInstance): number | null {
if (typeof instance !== 'object' || instance === null) {
return null;
}
if (
instance.canonical != null &&
typeof instance.canonical.nativeTag === 'number'
) {
return instance.canonical.nativeTag;
}
if (typeof instance._nativeTag === 'number') {
return instance._nativeTag;
}
return null;
}
function aquireHostInstance(
nearestInstance: DevToolsInstance,
hostInstance: HostInstance,
): void {
const publicInstance = getPublicInstance(hostInstance);
publicInstanceToDevToolsInstanceMap.set(publicInstance, nearestInstance);
}
function releaseHostInstance(
nearestInstance: DevToolsInstance,
hostInstance: HostInstance,
): void {
const publicInstance = getPublicInstance(hostInstance);
if (
publicInstanceToDevToolsInstanceMap.get(publicInstance) === nearestInstance
) {
publicInstanceToDevToolsInstanceMap.delete(publicInstance);
}
}
function aquireHostResource(
nearestInstance: DevToolsInstance,
resource: ?{instance?: HostInstance},
): void {
const hostInstance = resource && resource.instance;
if (hostInstance) {
const publicInstance = getPublicInstance(hostInstance);
let resourceInstances =
hostResourceToDevToolsInstanceMap.get(publicInstance);
if (resourceInstances === undefined) {
resourceInstances = new Set();
hostResourceToDevToolsInstanceMap.set(publicInstance, resourceInstances);
publicInstanceToDevToolsInstanceMap.set(publicInstance, nearestInstance);
}
resourceInstances.add(nearestInstance);
}
}
function releaseHostResource(
nearestInstance: DevToolsInstance,
resource: ?{instance?: HostInstance},
): void {
const hostInstance = resource && resource.instance;
if (hostInstance) {
const publicInstance = getPublicInstance(hostInstance);
const resourceInstances =
hostResourceToDevToolsInstanceMap.get(publicInstance);
if (resourceInstances !== undefined) {
resourceInstances.delete(nearestInstance);
if (resourceInstances.size === 0) {
hostResourceToDevToolsInstanceMap.delete(publicInstance);
publicInstanceToDevToolsInstanceMap.delete(publicInstance);
} else if (
publicInstanceToDevToolsInstanceMap.get(publicInstance) ===
nearestInstance
) {
for (const firstInstance of resourceInstances) {
publicInstanceToDevToolsInstanceMap.set(
firstInstance,
nearestInstance,
);
break;
}
}
}
}
}
export function attach(
hook: DevToolsHook,
rendererID: number,
renderer: ReactRenderer,
global: Object,
shouldStartProfilingNow: boolean,
profilingSettings: ProfilingSettings,
componentFiltersOrComponentFiltersPromise:
| Array<ComponentFilter>
| Promise<Array<ComponentFilter>>,
): RendererInterface {
const version = renderer.reconcilerVersion || renderer.version;
const {
getDisplayNameForFiber,
getTypeSymbol,
ReactPriorityLevels,
ReactTypeOfWork,
StrictModeBits,
SuspenseyImagesMode,
} = getInternalReactConstants(version);
const {
ActivityComponent,
ClassComponent,
ContextConsumer,
DehydratedSuspenseComponent,
ForwardRef,
Fragment,
FunctionComponent,
HostRoot,
HostHoistable,
HostSingleton,
HostPortal,
HostComponent,
HostText,
IncompleteClassComponent,
IncompleteFunctionComponent,
IndeterminateComponent,
LegacyHiddenComponent,
MemoComponent,
OffscreenComponent,
SimpleMemoComponent,
SuspenseComponent,
SuspenseListComponent,
TracingMarkerComponent,
Throw,
ViewTransitionComponent,
} = ReactTypeOfWork;
const {
ImmediatePriority,
UserBlockingPriority,
NormalPriority,
LowPriority,
IdlePriority,
NoPriority,
} = ReactPriorityLevels;
const {
getLaneLabelMap,
injectProfilingHooks,
overrideHookState,
overrideHookStateDeletePath,
overrideHookStateRenamePath,
overrideProps,
overridePropsDeletePath,
overridePropsRenamePath,
scheduleRefresh,
setErrorHandler,
setSuspenseHandler,
scheduleUpdate,
scheduleRetry,
getCurrentFiber,
} = renderer;
const supportsTogglingError =
typeof setErrorHandler === 'function' &&
typeof scheduleUpdate === 'function';
const supportsTogglingSuspense =
typeof setSuspenseHandler === 'function' &&
typeof scheduleUpdate === 'function';
const supportsPerformanceTracks = gte(version, '19.2.0');
if (typeof scheduleRefresh === 'function') {
renderer.scheduleRefresh = (...args) => {
try {
hook.emit('fastRefreshScheduled');
} finally {
return scheduleRefresh(...args);
}
};
}
let getTimelineData: null | GetTimelineData = null;
let toggleProfilingStatus: null | ToggleProfilingStatus = null;
if (typeof injectProfilingHooks === 'function') {
const response = createProfilingHooks({
getDisplayNameForFiber,
getIsProfiling: () => isProfiling,
getLaneLabelMap,
currentDispatcherRef: getDispatcherRef(renderer),
workTagMap: ReactTypeOfWork,
reactVersion: version,
});
injectProfilingHooks(response.profilingHooks);
getTimelineData = response.getTimelineData;
toggleProfilingStatus = response.toggleProfilingStatus;
}
type ComponentLogs = {
errors: Map<string, number>,
errorsCount: number,
warnings: Map<string, number>,
warningsCount: number,
};
const fiberToComponentLogsMap: WeakMap<Fiber, ComponentLogs> = new WeakMap();
let needsToFlushComponentLogs = false;
function bruteForceFlushErrorsAndWarnings() {
let hasChanges = false;
for (const devtoolsInstance of idToDevToolsInstanceMap.values()) {
if (devtoolsInstance.kind === FIBER_INSTANCE) {
const fiber = devtoolsInstance.data;
const componentLogsEntry = fiberToComponentLogsMap.get(fiber);
const changed = recordConsoleLogs(devtoolsInstance, componentLogsEntry);
if (changed) {
hasChanges = true;
updateMostRecentlyInspectedElementIfNecessary(devtoolsInstance.id);
}
} else {
}
}
if (hasChanges) {
flushPendingEvents();
}
}
function clearErrorsAndWarnings() {
for (const devtoolsInstance of idToDevToolsInstanceMap.values()) {
if (devtoolsInstance.kind === FIBER_INSTANCE) {
const fiber = devtoolsInstance.data;
fiberToComponentLogsMap.delete(fiber);
if (fiber.alternate) {
fiberToComponentLogsMap.delete(fiber.alternate);
}
} else {
componentInfoToComponentLogsMap.delete(devtoolsInstance.data);
}
const changed = recordConsoleLogs(devtoolsInstance, undefined);
if (changed) {
updateMostRecentlyInspectedElementIfNecessary(devtoolsInstance.id);
}
}
flushPendingEvents();
}
function clearConsoleLogsHelper(instanceID: number, type: 'error' | 'warn') {
const devtoolsInstance = idToDevToolsInstanceMap.get(instanceID);
if (devtoolsInstance !== undefined) {
let componentLogsEntry;
if (devtoolsInstance.kind === FIBER_INSTANCE) {
const fiber = devtoolsInstance.data;
componentLogsEntry = fiberToComponentLogsMap.get(fiber);
if (componentLogsEntry === undefined && fiber.alternate !== null) {
componentLogsEntry = fiberToComponentLogsMap.get(fiber.alternate);
}
} else {
const componentInfo = devtoolsInstance.data;
componentLogsEntry = componentInfoToComponentLogsMap.get(componentInfo);
}
if (componentLogsEntry !== undefined) {
if (type === 'error') {
componentLogsEntry.errors.clear();
componentLogsEntry.errorsCount = 0;
} else {
componentLogsEntry.warnings.clear();
componentLogsEntry.warningsCount = 0;
}
const changed = recordConsoleLogs(devtoolsInstance, componentLogsEntry);
if (changed) {
flushPendingEvents();
updateMostRecentlyInspectedElementIfNecessary(devtoolsInstance.id);
}
}
}
}
function clearErrorsForElementID(instanceID: number) {
clearConsoleLogsHelper(instanceID, 'error');
}
function clearWarningsForElementID(instanceID: number) {
clearConsoleLogsHelper(instanceID, 'warn');
}
function updateMostRecentlyInspectedElementIfNecessary(
fiberID: number,
): void {
if (
mostRecentlyInspectedElement !== null &&
mostRecentlyInspectedElement.id === fiberID
) {
hasElementUpdatedSinceLastInspected = true;
}
}
function getComponentStack(
topFrame: Error,
): null | {enableOwnerStacks: boolean, componentStack: string} {
if (getCurrentFiber == null) {
return null;
}
const current = getCurrentFiber();
if (current === null) {
return null;
}
if (supportsConsoleTasks(current)) {
return null;
}
const dispatcherRef = getDispatcherRef(renderer);
if (dispatcherRef === undefined) {
return null;
}
const enableOwnerStacks = supportsOwnerStacks(current);
let componentStack = '';
if (enableOwnerStacks) {
const topStackFrames = formatOwnerStack(topFrame);
if (topStackFrames) {
componentStack += '\n' + topStackFrames;
}
componentStack += getOwnerStackByFiberInDev(
ReactTypeOfWork,
current,
dispatcherRef,
);
} else {
componentStack = getStackByFiberInDevAndProd(
ReactTypeOfWork,
current,
dispatcherRef,
);
}
return {enableOwnerStacks, componentStack};
}
function onErrorOrWarning(
type: 'error' | 'warn',
args: $ReadOnlyArray<any>,
): void {
if (getCurrentFiber == null) {
return;
}
const fiber = getCurrentFiber();
if (fiber === null) {
return;
}
if (type === 'error') {
if (
forceErrorForFibers.get(fiber) === true ||
(fiber.alternate !== null &&
forceErrorForFibers.get(fiber.alternate) === true)
) {
return;
}
}
const message = formatConsoleArgumentsToSingleString(...args);
let componentLogsEntry = fiberToComponentLogsMap.get(fiber);
if (componentLogsEntry === undefined && fiber.alternate !== null) {
componentLogsEntry = fiberToComponentLogsMap.get(fiber.alternate);
if (componentLogsEntry !== undefined) {
fiberToComponentLogsMap.set(fiber, componentLogsEntry);
}
}
if (componentLogsEntry === undefined) {
componentLogsEntry = {
errors: new Map(),
errorsCount: 0 as number,
warnings: new Map(),
warningsCount: 0 as number,
};
fiberToComponentLogsMap.set(fiber, componentLogsEntry);
}
const messageMap =
type === 'error'
? componentLogsEntry.errors
: componentLogsEntry.warnings;
const count = messageMap.get(message) || 0;
messageMap.set(message, count + 1);
if (type === 'error') {
componentLogsEntry.errorsCount++;
} else {
componentLogsEntry.warningsCount++;
}
needsToFlushComponentLogs = true;
}
function debug(
name: string,
instance: DevToolsInstance,
parentInstance: null | DevToolsInstance,
extraString: string = '',
): void {
if (__DEBUG__) {
const displayName =
instance.kind === VIRTUAL_INSTANCE
? instance.data.name || 'null'
: instance.data.tag +
':' +
(getDisplayNameForFiber(instance.data) || 'null');
const maybeID =
instance.kind === FILTERED_FIBER_INSTANCE ? '<no id>' : instance.id;
const parentDisplayName =
parentInstance === null
? ''
: parentInstance.kind === VIRTUAL_INSTANCE
? parentInstance.data.name || 'null'
: parentInstance.data.tag +
':' +
(getDisplayNameForFiber(parentInstance.data) || 'null');
const maybeParentID =
parentInstance === null ||
parentInstance.kind === FILTERED_FIBER_INSTANCE
? '<no id>'
: parentInstance.id;
console.groupCollapsed(
`[renderer] %c${name} %c${displayName} (${maybeID}) %c${
parentInstance ? `${parentDisplayName} (${maybeParentID})` : ''
} %c${extraString}`,
'color: red; font-weight: bold;',
'color: blue;',
'color: purple;',
'color: black;',
);
console.log(new Error().stack.split('\n').slice(1).join('\n'));
console.groupEnd();
}
}
function debugTree(instance: DevToolsInstance, indent: number = 0) {
if (__DEBUG__) {
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;
}
}
}
const hideElementsWithDisplayNames: Set<RegExp> = new Set();
const hideElementsWithPaths: Set<RegExp> = new Set();
const hideElementsWithTypes: Set<ElementType> = new Set();
const hideElementsWithEnvs: Set<string> = new Set();
let isInFocusedActivity: boolean = true;
let traceUpdatesEnabled: boolean = false;
const traceUpdatesForNodes: Set<HostInstance> = new Set();
function applyComponentFilters(
componentFilters: Array<ComponentFilter>,
nextActivitySlice: null | Fiber,
) {
hideElementsWithTypes.clear();
hideElementsWithDisplayNames.clear();
hideElementsWithPaths.clear();
hideElementsWithEnvs.clear();
const previousFocusedActivityID = focusedActivityID;
focusedActivityID = null;
focusedActivity = null;
isInFocusedActivity = true;
componentFilters.forEach(componentFilter => {
if (!componentFilter.isEnabled) {
return;
}
switch (componentFilter.type) {
case ComponentFilterDisplayName:
if (componentFilter.isValid && componentFilter.value !== '') {
hideElementsWithDisplayNames.add(
new RegExp(componentFilter.value, 'i'),
);
}
break;
case ComponentFilterElementType:
hideElementsWithTypes.add(componentFilter.value);
break;
case ComponentFilterLocation:
if (componentFilter.isValid && componentFilter.value !== '') {
hideElementsWithPaths.add(new RegExp(componentFilter.value, 'i'));
}
break;
case ComponentFilterHOC:
hideElementsWithDisplayNames.add(new RegExp('\\('));
break;
case ComponentFilterEnvironmentName:
hideElementsWithEnvs.add(componentFilter.value);
break;
case ComponentFilterActivitySlice:
if (
nextActivitySlice !== null &&
nextActivitySlice.tag === ActivityComponent
) {
focusedActivity = nextActivitySlice;
isInFocusedActivity = false;
if (componentFilter.rendererID !== rendererID) {
focusedActivityID = previousFocusedActivityID;
}
} else {
}
break;
default:
console.warn(
`Invalid component filter type "${componentFilter.type}"`,
);
break;
}
});
}
if (Array.isArray(componentFiltersOrComponentFiltersPromise)) {
applyComponentFilters(componentFiltersOrComponentFiltersPromise, null);
} else {
componentFiltersOrComponentFiltersPromise.then(componentFilters => {
applyComponentFilters(componentFilters, null);
});
}
function updateComponentFilters(componentFilters: Array<ComponentFilter>) {
if (isProfiling) {
throw Error('Cannot modify filter preferences while profiling');
}
const previousForcedFallbacks =
forceFallbackForFibers.size > 0 ? new Set(forceFallbackForFibers) : null;
const previousForcedErrors =
forceErrorForFibers.size > 0 ? new Map(forceErrorForFibers) : null;
let nextFocusedActivity: null | Fiber = null;
let focusedActivityFilter: null | ActivitySliceFilter = null;
for (let i = 0; i < componentFilters.length; i++) {
const filter = componentFilters[i];
if (filter.type === ComponentFilterActivitySlice && filter.isEnabled) {
focusedActivityFilter = filter;
const instance = idToDevToolsInstanceMap.get(filter.activityID);
if (instance !== undefined && instance.kind === FIBER_INSTANCE) {
nextFocusedActivity = instance.data;
}
}
}
hook.getFiberRoots(rendererID).forEach(root => {
const rootInstance = rootToFiberInstanceMap.get(root);
if (rootInstance === undefined) {
throw new Error(
'Expected the root instance to already exist when applying filters',
);
}
currentRoot = rootInstance;
unmountInstanceRecursively(rootInstance);
rootToFiberInstanceMap.delete(root);
currentRoot = (null: any);
});
if (
nextFocusedActivity !== focusedActivity &&
(focusedActivityFilter === null ||
focusedActivityFilter.rendererID === rendererID)
) {
pushOperation(TREE_OPERATION_APPLIED_ACTIVITY_SLICE_CHANGE);
pushOperation(0);
}
applyComponentFilters(componentFilters, nextFocusedActivity);
rootDisplayNameCounter.clear();
if (typeof scheduleUpdate === 'function') {
if (previousForcedFallbacks !== null) {
for (const fiber of previousForcedFallbacks) {
if (typeof scheduleRetry === 'function') {
scheduleRetry(fiber);
} else {
scheduleUpdate(fiber);
}
}
}
if (
previousForcedErrors !== null &&
typeof setErrorHandler === 'function'
) {
setErrorHandler(shouldErrorFiberAccordingToMap);
for (const [fiber, shouldError] of previousForcedErrors) {
forceErrorForFibers.set(fiber, false);
if (shouldError) {
if (typeof scheduleRetry === 'function') {
scheduleRetry(fiber);
} else {
scheduleUpdate(fiber);
}
}
}
}
}
hook.getFiberRoots(rendererID).forEach(root => {
const current = root.current;
const newRoot = createFiberInstance(current);
rootToFiberInstanceMap.set(root, newRoot);
idToDevToolsInstanceMap.set(newRoot.id, newRoot);
if (trackedPath !== null) {
mightBeOnTrackedPath = true;
}
currentRoot = newRoot;
setRootPseudoKey(currentRoot.id, root.current);
mountFiberRecursively(root.current, false);
currentRoot = (null: any);
});
if (focusedActivityFilter !== null && focusedActivityID !== null) {
focusedActivityFilter.activityID = focusedActivityID;
}
flushPendingEvents();
needsToFlushComponentLogs = false;
}
function getEnvironmentNames(): Array<string> {
return Array.from(knownEnvironmentNames);
}
function isFiberHydrated(fiber: Fiber): boolean {
if (OffscreenComponent === -1) {
throw new Error('not implemented for legacy suspense');
}
switch (fiber.tag) {
case HostRoot:
const rootState = fiber.memoizedState;
return !rootState.isDehydrated;
case SuspenseComponent:
const suspenseState = fiber.memoizedState;
return suspenseState === null || suspenseState.dehydrated === null;
default:
throw new Error('not implemented for work tag ' + fiber.tag);
}
}
function shouldFilterVirtual(
data: ReactComponentInfo,
secondaryEnv: null | string,
): boolean {
if (!isInFocusedActivity) {
return true;
}
if (hideElementsWithTypes.has(ElementTypeFunction)) {
return true;
}
if (hideElementsWithDisplayNames.size > 0) {
const displayName = data.name;
if (displayName != null) {
for (const displayNameRegExp of hideElementsWithDisplayNames) {
if (displayNameRegExp.test(displayName)) {
return true;
}
}
}
}
if (
(data.env == null || hideElementsWithEnvs.has(data.env)) &&
(secondaryEnv === null || hideElementsWithEnvs.has(secondaryEnv))
) {
return true;
}
return false;
}
function shouldFilterFiber(fiber: Fiber): boolean {
const {tag, type, key} = fiber;
if (tag !== HostRoot && !isInFocusedActivity) {
return true;
}
switch (tag) {
case DehydratedSuspenseComponent:
return true;
case HostPortal:
case HostText:
case LegacyHiddenComponent:
case OffscreenComponent:
case Throw:
return true;
case HostRoot:
return false;
case Fragment:
return key === null;
default:
const typeSymbol = getTypeSymbol(type);
switch (typeSymbol) {
case CONCURRENT_MODE_NUMBER:
case CONCURRENT_MODE_SYMBOL_STRING:
case DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
case STRICT_MODE_NUMBER:
case STRICT_MODE_SYMBOL_STRING:
return true;
default:
break;
}
}
const elementType = getElementTypeForFiber(fiber);
if (hideElementsWithTypes.has(elementType)) {
return true;
}
if (hideElementsWithDisplayNames.size > 0) {
const displayName = getDisplayNameForFiber(fiber);
if (displayName != null) {
for (const displayNameRegExp of hideElementsWithDisplayNames) {
if (displayNameRegExp.test(displayName)) {
return true;
}
}
}
}
if (hideElementsWithEnvs.has('Client')) {
switch (tag) {
case ClassComponent:
case IncompleteClassComponent:
case IncompleteFunctionComponent:
case FunctionComponent:
case IndeterminateComponent:
case ForwardRef:
case MemoComponent:
case SimpleMemoComponent:
return true;
}
}
return false;
}
function getElementTypeForFiber(fiber: Fiber): ElementType {
const {type, tag} = fiber;
switch (tag) {
case ActivityComponent:
return ElementTypeActivity;
case ClassComponent:
case IncompleteClassComponent:
return ElementTypeClass;
case IncompleteFunctionComponent:
case FunctionComponent:
case IndeterminateComponent:
return ElementTypeFunction;
case ForwardRef:
return ElementTypeForwardRef;
case HostRoot:
return ElementTypeRoot;
case HostComponent:
case HostHoistable:
case HostSingleton:
return ElementTypeHostComponent;
case HostPortal:
case HostText:
case Fragment:
return ElementTypeOtherOrUnknown;
case MemoComponent:
case SimpleMemoComponent:
return ElementTypeMemo;
case SuspenseComponent:
return ElementTypeSuspense;
case SuspenseListComponent:
return ElementTypeSuspenseList;
case TracingMarkerComponent:
return ElementTypeTracingMarker;
case ViewTransitionComponent:
return ElementTypeViewTransition;
default:
const typeSymbol = getTypeSymbol(type);
switch (typeSymbol) {
case CONCURRENT_MODE_NUMBER:
case CONCURRENT_MODE_SYMBOL_STRING:
case DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
return ElementTypeOtherOrUnknown;
case PROVIDER_NUMBER:
case PROVIDER_SYMBOL_STRING:
return ElementTypeContext;
case CONTEXT_NUMBER:
case CONTEXT_SYMBOL_STRING:
return ElementTypeContext;
case STRICT_MODE_NUMBER:
case STRICT_MODE_SYMBOL_STRING:
return ElementTypeOtherOrUnknown;
case PROFILER_NUMBER:
case PROFILER_SYMBOL_STRING:
return ElementTypeProfiler;
default:
return ElementTypeOtherOrUnknown;
}
}
}
let currentRoot: FiberInstance = (null: any);
function untrackFiber(nearestInstance: DevToolsInstance, fiber: Fiber) {
if (forceErrorForFibers.size > 0) {
forceErrorForFibers.delete(fiber);
if (fiber.alternate) {
forceErrorForFibers.delete(fiber.alternate);
}
if (forceErrorForFibers.size === 0 && setErrorHandler != null) {
setErrorHandler(shouldErrorFiberAlwaysNull);
}
}
if (forceFallbackForFibers.size > 0) {
forceFallbackForFibers.delete(fiber);
if (fiber.alternate) {
forceFallbackForFibers.delete(fiber.alternate);
}
if (forceFallbackForFibers.size === 0 && setSuspenseHandler != null) {
setSuspenseHandler(shouldSuspendFiberAlwaysFalse);
}
}
if (fiber.tag === HostHoistable) {
releaseHostResource(nearestInstance, fiber.memoizedState);
} else if (
fiber.tag === HostComponent ||
fiber.tag === HostText ||
fiber.tag === HostSingleton
) {
releaseHostInstance(nearestInstance, fiber.stateNode);
}
for (let child = fiber.child; child !== null; child = child.sibling) {
if (shouldFilterFiber(child)) {
untrackFiber(nearestInstance, child);
}
}
}
function getChangeDescription(
prevFiber: Fiber | null,
nextFiber: Fiber,
): ChangeDescription | null {
switch (nextFiber.tag) {
case ClassComponent:
if (prevFiber === null) {
return {
context: null,
didHooksChange: false,
isFirstMount: true,
props: null,
state: null,
};
} else {
const data: ChangeDescription = {
context: getContextChanged(prevFiber, nextFiber),
didHooksChange: false,
isFirstMount: false,
props: getChangedKeys(
prevFiber.memoizedProps,
nextFiber.memoizedProps,
),
state: getChangedKeys(
prevFiber.memoizedState,
nextFiber.memoizedState,
),
};
return data;
}
case IncompleteFunctionComponent:
case FunctionComponent:
case IndeterminateComponent:
case ForwardRef:
case MemoComponent:
case SimpleMemoComponent:
if (prevFiber === null) {
return {
context: null,
didHooksChange: false,
isFirstMount: true,
props: null,
state: null,
};
} else {
const prevHooks = inspectHooks(prevFiber);
const nextHooks = inspectHooks(nextFiber);
const indices = getChangedHooksIndices(prevHooks, nextHooks);
const data: ChangeDescription = {
context: getContextChanged(prevFiber, nextFiber),
didHooksChange: indices !== null && indices.length > 0,
isFirstMount: false,
props: getChangedKeys(
prevFiber.memoizedProps,
nextFiber.memoizedProps,
),
state: null,
hooks: indices,
};
return data;
}
default:
return null;
}
}
function getContextChanged(prevFiber: Fiber, nextFiber: Fiber): boolean {
let prevContext =
prevFiber.dependencies && prevFiber.dependencies.firstContext;
let nextContext =
nextFiber.dependencies && nextFiber.dependencies.firstContext;
while (prevContext && nextContext) {
if (prevContext.context !== nextContext.context) {
return false;
}
if (!is(prevContext.memoizedValue, nextContext.memoizedValue)) {
return true;
}
prevContext = prevContext.next;
nextContext = nextContext.next;
}
return false;
}
function didStatefulHookChange(prev: HooksNode, next: HooksNode): boolean {
const isStatefulHook =
prev.isStateEditable === true ||
prev.name === 'SyncExternalStore' ||
prev.name === 'Transition' ||
prev.name === 'ActionState' ||
prev.name === 'FormState';
if (isStatefulHook) {
return prev.value !== next.value;
}
return false;
}
function getChangedHooksIndices(
prevHooks: HooksTree | null,
nextHooks: HooksTree | null,
): null | Array<number> {
if (prevHooks == null || nextHooks == null) {
return null;
}
const indices: Array<number> = [];
let index = 0;
function traverse(prevTree: HooksTree, nextTree: HooksTree): void {
for (let i = 0; i < prevTree.length; i++) {
const prevHook = prevTree[i];
const nextHook = nextTree[i];
if (prevHook.subHooks.length > 0 && nextHook.subHooks.length > 0) {
traverse(prevHook.subHooks, nextHook.subHooks);
continue;
}
if (didStatefulHookChange(prevHook, nextHook)) {
indices.push(index);
}
index++;
}
}
traverse(prevHooks, nextHooks);
return indices;
}
function getChangedKeys(prev: any, next: any): null | Array<string> {
if (prev == null || next == null) {
return null;
}
const keys = new Set([...Object.keys(prev), ...Object.keys(next)]);
const changedKeys = [];
for (const key of keys) {
if (prev[key] !== next[key]) {
changedKeys.push(key);
}
}
return changedKeys;
}
function didFiberRender(prevFiber: Fiber, nextFiber: Fiber): boolean {
switch (nextFiber.tag) {
case ClassComponent:
case FunctionComponent:
case ContextConsumer:
case MemoComponent:
case SimpleMemoComponent:
case ForwardRef:
const PerformedWork = 0b000000000000000000000000001;
return (getFiberFlags(nextFiber) & PerformedWork) === PerformedWork;
default:
return (
prevFiber.memoizedProps !== nextFiber.memoizedProps ||
prevFiber.memoizedState !== nextFiber.memoizedState ||
prevFiber.ref !== nextFiber.ref
);
}
}
type OperationsArray = Array<number>;
type StringTableEntry = {
encodedString: Array<number>,
id: number,
};
const pendingOperations: OperationsArray = [];
const pendingRealUnmountedIDs: Array<FiberInstance['id']> = [];
const pendingRealUnmountedSuspenseIDs: Array<FiberInstance['id']> = [];
const pendingSuspenderChanges: Set<FiberInstance['id']> = new Set();
let pendingOperationsQueue: Array<OperationsArray> | null = [];
const pendingStringTable: Map<string, StringTableEntry> = new Map();
let pendingStringTableLength: number = 0;
function pushOperation(op: number): void {
if (__DEV__) {
if (!Number.isInteger(op)) {
console.error(
'pushOperation() was called but the value is not an integer.',
op,
);
}
}
pendingOperations.push(op);
}
function shouldBailoutWithPendingOperations() {
if (isProfiling) {
if (
currentCommitProfilingMetadata != null &&
currentCommitProfilingMetadata.durations.length > 0
) {
return false;
}
}
return (
pendingOperations.length === 0 &&
pendingRealUnmountedIDs.length === 0 &&
pendingRealUnmountedSuspenseIDs.length === 0 &&
pendingSuspenderChanges.size === 0
);
}
function flushOrQueueOperations(operations: OperationsArray): void {
if (shouldBailoutWithPendingOperations()) {
return;
}
if (pendingOperationsQueue !== null) {
pendingOperationsQueue.push(operations);
} else {
hook.emit('operations', operations);
}
}
function recordConsoleLogs(
instance: FiberInstance | VirtualInstance,
componentLogsEntry: void | ComponentLogs,
): boolean {
if (componentLogsEntry === undefined) {
if (instance.logCount === 0) {
return false;
}
instance.logCount = 0;
pushOperation(TREE_OPERATION_UPDATE_ERRORS_OR_WARNINGS);
pushOperation(instance.id);
pushOperation(0);
pushOperation(0);
return true;
} else {
const totalCount =
componentLogsEntry.errorsCount + componentLogsEntry.warningsCount;
if (instance.logCount === totalCount) {
return false;
}
instance.logCount = totalCount;
pushOperation(TREE_OPERATION_UPDATE_ERRORS_OR_WARNINGS);
pushOperation(instance.id);
pushOperation(componentLogsEntry.errorsCount);
pushOperation(componentLogsEntry.warningsCount);
return true;
}
}
function flushPendingEvents(): void {
if (shouldBailoutWithPendingOperations()) {
return;
}
const numUnmountIDs = pendingRealUnmountedIDs.length;
const numUnmountSuspenseIDs = pendingRealUnmountedSuspenseIDs.length;
const numSuspenderChanges = pendingSuspenderChanges.size;
const operations = new Array<number>(
2 +
1 +
pendingStringTableLength +
(numUnmountSuspenseIDs > 0 ? 2 + numUnmountSuspenseIDs : 0) +
(numUnmountIDs > 0 ? 2 + numUnmountIDs : 0) +
pendingOperations.length +
(numSuspenderChanges > 0 ? 2 + numSuspenderChanges * 4 : 0),
);
let i = 0;
operations[i++] = rendererID;
if (currentRoot === null) {
operations[i++] = -1;
} else {
operations[i++] = currentRoot.id;
}
operations[i++] = pendingStringTableLength;
pendingStringTable.forEach((entry, stringKey) => {
const encodedString = entry.encodedString;
const length = encodedString.length;
operations[i++] = length;
for (let j = 0; j < length; j++) {
operations[i + j] = encodedString[j];
}
i += length;
});
if (numUnmountSuspenseIDs > 0) {
operations[i++] = SUSPENSE_TREE_OPERATION_REMOVE;
operations[i++] = numUnmountSuspenseIDs;
for (let j = 0; j < pendingRealUnmountedSuspenseIDs.length; j++) {
operations[i++] = pendingRealUnmountedSuspenseIDs[j];
}
}
if (numUnmountIDs > 0) {
operations[i++] = TREE_OPERATION_REMOVE;
operations[i++] = numUnmountIDs;
for (let j = 0; j < pendingRealUnmountedIDs.length; j++) {
operations[i++] = pendingRealUnmountedIDs[j];
}
}
for (let j = 0; j < pendingOperations.length; j++) {
operations[i + j] = pendingOperations[j];
}
i += pendingOperations.length;
if (numSuspenderChanges > 0) {
operations[i++] = SUSPENSE_TREE_OPERATION_SUSPENDERS;
operations[i++] = numSuspenderChanges;
pendingSuspenderChanges.forEach(fiberIdWithChanges => {
const suspense = idToSuspenseNodeMap.get(fiberIdWithChanges);
if (suspense === undefined) {
throw new Error(
`Could not send suspender changes for "${fiberIdWithChanges}" since the Fiber no longer exists.`,
);
}
operations[i++] = fiberIdWithChanges;
operations[i++] = suspense.hasUniqueSuspenders ? 1 : 0;
operations[i++] = Math.round(suspense.endTime * 1000);
const instance = suspense.instance;
const isSuspended =
(instance.kind === FIBER_INSTANCE ||
instance.kind === FILTERED_FIBER_INSTANCE) &&
instance.data.tag === SuspenseComponent &&
instance.data.memoizedState !== null;
operations[i++] = isSuspended ? 1 : 0;
operations[i++] = suspense.environments.size;
suspense.environments.forEach((count, env) => {
operations[i++] = getStringID(env);
});
});
}
flushOrQueueOperations(operations);
pendingOperations.length = 0;
pendingRealUnmountedIDs.length = 0;
pendingRealUnmountedSuspenseIDs.length = 0;
pendingSuspenderChanges.clear();
pendingStringTable.clear();
pendingStringTableLength = 0;
}
function measureHostInstance(instance: HostInstance): null | Array<Rect> {
if (typeof instance !== 'object' || instance === null) {
return null;
}
if (
typeof instance.getClientRects === 'function' ||
instance.nodeType === 3
) {
const doc = instance.ownerDocument;
if (instance === doc.documentElement) {
return [
{
x: 0,
y: 0,
width: instance.scrollWidth,
height: instance.scrollHeight,
},
];
}
const result: Array<Rect> = [];
const win = doc && doc.defaultView;
const scrollX = win ? win.scrollX : 0;
const scrollY = win ? win.scrollY : 0;
let rects;
if (instance.nodeType === 3) {
if (typeof doc.createRange !== 'function') {
return null;
}
const range = doc.createRange();
if (typeof range.getClientRects !== 'function') {
return null;
}
range.selectNodeContents(instance);
rects = range.getClientRects();
} else {
rects = instance.getClientRects();
}
for (let i = 0; i < rects.length; i++) {
const rect = rects[i];
result.push({
x: rect.x + scrollX,
y: rect.y + scrollY,
width: rect.width,
height: rect.height,
});
}
return result;
}
if (instance.canonical) {
const publicInstance = instance.canonical.publicInstance;
if (!publicInstance) {
return null;
}
if (typeof publicInstance.getBoundingClientRect === 'function') {
return [publicInstance.getBoundingClientRect()];
}
if (typeof publicInstance.unstable_getBoundingClientRect === 'function') {
return [publicInstance.unstable_getBoundingClientRect()];
}
}
return null;
}
function measureInstance(instance: DevToolsInstance): null | Array<Rect> {
const hostInstances = findAllCurrentHostInstances(instance);
let result: null | Array<Rect> = null;
for (let i = 0; i < hostInstances.length; i++) {
const childResult = measureHostInstance(hostInstances[i]);
if (childResult !== null) {
if (result === null) {
result = childResult;
} else {
result = result.concat(childResult);
}
}
}
return result;
}
function getStringID(string: string | null): number {
if (string === null) {
return 0;
}
const existingEntry = pendingStringTable.get(string);
if (existingEntry !== undefined) {
return existingEntry.id;
}
const id = pendingStringTable.size + 1;
const encodedString = utfEncodeString(string);
pendingStringTable.set(string, {
encodedString,
id,
});
pendingStringTableLength += encodedString.length + 1;
return id;
}
let isInDisconnectedSubtree = false;
function recordMount(
fiber: Fiber,
parentInstance: DevToolsInstance | null,
): FiberInstance {
const isRoot = fiber.tag === HostRoot;
let fiberInstance;
if (isRoot) {
const entry = rootToFiberInstanceMap.get(fiber.stateNode);
if (entry === undefined) {
throw new Error('The root should have been registered at this point');
}
fiberInstance = entry;
} else {
fiberInstance = createFiberInstance(fiber);
}
idToDevToolsInstanceMap.set(fiberInstance.id, fiberInstance);
if (__DEBUG__) {
debug('recordMount()', fiberInstance, parentInstance);
}
recordReconnect(fiberInstance, parentInstance);
return fiberInstance;
}
function recordReconnect(
fiberInstance: FiberInstance,
parentInstance: DevToolsInstance | null,
): void {
if (isInDisconnectedSubtree) {
return;
}
const id = fiberInstance.id;
const fiber = fiberInstance.data;
const isProfilingSupported = fiber.hasOwnProperty('treeBaseDuration');
const isRoot = fiber.tag === HostRoot;
if (isRoot) {
const hasOwnerMetadata = fiber.hasOwnProperty('_debugOwner');
let profilingFlags = 0;
if (isProfilingSupported) {
profilingFlags = PROFILING_FLAG_BASIC_SUPPORT;
if (typeof injectProfilingHooks === 'function') {
profilingFlags |= PROFILING_FLAG_TIMELINE_SUPPORT;
}
if (supportsPerformanceTracks) {
profilingFlags |= PROFILING_FLAG_PERFORMANCE_TRACKS_SUPPORT;
}
}
const isProductionBuildOfRenderer = renderer.bundleType === 0;
pushOperation(TREE_OPERATION_ADD);
pushOperation(id);
pushOperation(ElementTypeRoot);
pushOperation((fiber.mode & StrictModeBits) !== 0 ? 1 : 0);
pushOperation(profilingFlags);
pushOperation(
!isProductionBuildOfRenderer && StrictModeBits !== 0 ? 1 : 0,
);
pushOperation(hasOwnerMetadata ? 1 : 0);
if (isProfiling) {
if (displayNamesByRootID !== null) {
displayNamesByRootID.set(id, getDisplayNameForRoot(fiber));
}
}
} else {
const suspenseNode = fiberInstance.suspenseNode;
if (suspenseNode !== null && fiber.memoizedState === null) {
const prevRects = suspenseNode.rects;
const nextRects = measureInstance(fiberInstance);
if (!areEqualRects(prevRects, nextRects)) {
suspenseNode.rects = nextRects;
recordSuspenseResize(suspenseNode);
}
}
const {key} = fiber;
const displayName = getDisplayNameForFiber(fiber);
const elementType = getElementTypeForFiber(fiber);
const debugOwner = getUnfilteredOwner(fiber);
const ownerInstance = findNearestOwnerInstance(
parentInstance,
debugOwner,
);
if (
ownerInstance !== null &&
debugOwner === fiber._debugOwner &&
fiber._debugStack != null &&
ownerInstance.source === null
) {
ownerInstance.source = fiber._debugStack;
}
let unfilteredParent = parentInstance;
while (
unfilteredParent !== null &&
unfilteredParent.kind === FILTERED_FIBER_INSTANCE
) {
unfilteredParent = unfilteredParent.parent;
}
const ownerID = ownerInstance === null ? 0 : ownerInstance.id;
const parentID = unfilteredParent === null ? 0 : unfilteredParent.id;
const displayNameStringID = getStringID(displayName);
const keyString = key === null ? null : String(key);
const keyStringID = getStringID(keyString);
const nameProp =
fiber.tag === SuspenseComponent
? fiber.memoizedProps.name
: fiber.tag === ActivityComponent
? fiber.memoizedProps.name
: null;
const namePropString = nameProp == null ? null : String(nameProp);
const namePropStringID = getStringID(namePropString);
pushOperation(TREE_OPERATION_ADD);
pushOperation(id);
pushOperation(elementType);
pushOperation(parentID);
pushOperation(ownerID);
pushOperation(displayNameStringID);
pushOperation(keyStringID);
pushOperation(namePropStringID);
if ((fiber.mode & StrictModeBits) !== 0) {
let parentFiber = null;
let parentFiberInstance = parentInstance;
while (parentFiberInstance !== null) {
if (parentFiberInstance.kind === FIBER_INSTANCE) {
parentFiber = parentFiberInstance.data;
break;
}
parentFiberInstance = parentFiberInstance.parent;
}
if (parentFiber === null || (parentFiber.mode & StrictModeBits) === 0) {
pushOperation(TREE_OPERATION_SET_SUBTREE_MODE);
pushOperation(id);
pushOperation(StrictMode);
}
}
}
let componentLogsEntry = fiberToComponentLogsMap.get(fiber);
if (componentLogsEntry === undefined && fiber.alternate !== null) {
componentLogsEntry = fiberToComponentLogsMap.get(fiber.alternate);
}
recordConsoleLogs(fiberInstance, componentLogsEntry);
if (isProfilingSupported) {
recordProfilingDurations(fiberInstance, null);
}
}
function recordVirtualMount(
instance: VirtualInstance,
parentInstance: DevToolsInstance | null,
secondaryEnv: null | string,
): void {
const id = instance.id;
idToDevToolsInstanceMap.set(id, instance);
recordVirtualReconnect(instance, parentInstance, secondaryEnv);
}
function recordVirtualReconnect(
instance: VirtualInstance,
parentInstance: DevToolsInstance | null,
secondaryEnv: null | string,
): void {
if (isInDisconnectedSubtree) {
return;
}
const componentInfo = instance.data;
const key =
typeof componentInfo.key === 'string' ? componentInfo.key : null;
const env = componentInfo.env;
let displayName = componentInfo.name || '';
if (typeof env === 'string') {
if (secondaryEnv !== null) {
displayName = secondaryEnv + '(' + displayName + ')';
}
displayName = env + '(' + displayName + ')';
}
const elementType = ElementTypeVirtual;
const debugOwner = getUnfilteredOwner(componentInfo);
const ownerInstance = findNearestOwnerInstance(parentInstance, debugOwner);
if (
ownerInstance !== null &&
debugOwner === componentInfo.owner &&
componentInfo.debugStack != null &&
ownerInstance.source === null
) {
ownerInstance.source = componentInfo.debugStack;
}
let unfilteredParent = parentInstance;
while (
unfilteredParent !== null &&
unfilteredParent.kind === FILTERED_FIBER_INSTANCE
) {
unfilteredParent = unfilteredParent.parent;
}
const ownerID = ownerInstance === null ? 0 : ownerInstance.id;
const parentID = unfilteredParent === null ? 0 : unfilteredParent.id;
const displayNameStringID = getStringID(displayName);
const keyString = key === null ? null : String(key);
const keyStringID = getStringID(keyString);
const namePropStringID = getStringID(null);
const id = instance.id;
pushOperation(TREE_OPERATION_ADD);
pushOperation(id);
pushOperation(elementType);
pushOperation(parentID);
pushOperation(ownerID);
pushOperation(displayNameStringID);
pushOperation(keyStringID);
pushOperation(namePropStringID);
const componentLogsEntry =
componentInfoToComponentLogsMap.get(componentInfo);
recordConsoleLogs(instance, componentLogsEntry);
}
function recordSuspenseMount(
suspenseInstance: SuspenseNode,
parentSuspenseInstance: SuspenseNode | null,
): void {
const fiberInstance = suspenseInstance.instance;
if (fiberInstance.kind === FILTERED_FIBER_INSTANCE) {
throw new Error('Cannot record a mount for a filtered Fiber instance.');
}
const fiberID = fiberInstance.id;
let unfilteredParent = parentSuspenseInstance;
while (
unfilteredParent !== null &&
unfilteredParent.instance.kind === FILTERED_FIBER_INSTANCE
) {
unfilteredParent = unfilteredParent.parent;
}
const unfilteredParentInstance =
unfilteredParent !== null ? unfilteredParent.instance : null;
if (
unfilteredParentInstance !== null &&
unfilteredParentInstance.kind === FILTERED_FIBER_INSTANCE
) {
throw new Error(
'Should not have a filtered instance at this point. This is a bug.',
);
}
const parentID =
unfilteredParentInstance === null ? 0 : unfilteredParentInstance.id;
const fiber = fiberInstance.data;
const props = fiber.memoizedProps;
const name =
fiber.tag !== SuspenseComponent || props === null
? null
: props.name || null;
const nameStringID = getStringID(name);
const isSuspended =
fiber.tag === SuspenseComponent && fiber.memoizedState !== null;
if (__DEBUG__) {
console.log('recordSuspenseMount()', suspenseInstance);
}
idToSuspenseNodeMap.set(fiberID, suspenseInstance);
pushOperation(SUSPENSE_TREE_OPERATION_ADD);
pushOperation(fiberID);
pushOperation(parentID);
pushOperation(nameStringID);
pushOperation(isSuspended ? 1 : 0);
const rects = suspenseInstance.rects;
if (rects === null) {
pushOperation(-1);
} else {
pushOperation(rects.length);
for (let i = 0; i < rects.length; ++i) {
const rect = rects[i];
pushOperation(Math.round(rect.x * 1000));
pushOperation(Math.round(rect.y * 1000));
pushOperation(Math.round(rect.width * 1000));
pushOperation(Math.round(rect.height * 1000));
}
}
}
function recordUnmount(fiberInstance: FiberInstance): void {
if (__DEBUG__) {
debug('recordUnmount()', fiberInstance, reconcilingParent);
}
recordDisconnect(fiberInstance);
const suspenseNode = fiberInstance.suspenseNode;
if (suspenseNode !== null) {
recordSuspenseUnmount(suspenseNode);
}
idToDevToolsInstanceMap.delete(fiberInstance.id);
untrackFiber(fiberInstance, fiberInstance.data);
}
function recordDisconnect(fiberInstance: FiberInstance): void {
if (isInDisconnectedSubtree) {
return;
}
if (trackedPathMatchInstance === fiberInstance) {
setTrackedPath(null);
}
const id = fiberInstance.id;
pendingRealUnmountedIDs.push(id);
}
function recordSuspenseResize(suspenseNode: SuspenseNode): void {
if (__DEBUG__) {
console.log('recordSuspenseResize()', suspenseNode);
}
const fiberInstance = suspenseNode.instance;
if (fiberInstance.kind !== FIBER_INSTANCE) {
return;
}
pushOperation(SUSPENSE_TREE_OPERATION_RESIZE);
pushOperation(fiberInstance.id);
const rects = suspenseNode.rects;
if (rects === null) {
pushOperation(-1);
} else {
pushOperation(rects.length);
for (let i = 0; i < rects.length; ++i) {
const rect = rects[i];
pushOperation(Math.round(rect.x * 1000));
pushOperation(Math.round(rect.y * 1000));
pushOperation(Math.round(rect.width * 1000));
pushOperation(Math.round(rect.height * 1000));
}
}
}
function recordSuspenseSuspenders(suspenseNode: SuspenseNode): void {
if (__DEBUG__) {
console.log('recordSuspenseSuspenders()', suspenseNode);
}
const fiberInstance = suspenseNode.instance;
if (fiberInstance.kind !== FIBER_INSTANCE) {
return;
}
suspenseNode.environments.forEach((count, env) => {
getStringID(env);
});
pendingSuspenderChanges.add(fiberInstance.id);
}
function recordSuspenseUnmount(suspenseInstance: SuspenseNode): void {
if (__DEBUG__) {
console.log(
'recordSuspenseUnmount()',
suspenseInstance,
reconcilingParentSuspenseNode,
);
}
const devtoolsInstance = suspenseInstance.instance;
if (devtoolsInstance.kind !== FIBER_INSTANCE) {
throw new Error("Can't unmount a filtered SuspenseNode. This is a bug.");
}
const fiberInstance = devtoolsInstance;
const id = fiberInstance.id;
pendingRealUnmountedSuspenseIDs.push(id);
pendingSuspenderChanges.delete(id);
idToSuspenseNodeMap.delete(id);
}
let remainingReconcilingChildren: null | DevToolsInstance = null;
let previouslyReconciledSibling: null | DevToolsInstance = null;
let reconcilingParent: null | DevToolsInstance = null;
let remainingReconcilingChildrenSuspenseNodes: null | SuspenseNode = null;
let previouslyReconciledSiblingSuspenseNode: null | SuspenseNode = null;
let reconcilingParentSuspenseNode: null | SuspenseNode = null;
function ioExistsInSuspenseAncestor(
suspenseNode: SuspenseNode,
ioInfo: ReactIOInfo,
): boolean {
let ancestor = suspenseNode.parent;
while (ancestor !== null) {
if (ancestor.suspendedBy.has(ioInfo)) {
return true;
}
ancestor = ancestor.parent;
}
return false;
}
function insertSuspendedBy(asyncInfo: ReactAsyncInfo): void {
if (reconcilingParent === null || reconcilingParentSuspenseNode === null) {
throw new Error(
'It should not be possible to have suspended data outside the root. ' +
'Even suspending at the first position is still a child of the root.',
);
}
const parentSuspenseNode = reconcilingParentSuspenseNode;
let parentInstance = reconcilingParent;
while (
parentInstance.kind === FILTERED_FIBER_INSTANCE &&
parentInstance.parent !== null &&
parentInstance !== parentSuspenseNode.instance
) {
parentInstance = parentInstance.parent;
}
if (parentInstance.kind === FIBER_INSTANCE) {
const fiber = parentInstance.data;
if (
fiber.tag === SuspenseComponent &&
parentInstance !== parentSuspenseNode.instance
) {
const parent = parentInstance.parent;
if (parent === null) {
throw new Error(
'Did not find a suitable instance for this async info. This is a bug in React.',
);
}
parentInstance = parent;
}
}
const suspenseNodeSuspendedBy = parentSuspenseNode.suspendedBy;
const ioInfo = asyncInfo.awaited;
let suspendedBySet = suspenseNodeSuspendedBy.get(ioInfo);
if (suspendedBySet === undefined) {
suspendedBySet = new Set();
suspenseNodeSuspendedBy.set(ioInfo, suspendedBySet);
const env = ioInfo.env;
if (env != null) {
const environmentCounts = parentSuspenseNode.environments;
const count = environmentCounts.get(env);
if (count === undefined || count === 0) {
environmentCounts.set(env, 1);
recordSuspenseSuspenders(parentSuspenseNode);
} else {
environmentCounts.set(env, count + 1);
}
}
}
if (!suspendedBySet.has(parentInstance)) {
suspendedBySet.add(parentInstance);
const virtualEndTime = getVirtualEndTime(ioInfo);
if (
!parentSuspenseNode.hasUniqueSuspenders &&
!ioExistsInSuspenseAncestor(parentSuspenseNode, ioInfo)
) {
parentSuspenseNode.hasUniqueSuspenders = true;
if (parentSuspenseNode.endTime < virtualEndTime) {
parentSuspenseNode.endTime = virtualEndTime;
}
recordSuspenseSuspenders(parentSuspenseNode);
} else if (parentSuspenseNode.endTime < virtualEndTime) {
parentSuspenseNode.endTime = virtualEndTime;
recordSuspenseSuspenders(parentSuspenseNode);
}
}
parentSuspenseNode.hasUnknownSuspenders = false;
const suspendedBy = parentInstance.suspendedBy;
if (suspendedBy === null) {
parentInstance.suspendedBy = [asyncInfo];
} else if (suspendedBy.indexOf(asyncInfo) === -1) {
suspendedBy.push(asyncInfo);
}
}
function getAwaitInSuspendedByFromIO(
suspensedBy: Array<ReactAsyncInfo>,
ioInfo: ReactIOInfo,
): null | ReactAsyncInfo {
for (let i = 0; i < suspensedBy.length; i++) {
const asyncInfo = suspensedBy[i];
if (asyncInfo.awaited === ioInfo) {
return asyncInfo;
}
}
return null;
}
function unblockSuspendedBy(
parentSuspenseNode: SuspenseNode,
ioInfo: ReactIOInfo,
): void {
const firstChild = parentSuspenseNode.firstChild;
if (firstChild === null) {
return;
}
let node: SuspenseNode = firstChild;
while (node !== null) {
if (node.suspendedBy.has(ioInfo)) {
if (!node.hasUniqueSuspenders) {
recordSuspenseSuspenders(node);
}
node.hasUniqueSuspenders = true;
node.hasUnknownSuspenders = false;
} else if (node.firstChild !== null) {
node = node.firstChild;
continue;
}
while (node.nextSibling === null) {
if (node.parent === null || node.parent === parentSuspenseNode) {
return;
}
node = node.parent;
}
node = node.nextSibling;
}
}
function getVirtualEndTime(ioInfo: ReactIOInfo): number {
if (ioInfo.env != null) {
return ioInfo.end + 1000000;
}
return ioInfo.end;
}
function computeEndTime(suspenseNode: SuspenseNode) {
let maxEndTime = 0;
suspenseNode.suspendedBy.forEach((set, ioInfo) => {
const virtualEndTime = getVirtualEndTime(ioInfo);
if (virtualEndTime > maxEndTime) {
maxEndTime = virtualEndTime;
}
});
return maxEndTime;
}
function removePreviousSuspendedBy(
instance: DevToolsInstance,
previousSuspendedBy: null | Array<ReactAsyncInfo>,
parentSuspenseNode: null | SuspenseNode,
): void {
const suspenseNode =
instance.suspenseNode === null
? parentSuspenseNode
: instance.suspenseNode;
if (previousSuspendedBy !== null && suspenseNode !== null) {
const nextSuspendedBy = instance.suspendedBy;
let changedEnvironment = false;
let mayHaveChangedEndTime = false;
for (let i = 0; i < previousSuspendedBy.length; i++) {
const asyncInfo = previousSuspendedBy[i];
if (
nextSuspendedBy === null ||
(nextSuspendedBy.indexOf(asyncInfo) === -1 &&
getAwaitInSuspendedByFromIO(nextSuspendedBy, asyncInfo.awaited) ===
null)
) {
const ioInfo = asyncInfo.awaited;
const suspendedBySet = suspenseNode.suspendedBy.get(ioInfo);
if (suspenseNode.endTime === getVirtualEndTime(ioInfo)) {
mayHaveChangedEndTime = true;
}
if (
suspendedBySet === undefined ||
!suspendedBySet.delete(instance)
) {
let alreadyRemovedIO = false;
for (let j = 0; j < i; j++) {
const removedIOInfo = previousSuspendedBy[j].awaited;
if (removedIOInfo === ioInfo) {
alreadyRemovedIO = true;
break;
}
}
if (!alreadyRemovedIO) {
throw new Error(
'We are cleaning up async info that was not on the parent Suspense boundary. ' +
'This is a bug in React.',
);
}
}
if (suspendedBySet !== undefined && suspendedBySet.size === 0) {
suspenseNode.suspendedBy.delete(ioInfo);
const env = ioInfo.env;
if (env != null) {
const environmentCounts = suspenseNode.environments;
const count = environmentCounts.get(env);
if (count === undefined || count === 0) {
throw new Error(
'We are removing an environment but it was not in the set. ' +
'This is a bug in React.',
);
}
if (count === 1) {
environmentCounts.delete(env);
changedEnvironment = true;
} else {
environmentCounts.set(env, count - 1);
}
}
}
if (
suspenseNode.hasUniqueSuspenders &&
!ioExistsInSuspenseAncestor(suspenseNode, ioInfo)
) {
unblockSuspendedBy(suspenseNode, ioInfo);
}
}
}
const newEndTime = mayHaveChangedEndTime
? computeEndTime(suspenseNode)
: suspenseNode.endTime;
if (changedEnvironment || newEndTime !== suspenseNode.endTime) {
suspenseNode.endTime = newEndTime;
recordSuspenseSuspenders(suspenseNode);
}
}
}
function insertChild(instance: DevToolsInstance): void {
const parentInstance = reconcilingParent;
if (parentInstance === null) {
return;
}
instance.parent = parentInstance;
if (previouslyReconciledSibling === null) {
previouslyReconciledSibling = instance;
parentInstance.firstChild = instance;
} else {
previouslyReconciledSibling.nextSibling = instance;
previouslyReconciledSibling = instance;
}
instance.nextSibling = null;
const suspenseNode = instance.suspenseNode;
if (suspenseNode !== null) {
const parentNode = reconcilingParentSuspenseNode;
if (parentNode !== null) {
suspenseNode.parent = parentNode;
if (previouslyReconciledSiblingSuspenseNode === null) {
previouslyReconciledSiblingSuspenseNode = suspenseNode;
parentNode.firstChild = suspenseNode;
} else {
previouslyReconciledSiblingSuspenseNode.nextSibling = suspenseNode;
previouslyReconciledSiblingSuspenseNode = suspenseNode;
}
suspenseNode.nextSibling = null;
}
}
}
function moveChild(
instance: DevToolsInstance,
previousSibling: null | DevToolsInstance,
): void {
removeChild(instance, previousSibling);
insertChild(instance);
}
function removeChild(
instance: DevToolsInstance,
previousSibling: null | DevToolsInstance,
): void {
if (instance.parent === null) {
if (remainingReconcilingChildren === instance) {
throw new Error(
'Remaining children should not have items with no parent',
);
} else if (instance.nextSibling !== null) {
throw new Error('A deleted instance should not have next siblings');
}
return;
}
const parentInstance = reconcilingParent;
if (parentInstance === null) {
throw new Error('Should not have a parent if we are at the root');
}
if (instance.parent !== parentInstance) {
throw new Error(
'Cannot remove a node from a different parent than is being reconciled.',
);
}
if (previousSibling === null) {
if (remainingReconcilingChildren !== instance) {
throw new Error(
'Expected a placed child to be moved from the remaining set.',
);
}
remainingReconcilingChildren = instance.nextSibling;
} else {
previousSibling.nextSibling = instance.nextSibling;
}
instance.nextSibling = null;
instance.parent = null;
const suspenseNode = instance.suspenseNode;
if (suspenseNode !== null && suspenseNode.parent !== null) {
const parentNode = reconcilingParentSuspenseNode;
if (parentNode === null) {
throw new Error('Should not have a parent if we are at the root');
}
if (suspenseNode.parent !== parentNode) {
throw new Error(
'Cannot remove a Suspense node from a different parent than is being reconciled.',
);
}
let previousSuspenseSibling = remainingReconcilingChildrenSuspenseNodes;
if (previousSuspenseSibling === suspenseNode) {
remainingReconcilingChildrenSuspenseNodes = suspenseNode.nextSibling;
} else {
while (previousSuspenseSibling !== null) {
if (previousSuspenseSibling.nextSibling === suspenseNode) {
previousSuspenseSibling.nextSibling = suspenseNode.nextSibling;
break;
}
previousSuspenseSibling = previousSuspenseSibling.nextSibling;
}
}
suspenseNode.nextSibling = null;
suspenseNode.parent = null;
}
}
function isHiddenOffscreen(fiber: Fiber): boolean {
switch (fiber.tag) {
case LegacyHiddenComponent:
case OffscreenComponent:
return fiber.memoizedState !== null;
default:
return false;
}
}
function isSuspendedOffscreen(fiber: Fiber): boolean {
switch (fiber.tag) {
case LegacyHiddenComponent:
case OffscreenComponent:
return (
fiber.memoizedState !== null &&
fiber.return !== null &&
fiber.return.tag === SuspenseComponent
);
default:
return false;
}
}
function unmountRemainingChildren() {
if (
reconcilingParent !== null &&
(reconcilingParent.kind === FIBER_INSTANCE ||
reconcilingParent.kind === FILTERED_FIBER_INSTANCE) &&
isSuspendedOffscreen(reconcilingParent.data) &&
!isInDisconnectedSubtree
) {
isInDisconnectedSubtree = true;
try {
let child = remainingReconcilingChildren;
while (child !== null) {
unmountInstanceRecursively(child);
child = remainingReconcilingChildren;
}
} finally {
isInDisconnectedSubtree = false;
}
} else {
let child = remainingReconcilingChildren;
while (child !== null) {
unmountInstanceRecursively(child);
child = remainingReconcilingChildren;
}
}
}
function unmountSuspenseChildrenRecursively(
contentInstance: DevToolsInstance,
stashedSuspenseParent: null | SuspenseNode,
stashedSuspensePrevious: null | SuspenseNode,
stashedSuspenseRemaining: null | SuspenseNode,
): void {
unmountInstanceRecursively(contentInstance);
reconcilingParentSuspenseNode = stashedSuspenseParent;
previouslyReconciledSiblingSuspenseNode = stashedSuspensePrevious;
remainingReconcilingChildrenSuspenseNodes = stashedSuspenseRemaining;
unmountRemainingChildren();
}
function isChildOf(
parentInstance: DevToolsInstance,
childInstance: DevToolsInstance,
grandParent: DevToolsInstance,
): boolean {
let instance = childInstance.parent;
while (instance !== null) {
if (parentInstance === instance) {
return true;
}
if (instance === parentInstance.parent || instance === grandParent) {
break;
}
instance = instance.parent;
}
return false;
}
function areEqualRects(
a: null | Array<Rect>,
b: null | Array<Rect>,
): boolean {
if (a === null) {
return b === null;
}
if (b === null) {
return false;
}
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
const aRect = a[i];
const bRect = b[i];
if (
aRect.x !== bRect.x ||
aRect.y !== bRect.y ||
aRect.width !== bRect.width ||
aRect.height !== bRect.height
) {
return false;
}
}
return true;
}
function measureUnchangedSuspenseNodesRecursively(
suspenseNode: SuspenseNode,
): void {
if (isInDisconnectedSubtree) {
return;
}
const instance = suspenseNode.instance;
const isSuspendedSuspenseComponent =
(instance.kind === FIBER_INSTANCE ||
instance.kind === FILTERED_FIBER_INSTANCE) &&
instance.data.tag === SuspenseComponent &&
instance.data.memoizedState !== null;
if (isSuspendedSuspenseComponent) {
return;
}
let parent = instance.parent;
while (parent !== null) {
if (
(parent.kind === FIBER_INSTANCE ||
parent.kind === FILTERED_FIBER_INSTANCE) &&
isHiddenOffscreen(parent.data)
) {
return;
}
if (parent.suspenseNode !== null) {
break;
}
parent = parent.parent;
}
const nextRects = measureInstance(suspenseNode.instance);
const prevRects = suspenseNode.rects;
if (areEqualRects(prevRects, nextRects)) {
return;
}
for (
let child = suspenseNode.firstChild;
child !== null;
child = child.nextSibling
) {
measureUnchangedSuspenseNodesRecursively(child);
}
suspenseNode.rects = nextRects;
recordSuspenseResize(suspenseNode);
}
function consumeSuspenseNodesOfExistingInstance(
instance: DevToolsInstance,
): void {
let suspenseNode = remainingReconcilingChildrenSuspenseNodes;
if (suspenseNode === null) {
return;
}
const parentSuspenseNode = reconcilingParentSuspenseNode;
if (parentSuspenseNode === null) {
throw new Error(
'The should not be any remaining suspense node children if there is no parent.',
);
}
let foundOne = false;
let previousSkippedSibling = null;
while (suspenseNode !== null) {
if (
isChildOf(instance, suspenseNode.instance, parentSuspenseNode.instance)
) {
foundOne = true;
const nextRemainingSibling = suspenseNode.nextSibling;
if (previousSkippedSibling === null) {
remainingReconcilingChildrenSuspenseNodes = nextRemainingSibling;
} else {
previousSkippedSibling.nextSibling = nextRemainingSibling;
}
suspenseNode.nextSibling = null;
if (previouslyReconciledSiblingSuspenseNode === null) {
parentSuspenseNode.firstChild = suspenseNode;
} else {
previouslyReconciledSiblingSuspenseNode.nextSibling = suspenseNode;
}
previouslyReconciledSiblingSuspenseNode = suspenseNode;
measureUnchangedSuspenseNodesRecursively(suspenseNode);
suspenseNode = nextRemainingSibling;
} else if (foundOne) {
break;
} else {
previousSkippedSibling = suspenseNode;
suspenseNode = suspenseNode.nextSibling;
}
}
}
function mountVirtualInstanceRecursively(
virtualInstance: VirtualInstance,
firstChild: Fiber,
lastChild: null | Fiber,
traceNearestHostComponentUpdate: boolean,
virtualLevel: number,
): void {
const mightSiblingsBeOnTrackedPath =
updateVirtualTrackedPathStateBeforeMount(
virtualInstance,
reconcilingParent,
);
const stashedParent = reconcilingParent;
const stashedPrevious = previouslyReconciledSibling;
const stashedRemaining = remainingReconcilingChildren;
reconcilingParent = virtualInstance;
previouslyReconciledSibling = null;
remainingReconcilingChildren = null;
try {
mountVirtualChildrenRecursively(
firstChild,
lastChild,
traceNearestHostComponentUpdate,
virtualLevel + 1,
);
recordVirtualProfilingDurations(virtualInstance);
} finally {
reconcilingParent = stashedParent;
previouslyReconciledSibling = stashedPrevious;
remainingReconcilingChildren = stashedRemaining;
updateTrackedPathStateAfterMount(mightSiblingsBeOnTrackedPath);
}
}
function recordVirtualUnmount(instance: VirtualInstance) {
recordVirtualDisconnect(instance);
idToDevToolsInstanceMap.delete(instance.id);
}
function recordVirtualDisconnect(instance: VirtualInstance) {
if (isInDisconnectedSubtree) {
return;
}
if (trackedPathMatchInstance === instance) {
setTrackedPath(null);
}
const id = instance.id;
pendingRealUnmountedIDs.push(id);
}
function getSecondaryEnvironmentName(
debugInfo: ?ReactDebugInfo,
index: number,
): null | string {
if (debugInfo != null) {
const componentInfo: ReactComponentInfo = (debugInfo[index]: any);
for (let i = index + 1; i < debugInfo.length; i++) {
const debugEntry = debugInfo[i];
if (typeof debugEntry.env === 'string') {
return componentInfo.env !== debugEntry.env ? debugEntry.env : null;
}
}
}
return null;
}
function trackDebugInfoFromLazyType(fiber: Fiber): void {
const type = fiber.elementType;
const typeSymbol = getTypeSymbol(type);
if (typeSymbol === LAZY_SYMBOL_STRING) {
const debugInfo: ?ReactDebugInfo = type._debugInfo;
if (debugInfo) {
for (let i = 0; i < debugInfo.length; i++) {
const debugEntry = debugInfo[i];
if (debugEntry.awaited) {
const asyncInfo: ReactAsyncInfo = (debugEntry: any);
insertSuspendedBy(asyncInfo);
}
}
}
}
}
function trackDebugInfoFromUsedThenables(fiber: Fiber): void {
const dependencies = fiber.dependencies;
if (dependencies == null) {
return;
}
const thenableState = dependencies._debugThenableState;
if (thenableState == null) {
return;
}
const usedThenables: any = thenableState.thenables || thenableState;
if (!Array.isArray(usedThenables)) {
return;
}
for (let i = 0; i < usedThenables.length; i++) {
const thenable: Thenable<mixed> = usedThenables[i];
const debugInfo = thenable._debugInfo;
if (debugInfo) {
for (let j = 0; j < debugInfo.length; j++) {
const debugEntry = debugInfo[j];
if (debugEntry.awaited) {
const asyncInfo: ReactAsyncInfo = (debugEntry: any);
insertSuspendedBy(asyncInfo);
}
}
}
}
}
const hostAsyncInfoCache: WeakMap<{...}, ReactAsyncInfo> = new WeakMap();
function trackDebugInfoFromHostResource(
devtoolsInstance: DevToolsInstance,
fiber: Fiber,
): void {
const resource: ?{
type: 'stylesheet' | 'style' | 'script' | 'void',
instance?: null | HostInstance,
...
} = fiber.memoizedState;
if (resource == null) {
return;
}
const existingEntry = hostAsyncInfoCache.get(resource);
if (existingEntry !== undefined) {
insertSuspendedBy(existingEntry);
return;
}
const props: {
href?: string,
media?: string,
...
} = fiber.memoizedProps;
const mayResourceSuspendCommit =
resource.type === 'stylesheet' &&
(typeof props.media !== 'string' ||
typeof matchMedia !== 'function' ||
matchMedia(props.media));
if (!mayResourceSuspendCommit) {
return;
}
const instance = resource.instance;
if (instance == null) {
return;
}
const href = instance.href;
if (typeof href !== 'string') {
return;
}
let start = -1;
let end = -1;
let byteSize = 0;
if (typeof performance.getEntriesByType === 'function') {
const resourceEntries = performance.getEntriesByType('resource');
for (let i = 0; i < resourceEntries.length; i++) {
const resourceEntry = resourceEntries[i];
if (resourceEntry.name === href) {
start = resourceEntry.startTime;
end = start + resourceEntry.duration;
byteSize = (resourceEntry.transferSize: any) || 0;
}
}
}
const value = instance.sheet;
const promise = Promise.resolve(value);
(promise: any).status = 'fulfilled';
(promise: any).value = value;
const ioInfo: ReactIOInfo = {
name: 'stylesheet',
start,
end,
value: promise,
owner: fiber,
};
if (byteSize > 0) {
ioInfo.byteSize = byteSize;
}
const asyncInfo: ReactAsyncInfo = {
awaited: ioInfo,
owner: fiber._debugOwner == null ? null : fiber._debugOwner,
debugStack: fiber._debugStack == null ? null : fiber._debugStack,
debugTask: fiber._debugTask == null ? null : fiber._debugTask,
};
hostAsyncInfoCache.set(resource, asyncInfo);
insertSuspendedBy(asyncInfo);
}
function trackDebugInfoFromHostComponent(
devtoolsInstance: DevToolsInstance,
fiber: Fiber,
): void {
if (fiber.tag !== HostComponent) {
return;
}
if ((fiber.mode & SuspenseyImagesMode) === 0) {
return;
}
const type = fiber.type;
const props: {
src?: string,
onLoad?: (event: any) => void,
loading?: 'eager' | 'lazy',
...
} = fiber.memoizedProps;
const maySuspendCommit =
type === 'img' &&
props.src != null &&
props.src !== '' &&
props.onLoad == null &&
props.loading !== 'lazy';
if (!maySuspendCommit) {
return;
}
const instance = fiber.stateNode;
if (instance == null) {
return;
}
const src = instance.currentSrc;
if (typeof src !== 'string' || src === '') {
return;
}
let start = -1;
let end = -1;
let byteSize = 0;
let fileSize = 0;
if (typeof performance.getEntriesByType === 'function') {
const resourceEntries = performance.getEntriesByType('resource');
for (let i = 0; i < resourceEntries.length; i++) {
const resourceEntry = resourceEntries[i];
if (resourceEntry.name === src) {
start = resourceEntry.startTime;
end = start + resourceEntry.duration;
fileSize = (resourceEntry.decodedBodySize: any) || 0;
byteSize = (resourceEntry.transferSize: any) || 0;
}
}
}
const value: {
currentSrc: string,
naturalWidth?: number,
naturalHeight?: number,
fileSize?: number,
} = {
currentSrc: src,
};
if (instance.naturalWidth > 0 && instance.naturalHeight > 0) {
value.naturalWidth = instance.naturalWidth;
value.naturalHeight = instance.naturalHeight;
}
if (fileSize > 0) {
value.fileSize = fileSize;
}
const promise = Promise.resolve(value);
(promise: any).status = 'fulfilled';
(promise: any).value = value;
const ioInfo: ReactIOInfo = {
name: 'img',
start,
end,
value: promise,
owner: fiber,
};
if (byteSize > 0) {
ioInfo.byteSize = byteSize;
}
const asyncInfo: ReactAsyncInfo = {
awaited: ioInfo,
owner: fiber._debugOwner == null ? null : fiber._debugOwner,
debugStack: fiber._debugStack == null ? null : fiber._debugStack,
debugTask: fiber._debugTask == null ? null : fiber._debugTask,
};
insertSuspendedBy(asyncInfo);
}
function trackThrownPromisesFromRetryCache(
suspenseNode: SuspenseNode,
retryCache: ?WeakSet<Wakeable>,
): void {
if (retryCache != null) {
if (!suspenseNode.hasUniqueSuspenders) {
recordSuspenseSuspenders(suspenseNode);
}
suspenseNode.hasUniqueSuspenders = true;
suspenseNode.hasUnknownSuspenders = true;
}
}
function mountVirtualChildrenRecursively(
firstChild: Fiber,
lastChild: null | Fiber,
traceNearestHostComponentUpdate: boolean,
virtualLevel: number,
): void {
let fiber: Fiber | null = firstChild;
let previousVirtualInstance: null | VirtualInstance = null;
let previousVirtualInstanceFirstFiber: Fiber = firstChild;
while (fiber !== null && fiber !== lastChild) {
let level = 0;
if (fiber._debugInfo) {
for (let i = 0; i < fiber._debugInfo.length; i++) {
const debugEntry = fiber._debugInfo[i];
if (debugEntry.awaited) {
const asyncInfo: ReactAsyncInfo = (debugEntry: any);
if (level === virtualLevel) {
insertSuspendedBy(asyncInfo);
}
continue;
}
if (typeof debugEntry.name !== 'string') {
continue;
}
const componentInfo: ReactComponentInfo = (debugEntry: any);
const secondaryEnv = getSecondaryEnvironmentName(fiber._debugInfo, i);
if (componentInfo.env != null) {
knownEnvironmentNames.add(componentInfo.env);
}
if (secondaryEnv !== null) {
knownEnvironmentNames.add(secondaryEnv);
}
if (shouldFilterVirtual(componentInfo, secondaryEnv)) {
continue;
}
if (level === virtualLevel) {
if (
previousVirtualInstance === null ||
previousVirtualInstance.data !== debugEntry
) {
if (previousVirtualInstance !== null) {
mountVirtualInstanceRecursively(
previousVirtualInstance,
previousVirtualInstanceFirstFiber,
fiber,
traceNearestHostComponentUpdate,
virtualLevel,
);
}
previousVirtualInstance = createVirtualInstance(componentInfo);
recordVirtualMount(
previousVirtualInstance,
reconcilingParent,
secondaryEnv,
);
insertChild(previousVirtualInstance);
previousVirtualInstanceFirstFiber = fiber;
}
level++;
break;
} else {
level++;
}
}
}
if (level === virtualLevel) {
if (previousVirtualInstance !== null) {
mountVirtualInstanceRecursively(
previousVirtualInstance,
previousVirtualInstanceFirstFiber,
fiber,
traceNearestHostComponentUpdate,
virtualLevel,
);
previousVirtualInstance = null;
}
mountFiberRecursively(fiber, traceNearestHostComponentUpdate);
}
fiber = fiber.sibling;
}
if (previousVirtualInstance !== null) {
mountVirtualInstanceRecursively(
previousVirtualInstance,
previousVirtualInstanceFirstFiber,
null,
traceNearestHostComponentUpdate,
virtualLevel,
);
}
}
function mountChildrenRecursively(
firstChild: Fiber,
traceNearestHostComponentUpdate: boolean,
): void {
mountVirtualChildrenRecursively(
firstChild,
null,
traceNearestHostComponentUpdate,
0,
);
}
function mountSuspenseChildrenRecursively(
contentFiber: Fiber,
traceNearestHostComponentUpdate: boolean,
stashedSuspenseParent: SuspenseNode | null,
stashedSuspensePrevious: SuspenseNode | null,
stashedSuspenseRemaining: SuspenseNode | null,
) {
const fallbackFiber = contentFiber.sibling;
mountVirtualChildrenRecursively(
contentFiber,
fallbackFiber,
traceNearestHostComponentUpdate,
0,
);
reconcilingParentSuspenseNode = stashedSuspenseParent;
previouslyReconciledSiblingSuspenseNode = stashedSuspensePrevious;
remainingReconcilingChildrenSuspenseNodes = stashedSuspenseRemaining;
if (fallbackFiber !== null) {
mountVirtualChildrenRecursively(
fallbackFiber,
null,
traceNearestHostComponentUpdate,
0,
);
}
}
function mountFiberRecursively(
fiber: Fiber,
traceNearestHostComponentUpdate: boolean,
): void {
const isFocusedActivityEntry =
focusedActivity !== null &&
(fiber === focusedActivity || fiber.alternate === focusedActivity);
if (isFocusedActivityEntry) {
isInFocusedActivity = true;
}
const shouldIncludeInTree = !shouldFilterFiber(fiber);
let newInstance = null;
let newSuspenseNode = null;
if (shouldIncludeInTree) {
newInstance = recordMount(fiber, reconcilingParent);
if (isFocusedActivityEntry) {
focusedActivityID = newInstance.id;
pushOperation(TREE_OPERATION_APPLIED_ACTIVITY_SLICE_CHANGE);
pushOperation(newInstance.id);
}
if (fiber.tag === SuspenseComponent || fiber.tag === HostRoot) {
newSuspenseNode = createSuspenseNode(newInstance);
if (fiber.tag === SuspenseComponent) {
if (OffscreenComponent === -1) {
const isTimedOut = fiber.memoizedState !== null;
if (!isTimedOut) {
newSuspenseNode.rects = measureInstance(newInstance);
}
} else {
const hydrated = isFiberHydrated(fiber);
if (hydrated) {
const contentFiber = fiber.child;
if (contentFiber === null) {
throw new Error(
'There should always be an Offscreen Fiber child in a hydrated Suspense boundary.',
);
}
} else {
}
const isTimedOut = fiber.memoizedState !== null;
if (!isTimedOut) {
newSuspenseNode.rects = measureInstance(newInstance);
}
}
} else {
newSuspenseNode.rects = measureInstance(newInstance);
}
recordSuspenseMount(newSuspenseNode, reconcilingParentSuspenseNode);
}
insertChild(newInstance);
if (__DEBUG__) {
debug('mountFiberRecursively()', newInstance, reconcilingParent);
}
} else if (
(reconcilingParent !== null &&
reconcilingParent.kind === VIRTUAL_INSTANCE) ||
fiber.tag === SuspenseComponent ||
fiber.tag === OffscreenComponent ||
fiber.tag === LegacyHiddenComponent
) {
if (
reconcilingParent !== null &&
reconcilingParent.kind === VIRTUAL_INSTANCE &&
reconcilingParent.data === fiber._debugOwner &&
fiber._debugStack != null &&
reconcilingParent.source === null
) {
reconcilingParent.source = fiber._debugStack;
}
newInstance = createFilteredFiberInstance(fiber);
if (fiber.tag === SuspenseComponent) {
newSuspenseNode = createSuspenseNode(newInstance);
if (OffscreenComponent === -1) {
const isTimedOut = fiber.memoizedState !== null;
if (!isTimedOut) {
newSuspenseNode.rects = measureInstance(newInstance);
}
} else {
const hydrated = isFiberHydrated(fiber);
if (hydrated) {
const contentFiber = fiber.child;
if (contentFiber === null) {
throw new Error(
'There should always be an Offscreen Fiber child in a hydrated Suspense boundary.',
);
}
} else {
}
const suspenseState = fiber.memoizedState;
const isTimedOut = suspenseState !== null;
if (!isTimedOut) {
newSuspenseNode.rects = measureInstance(newInstance);
}
}
}
insertChild(newInstance);
if (__DEBUG__) {
debug('mountFiberRecursively()', newInstance, reconcilingParent);
}
}
const mightSiblingsBeOnTrackedPath = updateTrackedPathStateBeforeMount(
fiber,
newInstance,
);
const stashedParent = reconcilingParent;
const stashedPrevious = previouslyReconciledSibling;
const stashedRemaining = remainingReconcilingChildren;
const stashedSuspenseParent = reconcilingParentSuspenseNode;
const stashedSuspensePrevious = previouslyReconciledSiblingSuspenseNode;
const stashedSuspenseRemaining = remainingReconcilingChildrenSuspenseNodes;
const stashedIsInActivitySlice = isInFocusedActivity;
if (newInstance !== null) {
reconcilingParent = newInstance;
previouslyReconciledSibling = null;
remainingReconcilingChildren = null;
}
let shouldPopSuspenseNode = false;
if (newSuspenseNode !== null) {
reconcilingParentSuspenseNode = newSuspenseNode;
previouslyReconciledSiblingSuspenseNode = null;
remainingReconcilingChildrenSuspenseNodes = null;
shouldPopSuspenseNode = true;
}
if (
!isFocusedActivityEntry &&
focusedActivity !== null &&
fiber.tag === ActivityComponent
) {
}
try {
if (traceUpdatesEnabled) {
if (traceNearestHostComponentUpdate) {
const elementType = getElementTypeForFiber(fiber);
if (elementType === ElementTypeHostComponent) {
traceUpdatesForNodes.add(fiber.stateNode);
traceNearestHostComponentUpdate = false;
}
}
}
trackDebugInfoFromLazyType(fiber);
trackDebugInfoFromUsedThenables(fiber);
if (fiber.tag === HostHoistable) {
const nearestInstance = reconcilingParent;
if (nearestInstance === null) {
throw new Error('Did not expect a host hoistable to be the root');
}
aquireHostResource(nearestInstance, fiber.memoizedState);
trackDebugInfoFromHostResource(nearestInstance, fiber);
} else if (
fiber.tag === HostComponent ||
fiber.tag === HostText ||
fiber.tag === HostSingleton
) {
const nearestInstance = reconcilingParent;
if (nearestInstance === null) {
throw new Error('Did not expect a host hoistable to be the root');
}
aquireHostInstance(nearestInstance, fiber.stateNode);
trackDebugInfoFromHostComponent(nearestInstance, fiber);
}
if (isSuspendedOffscreen(fiber)) {
const stashedDisconnected = isInDisconnectedSubtree;
isInDisconnectedSubtree = true;
try {
if (fiber.child !== null) {
mountChildrenRecursively(fiber.child, false);
}
} finally {
isInDisconnectedSubtree = stashedDisconnected;
}
} else if (isHiddenOffscreen(fiber)) {
} else if (fiber.tag === SuspenseComponent && OffscreenComponent === -1) {
if (newSuspenseNode !== null) {
trackThrownPromisesFromRetryCache(newSuspenseNode, fiber.stateNode);
}
const isTimedOut = fiber.memoizedState !== null;
if (isTimedOut) {
const primaryChildFragment = fiber.child;
const fallbackChildFragment = primaryChildFragment
? primaryChildFragment.sibling
: null;
if (fallbackChildFragment) {
const fallbackChild = fallbackChildFragment.child;
if (fallbackChild !== null) {
updateTrackedPathStateBeforeMount(fallbackChildFragment, null);
mountChildrenRecursively(
fallbackChild,
traceNearestHostComponentUpdate,
);
}
}
} else {
const primaryChild: Fiber | null = fiber.child;
if (primaryChild !== null) {
mountChildrenRecursively(
primaryChild,
traceNearestHostComponentUpdate,
);
}
}
} else if (
fiber.tag === SuspenseComponent &&
OffscreenComponent !== -1 &&
newInstance !== null &&
newSuspenseNode !== null
) {
const contentFiber = fiber.child;
const hydrated = isFiberHydrated(fiber);
if (hydrated) {
if (contentFiber === null) {
throw new Error(
'There should always be an Offscreen Fiber child in a hydrated Suspense boundary.',
);
}
trackThrownPromisesFromRetryCache(newSuspenseNode, fiber.stateNode);
mountSuspenseChildrenRecursively(
contentFiber,
traceNearestHostComponentUpdate,
stashedSuspenseParent,
stashedSuspensePrevious,
stashedSuspenseRemaining,
);
shouldPopSuspenseNode = false;
} else {
}
} else {
if (fiber.child !== null) {
mountChildrenRecursively(
fiber.child,
traceNearestHostComponentUpdate,
);
}
}
} finally {
isInFocusedActivity = stashedIsInActivitySlice;
if (newInstance !== null) {
reconcilingParent = stashedParent;
previouslyReconciledSibling = stashedPrevious;
remainingReconcilingChildren = stashedRemaining;
}
if (shouldPopSuspenseNode) {
reconcilingParentSuspenseNode = stashedSuspenseParent;
previouslyReconciledSiblingSuspenseNode = stashedSuspensePrevious;
remainingReconcilingChildrenSuspenseNodes = stashedSuspenseRemaining;
}
}
updateTrackedPathStateAfterMount(mightSiblingsBeOnTrackedPath);
}
function unmountInstanceRecursively(instance: DevToolsInstance) {
if (__DEBUG__) {
debug('unmountInstanceRecursively()', instance, reconcilingParent);
}
let shouldPopSuspenseNode = false;
const stashedParent = reconcilingParent;
const stashedPrevious = previouslyReconciledSibling;
const stashedRemaining = remainingReconcilingChildren;
const stashedSuspenseParent = reconcilingParentSuspenseNode;
const stashedSuspensePrevious = previouslyReconciledSiblingSuspenseNode;
const stashedSuspenseRemaining = remainingReconcilingChildrenSuspenseNodes;
const stashedIsInActivitySlice = isInFocusedActivity;
const previousSuspendedBy = instance.suspendedBy;
reconcilingParent = instance;
previouslyReconciledSibling = null;
remainingReconcilingChildren = instance.firstChild;
instance.firstChild = null;
instance.suspendedBy = null;
if (instance.suspenseNode !== null) {
reconcilingParentSuspenseNode = instance.suspenseNode;
previouslyReconciledSiblingSuspenseNode = null;
remainingReconcilingChildrenSuspenseNodes =
instance.suspenseNode.firstChild;
shouldPopSuspenseNode = true;
}
if (focusedActivity !== null) {
if (instance.id === focusedActivityID) {
isInFocusedActivity = true;
} else if (
instance.kind === FIBER_INSTANCE &&
instance.data !== null &&
instance.data.tag === ActivityComponent
) {
}
}
try {
if (
(instance.kind === FIBER_INSTANCE ||
instance.kind === FILTERED_FIBER_INSTANCE) &&
instance.data.tag === SuspenseComponent &&
OffscreenComponent !== -1
) {
const fiber = instance.data;
const contentFiberInstance = remainingReconcilingChildren;
const hydrated = isFiberHydrated(fiber);
if (hydrated) {
if (contentFiberInstance === null) {
throw new Error(
'There should always be an Offscreen Fiber child in a hydrated Suspense boundary.',
);
}
unmountSuspenseChildrenRecursively(
contentFiberInstance,
stashedSuspenseParent,
stashedSuspensePrevious,
stashedSuspenseRemaining,
);
shouldPopSuspenseNode = false;
} else {
if (contentFiberInstance !== null) {
throw new Error(
'A dehydrated Suspense node should not have a content Fiber.',
);
}
}
} else {
unmountRemainingChildren();
}
removePreviousSuspendedBy(
instance,
previousSuspendedBy,
reconcilingParentSuspenseNode,
);
} finally {
reconcilingParent = stashedParent;
previouslyReconciledSibling = stashedPrevious;
remainingReconcilingChildren = stashedRemaining;
if (shouldPopSuspenseNode) {
reconcilingParentSuspenseNode = stashedSuspenseParent;
previouslyReconciledSiblingSuspenseNode = stashedSuspensePrevious;
remainingReconcilingChildrenSuspenseNodes = stashedSuspenseRemaining;
}
isInFocusedActivity = stashedIsInActivitySlice;
}
if (instance.kind === FIBER_INSTANCE) {
recordUnmount(instance);
} else if (instance.kind === VIRTUAL_INSTANCE) {
recordVirtualUnmount(instance);
} else {
untrackFiber(instance, instance.data);
}
removeChild(instance, null);
}
function recordProfilingDurations(
fiberInstance: FiberInstance,
prevFiber: null | Fiber,
) {
const id = fiberInstance.id;
const fiber = fiberInstance.data;
const {actualDuration, treeBaseDuration} = fiber;
fiberInstance.treeBaseDuration = treeBaseDuration || 0;
if (isProfiling) {
if (
prevFiber == null ||
treeBaseDuration !== prevFiber.treeBaseDuration
) {
const convertedTreeBaseDuration = Math.floor(
(treeBaseDuration || 0) * 1000,
);
pushOperation(TREE_OPERATION_UPDATE_TREE_BASE_DURATION);
pushOperation(id);
pushOperation(convertedTreeBaseDuration);
}
if (prevFiber == null || didFiberRender(prevFiber, fiber)) {
if (actualDuration != null) {
let selfDuration = actualDuration;
let child = fiber.child;
while (child !== null) {
selfDuration -= child.actualDuration || 0;
child = child.sibling;
}
const metadata =
((currentCommitProfilingMetadata: any): CommitProfilingData);
metadata.durations.push(id, actualDuration, selfDuration);
metadata.maxActualDuration = Math.max(
metadata.maxActualDuration,
actualDuration,
);
if (recordChangeDescriptions) {
const changeDescription = getChangeDescription(prevFiber, fiber);
if (changeDescription !== null) {
if (metadata.changeDescriptions !== null) {
metadata.changeDescriptions.set(id, changeDescription);
}
}
}
}
}
const fiberRoot: FiberRoot = currentRoot.data.stateNode;
const updaters = fiberRoot.memoizedUpdaters;
if (
updaters != null &&
(updaters.has(fiber) ||
(fiber.alternate !== null && updaters.has(fiber.alternate)))
) {
const metadata =
((currentCommitProfilingMetadata: any): CommitProfilingData);
if (metadata.updaters === null) {
metadata.updaters = [];
}
metadata.updaters.push(instanceToSerializedElement(fiberInstance));
}
}
}
function recordVirtualProfilingDurations(virtualInstance: VirtualInstance) {
const id = virtualInstance.id;
let treeBaseDuration = 0;
for (
let child = virtualInstance.firstChild;
child !== null;
child = child.nextSibling
) {
treeBaseDuration += child.treeBaseDuration;
}
if (isProfiling) {
const previousTreeBaseDuration = virtualInstance.treeBaseDuration;
if (treeBaseDuration !== previousTreeBaseDuration) {
const convertedTreeBaseDuration = Math.floor(
(treeBaseDuration || 0) * 1000,
);
pushOperation(TREE_OPERATION_UPDATE_TREE_BASE_DURATION);
pushOperation(id);
pushOperation(convertedTreeBaseDuration);
}
}
virtualInstance.treeBaseDuration = treeBaseDuration;
}
function addUnfilteredChildrenIDs(
parentInstance: DevToolsInstance,
nextChildren: Array<number>,
): void {
let child: null | DevToolsInstance = parentInstance.firstChild;
while (child !== null) {
if (child.kind === FILTERED_FIBER_INSTANCE) {
const fiber = child.data;
if (isHiddenOffscreen(fiber)) {
} else {
addUnfilteredChildrenIDs(child, nextChildren);
}
} else {
nextChildren.push(child.id);
}
child = child.nextSibling;
}
}
function recordResetChildren(
parentInstance: FiberInstance | VirtualInstance,
) {
if (__DEBUG__) {
if (parentInstance.firstChild !== null) {
debug(
'recordResetChildren()',
parentInstance.firstChild,
parentInstance,
);
}
}
const nextChildren: Array<number> = [];
addUnfilteredChildrenIDs(parentInstance, nextChildren);
const numChildren = nextChildren.length;
if (numChildren < 2) {
return;
}
pushOperation(TREE_OPERATION_REORDER_CHILDREN);
pushOperation(parentInstance.id);
pushOperation(numChildren);
for (let i = 0; i < nextChildren.length; i++) {
pushOperation(nextChildren[i]);
}
}
function addUnfilteredSuspenseChildrenIDs(
parentInstance: SuspenseNode,
nextChildren: Array<number>,
): void {
let child: null | SuspenseNode = parentInstance.firstChild;
while (child !== null) {
if (child.instance.kind === FILTERED_FIBER_INSTANCE) {
addUnfilteredSuspenseChildrenIDs(child, nextChildren);
} else {
nextChildren.push(child.instance.id);
}
child = child.nextSibling;
}
}
function recordResetSuspenseChildren(parentInstance: SuspenseNode) {
if (__DEBUG__) {
if (parentInstance.firstChild !== null) {
console.log(
'recordResetSuspenseChildren()',
parentInstance.firstChild,
parentInstance,
);
}
}
const nextChildren: Array<number> = [];
addUnfilteredSuspenseChildrenIDs(parentInstance, nextChildren);
const numChildren = nextChildren.length;
if (numChildren < 2) {
return;
}
pushOperation(SUSPENSE_TREE_OPERATION_REORDER_CHILDREN);
pushOperation(parentInstance.instance.id);
pushOperation(numChildren);
for (let i = 0; i < nextChildren.length; i++) {
pushOperation(nextChildren[i]);
}
}
function updateVirtualInstanceRecursively(
virtualInstance: VirtualInstance,
nextFirstChild: Fiber,
nextLastChild: null | Fiber,
prevFirstChild: null | Fiber,
traceNearestHostComponentUpdate: boolean,
virtualLevel: number,
): UpdateFlags {
const stashedParent = reconcilingParent;
const stashedPrevious = previouslyReconciledSibling;
const stashedRemaining = remainingReconcilingChildren;
const previousSuspendedBy = virtualInstance.suspendedBy;
reconcilingParent = virtualInstance;
previouslyReconciledSibling = null;
remainingReconcilingChildren = virtualInstance.firstChild;
virtualInstance.firstChild = null;
virtualInstance.suspendedBy = null;
try {
let updateFlags = updateVirtualChildrenRecursively(
nextFirstChild,
nextLastChild,
prevFirstChild,
traceNearestHostComponentUpdate,
virtualLevel + 1,
);
if ((updateFlags & ShouldResetChildren) !== NoUpdate) {
if (!isInDisconnectedSubtree) {
recordResetChildren(virtualInstance);
}
updateFlags &= ~ShouldResetChildren;
}
removePreviousSuspendedBy(
virtualInstance,
previousSuspendedBy,
reconcilingParentSuspenseNode,
);
const componentLogsEntry = componentInfoToComponentLogsMap.get(
virtualInstance.data,
);
recordConsoleLogs(virtualInstance, componentLogsEntry);
recordVirtualProfilingDurations(virtualInstance);
return updateFlags;
} finally {
unmountRemainingChildren();
reconcilingParent = stashedParent;
previouslyReconciledSibling = stashedPrevious;
remainingReconcilingChildren = stashedRemaining;
}
}
function updateVirtualChildrenRecursively(
nextFirstChild: Fiber,
nextLastChild: null | Fiber,
prevFirstChild: null | Fiber,
traceNearestHostComponentUpdate: boolean,
virtualLevel: number,
): UpdateFlags {
let updateFlags = NoUpdate;
let nextChild: null | Fiber = nextFirstChild;
let prevChildAtSameIndex = prevFirstChild;
let previousVirtualInstance: null | VirtualInstance = null;
let previousVirtualInstanceWasMount: boolean = false;
let previousVirtualInstanceNextFirstFiber: Fiber = nextFirstChild;
let previousVirtualInstancePrevFirstFiber: null | Fiber = prevFirstChild;
while (nextChild !== null && nextChild !== nextLastChild) {
let level = 0;
if (nextChild._debugInfo) {
for (let i = 0; i < nextChild._debugInfo.length; i++) {
const debugEntry = nextChild._debugInfo[i];
if (debugEntry.awaited) {
const asyncInfo: ReactAsyncInfo = (debugEntry: any);
if (level === virtualLevel) {
insertSuspendedBy(asyncInfo);
}
continue;
}
if (typeof debugEntry.name !== 'string') {
continue;
}
const componentInfo: ReactComponentInfo = (debugEntry: any);
const secondaryEnv = getSecondaryEnvironmentName(
nextChild._debugInfo,
i,
);
if (componentInfo.env != null) {
knownEnvironmentNames.add(componentInfo.env);
}
if (secondaryEnv !== null) {
knownEnvironmentNames.add(secondaryEnv);
}
if (shouldFilterVirtual(componentInfo, secondaryEnv)) {
continue;
}
if (level === virtualLevel) {
if (
previousVirtualInstance === null ||
previousVirtualInstance.data !== componentInfo
) {
if (previousVirtualInstance !== null) {
if (previousVirtualInstanceWasMount) {
mountVirtualInstanceRecursively(
previousVirtualInstance,
previousVirtualInstanceNextFirstFiber,
nextChild,
traceNearestHostComponentUpdate,
virtualLevel,
);
updateFlags |=
ShouldResetChildren | ShouldResetSuspenseChildren;
} else {
updateFlags |= updateVirtualInstanceRecursively(
previousVirtualInstance,
previousVirtualInstanceNextFirstFiber,
nextChild,
previousVirtualInstancePrevFirstFiber,
traceNearestHostComponentUpdate,
virtualLevel,
);
}
}
let previousSiblingOfBestMatch = null;
let bestMatch = remainingReconcilingChildren;
if (
componentInfo.key != null &&
componentInfo.key !== REACT_OPTIMISTIC_KEY
) {
bestMatch = remainingReconcilingChildren;
while (bestMatch !== null) {
if (
bestMatch.kind === VIRTUAL_INSTANCE &&
bestMatch.data.key === componentInfo.key
) {
break;
}
previousSiblingOfBestMatch = bestMatch;
bestMatch = bestMatch.nextSibling;
}
}
if (
bestMatch !== null &&
bestMatch.kind === VIRTUAL_INSTANCE &&
bestMatch.data.name === componentInfo.name &&
bestMatch.data.env === componentInfo.env &&
bestMatch.data.key === componentInfo.key
) {
bestMatch.data = componentInfo;
moveChild(bestMatch, previousSiblingOfBestMatch);
previousVirtualInstance = bestMatch;
previousVirtualInstanceWasMount = false;
} else {
const newVirtualInstance = createVirtualInstance(componentInfo);
recordVirtualMount(
newVirtualInstance,
reconcilingParent,
secondaryEnv,
);
insertChild(newVirtualInstance);
previousVirtualInstance = newVirtualInstance;
previousVirtualInstanceWasMount = true;
updateFlags |= ShouldResetChildren;
}
previousVirtualInstanceNextFirstFiber = nextChild;
previousVirtualInstancePrevFirstFiber = prevChildAtSameIndex;
}
level++;
break;
} else {
level++;
}
}
}
if (level === virtualLevel) {
if (previousVirtualInstance !== null) {
if (previousVirtualInstanceWasMount) {
mountVirtualInstanceRecursively(
previousVirtualInstance,
previousVirtualInstanceNextFirstFiber,
nextChild,
traceNearestHostComponentUpdate,
virtualLevel,
);
updateFlags |= ShouldResetChildren | ShouldResetSuspenseChildren;
} else {
updateFlags |= updateVirtualInstanceRecursively(
previousVirtualInstance,
previousVirtualInstanceNextFirstFiber,
nextChild,
previousVirtualInstancePrevFirstFiber,
traceNearestHostComponentUpdate,
virtualLevel,
);
}
previousVirtualInstance = null;
}
let prevChild;
if (prevChildAtSameIndex === nextChild) {
prevChild = nextChild;
} else {
prevChild = nextChild.alternate;
}
let previousSiblingOfExistingInstance = null;
let existingInstance = null;
if (prevChild !== null) {
existingInstance = remainingReconcilingChildren;
while (existingInstance !== null) {
if (existingInstance.data === prevChild) {
break;
}
previousSiblingOfExistingInstance = existingInstance;
existingInstance = existingInstance.nextSibling;
}
}
if (existingInstance !== null) {
const fiberInstance: FiberInstance | FilteredFiberInstance =
(existingInstance: any);
if (prevChild !== prevChildAtSameIndex) {
updateFlags |= ShouldResetChildren | ShouldResetSuspenseChildren;
}
moveChild(fiberInstance, previousSiblingOfExistingInstance);
updateFlags |= updateFiberRecursively(
fiberInstance,
nextChild,
(prevChild: any),
traceNearestHostComponentUpdate,
);
} else if (prevChild !== null && shouldFilterFiber(nextChild)) {
if (prevChild !== prevChildAtSameIndex) {
updateFlags |= ShouldResetChildren | ShouldResetSuspenseChildren;
}
updateFlags |= updateFiberRecursively(
null,
nextChild,
prevChild,
traceNearestHostComponentUpdate,
);
} else {
mountFiberRecursively(nextChild, traceNearestHostComponentUpdate);
updateFlags |= ShouldResetChildren | ShouldResetSuspenseChildren;
}
}
nextChild = nextChild.sibling;
if (
(updateFlags & ShouldResetChildren) === NoUpdate &&
prevChildAtSameIndex !== null
) {
prevChildAtSameIndex = prevChildAtSameIndex.sibling;
}
}
if (previousVirtualInstance !== null) {
if (previousVirtualInstanceWasMount) {
mountVirtualInstanceRecursively(
previousVirtualInstance,
previousVirtualInstanceNextFirstFiber,
null,
traceNearestHostComponentUpdate,
virtualLevel,
);
updateFlags |= ShouldResetChildren | ShouldResetSuspenseChildren;
} else {
updateFlags |= updateVirtualInstanceRecursively(
previousVirtualInstance,
previousVirtualInstanceNextFirstFiber,
null,
previousVirtualInstancePrevFirstFiber,
traceNearestHostComponentUpdate,
virtualLevel,
);
}
}
if (prevChildAtSameIndex !== null) {
updateFlags |= ShouldResetChildren | ShouldResetSuspenseChildren;
}
return updateFlags;
}
function updateChildrenRecursively(
nextFirstChild: null | Fiber,
prevFirstChild: null | Fiber,
traceNearestHostComponentUpdate: boolean,
): UpdateFlags {
if (nextFirstChild === null) {
return prevFirstChild !== null ? ShouldResetChildren : NoUpdate;
}
return updateVirtualChildrenRecursively(
nextFirstChild,
null,
prevFirstChild,
traceNearestHostComponentUpdate,
0,
);
}
function updateSuspenseChildrenRecursively(
nextContentFiber: Fiber,
prevContentFiber: Fiber,
traceNearestHostComponentUpdate: boolean,
stashedSuspenseParent: null | SuspenseNode,
stashedSuspensePrevious: null | SuspenseNode,
stashedSuspenseRemaining: null | SuspenseNode,
): UpdateFlags {
let updateFlags = NoUpdate;
const prevFallbackFiber = prevContentFiber.sibling;
const nextFallbackFiber = nextContentFiber.sibling;
updateFlags |= updateVirtualChildrenRecursively(
nextContentFiber,
nextFallbackFiber,
prevContentFiber,
traceNearestHostComponentUpdate,
0,
);
reconcilingParentSuspenseNode = stashedSuspenseParent;
previouslyReconciledSiblingSuspenseNode = stashedSuspensePrevious;
remainingReconcilingChildrenSuspenseNodes = stashedSuspenseRemaining;
if (prevFallbackFiber !== null || nextFallbackFiber !== null) {
if (nextFallbackFiber === null) {
unmountRemainingChildren();
} else {
updateFlags |= updateVirtualChildrenRecursively(
nextFallbackFiber,
null,
prevFallbackFiber,
traceNearestHostComponentUpdate,
0,
);
if ((updateFlags & ShouldResetSuspenseChildren) !== NoUpdate) {
updateFlags |= ShouldResetParentSuspenseChildren;
updateFlags &= ~ShouldResetSuspenseChildren;
}
}
}
return updateFlags;
}
function updateFiberRecursively(
fiberInstance: null | FiberInstance | FilteredFiberInstance,
nextFiber: Fiber,
prevFiber: Fiber,
traceNearestHostComponentUpdate: boolean,
): UpdateFlags {
if (__DEBUG__) {
if (fiberInstance !== null) {
debug('updateFiberRecursively()', fiberInstance, reconcilingParent);
}
}
if (traceUpdatesEnabled) {
const elementType = getElementTypeForFiber(nextFiber);
if (traceNearestHostComponentUpdate) {
if (elementType === ElementTypeHostComponent) {
traceUpdatesForNodes.add(nextFiber.stateNode);
traceNearestHostComponentUpdate = false;
}
} else {
if (
elementType === ElementTypeFunction ||
elementType === ElementTypeClass ||
elementType === ElementTypeContext ||
elementType === ElementTypeMemo ||
elementType === ElementTypeForwardRef
) {
traceNearestHostComponentUpdate = didFiberRender(
prevFiber,
nextFiber,
);
}
}
}
const stashedParent = reconcilingParent;
const stashedPrevious = previouslyReconciledSibling;
const stashedRemaining = remainingReconcilingChildren;
const stashedSuspenseParent = reconcilingParentSuspenseNode;
const stashedSuspensePrevious = previouslyReconciledSiblingSuspenseNode;
const stashedSuspenseRemaining = remainingReconcilingChildrenSuspenseNodes;
const stashedIsInActivitySlice = isInFocusedActivity;
let updateFlags = NoUpdate;
let shouldMeasureSuspenseNode = false;
let shouldPopSuspenseNode = false;
let previousSuspendedBy = null;
if (fiberInstance !== null) {
previousSuspendedBy = fiberInstance.suspendedBy;
fiberInstance.data = nextFiber;
if (
mostRecentlyInspectedElement !== null &&
(mostRecentlyInspectedElement.id === fiberInstance.id ||
(mostRecentlyInspectedElement.type === ElementTypeRoot &&
nextFiber.tag === HostRoot)) &&
didFiberRender(prevFiber, nextFiber)
) {
hasElementUpdatedSinceLastInspected = true;
}
reconcilingParent = fiberInstance;
previouslyReconciledSibling = null;
remainingReconcilingChildren = fiberInstance.firstChild;
fiberInstance.firstChild = null;
fiberInstance.suspendedBy = null;
const suspenseNode = fiberInstance.suspenseNode;
if (suspenseNode !== null) {
reconcilingParentSuspenseNode = suspenseNode;
previouslyReconciledSiblingSuspenseNode = null;
remainingReconcilingChildrenSuspenseNodes = suspenseNode.firstChild;
suspenseNode.firstChild = null;
shouldMeasureSuspenseNode = true;
shouldPopSuspenseNode = true;
}
if (focusedActivity !== null) {
if (fiberInstance.id === focusedActivityID) {
isInFocusedActivity = true;
} else if (nextFiber.tag === ActivityComponent) {
}
}
}
try {
trackDebugInfoFromLazyType(nextFiber);
trackDebugInfoFromUsedThenables(nextFiber);
if (nextFiber.tag === HostHoistable) {
const nearestInstance = reconcilingParent;
if (nearestInstance === null) {
throw new Error('Did not expect a host hoistable to be the root');
}
if (prevFiber.memoizedState !== nextFiber.memoizedState) {
releaseHostResource(nearestInstance, prevFiber.memoizedState);
aquireHostResource(nearestInstance, nextFiber.memoizedState);
}
trackDebugInfoFromHostResource(nearestInstance, nextFiber);
} else if (
nextFiber.tag === HostComponent ||
nextFiber.tag === HostText ||
nextFiber.tag === HostSingleton
) {
const nearestInstance = reconcilingParent;
if (nearestInstance === null) {
throw new Error('Did not expect a host hoistable to be the root');
}
if (prevFiber.stateNode !== nextFiber.stateNode) {
releaseHostInstance(nearestInstance, prevFiber.stateNode);
aquireHostInstance(nearestInstance, nextFiber.stateNode);
}
trackDebugInfoFromHostComponent(nearestInstance, nextFiber);
}
const isLegacySuspense =
nextFiber.tag === SuspenseComponent && OffscreenComponent === -1;
const prevDidTimeout =
isLegacySuspense && prevFiber.memoizedState !== null;
const nextDidTimeOut =
isLegacySuspense && nextFiber.memoizedState !== null;
const prevWasHidden = isHiddenOffscreen(prevFiber);
const nextIsHidden = isHiddenOffscreen(nextFiber);
const prevWasSuspended = isSuspendedOffscreen(prevFiber);
const nextIsSuspended = isSuspendedOffscreen(nextFiber);
if (isLegacySuspense) {
if (fiberInstance !== null && fiberInstance.suspenseNode !== null) {
const suspenseNode = fiberInstance.suspenseNode;
if (
(prevFiber.stateNode === null) !==
(nextFiber.stateNode === null)
) {
trackThrownPromisesFromRetryCache(
suspenseNode,
nextFiber.stateNode,
);
}
if (
(prevFiber.memoizedState === null) !==
(nextFiber.memoizedState === null)
) {
recordSuspenseSuspenders(suspenseNode);
}
}
}
if (prevDidTimeout && nextDidTimeOut) {
const nextFiberChild = nextFiber.child;
const nextFallbackChildSet = nextFiberChild
? nextFiberChild.sibling
: null;
const prevFiberChild = prevFiber.child;
const prevFallbackChildSet = prevFiberChild
? prevFiberChild.sibling
: null;
if (prevFallbackChildSet == null && nextFallbackChildSet != null) {
mountChildrenRecursively(
nextFallbackChildSet,
traceNearestHostComponentUpdate,
);
updateFlags |= ShouldResetChildren | ShouldResetSuspenseChildren;
}
const childrenUpdateFlags =
nextFallbackChildSet != null && prevFallbackChildSet != null
? updateChildrenRecursively(
nextFallbackChildSet,
prevFallbackChildSet,
traceNearestHostComponentUpdate,
)
: NoUpdate;
updateFlags |= childrenUpdateFlags;
} else if (prevDidTimeout && !nextDidTimeOut) {
const nextPrimaryChildSet = nextFiber.child;
if (nextPrimaryChildSet !== null) {
mountChildrenRecursively(
nextPrimaryChildSet,
traceNearestHostComponentUpdate,
);
updateFlags |= ShouldResetChildren | ShouldResetSuspenseChildren;
}
} else if (!prevDidTimeout && nextDidTimeOut) {
const nextFiberChild = nextFiber.child;
const nextFallbackChildSet = nextFiberChild
? nextFiberChild.sibling
: null;
if (nextFallbackChildSet != null) {
mountChildrenRecursively(
nextFallbackChildSet,
traceNearestHostComponentUpdate,
);
updateFlags |= ShouldResetChildren | ShouldResetSuspenseChildren;
}
} else if (nextIsSuspended) {
if (!prevWasSuspended) {
if (fiberInstance !== null && !isInDisconnectedSubtree) {
disconnectChildrenRecursively(remainingReconcilingChildren);
}
}
const stashedDisconnected = isInDisconnectedSubtree;
isInDisconnectedSubtree = true;
try {
updateFlags |= updateChildrenRecursively(
nextFiber.child,
prevFiber.child,
false,
);
} finally {
isInDisconnectedSubtree = stashedDisconnected;
}
} else if (prevWasSuspended && !nextIsSuspended) {
const stashedDisconnected = isInDisconnectedSubtree;
isInDisconnectedSubtree = true;
try {
if (nextFiber.child !== null) {
updateFlags |= updateChildrenRecursively(
nextFiber.child,
prevFiber.child,
false,
);
}
unmountRemainingChildren();
remainingReconcilingChildren = null;
} finally {
isInDisconnectedSubtree = stashedDisconnected;
}
if (fiberInstance !== null && !isInDisconnectedSubtree) {
reconnectChildrenRecursively(fiberInstance);
updateFlags |= ShouldResetChildren | ShouldResetSuspenseChildren;
}
} else if (nextIsHidden) {
if (prevWasHidden) {
} else {
unmountRemainingChildren();
}
} else if (
nextFiber.tag === SuspenseComponent &&
OffscreenComponent !== -1 &&
fiberInstance !== null &&
fiberInstance.suspenseNode !== null
) {
const suspenseNode = fiberInstance.suspenseNode;
const prevContentFiber = prevFiber.child;
const nextContentFiber = nextFiber.child;
const previousHydrated = isFiberHydrated(prevFiber);
const nextHydrated = isFiberHydrated(nextFiber);
if (previousHydrated && nextHydrated) {
if (nextContentFiber === null || prevContentFiber === null) {
throw new Error(
'There should always be an Offscreen Fiber child in a hydrated Suspense boundary.',
);
}
if (
(prevFiber.stateNode === null) !==
(nextFiber.stateNode === null)
) {
trackThrownPromisesFromRetryCache(
suspenseNode,
nextFiber.stateNode,
);
}
if (
(prevFiber.memoizedState === null) !==
(nextFiber.memoizedState === null)
) {
recordSuspenseSuspenders(suspenseNode);
}
shouldMeasureSuspenseNode = false;
updateFlags |= updateSuspenseChildrenRecursively(
nextContentFiber,
prevContentFiber,
traceNearestHostComponentUpdate,
stashedSuspenseParent,
stashedSuspensePrevious,
stashedSuspenseRemaining,
);
shouldPopSuspenseNode = false;
if (nextFiber.memoizedState === null) {
shouldMeasureSuspenseNode = !isInDisconnectedSubtree;
}
} else if (!previousHydrated && nextHydrated) {
if (nextContentFiber === null) {
throw new Error(
'There should always be an Offscreen Fiber child in a hydrated Suspense boundary.',
);
}
trackThrownPromisesFromRetryCache(suspenseNode, nextFiber.stateNode);
recordSuspenseSuspenders(suspenseNode);
mountSuspenseChildrenRecursively(
nextContentFiber,
traceNearestHostComponentUpdate,
stashedSuspenseParent,
stashedSuspensePrevious,
stashedSuspenseRemaining,
);
shouldPopSuspenseNode = false;
} else if (previousHydrated && !nextHydrated) {
throw new Error(
'Encountered a dehydrated Suspense boundary that was previously hydrated.',
);
} else {
}
} else {
if (nextFiber.child !== prevFiber.child) {
updateFlags |= updateChildrenRecursively(
nextFiber.child,
prevFiber.child,
traceNearestHostComponentUpdate,
);
} else {
if (fiberInstance !== null) {
fiberInstance.firstChild = remainingReconcilingChildren;
remainingReconcilingChildren = null;
consumeSuspenseNodesOfExistingInstance(fiberInstance);
if (traceUpdatesEnabled) {
if (traceNearestHostComponentUpdate) {
const hostInstances =
findAllCurrentHostInstances(fiberInstance);
hostInstances.forEach(hostInstance => {
traceUpdatesForNodes.add(hostInstance);
});
}
}
} else {
const childrenUpdateFlags = updateChildrenRecursively(
nextFiber.child,
prevFiber.child,
false,
);
if ((childrenUpdateFlags & ShouldResetChildren) !== NoUpdate) {
throw new Error(
'The children should not have changed if we pass in the same set.',
);
}
updateFlags |= childrenUpdateFlags;
}
}
}
if (fiberInstance !== null) {
removePreviousSuspendedBy(
fiberInstance,
previousSuspendedBy,
shouldPopSuspenseNode
? reconcilingParentSuspenseNode
: stashedSuspenseParent,
);
if (fiberInstance.kind === FIBER_INSTANCE) {
let componentLogsEntry = fiberToComponentLogsMap.get(
fiberInstance.data,
);
if (
componentLogsEntry === undefined &&
fiberInstance.data.alternate
) {
componentLogsEntry = fiberToComponentLogsMap.get(
fiberInstance.data.alternate,
);
}
recordConsoleLogs(fiberInstance, componentLogsEntry);
const isProfilingSupported =
nextFiber.hasOwnProperty('treeBaseDuration');
if (isProfilingSupported) {
recordProfilingDurations(fiberInstance, prevFiber);
}
}
}
if ((updateFlags & ShouldResetChildren) !== NoUpdate) {
if (fiberInstance !== null && fiberInstance.kind === FIBER_INSTANCE) {
if (!nextIsSuspended && !isInDisconnectedSubtree) {
recordResetChildren(fiberInstance);
}
updateFlags &= ~ShouldResetChildren;
} else {
}
} else {
}
if ((updateFlags & ShouldResetSuspenseChildren) !== NoUpdate) {
if (fiberInstance !== null && fiberInstance.kind === FIBER_INSTANCE) {
const suspenseNode = fiberInstance.suspenseNode;
if (suspenseNode !== null) {
recordResetSuspenseChildren(suspenseNode);
updateFlags &= ~ShouldResetSuspenseChildren;
}
} else {
}
}
if ((updateFlags & ShouldResetParentSuspenseChildren) !== NoUpdate) {
if (fiberInstance !== null && fiberInstance.kind === FIBER_INSTANCE) {
const suspenseNode = fiberInstance.suspenseNode;
if (suspenseNode !== null) {
updateFlags &= ~ShouldResetParentSuspenseChildren;
updateFlags |= ShouldResetSuspenseChildren;
}
} else {
}
}
return updateFlags;
} finally {
if (fiberInstance !== null) {
unmountRemainingChildren();
reconcilingParent = stashedParent;
previouslyReconciledSibling = stashedPrevious;
remainingReconcilingChildren = stashedRemaining;
if (shouldMeasureSuspenseNode) {
if (!isInDisconnectedSubtree) {
const suspenseNode = fiberInstance.suspenseNode;
if (suspenseNode === null) {
throw new Error(
'Attempted to measure a Suspense node that does not exist.',
);
}
const prevRects = suspenseNode.rects;
const nextRects = measureInstance(fiberInstance);
if (!areEqualRects(prevRects, nextRects)) {
suspenseNode.rects = nextRects;
recordSuspenseResize(suspenseNode);
}
}
}
if (shouldPopSuspenseNode) {
reconcilingParentSuspenseNode = stashedSuspenseParent;
previouslyReconciledSiblingSuspenseNode = stashedSuspensePrevious;
remainingReconcilingChildrenSuspenseNodes = stashedSuspenseRemaining;
}
isInFocusedActivity = stashedIsInActivitySlice;
}
}
}
function disconnectChildrenRecursively(firstChild: null | DevToolsInstance) {
for (let child = firstChild; child !== null; child = child.nextSibling) {
if (
(child.kind === FIBER_INSTANCE ||
child.kind === FILTERED_FIBER_INSTANCE) &&
isSuspendedOffscreen(child.data)
) {
} else {
disconnectChildrenRecursively(child.firstChild);
}
if (child.kind === FIBER_INSTANCE) {
recordDisconnect(child);
} else if (child.kind === VIRTUAL_INSTANCE) {
recordVirtualDisconnect(child);
}
}
}
function reconnectChildrenRecursively(parentInstance: DevToolsInstance) {
for (
let child = parentInstance.firstChild;
child !== null;
child = child.nextSibling
) {
if (child.kind === FIBER_INSTANCE) {
recordReconnect(child, parentInstance);
} else if (child.kind === VIRTUAL_INSTANCE) {
const secondaryEnv = null;
recordVirtualReconnect(child, parentInstance, secondaryEnv);
}
if (
(child.kind === FIBER_INSTANCE ||
child.kind === FILTERED_FIBER_INSTANCE) &&
isHiddenOffscreen(child.data)
) {
} else {
reconnectChildrenRecursively(child);
}
}
}
function cleanup() {
isProfiling = false;
}
function rootSupportsProfiling(root: any) {
if (root.memoizedInteractions != null) {
return true;
} else if (
root.current != null &&
root.current.hasOwnProperty('treeBaseDuration')
) {
return true;
} else {
return false;
}
}
function flushInitialOperations() {
const localPendingOperationsQueue = pendingOperationsQueue;
pendingOperationsQueue = null;
if (
localPendingOperationsQueue !== null &&
localPendingOperationsQueue.length > 0
) {
localPendingOperationsQueue.forEach(operations => {
hook.emit('operations', operations);
});
} else {
if (trackedPath !== null) {
mightBeOnTrackedPath = true;
}
hook.getFiberRoots(rendererID).forEach(root => {
const current = root.current;
const newRoot = createFiberInstance(current);
rootToFiberInstanceMap.set(root, newRoot);
idToDevToolsInstanceMap.set(newRoot.id, newRoot);
currentRoot = newRoot;
setRootPseudoKey(currentRoot.id, root.current);
if (isProfiling && rootSupportsProfiling(root)) {
currentCommitProfilingMetadata = {
changeDescriptions: recordChangeDescriptions ? new Map() : null,
durations: [],
commitTime: getCurrentTime() - profilingStartTime,
maxActualDuration: 0,
priorityLevel: null,
updaters: null,
effectDuration: null,
passiveEffectDuration: null,
};
}
mountFiberRecursively(root.current, false);
currentRoot = (null: any);
});
flushPendingEvents();
needsToFlushComponentLogs = false;
}
}
function handleCommitFiberUnmount(fiber: any) {
}
function handlePostCommitFiberRoot(root: any) {
if (isProfiling && rootSupportsProfiling(root)) {
if (currentCommitProfilingMetadata !== null) {
const {effectDuration, passiveEffectDuration} =
getEffectDurations(root);
currentCommitProfilingMetadata.effectDuration = effectDuration;
currentCommitProfilingMetadata.passiveEffectDuration =
passiveEffectDuration;
}
}
if (needsToFlushComponentLogs) {
bruteForceFlushErrorsAndWarnings();
}
}
function handleCommitFiberRoot(
root: FiberRoot,
priorityLevel: void | number,
) {
const nextFiber = root.current;
let prevFiber: null | Fiber = null;
let rootInstance = rootToFiberInstanceMap.get(root);
if (!rootInstance) {
rootInstance = createFiberInstance(nextFiber);
rootToFiberInstanceMap.set(root, rootInstance);
idToDevToolsInstanceMap.set(rootInstance.id, rootInstance);
} else {
prevFiber = rootInstance.data;
}
currentRoot = rootInstance;
if (trackedPath !== null) {
mightBeOnTrackedPath = true;
}
if (traceUpdatesEnabled) {
traceUpdatesForNodes.clear();
}
const isProfilingSupported = rootSupportsProfiling(root);
if (isProfiling && isProfilingSupported) {
currentCommitProfilingMetadata = {
changeDescriptions: recordChangeDescriptions ? new Map() : null,
durations: [],
commitTime: getCurrentTime() - profilingStartTime,
maxActualDuration: 0,
priorityLevel:
priorityLevel == null ? null : formatPriorityLevel(priorityLevel),
updaters: null,
effectDuration: null,
passiveEffectDuration: null,
};
}
const nextIsMounted = nextFiber.child !== null;
const prevWasMounted = prevFiber !== null && prevFiber.child !== null;
if (!prevWasMounted && nextIsMounted) {
setRootPseudoKey(currentRoot.id, nextFiber);
mountFiberRecursively(nextFiber, false);
} else if (prevWasMounted && nextIsMounted) {
if (prevFiber === null) {
throw new Error(
'Expected a previous Fiber when updating an existing root.',
);
}
updateFiberRecursively(rootInstance, nextFiber, prevFiber, false);
} else if (prevWasMounted && !nextIsMounted) {
unmountInstanceRecursively(rootInstance);
removeRootPseudoKey(currentRoot.id);
rootToFiberInstanceMap.delete(root);
} else if (!prevWasMounted && !nextIsMounted) {
rootToFiberInstanceMap.delete(root);
}
if (isProfiling && isProfilingSupported) {
if (!shouldBailoutWithPendingOperations()) {
const commitProfilingMetadata =
((rootToCommitProfilingMetadataMap: any): CommitProfilingMetadataMap).get(
currentRoot.id,
);
if (commitProfilingMetadata != null) {
commitProfilingMetadata.push(
((currentCommitProfilingMetadata: any): CommitProfilingData),
);
} else {
((rootToCommitProfilingMetadataMap: any): CommitProfilingMetadataMap).set(
currentRoot.id,
[((currentCommitProfilingMetadata: any): CommitProfilingData)],
);
}
}
}
flushPendingEvents();
needsToFlushComponentLogs = false;
if (traceUpdatesEnabled) {
hook.emit('traceUpdates', traceUpdatesForNodes);
}
currentRoot = (null: any);
}
function getResourceInstance(fiber: Fiber): HostInstance | null {
if (fiber.tag === HostHoistable) {
const resource = fiber.memoizedState;
if (
typeof resource === 'object' &&
resource !== null &&
resource.instance != null
) {
return resource.instance;
}
}
return null;
}
function appendHostInstancesByDevToolsInstance(
devtoolsInstance: DevToolsInstance,
hostInstances: Array<HostInstance>,
) {
if (devtoolsInstance.kind !== VIRTUAL_INSTANCE) {
const fiber = devtoolsInstance.data;
appendHostInstancesByFiber(fiber, hostInstances);
return;
}
for (
let child = devtoolsInstance.firstChild;
child !== null;
child = child.nextSibling
) {
appendHostInstancesByDevToolsInstance(child, hostInstances);
}
}
function appendHostInstancesByFiber(
fiber: Fiber,
hostInstances: Array<HostInstance>,
): void {
let node: Fiber = fiber;
while (true) {
if (
node.tag === HostComponent ||
node.tag === HostText ||
node.tag === HostSingleton ||
node.tag === HostHoistable
) {
const hostInstance = node.stateNode || getResourceInstance(node);
if (hostInstance) {
hostInstances.push(hostInstance);
}
} else if (node.child) {
node.child.return = node;
node = node.child;
continue;
}
if (node === fiber) {
return;
}
while (!node.sibling) {
if (!node.return || node.return === fiber) {
return;
}
node = node.return;
}
node.sibling.return = node.return;
node = node.sibling;
}
}
function findAllCurrentHostInstances(
devtoolsInstance: DevToolsInstance,
): $ReadOnlyArray<HostInstance> {
const hostInstances: Array<HostInstance> = [];
appendHostInstancesByDevToolsInstance(devtoolsInstance, hostInstances);
return hostInstances;
}
function findHostInstancesForElementID(id: number) {
try {
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
if (devtoolsInstance === undefined) {
console.warn(`Could not find DevToolsInstance with id "${id}"`);
return null;
}
return findAllCurrentHostInstances(devtoolsInstance);
} catch (err) {
return null;
}
}
function findLastKnownRectsForID(id: number): null | Array<Rect> {
try {
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
if (devtoolsInstance === undefined) {
console.warn(`Could not find DevToolsInstance with id "${id}"`);
return null;
}
if (devtoolsInstance.suspenseNode === null) {
return null;
}
return devtoolsInstance.suspenseNode.rects;
} catch (err) {
return null;
}
}
function getDisplayNameForElementID(id: number): null | string {
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
if (devtoolsInstance === undefined) {
return null;
}
if (devtoolsInstance.kind === FIBER_INSTANCE) {
const fiber = devtoolsInstance.data;
if (fiber.tag === HostRoot) {
return 'Initial Paint';
}
if (fiber.tag === SuspenseComponent || fiber.tag === ActivityComponent) {
const props = fiber.memoizedProps;
if (props.name != null) {
return props.name;
}
const owner = getUnfilteredOwner(fiber);
if (owner != null) {
if (typeof owner.tag === 'number') {
return getDisplayNameForFiber((owner: any));
} else {
return owner.name || '';
}
}
}
return getDisplayNameForFiber(fiber);
} else {
return devtoolsInstance.data.name || '';
}
}
function getNearestSuspenseNode(instance: DevToolsInstance): SuspenseNode {
while (instance.suspenseNode === null) {
if (instance.parent === null) {
throw new Error(
'There should always be a SuspenseNode parent on a mounted instance.',
);
}
instance = instance.parent;
}
return instance.suspenseNode;
}
function getNearestMountedDOMNode(publicInstance: Element): null | Element {
let domNode: null | Element = publicInstance;
while (domNode && !publicInstanceToDevToolsInstanceMap.has(domNode)) {
domNode = domNode.parentNode;
}
return domNode;
}
function getElementIDForHostInstance(
publicInstance: HostInstance,
): number | null {
const instance = publicInstanceToDevToolsInstanceMap.get(publicInstance);
if (instance !== undefined) {
if (instance.kind === FILTERED_FIBER_INSTANCE) {
return ((instance.parent: any): VirtualInstance).id;
}
return instance.id;
}
return null;
}
function getSuspenseNodeIDForHostInstance(
publicInstance: HostInstance,
): number | null {
const instance = publicInstanceToDevToolsInstanceMap.get(publicInstance);
if (instance !== undefined) {
let suspenseInstance = instance;
while (
suspenseInstance.suspenseNode === null ||
suspenseInstance.kind === FILTERED_FIBER_INSTANCE
) {
if (suspenseInstance.parent === null) {
return null;
}
suspenseInstance = suspenseInstance.parent;
}
return suspenseInstance.id;
}
return null;
}
function getElementAttributeByPath(
id: number,
path: Array<string | number>,
): mixed {
if (isMostRecentlyInspectedElement(id)) {
return getInObject(
((mostRecentlyInspectedElement: any): InspectedElement),
path,
);
}
return undefined;
}
function getElementSourceFunctionById(id: number): null | Function {
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
if (devtoolsInstance === undefined) {
console.warn(`Could not find DevToolsInstance with id "${id}"`);
return null;
}
if (devtoolsInstance.kind !== FIBER_INSTANCE) {
return null;
}
const fiber = devtoolsInstance.data;
const {elementType, tag, type} = fiber;
switch (tag) {
case ClassComponent:
case IncompleteClassComponent:
case IncompleteFunctionComponent:
case IndeterminateComponent:
case FunctionComponent:
return type;
case ForwardRef:
return type.render;
case MemoComponent:
case SimpleMemoComponent:
return elementType != null && elementType.type != null
? elementType.type
: type;
default:
return null;
}
}
function instanceToSerializedElement(
instance: FiberInstance | VirtualInstance,
): SerializedElement {
if (instance.kind === FIBER_INSTANCE) {
const fiber = instance.data;
return {
displayName: getDisplayNameForFiber(fiber) || 'Anonymous',
id: instance.id,
key: fiber.key === REACT_OPTIMISTIC_KEY ? null : fiber.key,
env: null,
stack:
fiber._debugOwner == null || fiber._debugStack == null
? null
: parseStackTrace(fiber._debugStack, 1),
type: getElementTypeForFiber(fiber),
};
} else {
const componentInfo = instance.data;
return {
displayName: componentInfo.name || 'Anonymous',
id: instance.id,
key:
componentInfo.key == null ||
componentInfo.key === REACT_OPTIMISTIC_KEY
? null
: componentInfo.key,
env: componentInfo.env == null ? null : componentInfo.env,
stack:
componentInfo.owner == null || componentInfo.debugStack == null
? null
: parseStackTrace(componentInfo.debugStack, 1),
type: ElementTypeVirtual,
};
}
}
function getOwnersList(id: number): Array<SerializedElement> | null {
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
if (devtoolsInstance === undefined) {
console.warn(`Could not find DevToolsInstance with id "${id}"`);
return null;
}
const self = instanceToSerializedElement(devtoolsInstance);
const owners = getOwnersListFromInstance(devtoolsInstance);
if (owners === null) {
return [self];
}
owners.unshift(self);
owners.reverse();
return owners;
}
function getOwnersListFromInstance(
instance: DevToolsInstance,
): Array<SerializedElement> | null {
let owner = getUnfilteredOwner(instance.data);
if (owner === null) {
return null;
}
const owners: Array<SerializedElement> = [];
let parentInstance: null | DevToolsInstance = instance.parent;
while (parentInstance !== null && owner !== null) {
const ownerInstance = findNearestOwnerInstance(parentInstance, owner);
if (ownerInstance !== null) {
owners.push(instanceToSerializedElement(ownerInstance));
owner = getUnfilteredOwner(owner);
parentInstance = ownerInstance.parent;
} else {
break;
}
}
return owners;
}
function getUnfilteredOwner(
owner: ReactComponentInfo | Fiber | null | void,
): ReactComponentInfo | Fiber | null {
if (owner == null) {
return null;
}
if (typeof owner.tag === 'number') {
const ownerFiber: Fiber = (owner: any);
owner = ownerFiber._debugOwner;
} else {
const ownerInfo: ReactComponentInfo = (owner: any);
owner = ownerInfo.owner;
}
while (owner) {
if (typeof owner.tag === 'number') {
const ownerFiber: Fiber = (owner: any);
if (!shouldFilterFiber(ownerFiber)) {
return ownerFiber;
}
owner = ownerFiber._debugOwner;
} else {
const ownerInfo: ReactComponentInfo = (owner: any);
if (!shouldFilterVirtual(ownerInfo, null)) {
return ownerInfo;
}
owner = ownerInfo.owner;
}
}
return null;
}
function findNearestOwnerInstance(
parentInstance: null | DevToolsInstance,
owner: void | null | ReactComponentInfo | Fiber,
): null | FiberInstance | VirtualInstance {
if (owner == null) {
return null;
}
while (parentInstance !== null) {
if (
parentInstance.data === owner ||
parentInstance.data === (owner: any).alternate
) {
if (parentInstance.kind === FILTERED_FIBER_INSTANCE) {
return null;
}
return parentInstance;
}
parentInstance = parentInstance.parent;
}
return null;
}
function inspectHooks(fiber: Fiber): HooksTree {
const originalConsoleMethods: {[string]: $FlowFixMe} = {};
for (const method in console) {
try {
originalConsoleMethods[method] = console[method];
console[method] = () => {};
} catch (error) {}
}
try {
return inspectHooksOfFiber(fiber, getDispatcherRef(renderer));
} finally {
for (const method in originalConsoleMethods) {
try {
console[method] = originalConsoleMethods[method];
} catch (error) {}
}
}
}
function getSuspendedByOfSuspenseNode(
suspenseNode: SuspenseNode,
filterByChildInstance: null | DevToolsInstance,
): Array<SerializedAsyncInfo> {
const result: Array<SerializedAsyncInfo> = [];
if (!suspenseNode.hasUniqueSuspenders) {
return result;
}
let hooksCacheKey: null | DevToolsInstance = null;
let hooksCache: null | HooksTree = null;
const streamEntries: Map<
Promise<mixed>,
{
asyncInfo: ReactAsyncInfo,
instance: DevToolsInstance,
hooks: null | HooksTree,
},
> = new Map();
suspenseNode.suspendedBy.forEach((set, ioInfo) => {
let parentNode = suspenseNode.parent;
while (parentNode !== null) {
if (parentNode.suspendedBy.has(ioInfo)) {
return;
}
parentNode = parentNode.parent;
}
if (set.size === 0) {
return;
}
let firstInstance: null | DevToolsInstance = null;
if (filterByChildInstance === null) {
firstInstance = (set.values().next().value: any);
} else {
for (const childInstance of set.values()) {
if (firstInstance === null) {
firstInstance = childInstance;
}
if (
childInstance !== filterByChildInstance &&
!isChildOf(
filterByChildInstance,
childInstance,
suspenseNode.instance,
)
) {
return;
}
}
}
if (firstInstance !== null && firstInstance.suspendedBy !== null) {
const asyncInfo = getAwaitInSuspendedByFromIO(
firstInstance.suspendedBy,
ioInfo,
);
if (asyncInfo !== null) {
let hooks: null | HooksTree = null;
if (asyncInfo.stack == null && asyncInfo.owner == null) {
if (hooksCacheKey === firstInstance) {
hooks = hooksCache;
} else if (firstInstance.kind !== VIRTUAL_INSTANCE) {
const fiber = firstInstance.data;
if (
fiber.dependencies &&
fiber.dependencies._debugThenableState
) {
hooksCacheKey = firstInstance;
hooksCache = hooks = inspectHooks(fiber);
}
}
}
const newIO = asyncInfo.awaited;
if (
(newIO.name === 'RSC stream' || newIO.name === 'rsc stream') &&
newIO.value != null
) {
const streamPromise = newIO.value;
const existingEntry = streamEntries.get(streamPromise);
if (existingEntry === undefined) {
streamEntries.set(streamPromise, {
asyncInfo,
instance: firstInstance,
hooks,
});
} else {
const existingIO = existingEntry.asyncInfo.awaited;
if (
newIO !== existingIO &&
((newIO.byteSize !== undefined &&
existingIO.byteSize !== undefined &&
newIO.byteSize > existingIO.byteSize) ||
newIO.end > existingIO.end)
) {
existingEntry.asyncInfo = asyncInfo;
existingEntry.instance = firstInstance;
existingEntry.hooks = hooks;
}
}
} else {
result.push(serializeAsyncInfo(asyncInfo, firstInstance, hooks));
}
}
}
});
streamEntries.forEach(({asyncInfo, instance, hooks}) => {
result.push(serializeAsyncInfo(asyncInfo, instance, hooks));
});
return result;
}
function getSuspendedByOfInstance(
devtoolsInstance: DevToolsInstance,
hooks: null | HooksTree,
): Array<SerializedAsyncInfo> {
const suspendedBy = devtoolsInstance.suspendedBy;
if (suspendedBy === null) {
return [];
}
const foundIOEntries: Set<ReactIOInfo> = new Set();
const streamEntries: Map<Promise<mixed>, ReactAsyncInfo> = new Map();
const result: Array<SerializedAsyncInfo> = [];
for (let i = 0; i < suspendedBy.length; i++) {
const asyncInfo = suspendedBy[i];
const ioInfo = asyncInfo.awaited;
if (foundIOEntries.has(ioInfo)) {
continue;
}
foundIOEntries.add(ioInfo);
if (
(ioInfo.name === 'RSC stream' || ioInfo.name === 'rsc stream') &&
ioInfo.value != null
) {
const streamPromise = ioInfo.value;
const existingEntry = streamEntries.get(streamPromise);
if (existingEntry === undefined) {
streamEntries.set(streamPromise, asyncInfo);
} else {
const existingIO = existingEntry.awaited;
if (
ioInfo !== existingIO &&
((ioInfo.byteSize !== undefined &&
existingIO.byteSize !== undefined &&
ioInfo.byteSize > existingIO.byteSize) ||
ioInfo.end > existingIO.end)
) {
streamEntries.set(streamPromise, asyncInfo);
}
}
} else {
result.push(serializeAsyncInfo(asyncInfo, devtoolsInstance, hooks));
}
}
streamEntries.forEach(asyncInfo => {
result.push(serializeAsyncInfo(asyncInfo, devtoolsInstance, hooks));
});
return result;
}
function getSuspendedByOfInstanceSubtree(
devtoolsInstance: DevToolsInstance,
): Array<SerializedAsyncInfo> {
let suspenseParentInstance = devtoolsInstance;
while (suspenseParentInstance.suspenseNode === null) {
if (suspenseParentInstance.parent === null) {
return [];
}
suspenseParentInstance = suspenseParentInstance.parent;
}
const suspenseNode: SuspenseNode = suspenseParentInstance.suspenseNode;
return getSuspendedByOfSuspenseNode(suspenseNode, devtoolsInstance);
}
const FALLBACK_THROTTLE_MS: number = 300;
function getSuspendedByRange(
suspenseNode: SuspenseNode,
): null | [number, number] {
let min = Infinity;
let max = -Infinity;
suspenseNode.suspendedBy.forEach((_, ioInfo) => {
if (ioInfo.end > max) {
max = ioInfo.end;
}
if (ioInfo.start < min) {
min = ioInfo.start;
}
});
const parentSuspenseNode = suspenseNode.parent;
if (parentSuspenseNode !== null) {
let parentMax = -Infinity;
parentSuspenseNode.suspendedBy.forEach((_, ioInfo) => {
if (ioInfo.end > parentMax) {
parentMax = ioInfo.end;
}
});
const throttleTime = parentMax + FALLBACK_THROTTLE_MS;
if (throttleTime > max) {
max = throttleTime;
}
let startTime = max - FALLBACK_THROTTLE_MS;
if (parentMax > startTime) {
startTime = parentMax;
}
if (startTime < min) {
min = startTime;
}
}
if (min < Infinity && max > -Infinity) {
return [min, max];
}
return null;
}
function getAwaitStackFromHooks(
hooks: HooksTree,
asyncInfo: ReactAsyncInfo,
): null | ReactStackTrace {
for (let i = 0; i < hooks.length; i++) {
const node = hooks[i];
const debugInfo = node.debugInfo;
if (debugInfo != null && debugInfo.indexOf(asyncInfo) !== -1) {
const source = node.hookSource;
if (
source != null &&
source.functionName !== null &&
source.fileName !== null &&
source.lineNumber !== null &&
source.columnNumber !== null
) {
const callSite: ReactCallSite = [
source.functionName,
source.fileName,
source.lineNumber,
source.columnNumber,
0,
0,
false,
];
return [callSite];
} else {
return [];
}
}
const matchedStack = getAwaitStackFromHooks(node.subHooks, asyncInfo);
if (matchedStack !== null) {
const source = node.hookSource;
if (
source != null &&
source.functionName !== null &&
source.fileName !== null &&
source.lineNumber !== null &&
source.columnNumber !== null
) {
const callSite: ReactCallSite = [
source.functionName,
source.fileName,
source.lineNumber,
source.columnNumber,
0,
0,
false,
];
matchedStack.push(callSite);
}
return matchedStack;
}
}
return null;
}
function serializeAsyncInfo(
asyncInfo: ReactAsyncInfo,
parentInstance: DevToolsInstance,
hooks: null | HooksTree,
): SerializedAsyncInfo {
const ioInfo = asyncInfo.awaited;
const ioOwnerInstance = findNearestOwnerInstance(
parentInstance,
ioInfo.owner,
);
let awaitStack =
asyncInfo.debugStack == null
? null
:
parseStackTrace(asyncInfo.debugStack, 1);
let awaitOwnerInstance: null | FiberInstance | VirtualInstance;
if (
asyncInfo.owner == null &&
(awaitStack === null || awaitStack.length === 0)
) {
awaitStack = null;
awaitOwnerInstance =
parentInstance.kind === FILTERED_FIBER_INSTANCE ? null : parentInstance;
if (
parentInstance.kind === FIBER_INSTANCE ||
parentInstance.kind === FILTERED_FIBER_INSTANCE
) {
const fiber = parentInstance.data;
switch (fiber.tag) {
case ClassComponent:
case FunctionComponent:
case IncompleteClassComponent:
case IncompleteFunctionComponent:
case IndeterminateComponent:
case MemoComponent:
case SimpleMemoComponent:
if (hooks !== null) {
awaitStack = getAwaitStackFromHooks(hooks, asyncInfo);
}
break;
default:
if (
fiber._debugOwner != null &&
fiber._debugStack != null &&
typeof fiber._debugStack !== 'string'
) {
awaitStack = parseStackTrace(fiber._debugStack, 1);
awaitOwnerInstance = findNearestOwnerInstance(
parentInstance,
fiber._debugOwner,
);
}
}
}
} else {
awaitOwnerInstance = findNearestOwnerInstance(
parentInstance,
asyncInfo.owner,
);
}
const value: any = ioInfo.value;
let resolvedValue = undefined;
if (
typeof value === 'object' &&
value !== null &&
typeof value.then === 'function'
) {
switch (value.status) {
case 'fulfilled':
resolvedValue = value.value;
break;
case 'rejected':
resolvedValue = value.reason;
break;
}
}
return {
awaited: {
name: ioInfo.name,
description: getIODescription(resolvedValue),
start: ioInfo.start,
end: ioInfo.end,
byteSize: ioInfo.byteSize == null ? null : ioInfo.byteSize,
value: ioInfo.value == null ? null : ioInfo.value,
env: ioInfo.env == null ? null : ioInfo.env,
owner:
ioOwnerInstance === null
? null
: instanceToSerializedElement(ioOwnerInstance),
stack:
ioInfo.debugStack == null
? null
:
parseStackTrace(ioInfo.debugStack, 1),
},
env: asyncInfo.env == null ? null : asyncInfo.env,
owner:
awaitOwnerInstance === null
? null
: instanceToSerializedElement(awaitOwnerInstance),
stack: awaitStack,
};
}
function getInstanceAndStyle(id: number): InstanceAndStyle {
let instance = null;
let style = null;
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
if (devtoolsInstance === undefined) {
console.warn(`Could not find DevToolsInstance with id "${id}"`);
return {instance, style};
}
if (devtoolsInstance.kind !== FIBER_INSTANCE) {
return {instance, style};
}
const fiber = devtoolsInstance.data;
if (fiber !== null) {
instance = fiber.stateNode;
if (fiber.memoizedProps !== null) {
style = fiber.memoizedProps.style;
}
}
return {instance, style};
}
function isErrorBoundary(fiber: Fiber): boolean {
const {tag, type} = fiber;
switch (tag) {
case ClassComponent:
case IncompleteClassComponent:
const instance = fiber.stateNode;
return (
typeof type.getDerivedStateFromError === 'function' ||
(instance !== null &&
typeof instance.componentDidCatch === 'function')
);
default:
return false;
}
}
function inspectElementRaw(id: number): InspectedElement | null {
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
if (devtoolsInstance === undefined) {
console.warn(`Could not find DevToolsInstance with id "${id}"`);
return null;
}
if (devtoolsInstance.kind === VIRTUAL_INSTANCE) {
return inspectVirtualInstanceRaw(devtoolsInstance);
}
if (devtoolsInstance.kind === FIBER_INSTANCE) {
const isRoot = devtoolsInstance.parent === null;
return isRoot
? inspectRootsRaw(devtoolsInstance.id)
: inspectFiberInstanceRaw(devtoolsInstance);
}
(devtoolsInstance: FilteredFiberInstance);
throw new Error('Unsupported instance kind');
}
function inspectFiberInstanceRaw(
fiberInstance: FiberInstance,
): InspectedElement | null {
const fiber = fiberInstance.data;
if (fiber == null) {
return null;
}
const {
stateNode,
key,
memoizedProps,
memoizedState,
dependencies,
tag,
type,
} = fiber;
const elementType = getElementTypeForFiber(fiber);
const usesHooks =
(tag === FunctionComponent ||
tag === SimpleMemoComponent ||
tag === ForwardRef) &&
(!!memoizedState || !!dependencies);
const showState =
tag === ClassComponent || tag === IncompleteClassComponent;
const typeSymbol = getTypeSymbol(type);
let canViewSource = false;
let context = null;
if (
tag === ClassComponent ||
tag === FunctionComponent ||
tag === IncompleteClassComponent ||
tag === IncompleteFunctionComponent ||
tag === IndeterminateComponent ||
tag === MemoComponent ||
tag === ForwardRef ||
tag === SimpleMemoComponent
) {
canViewSource = true;
if (stateNode && stateNode.context != null) {
const shouldHideContext =
elementType === ElementTypeClass &&
!(type.contextTypes || type.contextType);
if (!shouldHideContext) {
context = stateNode.context;
}
}
} else if (
(typeSymbol === CONTEXT_NUMBER || typeSymbol === CONTEXT_SYMBOL_STRING) &&
!(
(type._context === undefined && type.Provider === type)
)
) {
const consumerResolvedContext = type._context || type;
context = consumerResolvedContext._currentValue || null;
let current = ((fiber: any): Fiber).return;
while (current !== null) {
const currentType = current.type;
const currentTypeSymbol = getTypeSymbol(currentType);
if (
currentTypeSymbol === PROVIDER_NUMBER ||
currentTypeSymbol === PROVIDER_SYMBOL_STRING
) {
const providerResolvedContext =
currentType._context || currentType.context;
if (providerResolvedContext === consumerResolvedContext) {
context = current.memoizedProps.value;
break;
}
}
current = current.return;
}
} else if (
typeSymbol === CONSUMER_SYMBOL_STRING
) {
const consumerResolvedContext = type._context;
context = consumerResolvedContext._currentValue || null;
let current = ((fiber: any): Fiber).return;
while (current !== null) {
const currentType = current.type;
const currentTypeSymbol = getTypeSymbol(currentType);
if (
currentTypeSymbol === CONTEXT_SYMBOL_STRING
) {
const providerResolvedContext = currentType;
if (providerResolvedContext === consumerResolvedContext) {
context = current.memoizedProps.value;
break;
}
}
current = current.return;
}
}
let hasLegacyContext = false;
if (context !== null) {
hasLegacyContext = !!type.contextTypes;
context = {value: context};
}
const owners: null | Array<SerializedElement> =
getOwnersListFromInstance(fiberInstance);
let hooks: null | HooksTree = null;
if (usesHooks) {
hooks = inspectHooks(fiber);
}
let rootType = null;
let current = fiber;
let hasErrorBoundary = false;
let hasSuspenseBoundary = false;
while (current.return !== null) {
const temp = current;
current = current.return;
if (temp.tag === SuspenseComponent) {
hasSuspenseBoundary = true;
} else if (isErrorBoundary(temp)) {
hasErrorBoundary = true;
}
}
const fiberRoot = current.stateNode;
if (fiberRoot != null && fiberRoot._debugRootType !== null) {
rootType = fiberRoot._debugRootType;
}
let isErrored = false;
if (isErrorBoundary(fiber)) {
const DidCapture = 0b000000000000000000010000000;
isErrored =
(fiber.flags & DidCapture) !== 0 ||
forceErrorForFibers.get(fiber) === true ||
(fiber.alternate !== null &&
forceErrorForFibers.get(fiber.alternate) === true);
}
const plugins: Plugins = {
stylex: null,
};
if (enableStyleXFeatures) {
if (memoizedProps != null && memoizedProps.hasOwnProperty('xstyle')) {
plugins.stylex = getStyleXData(memoizedProps.xstyle);
}
}
let source = null;
if (canViewSource) {
source = getSourceForFiberInstance(fiberInstance);
}
let componentLogsEntry = fiberToComponentLogsMap.get(fiber);
if (componentLogsEntry === undefined && fiber.alternate !== null) {
componentLogsEntry = fiberToComponentLogsMap.get(fiber.alternate);
}
let nativeTag = null;
if (elementType === ElementTypeHostComponent) {
nativeTag = getNativeTag(fiber.stateNode);
}
let isSuspended: boolean | null = null;
if (tag === SuspenseComponent) {
isSuspended = memoizedState !== null;
}
const suspendedBy =
fiberInstance.suspenseNode !== null
?
getSuspendedByOfSuspenseNode(fiberInstance.suspenseNode, null)
: tag === ActivityComponent
?
getSuspendedByOfInstanceSubtree(fiberInstance)
:
getSuspendedByOfInstance(fiberInstance, hooks);
const suspendedByRange = getSuspendedByRange(
getNearestSuspenseNode(fiberInstance),
);
let unknownSuspenders = UNKNOWN_SUSPENDERS_NONE;
if (
fiberInstance.suspenseNode !== null &&
fiberInstance.suspenseNode.hasUnknownSuspenders &&
!isSuspended
) {
if (renderer.bundleType === 0) {
unknownSuspenders = UNKNOWN_SUSPENDERS_REASON_PRODUCTION;
} else if (!('_debugInfo' in fiber)) {
unknownSuspenders = UNKNOWN_SUSPENDERS_REASON_OLD_VERSION;
} else {
unknownSuspenders = UNKNOWN_SUSPENDERS_REASON_THROWN_PROMISE;
}
}
return {
id: fiberInstance.id,
canEditHooks: typeof overrideHookState === 'function',
canEditFunctionProps: typeof overrideProps === 'function',
canEditHooksAndDeletePaths:
typeof overrideHookStateDeletePath === 'function',
canEditHooksAndRenamePaths:
typeof overrideHookStateRenamePath === 'function',
canEditFunctionPropsDeletePaths:
typeof overridePropsDeletePath === 'function',
canEditFunctionPropsRenamePaths:
typeof overridePropsRenamePath === 'function',
canToggleError: supportsTogglingError && hasErrorBoundary,
isErrored,
canToggleSuspense:
supportsTogglingSuspense &&
hasSuspenseBoundary &&
(!isSuspended ||
forceFallbackForFibers.has(fiber) ||
(fiber.alternate !== null &&
forceFallbackForFibers.has(fiber.alternate))),
isSuspended: isSuspended,
source,
stack:
fiber._debugOwner == null || fiber._debugStack == null
? null
: parseStackTrace(fiber._debugStack, 1),
hasLegacyContext,
key: key != null && key !== REACT_OPTIMISTIC_KEY ? key : null,
type: elementType,
context,
hooks,
props: memoizedProps,
state: showState ? memoizedState : null,
errors:
componentLogsEntry === undefined
? []
: Array.from(componentLogsEntry.errors.entries()),
warnings:
componentLogsEntry === undefined
? []
: Array.from(componentLogsEntry.warnings.entries()),
suspendedBy: suspendedBy,
suspendedByRange: suspendedByRange,
unknownSuspenders: unknownSuspenders,
owners,
env: null,
rootType,
rendererPackageName: renderer.rendererPackageName,
rendererVersion: renderer.version,
plugins,
nativeTag,
};
}
function inspectVirtualInstanceRaw(
virtualInstance: VirtualInstance,
): InspectedElement | null {
const source = getSourceForInstance(virtualInstance);
const componentInfo = virtualInstance.data;
const key =
typeof componentInfo.key === 'string' ? componentInfo.key : null;
const props = componentInfo.props == null ? null : componentInfo.props;
const owners: null | Array<SerializedElement> =
getOwnersListFromInstance(virtualInstance);
let rootType = null;
let hasErrorBoundary = false;
let hasSuspenseBoundary = false;
const nearestFiber = getNearestFiber(virtualInstance);
if (nearestFiber !== null) {
let current = nearestFiber;
while (current.return !== null) {
const temp = current;
current = current.return;
if (temp.tag === SuspenseComponent) {
hasSuspenseBoundary = true;
} else if (isErrorBoundary(temp)) {
hasErrorBoundary = true;
}
}
const fiberRoot = current.stateNode;
if (fiberRoot != null && fiberRoot._debugRootType !== null) {
rootType = fiberRoot._debugRootType;
}
}
const plugins: Plugins = {
stylex: null,
};
const componentLogsEntry =
componentInfoToComponentLogsMap.get(componentInfo);
const isSuspended = null;
const suspendedBy = getSuspendedByOfInstance(virtualInstance, null);
const suspendedByRange = getSuspendedByRange(
getNearestSuspenseNode(virtualInstance),
);
return {
id: virtualInstance.id,
canEditHooks: false,
canEditFunctionProps: false,
canEditHooksAndDeletePaths: false,
canEditHooksAndRenamePaths: false,
canEditFunctionPropsDeletePaths: false,
canEditFunctionPropsRenamePaths: false,
canToggleError: supportsTogglingError && hasErrorBoundary,
isErrored: false,
canToggleSuspense: supportsTogglingSuspense && hasSuspenseBoundary,
isSuspended: isSuspended,
source,
stack:
componentInfo.owner == null || componentInfo.debugStack == null
? null
: parseStackTrace(componentInfo.debugStack, 1),
hasLegacyContext: false,
key: key,
type: ElementTypeVirtual,
context: null,
hooks: null,
props: props,
state: null,
errors:
componentLogsEntry === undefined
? []
: Array.from(componentLogsEntry.errors.entries()),
warnings:
componentLogsEntry === undefined
? []
: Array.from(componentLogsEntry.warnings.entries()),
suspendedBy: suspendedBy,
suspendedByRange: suspendedByRange,
unknownSuspenders: UNKNOWN_SUSPENDERS_NONE,
owners,
env: componentInfo.env == null ? null : componentInfo.env,
rootType,
rendererPackageName: renderer.rendererPackageName,
rendererVersion: renderer.version,
plugins,
nativeTag: null,
};
}
let mostRecentlyInspectedElement: InspectedElement | null = null;
let hasElementUpdatedSinceLastInspected: boolean = false;
let currentlyInspectedPaths: Object = {};
function isMostRecentlyInspectedElement(id: number): boolean {
if (mostRecentlyInspectedElement === null) {
return false;
}
if (mostRecentlyInspectedElement.id === id) {
return true;
}
if (mostRecentlyInspectedElement.type === ElementTypeRoot) {
const instance = idToDevToolsInstanceMap.get(id);
return (
instance !== undefined &&
instance.kind === FIBER_INSTANCE &&
instance.parent === null
);
}
return false;
}
function isMostRecentlyInspectedElementCurrent(id: number): boolean {
return (
isMostRecentlyInspectedElement(id) && !hasElementUpdatedSinceLastInspected
);
}
function mergeInspectedPaths(path: Array<string | number>) {
let current = currentlyInspectedPaths;
path.forEach(key => {
if (!current[key]) {
current[key] = {};
}
current = current[key];
});
}
function createIsPathAllowed(
key: string | null,
secondaryCategory: 'suspendedBy' | 'hooks' | null,
) {
return function isPathAllowed(path: Array<string | number>): boolean {
switch (secondaryCategory) {
case 'hooks':
if (path.length === 1) {
return true;
}
if (
path[path.length - 2] === 'hookSource' &&
path[path.length - 1] === 'fileName'
) {
return true;
}
if (
path[path.length - 1] === 'subHooks' ||
path[path.length - 2] === 'subHooks'
) {
return true;
}
break;
case 'suspendedBy':
if (path.length < 5) {
return true;
}
break;
default:
break;
}
let current =
key === null ? currentlyInspectedPaths : currentlyInspectedPaths[key];
if (!current) {
return false;
}
for (let i = 0; i < path.length; i++) {
current = current[path[i]];
if (!current) {
return false;
}
}
return true;
};
}
function updateSelectedElement(inspectedElement: InspectedElement): void {
const {hooks, id, props} = inspectedElement;
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
if (devtoolsInstance === undefined) {
console.warn(`Could not find DevToolsInstance with id "${id}"`);
return;
}
if (devtoolsInstance.kind !== FIBER_INSTANCE) {
return;
}
const fiber = devtoolsInstance.data;
const {elementType, stateNode, tag, type} = fiber;
switch (tag) {
case ClassComponent:
case IncompleteClassComponent:
case IndeterminateComponent:
global.$r = stateNode;
break;
case IncompleteFunctionComponent:
case FunctionComponent:
global.$r = {
hooks,
props,
type,
};
break;
case ForwardRef:
global.$r = {
hooks,
props,
type: type.render,
};
break;
case MemoComponent:
case SimpleMemoComponent:
global.$r = {
hooks,
props,
type:
elementType != null && elementType.type != null
? elementType.type
: type,
};
break;
default:
global.$r = null;
break;
}
}
function storeAsGlobal(
id: number,
path: Array<string | number>,
count: number,
): void {
if (isMostRecentlyInspectedElement(id)) {
const value = getInObject(
((mostRecentlyInspectedElement: any): InspectedElement),
path,
);
const key = `$reactTemp${count}`;
window[key] = value;
console.log(key);
console.log(value);
}
}
function getSerializedElementValueByPath(
id: number,
path: Array<string | number>,
): ?string {
if (isMostRecentlyInspectedElement(id)) {
const valueToCopy = getInObject(
((mostRecentlyInspectedElement: any): InspectedElement),
path,
);
return serializeToString(valueToCopy);
}
}
function inspectElement(
requestID: number,
id: number,
path: Array<string | number> | null,
forceFullData: boolean,
): InspectedElementPayload {
if (path !== null) {
mergeInspectedPaths(path);
}
if (isMostRecentlyInspectedElement(id) && !forceFullData) {
if (!hasElementUpdatedSinceLastInspected) {
if (path !== null) {
let secondaryCategory: 'suspendedBy' | 'hooks' | null = null;
if (path[0] === 'hooks' || path[0] === 'suspendedBy') {
secondaryCategory = path[0];
}
return {
id,
responseID: requestID,
type: 'hydrated-path',
path,
value: cleanForBridge(
getInObject(
((mostRecentlyInspectedElement: any): InspectedElement),
path,
),
createIsPathAllowed(null, secondaryCategory),
path,
),
};
} else {
return {
id,
responseID: requestID,
type: 'no-change',
};
}
}
} else {
currentlyInspectedPaths = {};
}
hasElementUpdatedSinceLastInspected = false;
try {
mostRecentlyInspectedElement = inspectElementRaw(id);
} catch (error) {
if (error.name === 'ReactDebugToolsRenderError') {
let message = 'Error rendering inspected element.';
let stack;
console.error(message + '\n\n', error);
if (error.cause != null) {
const componentName = getDisplayNameForElementID(id);
console.error(
'React DevTools encountered an error while trying to inspect hooks. ' +
'This is most likely caused by an error in current inspected component' +
(componentName != null ? `: "${componentName}".` : '.') +
'\nThe error thrown in the component is: \n\n',
error.cause,
);
if (error.cause instanceof Error) {
message = error.cause.message || message;
stack = error.cause.stack;
}
}
return {
type: 'error',
errorType: 'user',
id,
responseID: requestID,
message,
stack,
};
}
if (error.name === 'ReactDebugToolsUnsupportedHookError') {
return {
type: 'error',
errorType: 'unknown-hook',
id,
responseID: requestID,
message:
'Unsupported hook in the react-debug-tools package: ' +
error.message,
};
}
console.error('Error inspecting element.\n\n', error);
return {
type: 'error',
errorType: 'uncaught',
id,
responseID: requestID,
message: error.message,
stack: error.stack,
};
}
if (mostRecentlyInspectedElement === null) {
return {
id,
responseID: requestID,
type: 'not-found',
};
}
const inspectedElement = mostRecentlyInspectedElement;
updateSelectedElement(inspectedElement);
const cleanedInspectedElement = {...inspectedElement};
cleanedInspectedElement.context = cleanForBridge(
inspectedElement.context,
createIsPathAllowed('context', null),
);
cleanedInspectedElement.hooks = cleanForBridge(
inspectedElement.hooks,
createIsPathAllowed('hooks', 'hooks'),
);
cleanedInspectedElement.props = cleanForBridge(
inspectedElement.props,
createIsPathAllowed('props', null),
);
cleanedInspectedElement.state = cleanForBridge(
inspectedElement.state,
createIsPathAllowed('state', null),
);
cleanedInspectedElement.suspendedBy = cleanForBridge(
inspectedElement.suspendedBy,
createIsPathAllowed('suspendedBy', 'suspendedBy'),
);
return {
id,
responseID: requestID,
type: 'full-data',
value: cleanedInspectedElement,
};
}
function inspectRootsRaw(arbitraryRootID: number): InspectedElement | null {
const roots = hook.getFiberRoots(rendererID);
if (roots.size === 0) {
return null;
}
const inspectedRoots: InspectedElement = {
id: arbitraryRootID,
type: ElementTypeRoot,
isErrored: false,
errors: [],
warnings: [],
suspendedBy: [],
suspendedByRange: null,
unknownSuspenders: UNKNOWN_SUSPENDERS_NONE,
rootType: null,
plugins: {stylex: null},
nativeTag: null,
env: null,
source: null,
stack: null,
rendererPackageName: null,
rendererVersion: null,
key: null,
canEditFunctionProps: false,
canEditHooks: false,
canEditFunctionPropsDeletePaths: false,
canEditFunctionPropsRenamePaths: false,
canEditHooksAndDeletePaths: false,
canEditHooksAndRenamePaths: false,
canToggleError: false,
canToggleSuspense: false,
isSuspended: false,
hasLegacyContext: false,
context: null,
hooks: null,
props: null,
state: null,
owners: null,
};
let minSuspendedByRange = Infinity;
let maxSuspendedByRange = -Infinity;
roots.forEach(root => {
const rootInstance = rootToFiberInstanceMap.get(root);
if (rootInstance === undefined) {
throw new Error(
'Expected a root instance to exist for this Fiber root',
);
}
const inspectedRoot = inspectFiberInstanceRaw(rootInstance);
if (inspectedRoot === null) {
return;
}
if (inspectedRoot.isErrored) {
inspectedRoots.isErrored = true;
}
for (let i = 0; i < inspectedRoot.errors.length; i++) {
inspectedRoots.errors.push(inspectedRoot.errors[i]);
}
for (let i = 0; i < inspectedRoot.warnings.length; i++) {
inspectedRoots.warnings.push(inspectedRoot.warnings[i]);
}
for (let i = 0; i < inspectedRoot.suspendedBy.length; i++) {
inspectedRoots.suspendedBy.push(inspectedRoot.suspendedBy[i]);
}
const suspendedByRange = inspectedRoot.suspendedByRange;
if (suspendedByRange !== null) {
if (suspendedByRange[0] < minSuspendedByRange) {
minSuspendedByRange = suspendedByRange[0];
}
if (suspendedByRange[1] > maxSuspendedByRange) {
maxSuspendedByRange = suspendedByRange[1];
}
}
});
if (minSuspendedByRange !== Infinity || maxSuspendedByRange !== -Infinity) {
inspectedRoots.suspendedByRange = [
minSuspendedByRange,
maxSuspendedByRange,
];
}
return inspectedRoots;
}
function logElementToConsole(id: number) {
const result = isMostRecentlyInspectedElementCurrent(id)
? mostRecentlyInspectedElement
: inspectElementRaw(id);
if (result === null) {
console.warn(`Could not find DevToolsInstance with id "${id}"`);
return;
}
const displayName = getDisplayNameForElementID(id);
const supportsGroup = typeof console.groupCollapsed === 'function';
if (supportsGroup) {
console.groupCollapsed(
`[Click to expand] %c<${displayName || 'Component'} />`,
'color: var(--dom-tag-name-color); font-weight: normal;',
);
}
if (result.props !== null) {
console.log('Props:', result.props);
}
if (result.state !== null) {
console.log('State:', result.state);
}
if (result.hooks !== null) {
console.log('Hooks:', result.hooks);
}
const hostInstances = findHostInstancesForElementID(id);
if (hostInstances !== null) {
console.log('Nodes:', hostInstances);
}
if (window.chrome || /firefox/i.test(navigator.userAgent)) {
console.log(
'Right-click any value to save it as a global variable for further inspection.',
);
}
if (supportsGroup) {
console.groupEnd();
}
}
function deletePath(
type: 'context' | 'hooks' | 'props' | 'state',
id: number,
hookID: ?number,
path: Array<string | number>,
): void {
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
if (devtoolsInstance === undefined) {
console.warn(`Could not find DevToolsInstance with id "${id}"`);
return;
}
if (devtoolsInstance.kind !== FIBER_INSTANCE) {
return;
}
const fiber = devtoolsInstance.data;
if (fiber !== null) {
const instance = fiber.stateNode;
switch (type) {
case 'context':
path = path.slice(1);
switch (fiber.tag) {
case ClassComponent:
if (path.length === 0) {
} else {
deletePathInObject(instance.context, path);
}
instance.forceUpdate();
break;
case FunctionComponent:
break;
}
break;
case 'hooks':
if (typeof overrideHookStateDeletePath === 'function') {
overrideHookStateDeletePath(fiber, ((hookID: any): number), path);
}
break;
case 'props':
if (instance === null) {
if (typeof overridePropsDeletePath === 'function') {
overridePropsDeletePath(fiber, path);
}
} else {
fiber.pendingProps = copyWithDelete(instance.props, path);
instance.forceUpdate();
}
break;
case 'state':
deletePathInObject(instance.state, path);
instance.forceUpdate();
break;
}
}
}
function renamePath(
type: 'context' | 'hooks' | 'props' | 'state',
id: number,
hookID: ?number,
oldPath: Array<string | number>,
newPath: Array<string | number>,
): void {
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
if (devtoolsInstance === undefined) {
console.warn(`Could not find DevToolsInstance with id "${id}"`);
return;
}
if (devtoolsInstance.kind !== FIBER_INSTANCE) {
return;
}
const fiber = devtoolsInstance.data;
if (fiber !== null) {
const instance = fiber.stateNode;
switch (type) {
case 'context':
oldPath = oldPath.slice(1);
newPath = newPath.slice(1);
switch (fiber.tag) {
case ClassComponent:
if (oldPath.length === 0) {
} else {
renamePathInObject(instance.context, oldPath, newPath);
}
instance.forceUpdate();
break;
case FunctionComponent:
break;
}
break;
case 'hooks':
if (typeof overrideHookStateRenamePath === 'function') {
overrideHookStateRenamePath(
fiber,
((hookID: any): number),
oldPath,
newPath,
);
}
break;
case 'props':
if (instance === null) {
if (typeof overridePropsRenamePath === 'function') {
overridePropsRenamePath(fiber, oldPath, newPath);
}
} else {
fiber.pendingProps = copyWithRename(
instance.props,
oldPath,
newPath,
);
instance.forceUpdate();
}
break;
case 'state':
renamePathInObject(instance.state, oldPath, newPath);
instance.forceUpdate();
break;
}
}
}
function overrideValueAtPath(
type: 'context' | 'hooks' | 'props' | 'state',
id: number,
hookID: ?number,
path: Array<string | number>,
value: any,
): void {
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
if (devtoolsInstance === undefined) {
console.warn(`Could not find DevToolsInstance with id "${id}"`);
return;
}
if (devtoolsInstance.kind !== FIBER_INSTANCE) {
return;
}
const fiber = devtoolsInstance.data;
if (fiber !== null) {
const instance = fiber.stateNode;
switch (type) {
case 'context':
path = path.slice(1);
switch (fiber.tag) {
case ClassComponent:
if (path.length === 0) {
instance.context = value;
} else {
setInObject(instance.context, path, value);
}
instance.forceUpdate();
break;
case FunctionComponent:
break;
}
break;
case 'hooks':
if (typeof overrideHookState === 'function') {
overrideHookState(fiber, ((hookID: any): number), path, value);
}
break;
case 'props':
switch (fiber.tag) {
case ClassComponent:
fiber.pendingProps = copyWithSet(instance.props, path, value);
instance.forceUpdate();
break;
default:
if (typeof overrideProps === 'function') {
overrideProps(fiber, path, value);
}
break;
}
break;
case 'state':
switch (fiber.tag) {
case ClassComponent:
setInObject(instance.state, path, value);
instance.forceUpdate();
break;
}
break;
}
}
}
type CommitProfilingData = {
changeDescriptions: Map<number, ChangeDescription> | null,
commitTime: number,
durations: Array<number>,
effectDuration: number | null,
maxActualDuration: number,
passiveEffectDuration: number | null,
priorityLevel: string | null,
updaters: Array<SerializedElement> | null,
};
type CommitProfilingMetadataMap = Map<number, Array<CommitProfilingData>>;
type DisplayNamesByRootID = Map<number, string>;
let currentCommitProfilingMetadata: CommitProfilingData | null = null;
let displayNamesByRootID: DisplayNamesByRootID | null = null;
let initialTreeBaseDurationsMap: Map<number, Array<[number, number]>> | null =
null;
let isProfiling: boolean = false;
let profilingStartTime: number = 0;
let recordChangeDescriptions: boolean = false;
let recordTimeline: boolean = false;
let rootToCommitProfilingMetadataMap: CommitProfilingMetadataMap | null =
null;
function getProfilingData(): ProfilingDataBackend {
const dataForRoots: Array<ProfilingDataForRootBackend> = [];
if (rootToCommitProfilingMetadataMap === null) {
throw Error(
'getProfilingData() called before any profiling data was recorded',
);
}
rootToCommitProfilingMetadataMap.forEach(
(commitProfilingMetadata, rootID) => {
const commitData: Array<CommitDataBackend> = [];
const displayName =
(displayNamesByRootID !== null && displayNamesByRootID.get(rootID)) ||
'Unknown';
const initialTreeBaseDurations: Array<[number, number]> =
(initialTreeBaseDurationsMap !== null &&
initialTreeBaseDurationsMap.get(rootID)) ||
[];
commitProfilingMetadata.forEach((commitProfilingData, commitIndex) => {
const {
changeDescriptions,
durations,
effectDuration,
maxActualDuration,
passiveEffectDuration,
priorityLevel,
commitTime,
updaters,
} = commitProfilingData;
const fiberActualDurations: Array<[number, number]> = [];
const fiberSelfDurations: Array<[number, number]> = [];
for (let i = 0; i < durations.length; i += 3) {
const fiberID = durations[i];
fiberActualDurations.push([
fiberID,
formatDurationToMicrosecondsGranularity(durations[i + 1]),
]);
fiberSelfDurations.push([
fiberID,
formatDurationToMicrosecondsGranularity(durations[i + 2]),
]);
}
commitData.push({
changeDescriptions:
changeDescriptions !== null
? Array.from(changeDescriptions.entries())
: null,
duration:
formatDurationToMicrosecondsGranularity(maxActualDuration),
effectDuration:
effectDuration !== null
? formatDurationToMicrosecondsGranularity(effectDuration)
: null,
fiberActualDurations,
fiberSelfDurations,
passiveEffectDuration:
passiveEffectDuration !== null
? formatDurationToMicrosecondsGranularity(passiveEffectDuration)
: null,
priorityLevel,
timestamp: commitTime,
updaters,
});
});
dataForRoots.push({
commitData,
displayName,
initialTreeBaseDurations,
rootID,
});
},
);
let timelineData = null;
if (typeof getTimelineData === 'function') {
const currentTimelineData = getTimelineData();
if (currentTimelineData) {
const {
batchUIDToMeasuresMap,
internalModuleSourceToRanges,
laneToLabelMap,
laneToReactMeasureMap,
...rest
} = currentTimelineData;
timelineData = {
...rest,
batchUIDToMeasuresKeyValueArray: Array.from(
batchUIDToMeasuresMap.entries(),
),
internalModuleSourceToRanges: Array.from(
internalModuleSourceToRanges.entries(),
),
laneToLabelKeyValueArray: Array.from(laneToLabelMap.entries()),
laneToReactMeasureKeyValueArray: Array.from(
laneToReactMeasureMap.entries(),
),
};
}
}
return {
dataForRoots,
rendererID,
timelineData,
};
}
function snapshotTreeBaseDurations(
instance: DevToolsInstance,
target: Array<[number, number]>,
) {
if (instance.kind !== FILTERED_FIBER_INSTANCE) {
target.push([instance.id, instance.treeBaseDuration]);
}
for (
let child = instance.firstChild;
child !== null;
child = child.nextSibling
) {
snapshotTreeBaseDurations(child, target);
}
}
function startProfiling(
shouldRecordChangeDescriptions: boolean,
shouldRecordTimeline: boolean,
) {
if (isProfiling) {
return;
}
recordChangeDescriptions = shouldRecordChangeDescriptions;
recordTimeline = shouldRecordTimeline;
displayNamesByRootID = new Map();
initialTreeBaseDurationsMap = new Map();
hook.getFiberRoots(rendererID).forEach(root => {
const rootInstance = rootToFiberInstanceMap.get(root);
if (rootInstance === undefined) {
throw new Error(
'Expected the root instance to already exist when starting profiling',
);
}
const rootID = rootInstance.id;
((displayNamesByRootID: any): DisplayNamesByRootID).set(
rootID,
getDisplayNameForRoot(root.current),
);
const initialTreeBaseDurations: Array<[number, number]> = [];
snapshotTreeBaseDurations(rootInstance, initialTreeBaseDurations);
(initialTreeBaseDurationsMap: any).set(rootID, initialTreeBaseDurations);
});
isProfiling = true;
profilingStartTime = getCurrentTime();
rootToCommitProfilingMetadataMap = new Map();
if (toggleProfilingStatus !== null) {
toggleProfilingStatus(true, recordTimeline);
}
}
function stopProfiling() {
isProfiling = false;
recordChangeDescriptions = false;
if (toggleProfilingStatus !== null) {
toggleProfilingStatus(false, recordTimeline);
}
recordTimeline = false;
}
if (shouldStartProfilingNow) {
startProfiling(
profilingSettings.recordChangeDescriptions,
profilingSettings.recordTimeline,
);
}
function getNearestFiber(devtoolsInstance: DevToolsInstance): null | Fiber {
if (devtoolsInstance.kind === VIRTUAL_INSTANCE) {
let inst: DevToolsInstance = devtoolsInstance;
while (inst.kind === VIRTUAL_INSTANCE) {
if (inst.firstChild === null) {
return null;
}
inst = inst.firstChild;
}
return inst.data.return;
} else {
return devtoolsInstance.data;
}
}
function shouldErrorFiberAlwaysNull() {
return null;
}
const forceErrorForFibers = new Map<Fiber, boolean>();
function shouldErrorFiberAccordingToMap(fiber: any): boolean {
if (typeof setErrorHandler !== 'function') {
throw new Error(
'Expected overrideError() to not get called for earlier React versions.',
);
}
let status = forceErrorForFibers.get(fiber);
if (status === false) {
forceErrorForFibers.delete(fiber);
if (forceErrorForFibers.size === 0) {
setErrorHandler(shouldErrorFiberAlwaysNull);
}
return false;
}
if (status === undefined && fiber.alternate !== null) {
status = forceErrorForFibers.get(fiber.alternate);
if (status === false) {
forceErrorForFibers.delete(fiber.alternate);
if (forceErrorForFibers.size === 0) {
setErrorHandler(shouldErrorFiberAlwaysNull);
}
}
}
if (status === undefined) {
return false;
}
return status;
}
function overrideError(id: number, forceError: boolean) {
if (
typeof setErrorHandler !== 'function' ||
typeof scheduleUpdate !== 'function'
) {
throw new Error(
'Expected overrideError() to not get called for earlier React versions.',
);
}
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
if (devtoolsInstance === undefined) {
return;
}
const nearestFiber = getNearestFiber(devtoolsInstance);
if (nearestFiber === null) {
return;
}
let fiber = nearestFiber;
while (!isErrorBoundary(fiber)) {
if (fiber.return === null) {
return;
}
fiber = fiber.return;
}
forceErrorForFibers.set(fiber, forceError);
if (fiber.alternate !== null) {
forceErrorForFibers.delete(fiber.alternate);
}
if (forceErrorForFibers.size === 1) {
setErrorHandler(shouldErrorFiberAccordingToMap);
}
if (!forceError && typeof scheduleRetry === 'function') {
scheduleRetry(fiber);
} else {
scheduleUpdate(fiber);
}
}
function shouldSuspendFiberAlwaysFalse() {
return false;
}
const forceFallbackForFibers = new Set<Fiber>();
function shouldSuspendFiberAccordingToSet(fiber: Fiber): boolean {
return (
forceFallbackForFibers.has(fiber) ||
(fiber.alternate !== null && forceFallbackForFibers.has(fiber.alternate))
);
}
function overrideSuspense(id: number, forceFallback: boolean) {
if (
typeof setSuspenseHandler !== 'function' ||
typeof scheduleUpdate !== 'function'
) {
throw new Error(
'Expected overrideSuspense() to not get called for earlier React versions.',
);
}
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
if (devtoolsInstance === undefined) {
return;
}
const nearestFiber = getNearestFiber(devtoolsInstance);
if (nearestFiber === null) {
return;
}
let fiber = nearestFiber;
while (fiber.tag !== SuspenseComponent) {
if (fiber.return === null) {
return;
}
fiber = fiber.return;
}
if (fiber.alternate !== null) {
forceFallbackForFibers.delete(fiber.alternate);
}
if (forceFallback) {
forceFallbackForFibers.add(fiber);
if (forceFallbackForFibers.size === 1) {
setSuspenseHandler(shouldSuspendFiberAccordingToSet);
}
} else {
forceFallbackForFibers.delete(fiber);
if (forceFallbackForFibers.size === 0) {
setSuspenseHandler(shouldSuspendFiberAlwaysFalse);
}
}
if (!forceFallback && typeof scheduleRetry === 'function') {
scheduleRetry(fiber);
} else {
scheduleUpdate(fiber);
}
}
function overrideSuspenseMilestone(suspendedSet: Array<FiberInstance['id']>) {
if (
typeof setSuspenseHandler !== 'function' ||
typeof scheduleUpdate !== 'function'
) {
throw new Error(
'Expected overrideSuspenseMilestone() to not get called for earlier React versions.',
);
}
const unsuspendedSet: Set<Fiber> = new Set(forceFallbackForFibers);
let resuspended = false;
for (let i = 0; i < suspendedSet.length; ++i) {
const instance = idToDevToolsInstanceMap.get(suspendedSet[i]);
if (instance === undefined) {
console.warn(
`Could not suspend ID '${suspendedSet[i]}' since the instance can't be found.`,
);
continue;
}
if (instance.kind === FIBER_INSTANCE) {
const fiber = instance.data;
if (
forceFallbackForFibers.has(fiber) ||
(fiber.alternate !== null &&
forceFallbackForFibers.has(fiber.alternate))
) {
unsuspendedSet.delete(fiber);
if (fiber.alternate !== null) {
unsuspendedSet.delete(fiber.alternate);
}
} else {
forceFallbackForFibers.add(fiber);
scheduleUpdate(fiber);
resuspended = true;
}
} else {
console.warn(`Cannot not suspend ID '${suspendedSet[i]}'.`);
}
}
unsuspendedSet.forEach(fiber => {
forceFallbackForFibers.delete(fiber);
if (!resuspended && typeof scheduleRetry === 'function') {
scheduleRetry(fiber);
} else {
scheduleUpdate(fiber);
}
});
if (forceFallbackForFibers.size > 0) {
setSuspenseHandler(shouldSuspendFiberAccordingToSet);
} else {
setSuspenseHandler(shouldSuspendFiberAlwaysFalse);
}
}
let trackedPath: Array<PathFrame> | null = null;
let trackedPathMatchFiber: Fiber | null = null;
let trackedPathMatchInstance: FiberInstance | VirtualInstance | null = null;
let trackedPathMatchDepth = -1;
let mightBeOnTrackedPath = false;
function setTrackedPath(path: Array<PathFrame> | null) {
if (path === null) {
trackedPathMatchFiber = null;
trackedPathMatchInstance = null;
trackedPathMatchDepth = -1;
mightBeOnTrackedPath = false;
}
trackedPath = path;
}
function updateTrackedPathStateBeforeMount(
fiber: Fiber,
fiberInstance: null | FiberInstance | FilteredFiberInstance,
): boolean {
if (trackedPath === null || !mightBeOnTrackedPath) {
return false;
}
const returnFiber = fiber.return;
const returnAlternate = returnFiber !== null ? returnFiber.alternate : null;
if (
trackedPathMatchFiber === returnFiber ||
(trackedPathMatchFiber === returnAlternate && returnAlternate !== null)
) {
const actualFrame = getPathFrame(fiber);
const expectedFrame = trackedPath[trackedPathMatchDepth + 1];
if (expectedFrame === undefined) {
throw new Error('Expected to see a frame at the next depth.');
}
if (
actualFrame.index === expectedFrame.index &&
actualFrame.key === expectedFrame.key &&
actualFrame.displayName === expectedFrame.displayName
) {
trackedPathMatchFiber = fiber;
if (fiberInstance !== null && fiberInstance.kind === FIBER_INSTANCE) {
trackedPathMatchInstance = fiberInstance;
}
trackedPathMatchDepth++;
if (trackedPathMatchDepth === trackedPath.length - 1) {
mightBeOnTrackedPath = false;
} else {
mightBeOnTrackedPath = true;
}
return false;
}
}
if (trackedPathMatchFiber === null && fiberInstance === null) {
return true;
}
mightBeOnTrackedPath = false;
return true;
}
function updateVirtualTrackedPathStateBeforeMount(
virtualInstance: VirtualInstance,
parentInstance: null | DevToolsInstance,
): boolean {
if (trackedPath === null || !mightBeOnTrackedPath) {
return false;
}
if (trackedPathMatchInstance === parentInstance) {
const actualFrame = getVirtualPathFrame(virtualInstance);
const expectedFrame = trackedPath[trackedPathMatchDepth + 1];
if (expectedFrame === undefined) {
throw new Error('Expected to see a frame at the next depth.');
}
if (
actualFrame.index === expectedFrame.index &&
actualFrame.key === expectedFrame.key &&
actualFrame.displayName === expectedFrame.displayName
) {
trackedPathMatchFiber = null;
trackedPathMatchInstance = virtualInstance;
trackedPathMatchDepth++;
if (trackedPathMatchDepth === trackedPath.length - 1) {
mightBeOnTrackedPath = false;
} else {
mightBeOnTrackedPath = true;
}
return false;
}
}
if (trackedPathMatchFiber !== null) {
return true;
}
mightBeOnTrackedPath = false;
return true;
}
function updateTrackedPathStateAfterMount(
mightSiblingsBeOnTrackedPath: boolean,
) {
mightBeOnTrackedPath = mightSiblingsBeOnTrackedPath;
}
const rootPseudoKeys: Map<number, string> = new Map();
const rootDisplayNameCounter: Map<string, number> = new Map();
function setRootPseudoKey(id: number, fiber: Fiber) {
const name = getDisplayNameForRoot(fiber);
const counter = rootDisplayNameCounter.get(name) || 0;
rootDisplayNameCounter.set(name, counter + 1);
const pseudoKey = `${name}:${counter}`;
rootPseudoKeys.set(id, pseudoKey);
}
function removeRootPseudoKey(id: number) {
const pseudoKey = rootPseudoKeys.get(id);
if (pseudoKey === undefined) {
throw new Error('Expected root pseudo key to be known.');
}
const name = pseudoKey.slice(0, pseudoKey.lastIndexOf(':'));
const counter = rootDisplayNameCounter.get(name);
if (counter === undefined) {
throw new Error('Expected counter to be known.');
}
if (counter > 1) {
rootDisplayNameCounter.set(name, counter - 1);
} else {
rootDisplayNameCounter.delete(name);
}
rootPseudoKeys.delete(id);
}
function getDisplayNameForRoot(fiber: Fiber): string {
let preferredDisplayName = null;
let fallbackDisplayName = null;
let child = fiber.child;
for (let i = 0; i < 3; i++) {
if (child === null) {
break;
}
const displayName = getDisplayNameForFiber(child);
if (displayName !== null) {
if (typeof child.type === 'function') {
preferredDisplayName = displayName;
} else if (fallbackDisplayName === null) {
fallbackDisplayName = displayName;
}
}
if (preferredDisplayName !== null) {
break;
}
child = child.child;
}
return preferredDisplayName || fallbackDisplayName || 'Anonymous';
}
function getPathFrame(fiber: Fiber): PathFrame {
const {key} = fiber;
let displayName = getDisplayNameForFiber(fiber);
const index = fiber.index;
switch (fiber.tag) {
case HostRoot:
const rootInstance = rootToFiberInstanceMap.get(fiber.stateNode);
if (rootInstance === undefined) {
throw new Error(
'Expected the root instance to exist when computing a path',
);
}
const pseudoKey = rootPseudoKeys.get(rootInstance.id);
if (pseudoKey === undefined) {
throw new Error('Expected mounted root to have known pseudo key.');
}
displayName = pseudoKey;
break;
case HostComponent:
displayName = fiber.type;
break;
default:
break;
}
return {
displayName,
key: key === REACT_OPTIMISTIC_KEY ? null : key,
index,
};
}
function getVirtualPathFrame(virtualInstance: VirtualInstance): PathFrame {
return {
displayName: virtualInstance.data.name || '',
key:
virtualInstance.data.key == null ||
virtualInstance.data.key === REACT_OPTIMISTIC_KEY
? null
: virtualInstance.data.key,
index: -1,
};
}
function getPathForElement(id: number): Array<PathFrame> | null {
const devtoolsInstance = idToDevToolsInstanceMap.get(id);
if (devtoolsInstance === undefined) {
return null;
}
const keyPath = [];
let inst: DevToolsInstance = devtoolsInstance;
while (inst.kind === VIRTUAL_INSTANCE) {
keyPath.push(getVirtualPathFrame(inst));
if (inst.parent === null) {
return null;
}
inst = inst.parent;
}
let fiber: null | Fiber = inst.data;
while (fiber !== null) {
keyPath.push(getPathFrame(fiber));
fiber = fiber.return;
}
keyPath.reverse();
return keyPath;
}
function getBestMatchForTrackedPath(): PathMatch | null {
if (trackedPath === null) {
return null;
}
if (trackedPathMatchInstance === null) {
return null;
}
return {
id: trackedPathMatchInstance.id,
isFullMatch: trackedPathMatchDepth === trackedPath.length - 1,
};
}
const formatPriorityLevel = (priorityLevel: ?number) => {
if (priorityLevel == null) {
return 'Unknown';
}
switch (priorityLevel) {
case ImmediatePriority:
return 'Immediate';
case UserBlockingPriority:
return 'User-Blocking';
case NormalPriority:
return 'Normal';
case LowPriority:
return 'Low';
case IdlePriority:
return 'Idle';
case NoPriority:
default:
return 'Unknown';
}
};
function setTraceUpdatesEnabled(isEnabled: boolean): void {
traceUpdatesEnabled = isEnabled;
}
function hasElementWithId(id: number): boolean {
return idToDevToolsInstanceMap.has(id);
}
function getSourceForFiberInstance(
fiberInstance: FiberInstance,
): ReactFunctionLocation | null {
const ownerSource = getSourceForInstance(fiberInstance);
if (ownerSource !== null) {
return ownerSource;
}
const dispatcherRef = getDispatcherRef(renderer);
const stackFrame =
dispatcherRef == null
? null
: getSourceLocationByFiber(
ReactTypeOfWork,
fiberInstance.data,
dispatcherRef,
);
if (stackFrame === null) {
return null;
}
const source = extractLocationFromComponentStack(stackFrame);
fiberInstance.source = source;
return source;
}
function getSourceForInstance(
instance: DevToolsInstance,
): ReactFunctionLocation | null {
let unresolvedSource = instance.source;
if (unresolvedSource === null) {
return null;
}
if (instance.kind === VIRTUAL_INSTANCE) {
const debugLocation = instance.data.debugLocation;
if (debugLocation != null) {
unresolvedSource = debugLocation;
}
}
if (isError(unresolvedSource)) {
return (instance.source = extractLocationFromOwnerStack(
(unresolvedSource: any),
));
}
if (typeof unresolvedSource === 'string') {
const idx = unresolvedSource.lastIndexOf('\n');
const lastLine =
idx === -1 ? unresolvedSource : unresolvedSource.slice(idx + 1);
return (instance.source = extractLocationFromComponentStack(lastLine));
}
return unresolvedSource;
}
type InternalMcpFunctions = {
__internal_only_getComponentTree?: Function,
};
const internalMcpFunctions: InternalMcpFunctions = {};
if (__IS_INTERNAL_MCP_BUILD__) {
function __internal_only_getComponentTree(): string {
let treeString = '';
function buildTreeString(
instance: DevToolsInstance,
prefix: string = '',
isLastChild: boolean = true,
): void {
if (!instance) return;
const name =
(instance.kind !== VIRTUAL_INSTANCE
? getDisplayNameForFiber(instance.data)
: instance.data.name) || 'Unknown';
const id = instance.id !== undefined ? instance.id : 'unknown';
if (name !== 'createRoot()') {
treeString +=
prefix +
(isLastChild ? '└── ' : '├── ') +
name +
' (id: ' +
id +
')\n';
}
const childPrefix = prefix + (isLastChild ? ' ' : '│ ');
let childCount = 0;
let tempChild = instance.firstChild;
while (tempChild !== null) {
childCount++;
tempChild = tempChild.nextSibling;
}
let child = instance.firstChild;
let currentChildIndex = 0;
while (child !== null) {
currentChildIndex++;
const isLastSibling = currentChildIndex === childCount;
buildTreeString(child, childPrefix, isLastSibling);
child = child.nextSibling;
}
}
const rootInstances: Array<DevToolsInstance> = [];
idToDevToolsInstanceMap.forEach(instance => {
if (instance.parent === null || instance.parent.parent === null) {
rootInstances.push(instance);
}
});
if (rootInstances.length > 0) {
for (let i = 0; i < rootInstances.length; i++) {
const isLast = i === rootInstances.length - 1;
buildTreeString(rootInstances[i], '', isLast);
if (!isLast) {
treeString += '\n';
}
}
} else {
treeString = 'No component tree found.';
}
return treeString;
}
internalMcpFunctions.__internal_only_getComponentTree =
__internal_only_getComponentTree;
}
return {
cleanup,
clearErrorsAndWarnings,
clearErrorsForElementID,
clearWarningsForElementID,
getSerializedElementValueByPath,
deletePath,
findHostInstancesForElementID,
findLastKnownRectsForID,
flushInitialOperations,
getBestMatchForTrackedPath,
getDisplayNameForElementID,
getNearestMountedDOMNode,
getElementIDForHostInstance,
getSuspenseNodeIDForHostInstance,
getInstanceAndStyle,
getOwnersList,
getPathForElement,
getProfilingData,
handleCommitFiberRoot,
handleCommitFiberUnmount,
handlePostCommitFiberRoot,
hasElementWithId,
inspectElement,
logElementToConsole,
getComponentStack,
getElementAttributeByPath,
getElementSourceFunctionById,
onErrorOrWarning,
overrideError,
overrideSuspense,
overrideSuspenseMilestone,
overrideValueAtPath,
renamePath,
renderer,
setTraceUpdatesEnabled,
setTrackedPath,
startProfiling,
stopProfiling,
storeAsGlobal,
supportsTogglingSuspense,
updateComponentFilters,
getEnvironmentNames,
...internalMcpFunctions,
};
}