1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import type {
    
  11.   Fiber,
    
  12.   FiberRoot,
    
  13.   SuspenseHydrationCallbacks,
    
  14.   TransitionTracingCallbacks,
    
  15. } from './ReactInternalTypes';
    
  16. import type {RootTag} from './ReactRootTags';
    
  17. import type {
    
  18.   Instance,
    
  19.   TextInstance,
    
  20.   Container,
    
  21.   PublicInstance,
    
  22.   RendererInspectionConfig,
    
  23. } from './ReactFiberConfig';
    
  24. import type {ReactNodeList, ReactFormState} from 'shared/ReactTypes';
    
  25. import type {Lane} from './ReactFiberLane';
    
  26. import type {SuspenseState} from './ReactFiberSuspenseComponent';
    
  27. 
    
  28. import {
    
  29.   findCurrentHostFiber,
    
  30.   findCurrentHostFiberWithNoPortals,
    
  31. } from './ReactFiberTreeReflection';
    
  32. import {get as getInstance} from 'shared/ReactInstanceMap';
    
  33. import {
    
  34.   HostComponent,
    
  35.   HostSingleton,
    
  36.   ClassComponent,
    
  37.   HostRoot,
    
  38.   SuspenseComponent,
    
  39. } from './ReactWorkTags';
    
  40. import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
    
  41. import isArray from 'shared/isArray';
    
  42. import {enableSchedulingProfiler} from 'shared/ReactFeatureFlags';
    
  43. import ReactSharedInternals from 'shared/ReactSharedInternals';
    
  44. import {getPublicInstance} from './ReactFiberConfig';
    
  45. import {
    
  46.   findCurrentUnmaskedContext,
    
  47.   processChildContext,
    
  48.   emptyContextObject,
    
  49.   isContextProvider as isLegacyContextProvider,
    
  50. } from './ReactFiberContext';
    
  51. import {createFiberRoot} from './ReactFiberRoot';
    
  52. import {isRootDehydrated} from './ReactFiberShellHydration';
    
  53. import {
    
  54.   injectInternals,
    
  55.   markRenderScheduled,
    
  56.   onScheduleRoot,
    
  57. } from './ReactFiberDevToolsHook';
    
  58. import {
    
  59.   requestUpdateLane,
    
  60.   scheduleUpdateOnFiber,
    
  61.   scheduleInitialHydrationOnRoot,
    
  62.   flushRoot,
    
  63.   batchedUpdates,
    
  64.   flushSync,
    
  65.   isAlreadyRendering,
    
  66.   deferredUpdates,
    
  67.   discreteUpdates,
    
  68.   flushPassiveEffects,
    
  69. } from './ReactFiberWorkLoop';
    
  70. import {enqueueConcurrentRenderForLane} from './ReactFiberConcurrentUpdates';
    
  71. import {
    
  72.   createUpdate,
    
  73.   enqueueUpdate,
    
  74.   entangleTransitions,
    
  75. } from './ReactFiberClassUpdateQueue';
    
  76. import {
    
  77.   isRendering as ReactCurrentFiberIsRendering,
    
  78.   current as ReactCurrentFiberCurrent,
    
  79.   resetCurrentFiber as resetCurrentDebugFiberInDEV,
    
  80.   setCurrentFiber as setCurrentDebugFiberInDEV,
    
  81. } from './ReactCurrentFiber';
    
  82. import {StrictLegacyMode} from './ReactTypeOfMode';
    
  83. import {
    
  84.   SyncLane,
    
  85.   SelectiveHydrationLane,
    
  86.   getHighestPriorityPendingLanes,
    
  87.   higherPriorityLane,
    
  88. } from './ReactFiberLane';
    
  89. import {
    
  90.   getCurrentUpdatePriority,
    
  91.   runWithPriority,
    
  92. } from './ReactEventPriorities';
    
  93. import {
    
  94.   scheduleRefresh,
    
  95.   scheduleRoot,
    
  96.   setRefreshHandler,
    
  97.   findHostInstancesForRefresh,
    
  98. } from './ReactFiberHotReloading';
    
  99. import ReactVersion from 'shared/ReactVersion';
    
  100. export {createPortal} from './ReactPortal';
    
  101. export {
    
  102.   createComponentSelector,
    
  103.   createHasPseudoClassSelector,
    
  104.   createRoleSelector,
    
  105.   createTestNameSelector,
    
  106.   createTextSelector,
    
  107.   getFindAllNodesFailureDescription,
    
  108.   findAllNodes,
    
  109.   findBoundingRects,
    
  110.   focusWithin,
    
  111.   observeVisibleRects,
    
  112. } from './ReactTestSelectors';
    
  113. export {startHostTransition} from './ReactFiberHooks';
    
  114. 
    
  115. type OpaqueRoot = FiberRoot;
    
  116. 
    
  117. // 0 is PROD, 1 is DEV.
    
  118. // Might add PROFILE later.
    
  119. type BundleType = 0 | 1;
    
  120. 
    
  121. type DevToolsConfig = {
    
  122.   bundleType: BundleType,
    
  123.   version: string,
    
  124.   rendererPackageName: string,
    
  125.   // Note: this actually *does* depend on Fiber internal fields.
    
  126.   // Used by "inspect clicked DOM element" in React DevTools.
    
  127.   findFiberByHostInstance?: (instance: Instance | TextInstance) => Fiber | null,
    
  128.   rendererConfig?: RendererInspectionConfig,
    
  129. };
    
  130. 
    
  131. let didWarnAboutNestedUpdates;
    
  132. let didWarnAboutFindNodeInStrictMode;
    
  133. 
    
  134. if (__DEV__) {
    
  135.   didWarnAboutNestedUpdates = false;
    
  136.   didWarnAboutFindNodeInStrictMode = ({}: {[string]: boolean});
    
  137. }
    
  138. 
    
  139. function getContextForSubtree(
    
  140.   parentComponent: ?React$Component<any, any>,
    
  141. ): Object {
    
  142.   if (!parentComponent) {
    
  143.     return emptyContextObject;
    
  144.   }
    
  145. 
    
  146.   const fiber = getInstance(parentComponent);
    
  147.   const parentContext = findCurrentUnmaskedContext(fiber);
    
  148. 
    
  149.   if (fiber.tag === ClassComponent) {
    
  150.     const Component = fiber.type;
    
  151.     if (isLegacyContextProvider(Component)) {
    
  152.       return processChildContext(fiber, Component, parentContext);
    
  153.     }
    
  154.   }
    
  155. 
    
  156.   return parentContext;
    
  157. }
    
  158. 
    
  159. function findHostInstance(component: Object): PublicInstance | null {
    
  160.   const fiber = getInstance(component);
    
  161.   if (fiber === undefined) {
    
  162.     if (typeof component.render === 'function') {
    
  163.       throw new Error('Unable to find node on an unmounted component.');
    
  164.     } else {
    
  165.       const keys = Object.keys(component).join(',');
    
  166.       throw new Error(
    
  167.         `Argument appears to not be a ReactComponent. Keys: ${keys}`,
    
  168.       );
    
  169.     }
    
  170.   }
    
  171.   const hostFiber = findCurrentHostFiber(fiber);
    
  172.   if (hostFiber === null) {
    
  173.     return null;
    
  174.   }
    
  175.   return getPublicInstance(hostFiber.stateNode);
    
  176. }
    
  177. 
    
  178. function findHostInstanceWithWarning(
    
  179.   component: Object,
    
  180.   methodName: string,
    
  181. ): PublicInstance | null {
    
  182.   if (__DEV__) {
    
  183.     const fiber = getInstance(component);
    
  184.     if (fiber === undefined) {
    
  185.       if (typeof component.render === 'function') {
    
  186.         throw new Error('Unable to find node on an unmounted component.');
    
  187.       } else {
    
  188.         const keys = Object.keys(component).join(',');
    
  189.         throw new Error(
    
  190.           `Argument appears to not be a ReactComponent. Keys: ${keys}`,
    
  191.         );
    
  192.       }
    
  193.     }
    
  194.     const hostFiber = findCurrentHostFiber(fiber);
    
  195.     if (hostFiber === null) {
    
  196.       return null;
    
  197.     }
    
  198.     if (hostFiber.mode & StrictLegacyMode) {
    
  199.       const componentName = getComponentNameFromFiber(fiber) || 'Component';
    
  200.       if (!didWarnAboutFindNodeInStrictMode[componentName]) {
    
  201.         didWarnAboutFindNodeInStrictMode[componentName] = true;
    
  202. 
    
  203.         const previousFiber = ReactCurrentFiberCurrent;
    
  204.         try {
    
  205.           setCurrentDebugFiberInDEV(hostFiber);
    
  206.           if (fiber.mode & StrictLegacyMode) {
    
  207.             console.error(
    
  208.               '%s is deprecated in StrictMode. ' +
    
  209.                 '%s was passed an instance of %s which is inside StrictMode. ' +
    
  210.                 'Instead, add a ref directly to the element you want to reference. ' +
    
  211.                 'Learn more about using refs safely here: ' +
    
  212.                 'https://reactjs.org/link/strict-mode-find-node',
    
  213.               methodName,
    
  214.               methodName,
    
  215.               componentName,
    
  216.             );
    
  217.           } else {
    
  218.             console.error(
    
  219.               '%s is deprecated in StrictMode. ' +
    
  220.                 '%s was passed an instance of %s which renders StrictMode children. ' +
    
  221.                 'Instead, add a ref directly to the element you want to reference. ' +
    
  222.                 'Learn more about using refs safely here: ' +
    
  223.                 'https://reactjs.org/link/strict-mode-find-node',
    
  224.               methodName,
    
  225.               methodName,
    
  226.               componentName,
    
  227.             );
    
  228.           }
    
  229.         } finally {
    
  230.           // Ideally this should reset to previous but this shouldn't be called in
    
  231.           // render and there's another warning for that anyway.
    
  232.           if (previousFiber) {
    
  233.             setCurrentDebugFiberInDEV(previousFiber);
    
  234.           } else {
    
  235.             resetCurrentDebugFiberInDEV();
    
  236.           }
    
  237.         }
    
  238.       }
    
  239.     }
    
  240.     return getPublicInstance(hostFiber.stateNode);
    
  241.   }
    
  242.   return findHostInstance(component);
    
  243. }
    
  244. 
    
  245. export function createContainer(
    
  246.   containerInfo: Container,
    
  247.   tag: RootTag,
    
  248.   hydrationCallbacks: null | SuspenseHydrationCallbacks,
    
  249.   isStrictMode: boolean,
    
  250.   concurrentUpdatesByDefaultOverride: null | boolean,
    
  251.   identifierPrefix: string,
    
  252.   onRecoverableError: (error: mixed) => void,
    
  253.   transitionCallbacks: null | TransitionTracingCallbacks,
    
  254. ): OpaqueRoot {
    
  255.   const hydrate = false;
    
  256.   const initialChildren = null;
    
  257.   return createFiberRoot(
    
  258.     containerInfo,
    
  259.     tag,
    
  260.     hydrate,
    
  261.     initialChildren,
    
  262.     hydrationCallbacks,
    
  263.     isStrictMode,
    
  264.     concurrentUpdatesByDefaultOverride,
    
  265.     identifierPrefix,
    
  266.     onRecoverableError,
    
  267.     transitionCallbacks,
    
  268.     null,
    
  269.   );
    
  270. }
    
  271. 
    
  272. export function createHydrationContainer(
    
  273.   initialChildren: ReactNodeList,
    
  274.   // TODO: Remove `callback` when we delete legacy mode.
    
  275.   callback: ?Function,
    
  276.   containerInfo: Container,
    
  277.   tag: RootTag,
    
  278.   hydrationCallbacks: null | SuspenseHydrationCallbacks,
    
  279.   isStrictMode: boolean,
    
  280.   concurrentUpdatesByDefaultOverride: null | boolean,
    
  281.   identifierPrefix: string,
    
  282.   onRecoverableError: (error: mixed) => void,
    
  283.   transitionCallbacks: null | TransitionTracingCallbacks,
    
  284.   formState: ReactFormState<any, any> | null,
    
  285. ): OpaqueRoot {
    
  286.   const hydrate = true;
    
  287.   const root = createFiberRoot(
    
  288.     containerInfo,
    
  289.     tag,
    
  290.     hydrate,
    
  291.     initialChildren,
    
  292.     hydrationCallbacks,
    
  293.     isStrictMode,
    
  294.     concurrentUpdatesByDefaultOverride,
    
  295.     identifierPrefix,
    
  296.     onRecoverableError,
    
  297.     transitionCallbacks,
    
  298.     formState,
    
  299.   );
    
  300. 
    
  301.   // TODO: Move this to FiberRoot constructor
    
  302.   root.context = getContextForSubtree(null);
    
  303. 
    
  304.   // Schedule the initial render. In a hydration root, this is different from
    
  305.   // a regular update because the initial render must match was was rendered
    
  306.   // on the server.
    
  307.   // NOTE: This update intentionally doesn't have a payload. We're only using
    
  308.   // the update to schedule work on the root fiber (and, for legacy roots, to
    
  309.   // enqueue the callback if one is provided).
    
  310.   const current = root.current;
    
  311.   const lane = requestUpdateLane(current);
    
  312.   const update = createUpdate(lane);
    
  313.   update.callback =
    
  314.     callback !== undefined && callback !== null ? callback : null;
    
  315.   enqueueUpdate(current, update, lane);
    
  316.   scheduleInitialHydrationOnRoot(root, lane);
    
  317. 
    
  318.   return root;
    
  319. }
    
  320. 
    
  321. export function updateContainer(
    
  322.   element: ReactNodeList,
    
  323.   container: OpaqueRoot,
    
  324.   parentComponent: ?React$Component<any, any>,
    
  325.   callback: ?Function,
    
  326. ): Lane {
    
  327.   if (__DEV__) {
    
  328.     onScheduleRoot(container, element);
    
  329.   }
    
  330.   const current = container.current;
    
  331.   const lane = requestUpdateLane(current);
    
  332. 
    
  333.   if (enableSchedulingProfiler) {
    
  334.     markRenderScheduled(lane);
    
  335.   }
    
  336. 
    
  337.   const context = getContextForSubtree(parentComponent);
    
  338.   if (container.context === null) {
    
  339.     container.context = context;
    
  340.   } else {
    
  341.     container.pendingContext = context;
    
  342.   }
    
  343. 
    
  344.   if (__DEV__) {
    
  345.     if (
    
  346.       ReactCurrentFiberIsRendering &&
    
  347.       ReactCurrentFiberCurrent !== null &&
    
  348.       !didWarnAboutNestedUpdates
    
  349.     ) {
    
  350.       didWarnAboutNestedUpdates = true;
    
  351.       console.error(
    
  352.         'Render methods should be a pure function of props and state; ' +
    
  353.           'triggering nested component updates from render is not allowed. ' +
    
  354.           'If necessary, trigger nested updates in componentDidUpdate.\n\n' +
    
  355.           'Check the render method of %s.',
    
  356.         getComponentNameFromFiber(ReactCurrentFiberCurrent) || 'Unknown',
    
  357.       );
    
  358.     }
    
  359.   }
    
  360. 
    
  361.   const update = createUpdate(lane);
    
  362.   // Caution: React DevTools currently depends on this property
    
  363.   // being called "element".
    
  364.   update.payload = {element};
    
  365. 
    
  366.   callback = callback === undefined ? null : callback;
    
  367.   if (callback !== null) {
    
  368.     if (__DEV__) {
    
  369.       if (typeof callback !== 'function') {
    
  370.         console.error(
    
  371.           'render(...): Expected the last optional `callback` argument to be a ' +
    
  372.             'function. Instead received: %s.',
    
  373.           callback,
    
  374.         );
    
  375.       }
    
  376.     }
    
  377.     update.callback = callback;
    
  378.   }
    
  379. 
    
  380.   const root = enqueueUpdate(current, update, lane);
    
  381.   if (root !== null) {
    
  382.     scheduleUpdateOnFiber(root, current, lane);
    
  383.     entangleTransitions(root, current, lane);
    
  384.   }
    
  385. 
    
  386.   return lane;
    
  387. }
    
  388. 
    
  389. export {
    
  390.   batchedUpdates,
    
  391.   deferredUpdates,
    
  392.   discreteUpdates,
    
  393.   flushSync,
    
  394.   isAlreadyRendering,
    
  395.   flushPassiveEffects,
    
  396. };
    
  397. 
    
  398. export function getPublicRootInstance(
    
  399.   container: OpaqueRoot,
    
  400. ): React$Component<any, any> | PublicInstance | null {
    
  401.   const containerFiber = container.current;
    
  402.   if (!containerFiber.child) {
    
  403.     return null;
    
  404.   }
    
  405.   switch (containerFiber.child.tag) {
    
  406.     case HostSingleton:
    
  407.     case HostComponent:
    
  408.       return getPublicInstance(containerFiber.child.stateNode);
    
  409.     default:
    
  410.       return containerFiber.child.stateNode;
    
  411.   }
    
  412. }
    
  413. 
    
  414. export function attemptSynchronousHydration(fiber: Fiber): void {
    
  415.   switch (fiber.tag) {
    
  416.     case HostRoot: {
    
  417.       const root: FiberRoot = fiber.stateNode;
    
  418.       if (isRootDehydrated(root)) {
    
  419.         // Flush the first scheduled "update".
    
  420.         const lanes = getHighestPriorityPendingLanes(root);
    
  421.         flushRoot(root, lanes);
    
  422.       }
    
  423.       break;
    
  424.     }
    
  425.     case SuspenseComponent: {
    
  426.       flushSync(() => {
    
  427.         const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
    
  428.         if (root !== null) {
    
  429.           scheduleUpdateOnFiber(root, fiber, SyncLane);
    
  430.         }
    
  431.       });
    
  432.       // If we're still blocked after this, we need to increase
    
  433.       // the priority of any promises resolving within this
    
  434.       // boundary so that they next attempt also has higher pri.
    
  435.       const retryLane = SyncLane;
    
  436.       markRetryLaneIfNotHydrated(fiber, retryLane);
    
  437.       break;
    
  438.     }
    
  439.   }
    
  440. }
    
  441. 
    
  442. function markRetryLaneImpl(fiber: Fiber, retryLane: Lane) {
    
  443.   const suspenseState: null | SuspenseState = fiber.memoizedState;
    
  444.   if (suspenseState !== null && suspenseState.dehydrated !== null) {
    
  445.     suspenseState.retryLane = higherPriorityLane(
    
  446.       suspenseState.retryLane,
    
  447.       retryLane,
    
  448.     );
    
  449.   }
    
  450. }
    
  451. 
    
  452. // Increases the priority of thenables when they resolve within this boundary.
    
  453. function markRetryLaneIfNotHydrated(fiber: Fiber, retryLane: Lane) {
    
  454.   markRetryLaneImpl(fiber, retryLane);
    
  455.   const alternate = fiber.alternate;
    
  456.   if (alternate) {
    
  457.     markRetryLaneImpl(alternate, retryLane);
    
  458.   }
    
  459. }
    
  460. 
    
  461. export function attemptContinuousHydration(fiber: Fiber): void {
    
  462.   if (fiber.tag !== SuspenseComponent) {
    
  463.     // We ignore HostRoots here because we can't increase
    
  464.     // their priority and they should not suspend on I/O,
    
  465.     // since you have to wrap anything that might suspend in
    
  466.     // Suspense.
    
  467.     return;
    
  468.   }
    
  469.   const lane = SelectiveHydrationLane;
    
  470.   const root = enqueueConcurrentRenderForLane(fiber, lane);
    
  471.   if (root !== null) {
    
  472.     scheduleUpdateOnFiber(root, fiber, lane);
    
  473.   }
    
  474.   markRetryLaneIfNotHydrated(fiber, lane);
    
  475. }
    
  476. 
    
  477. export function attemptHydrationAtCurrentPriority(fiber: Fiber): void {
    
  478.   if (fiber.tag !== SuspenseComponent) {
    
  479.     // We ignore HostRoots here because we can't increase
    
  480.     // their priority other than synchronously flush it.
    
  481.     return;
    
  482.   }
    
  483.   const lane = requestUpdateLane(fiber);
    
  484.   const root = enqueueConcurrentRenderForLane(fiber, lane);
    
  485.   if (root !== null) {
    
  486.     scheduleUpdateOnFiber(root, fiber, lane);
    
  487.   }
    
  488.   markRetryLaneIfNotHydrated(fiber, lane);
    
  489. }
    
  490. 
    
  491. export {getCurrentUpdatePriority, runWithPriority};
    
  492. 
    
  493. export {findHostInstance};
    
  494. 
    
  495. export {findHostInstanceWithWarning};
    
  496. 
    
  497. export function findHostInstanceWithNoPortals(
    
  498.   fiber: Fiber,
    
  499. ): PublicInstance | null {
    
  500.   const hostFiber = findCurrentHostFiberWithNoPortals(fiber);
    
  501.   if (hostFiber === null) {
    
  502.     return null;
    
  503.   }
    
  504.   return getPublicInstance(hostFiber.stateNode);
    
  505. }
    
  506. 
    
  507. let shouldErrorImpl: Fiber => ?boolean = fiber => null;
    
  508. 
    
  509. export function shouldError(fiber: Fiber): ?boolean {
    
  510.   return shouldErrorImpl(fiber);
    
  511. }
    
  512. 
    
  513. let shouldSuspendImpl = (fiber: Fiber) => false;
    
  514. 
    
  515. export function shouldSuspend(fiber: Fiber): boolean {
    
  516.   return shouldSuspendImpl(fiber);
    
  517. }
    
  518. 
    
  519. let overrideHookState = null;
    
  520. let overrideHookStateDeletePath = null;
    
  521. let overrideHookStateRenamePath = null;
    
  522. let overrideProps = null;
    
  523. let overridePropsDeletePath = null;
    
  524. let overridePropsRenamePath = null;
    
  525. let scheduleUpdate = null;
    
  526. let setErrorHandler = null;
    
  527. let setSuspenseHandler = null;
    
  528. 
    
  529. if (__DEV__) {
    
  530.   const copyWithDeleteImpl = (
    
  531.     obj: Object | Array<any>,
    
  532.     path: Array<string | number>,
    
  533.     index: number,
    
  534.   ): $FlowFixMe => {
    
  535.     const key = path[index];
    
  536.     const updated = isArray(obj) ? obj.slice() : {...obj};
    
  537.     if (index + 1 === path.length) {
    
  538.       if (isArray(updated)) {
    
  539.         updated.splice(((key: any): number), 1);
    
  540.       } else {
    
  541.         delete updated[key];
    
  542.       }
    
  543.       return updated;
    
  544.     }
    
  545.     // $FlowFixMe[incompatible-use] number or string is fine here
    
  546.     updated[key] = copyWithDeleteImpl(obj[key], path, index + 1);
    
  547.     return updated;
    
  548.   };
    
  549. 
    
  550.   const copyWithDelete = (
    
  551.     obj: Object | Array<any>,
    
  552.     path: Array<string | number>,
    
  553.   ): Object | Array<any> => {
    
  554.     return copyWithDeleteImpl(obj, path, 0);
    
  555.   };
    
  556. 
    
  557.   const copyWithRenameImpl = (
    
  558.     obj: Object | Array<any>,
    
  559.     oldPath: Array<string | number>,
    
  560.     newPath: Array<string | number>,
    
  561.     index: number,
    
  562.   ): $FlowFixMe => {
    
  563.     const oldKey = oldPath[index];
    
  564.     const updated = isArray(obj) ? obj.slice() : {...obj};
    
  565.     if (index + 1 === oldPath.length) {
    
  566.       const newKey = newPath[index];
    
  567.       // $FlowFixMe[incompatible-use] number or string is fine here
    
  568.       updated[newKey] = updated[oldKey];
    
  569.       if (isArray(updated)) {
    
  570.         updated.splice(((oldKey: any): number), 1);
    
  571.       } else {
    
  572.         delete updated[oldKey];
    
  573.       }
    
  574.     } else {
    
  575.       // $FlowFixMe[incompatible-use] number or string is fine here
    
  576.       updated[oldKey] = copyWithRenameImpl(
    
  577.         // $FlowFixMe[incompatible-use] number or string is fine here
    
  578.         obj[oldKey],
    
  579.         oldPath,
    
  580.         newPath,
    
  581.         index + 1,
    
  582.       );
    
  583.     }
    
  584.     return updated;
    
  585.   };
    
  586. 
    
  587.   const copyWithRename = (
    
  588.     obj: Object | Array<any>,
    
  589.     oldPath: Array<string | number>,
    
  590.     newPath: Array<string | number>,
    
  591.   ): Object | Array<any> => {
    
  592.     if (oldPath.length !== newPath.length) {
    
  593.       console.warn('copyWithRename() expects paths of the same length');
    
  594.       return;
    
  595.     } else {
    
  596.       for (let i = 0; i < newPath.length - 1; i++) {
    
  597.         if (oldPath[i] !== newPath[i]) {
    
  598.           console.warn(
    
  599.             'copyWithRename() expects paths to be the same except for the deepest key',
    
  600.           );
    
  601.           return;
    
  602.         }
    
  603.       }
    
  604.     }
    
  605.     return copyWithRenameImpl(obj, oldPath, newPath, 0);
    
  606.   };
    
  607. 
    
  608.   const copyWithSetImpl = (
    
  609.     obj: Object | Array<any>,
    
  610.     path: Array<string | number>,
    
  611.     index: number,
    
  612.     value: any,
    
  613.   ): $FlowFixMe => {
    
  614.     if (index >= path.length) {
    
  615.       return value;
    
  616.     }
    
  617.     const key = path[index];
    
  618.     const updated = isArray(obj) ? obj.slice() : {...obj};
    
  619.     // $FlowFixMe[incompatible-use] number or string is fine here
    
  620.     updated[key] = copyWithSetImpl(obj[key], path, index + 1, value);
    
  621.     return updated;
    
  622.   };
    
  623. 
    
  624.   const copyWithSet = (
    
  625.     obj: Object | Array<any>,
    
  626.     path: Array<string | number>,
    
  627.     value: any,
    
  628.   ): Object | Array<any> => {
    
  629.     return copyWithSetImpl(obj, path, 0, value);
    
  630.   };
    
  631. 
    
  632.   const findHook = (fiber: Fiber, id: number) => {
    
  633.     // For now, the "id" of stateful hooks is just the stateful hook index.
    
  634.     // This may change in the future with e.g. nested hooks.
    
  635.     let currentHook = fiber.memoizedState;
    
  636.     while (currentHook !== null && id > 0) {
    
  637.       currentHook = currentHook.next;
    
  638.       id--;
    
  639.     }
    
  640.     return currentHook;
    
  641.   };
    
  642. 
    
  643.   // Support DevTools editable values for useState and useReducer.
    
  644.   overrideHookState = (
    
  645.     fiber: Fiber,
    
  646.     id: number,
    
  647.     path: Array<string | number>,
    
  648.     value: any,
    
  649.   ) => {
    
  650.     const hook = findHook(fiber, id);
    
  651.     if (hook !== null) {
    
  652.       const newState = copyWithSet(hook.memoizedState, path, value);
    
  653.       hook.memoizedState = newState;
    
  654.       hook.baseState = newState;
    
  655. 
    
  656.       // We aren't actually adding an update to the queue,
    
  657.       // because there is no update we can add for useReducer hooks that won't trigger an error.
    
  658.       // (There's no appropriate action type for DevTools overrides.)
    
  659.       // As a result though, React will see the scheduled update as a noop and bailout.
    
  660.       // Shallow cloning props works as a workaround for now to bypass the bailout check.
    
  661.       fiber.memoizedProps = {...fiber.memoizedProps};
    
  662. 
    
  663.       const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
    
  664.       if (root !== null) {
    
  665.         scheduleUpdateOnFiber(root, fiber, SyncLane);
    
  666.       }
    
  667.     }
    
  668.   };
    
  669.   overrideHookStateDeletePath = (
    
  670.     fiber: Fiber,
    
  671.     id: number,
    
  672.     path: Array<string | number>,
    
  673.   ) => {
    
  674.     const hook = findHook(fiber, id);
    
  675.     if (hook !== null) {
    
  676.       const newState = copyWithDelete(hook.memoizedState, path);
    
  677.       hook.memoizedState = newState;
    
  678.       hook.baseState = newState;
    
  679. 
    
  680.       // We aren't actually adding an update to the queue,
    
  681.       // because there is no update we can add for useReducer hooks that won't trigger an error.
    
  682.       // (There's no appropriate action type for DevTools overrides.)
    
  683.       // As a result though, React will see the scheduled update as a noop and bailout.
    
  684.       // Shallow cloning props works as a workaround for now to bypass the bailout check.
    
  685.       fiber.memoizedProps = {...fiber.memoizedProps};
    
  686. 
    
  687.       const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
    
  688.       if (root !== null) {
    
  689.         scheduleUpdateOnFiber(root, fiber, SyncLane);
    
  690.       }
    
  691.     }
    
  692.   };
    
  693.   overrideHookStateRenamePath = (
    
  694.     fiber: Fiber,
    
  695.     id: number,
    
  696.     oldPath: Array<string | number>,
    
  697.     newPath: Array<string | number>,
    
  698.   ) => {
    
  699.     const hook = findHook(fiber, id);
    
  700.     if (hook !== null) {
    
  701.       const newState = copyWithRename(hook.memoizedState, oldPath, newPath);
    
  702.       hook.memoizedState = newState;
    
  703.       hook.baseState = newState;
    
  704. 
    
  705.       // We aren't actually adding an update to the queue,
    
  706.       // because there is no update we can add for useReducer hooks that won't trigger an error.
    
  707.       // (There's no appropriate action type for DevTools overrides.)
    
  708.       // As a result though, React will see the scheduled update as a noop and bailout.
    
  709.       // Shallow cloning props works as a workaround for now to bypass the bailout check.
    
  710.       fiber.memoizedProps = {...fiber.memoizedProps};
    
  711. 
    
  712.       const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
    
  713.       if (root !== null) {
    
  714.         scheduleUpdateOnFiber(root, fiber, SyncLane);
    
  715.       }
    
  716.     }
    
  717.   };
    
  718. 
    
  719.   // Support DevTools props for function components, forwardRef, memo, host components, etc.
    
  720.   overrideProps = (fiber: Fiber, path: Array<string | number>, value: any) => {
    
  721.     fiber.pendingProps = copyWithSet(fiber.memoizedProps, path, value);
    
  722.     if (fiber.alternate) {
    
  723.       fiber.alternate.pendingProps = fiber.pendingProps;
    
  724.     }
    
  725.     const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
    
  726.     if (root !== null) {
    
  727.       scheduleUpdateOnFiber(root, fiber, SyncLane);
    
  728.     }
    
  729.   };
    
  730.   overridePropsDeletePath = (fiber: Fiber, path: Array<string | number>) => {
    
  731.     fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path);
    
  732.     if (fiber.alternate) {
    
  733.       fiber.alternate.pendingProps = fiber.pendingProps;
    
  734.     }
    
  735.     const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
    
  736.     if (root !== null) {
    
  737.       scheduleUpdateOnFiber(root, fiber, SyncLane);
    
  738.     }
    
  739.   };
    
  740.   overridePropsRenamePath = (
    
  741.     fiber: Fiber,
    
  742.     oldPath: Array<string | number>,
    
  743.     newPath: Array<string | number>,
    
  744.   ) => {
    
  745.     fiber.pendingProps = copyWithRename(fiber.memoizedProps, oldPath, newPath);
    
  746.     if (fiber.alternate) {
    
  747.       fiber.alternate.pendingProps = fiber.pendingProps;
    
  748.     }
    
  749.     const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
    
  750.     if (root !== null) {
    
  751.       scheduleUpdateOnFiber(root, fiber, SyncLane);
    
  752.     }
    
  753.   };
    
  754. 
    
  755.   scheduleUpdate = (fiber: Fiber) => {
    
  756.     const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
    
  757.     if (root !== null) {
    
  758.       scheduleUpdateOnFiber(root, fiber, SyncLane);
    
  759.     }
    
  760.   };
    
  761. 
    
  762.   setErrorHandler = (newShouldErrorImpl: Fiber => ?boolean) => {
    
  763.     shouldErrorImpl = newShouldErrorImpl;
    
  764.   };
    
  765. 
    
  766.   setSuspenseHandler = (newShouldSuspendImpl: Fiber => boolean) => {
    
  767.     shouldSuspendImpl = newShouldSuspendImpl;
    
  768.   };
    
  769. }
    
  770. 
    
  771. function findHostInstanceByFiber(fiber: Fiber): Instance | TextInstance | null {
    
  772.   const hostFiber = findCurrentHostFiber(fiber);
    
  773.   if (hostFiber === null) {
    
  774.     return null;
    
  775.   }
    
  776.   return hostFiber.stateNode;
    
  777. }
    
  778. 
    
  779. function emptyFindFiberByHostInstance(
    
  780.   instance: Instance | TextInstance,
    
  781. ): Fiber | null {
    
  782.   return null;
    
  783. }
    
  784. 
    
  785. function getCurrentFiberForDevTools() {
    
  786.   return ReactCurrentFiberCurrent;
    
  787. }
    
  788. 
    
  789. export function injectIntoDevTools(devToolsConfig: DevToolsConfig): boolean {
    
  790.   const {findFiberByHostInstance} = devToolsConfig;
    
  791.   const {ReactCurrentDispatcher} = ReactSharedInternals;
    
  792. 
    
  793.   return injectInternals({
    
  794.     bundleType: devToolsConfig.bundleType,
    
  795.     version: devToolsConfig.version,
    
  796.     rendererPackageName: devToolsConfig.rendererPackageName,
    
  797.     rendererConfig: devToolsConfig.rendererConfig,
    
  798.     overrideHookState,
    
  799.     overrideHookStateDeletePath,
    
  800.     overrideHookStateRenamePath,
    
  801.     overrideProps,
    
  802.     overridePropsDeletePath,
    
  803.     overridePropsRenamePath,
    
  804.     setErrorHandler,
    
  805.     setSuspenseHandler,
    
  806.     scheduleUpdate,
    
  807.     currentDispatcherRef: ReactCurrentDispatcher,
    
  808.     findHostInstanceByFiber,
    
  809.     findFiberByHostInstance:
    
  810.       findFiberByHostInstance || emptyFindFiberByHostInstance,
    
  811.     // React Refresh
    
  812.     findHostInstancesForRefresh: __DEV__ ? findHostInstancesForRefresh : null,
    
  813.     scheduleRefresh: __DEV__ ? scheduleRefresh : null,
    
  814.     scheduleRoot: __DEV__ ? scheduleRoot : null,
    
  815.     setRefreshHandler: __DEV__ ? setRefreshHandler : null,
    
  816.     // Enables DevTools to append owner stacks to error messages in DEV mode.
    
  817.     getCurrentFiber: __DEV__ ? getCurrentFiberForDevTools : null,
    
  818.     // Enables DevTools to detect reconciler version rather than renderer version
    
  819.     // which may not match for third party renderers.
    
  820.     reconcilerVersion: ReactVersion,
    
  821.   });
    
  822. }