text: /**
   * Copyright (c) Facebook, Inc. and its affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   * @flow
   */
  
  import type {Fiber} from './ReactFiber';
  import type {FiberRoot} from './ReactFiberRoot';
  import type {RootTag} from 'shared/ReactRootTags';
  import type {
    Instance,
    TextInstance,
    Container,
    PublicInstance,
  } from './ReactFiberHostConfig';
  import {FundamentalComponent} from 'shared/ReactWorkTags';
  import type {ReactNodeList} from 'shared/ReactTypes';
  import type {ExpirationTime} from './ReactFiberExpirationTime';
  import type {SuspenseConfig} from './ReactFiberSuspenseConfig';
  import type {SuspenseHydrationCallbacks} from './ReactFiberSuspenseComponent';
  
  import {
    findCurrentHostFiber,
    findCurrentHostFiberWithNoPortals,
  } from 'react-reconciler/reflection';
  import {get as getInstance} from 'shared/ReactInstanceMap';
  import {HostComponent, ClassComponent} from 'shared/ReactWorkTags';
  import getComponentName from 'shared/getComponentName';
  import invariant from 'shared/invariant';
  import warningWithoutStack from 'shared/warningWithoutStack';
  import ReactSharedInternals from 'shared/ReactSharedInternals';
  
  import {getPublicInstance} from './ReactFiberHostConfig';
  import {
    findCurrentUnmaskedContext,
    processChildContext,
    emptyContextObject,
    isContextProvider as isLegacyContextProvider,
  } from './ReactFiberContext';
  import {createFiberRoot} from './ReactFiberRoot';
  import {injectInternals} from './ReactFiberDevToolsHook';
  import {
    computeUniqueAsyncExpiration,
    requestCurrentTime,
    computeExpirationForFiber,
    scheduleWork,
    flushRoot,
    batchedEventUpdates,
    batchedUpdates,
    unbatchedUpdates,
    flushSync,
    flushControlled,
    deferredUpdates,
    syncUpdates,
    discreteUpdates,
    flushDiscreteUpdates,
    flushPassiveEffects,
    warnIfNotScopedWithMatchingAct,
    warnIfUnmockedScheduler,
    IsThisRendererActing,
  } from './ReactFiberWorkLoop';
  import {createUpdate, enqueueUpdate} from './ReactUpdateQueue';
  import ReactFiberInstrumentation from './ReactFiberInstrumentation';
  import {
    getStackByFiberInDevAndProd,
    phase as ReactCurrentFiberPhase,
    current as ReactCurrentFiberCurrent,
  } from './ReactCurrentFiber';
  import {StrictMode} from './ReactTypeOfMode';
  import {Sync} from './ReactFiberExpirationTime';
  import {requestCurrentSuspenseConfig} from './ReactFiberSuspenseConfig';
  import {
    scheduleRefresh,
    scheduleRoot,
    setRefreshHandler,
    findHostInstancesForRefresh,
  } from './ReactFiberHotReloading';
  
  type OpaqueRoot = FiberRoot;
  
  // 0 is PROD, 1 is DEV.
  // Might add PROFILE later.
  type BundleType = 0 | 1;
  
  type DevToolsConfig = {|
    bundleType: BundleType,
    version: string,
    rendererPackageName: string,
    // Note: this actually *does* depend on Fiber internal fields.
    // Used by "inspect clicked DOM element" in React DevTools.
    findFiberByHostInstance?: (instance: Instance | TextInstance) => Fiber,
    // Used by RN in-app inspector.
    // This API is unfortunately RN-specific.
    // TODO: Change it to accept Fiber instead and type it properly.
    getInspectorDataForViewTag?: (tag: number) => Object,
  |};
  
  let didWarnAboutNestedUpdates;
  let didWarnAboutFindNodeInStrictMode;
  
  if (__DEV__) {
    didWarnAboutNestedUpdates = false;
    didWarnAboutFindNodeInStrictMode = {};
  }
  
  function getContextForSubtree(
    parentComponent: ?React$Component<any, any>,
  ): Object {
    if (!parentComponent) {
      return emptyContextObject;
    }
  
    const fiber = getInstance(parentComponent);
    const parentContext = findCurrentUnmaskedContext(fiber);
  
    if (fiber.tag === ClassComponent) {
      const Component = fiber.type;
      if (isLegacyContextProvider(Component)) {
        return processChildContext(fiber, Component, parentContext);
      }
    }
  
    return parentContext;
  }
  
  function scheduleRootUpdate(
    current: Fiber,
    element: ReactNodeList,
    expirationTime: ExpirationTime,
    suspenseConfig: null | SuspenseConfig,
    callback: ?Function,
  ) {
    if (__DEV__) {
      if (
        ReactCurrentFiberPhase === 'render' &&
        ReactCurrentFiberCurrent !== null &&
        !didWarnAboutNestedUpdates
      ) {
        didWarnAboutNestedUpdates = true;
        warningWithoutStack(
          false,
          'Render methods should be a pure function of props and state; ' +
            'triggering nested component updates from render is not allowed. ' +
            'If necessary, trigger nested updates in componentDidUpdate.\n\n' +
            'Check the render method of %s.',
          getComponentName(ReactCurrentFiberCurrent.type) || 'Unknown',
        );
      }
    }
  
    const update = createUpdate(expirationTime, suspenseConfig);
    // Caution: React DevTools currently depends on this property
    // being called "element".
    update.payload = {element};
  
    callback = callback === undefined ? null : callback;
    if (callback !== null) {
      warningWithoutStack(
        typeof callback === 'function',
        'render(...): Expected the last optional `callback` argument to be a ' +
          'function. Instead received: %s.',
        callback,
      );
      update.callback = callback;
    }
  
    enqueueUpdate(current, update);
    scheduleWork(current, expirationTime);
  
    return expirationTime;
  }
  
  export function updateContainerAtExpirationTime(
    element: ReactNodeList,
    container: OpaqueRoot,
    parentComponent: ?React$Component<any, any>,
    expirationTime: ExpirationTime,
    suspenseConfig: null | SuspenseConfig,
    callback: ?Function,
  ) {
    // TODO: If this is a nested container, this won't be the root.
    const current = container.current;
  
    if (__DEV__) {
      if (ReactFiberInstrumentation.debugTool) {
        if (current.alternate === null) {
          ReactFiberInstrumentation.debugTool.onMountContainer(container);
        } else if (element === null) {
          ReactFiberInstrumentation.debugTool.onUnmountContainer(container);
        } else {
          ReactFiberInstrumentation.debugTool.onUpdateContainer(container);
        }
      }
    }
  
    const context = getContextForSubtree(parentComponent);
    if (container.context === null) {
      container.context = context;
    } else {
      container.pendingContext = context;
    }
  
    return scheduleRootUpdate(
      current,
      element,
      expirationTime,
      suspenseConfig,
      callback,
    );
  }
  
  function findHostInstance(component: Object): PublicInstance | null {
    const fiber = getInstance(component);
    if (fiber === undefined) {
      if (typeof component.render === 'function') {
        invariant(false, 'Unable to find node on an unmounted component.');
      } else {
        invariant(
          false,
          'Argument appears to not be a ReactComponent. Keys: %s',
          Object.keys(component),
        );
      }
    }
    const hostFiber = findCurrentHostFiber(fiber);
    if (hostFiber === null) {
      return null;
    }
    return hostFiber.stateNode;
  }
  
  function findHostInstanceWithWarning(
    component: Object,
    methodName: string,
  ): PublicInstance | null {
    if (__DEV__) {
      const fiber = getInstance(component);
      if (fiber === undefined) {
        if (typeof component.render === 'function') {
          invariant(false, 'Unable to find node on an unmounted component.');
        } else {
          invariant(
            false,
            'Argument appears to not be a ReactComponent. Keys: %s',
            Object.keys(component),
          );
        }
      }
      const hostFiber = findCurrentHostFiber(fiber);
      if (hostFiber === null) {
        return null;
      }
      if (hostFiber.mode & StrictMode) {
        const componentName = getComponentName(fiber.type) || 'Component';
        if (!didWarnAboutFindNodeInStrictMode[componentName]) {
          didWarnAboutFindNodeInStrictMode[componentName] = true;
          if (fiber.mode & StrictMode) {
            warningWithoutStack(
              false,
              '%s is deprecated in StrictMode. ' +
                '%s was passed an instance of %s which is inside StrictMode. ' +
                'Instead, add a ref directly to the element you want to reference. ' +
                'Learn more about using refs safely here: ' +
                'https://fb.me/react-strict-mode-find-node%s',
              methodName,
              methodName,
              componentName,
              getStackByFiberInDevAndProd(hostFiber),
            );
          } else {
            warningWithoutStack(
              false,
              '%s is deprecated in StrictMode. ' +
                '%s was passed an instance of %s which renders StrictMode children. ' +
                'Instead, add a ref directly to the element you want to reference. ' +
                'Learn more about using refs safely here: ' +
                'https://fb.me/react-strict-mode-find-node%s',
              methodName,
              methodName,
              componentName,
              getStackByFiberInDevAndProd(hostFiber),
            );
          }
        }
      }
      return hostFiber.stateNode;
    }
    return findHostInstance(component);
  }
  
  export function createContainer(
    containerInfo: Container,
    tag: RootTag,
    hydrate: boolean,
    hydrationCallbacks: null | SuspenseHydrationCallbacks,
  ): OpaqueRoot {
    return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks);
  }
  
  export function updateContainer(
    element: ReactNodeList,
    container: OpaqueRoot,
    parentComponent: ?React$Component<any, any>,
    callback: ?Function,
  ): ExpirationTime {
    const current = container.current;
    const currentTime = requestCurrentTime();
    if (__DEV__) {
      // $FlowExpectedError - jest isn't a global, and isn't recognized outside of tests
      if ('undefined' !== typeof jest) {
        warnIfUnmockedScheduler(current);
        warnIfNotScopedWithMatchingAct(current);
      }
    }
    const suspenseConfig = requestCurrentSuspenseConfig();
    const expirationTime = computeExpirationForFiber(
      currentTime,
      current,
      suspenseConfig,
    );
    return updateContainerAtExpirationTime(
      element,
      container,
      parentComponent,
      expirationTime,
      suspenseConfig,
      callback,
    );
  }
  
  export {
    flushRoot,
    computeUniqueAsyncExpiration,
    batchedEventUpdates,
    batchedUpdates,
    unbatchedUpdates,
    deferredUpdates,
    syncUpdates,
    discreteUpdates,
    flushDiscreteUpdates,
    flushControlled,
    flushSync,
    flushPassiveEffects,
    IsThisRendererActing,
  };
  
  export function getPublicRootInstance(
    container: OpaqueRoot,
  ): React$Component<any, any> | PublicInstance | null {
    const containerFiber = container.current;
    if (!containerFiber.child) {
      return null;
    }
    switch (containerFiber.child.tag) {
      case HostComponent:
        return getPublicInstance(containerFiber.child.stateNode);
      default:
        return containerFiber.child.stateNode;
    }
  }
  
  export {findHostInstance};
  
  export {findHostInstanceWithWarning};
  
  export function findHostInstanceWithNoPortals(
    fiber: Fiber,
  ): PublicInstance | null {
    const hostFiber = findCurrentHostFiberWithNoPortals(fiber);
    if (hostFiber === null) {
      return null;
    }
    if (hostFiber.tag === FundamentalComponent) {
      return hostFiber.stateNode.instance;
    }
    return hostFiber.stateNode;
  }
  
  let shouldSuspendImpl = fiber => false;
  
  export function shouldSuspend(fiber: Fiber): boolean {
    return shouldSuspendImpl(fiber);
  }
  
  let overrideHookState = null;
  let overrideProps = null;
  let scheduleUpdate = null;
  let setSuspenseHandler = null;
  
  if (__DEV__) {
    const copyWithSetImpl = (
      obj: Object | Array<any>,
      path: Array<string | number>,
      idx: number,
      value: any,
    ) => {
      if (idx >= path.length) {
        return value;
      }
      const key = path[idx];
      const updated = Array.isArray(obj) ? obj.slice() : {...obj};
      // $FlowFixMe number or string is fine here
      updated[key] = copyWithSetImpl(obj[key], path, idx + 1, value);
      return updated;
    };
  
    const copyWithSet = (
      obj: Object | Array<any>,
      path: Array<string | number>,
      value: any,
    ): Object | Array<any> => {
      return copyWithSetImpl(obj, path, 0, value);
    };
  
    // Support DevTools editable values for useState and useReducer.
    overrideHookState = (
      fiber: Fiber,
      id: number,
      path: Array<string | number>,
      value: any,
    ) => {
      // For now, the "id" of stateful hooks is just the stateful hook index.
      // This may change in the future with e.g. nested hooks.
      let currentHook = fiber.memoizedState;
      while (currentHook !== null && id > 0) {
        currentHook = currentHook.next;
        id--;
      }
      if (currentHook !== null) {
        const newState = copyWithSet(currentHook.memoizedState, path, value);
        currentHook.memoizedState = newState;
        currentHook.baseState = newState;
  
        // We aren't actually adding an update to the queue,
        // because there is no update we can add for useReducer hooks that won't trigger an error.
        // (There's no appropriate action type for DevTools overrides.)
        // As a result though, React will see the scheduled update as a noop and bailout.
        // Shallow cloning props works as a workaround for now to bypass the bailout check.
        fiber.memoizedProps = {...fiber.memoizedProps};
  
        scheduleWork(fiber, Sync);
      }
    };
  
    // Support DevTools props for function components, forwardRef, memo, host components, etc.
    overrideProps = (fiber: Fiber, path: Array<string | number>, value: any) => {
      fiber.pendingProps = copyWithSet(fiber.memoizedProps, path, value);
      if (fiber.alternate) {
        fiber.alternate.pendingProps = fiber.pendingProps;
      }
      scheduleWork(fiber, Sync);
    };
  
    scheduleUpdate = (fiber: Fiber) => {
      scheduleWork(fiber, Sync);
    };
  
    setSuspenseHandler = (newShouldSuspendImpl: Fiber => boolean) => {
      shouldSuspendImpl = newShouldSuspendImpl;
    };
  }
  
  export function injectIntoDevTools(devToolsConfig: DevToolsConfig): boolean {
    const {findFiberByHostInstance} = devToolsConfig;
    const {ReactCurrentDispatcher} = ReactSharedInternals;
  
    return injectInternals({
      ...devToolsConfig,
      overrideHookState,
      overrideProps,
      setSuspenseHandler,
      scheduleUpdate,
      currentDispatcherRef: ReactCurrentDispatcher,
      findHostInstanceByFiber(fiber: Fiber): Instance | TextInstance | null {
        const hostFiber = findCurrentHostFiber(fiber);
        if (hostFiber === null) {
          return null;
        }
        return hostFiber.stateNode;
      },
      findFiberByHostInstance(instance: Instance | TextInstance): Fiber | null {
        if (!findFiberByHostInstance) {
          // Might not be implemented by the renderer.
          return null;
        }
        return findFiberByHostInstance(instance);
      },
      // React Refresh
      findHostInstancesForRefresh: __DEV__ ? findHostInstancesForRefresh : null,
      scheduleRefresh: __DEV__ ? scheduleRefresh : null,
      scheduleRoot: __DEV__ ? scheduleRoot : null,
      setRefreshHandler: __DEV__ ? setRefreshHandler : null,
      // Enables DevTools to append owner stacks to error messages in DEV mode.
      getCurrentFiber: __DEV__ ? () => ReactCurrentFiberCurrent : null,
    });
  }
  