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 React from 'react';
  import {isForwardRef, isMemo, ForwardRef} from 'react-is';
  import describeComponentFrame from 'shared/describeComponentFrame';
  import getComponentName from 'shared/getComponentName';
  import shallowEqual from 'shared/shallowEqual';
  import invariant from 'shared/invariant';
  import checkPropTypes from 'prop-types/checkPropTypes';
  import ReactSharedInternals from 'shared/ReactSharedInternals';
  import warning from 'shared/warning';
  import is from 'shared/objectIs';
  
  import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactFiberHooks';
  import type {
    ReactContext,
    ReactEventResponderListener,
  } from 'shared/ReactTypes';
  import type {ReactElement} from 'shared/ReactElementType';
  
  type BasicStateAction<S> = (S => S) | S;
  type Dispatch<A> = A => void;
  
  type Update<A> = {
    action: A,
    next: Update<A> | null,
  };
  
  type UpdateQueue<A> = {
    first: Update<A> | null,
    dispatch: any,
  };
  
  type Hook = {
    memoizedState: any,
    queue: UpdateQueue<any> | null,
    next: Hook | null,
  };
  
  const {ReactCurrentDispatcher} = ReactSharedInternals;
  
  const RE_RENDER_LIMIT = 25;
  
  const emptyObject = {};
  if (__DEV__) {
    Object.freeze(emptyObject);
  }
  
  // In DEV, this is the name of the currently executing primitive hook
  let currentHookNameInDev: ?string;
  
  function areHookInputsEqual(
    nextDeps: Array<mixed>,
    prevDeps: Array<mixed> | null,
  ) {
    if (prevDeps === null) {
      if (__DEV__) {
        warning(
          '%s received a final argument during this render, but not during ' +
            'the previous render. Even though the final argument is optional, ' +
            'its type cannot change between renders.',
          currentHookNameInDev,
        );
      }
      return false;
    }
  
    if (__DEV__) {
      // Don't bother comparing lengths in prod because these arrays should be
      // passed inline.
      if (nextDeps.length !== prevDeps.length) {
        warning(
          'The final argument passed to %s changed size between renders. The ' +
            'order and size of this array must remain constant.\n\n' +
            'Previous: %s\n' +
            'Incoming: %s',
          currentHookNameInDev,
          `[${nextDeps.join(', ')}]`,
          `[${prevDeps.join(', ')}]`,
        );
      }
    }
    for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
      if (is(nextDeps[i], prevDeps[i])) {
        continue;
      }
      return false;
    }
    return true;
  }
  
  class Updater {
    constructor(renderer) {
      this._renderer = renderer;
      this._callbacks = [];
    }
  
    _renderer: ReactShallowRenderer;
    _callbacks: Array<any>;
  
    _enqueueCallback(callback, publicInstance) {
      if (typeof callback === 'function' && publicInstance) {
        this._callbacks.push({
          callback,
          publicInstance,
        });
      }
    }
  
    _invokeCallbacks() {
      const callbacks = this._callbacks;
      this._callbacks = [];
  
      callbacks.forEach(({callback, publicInstance}) => {
        callback.call(publicInstance);
      });
    }
  
    isMounted(publicInstance) {
      return !!this._renderer._element;
    }
  
    enqueueForceUpdate(publicInstance, callback, callerName) {
      this._enqueueCallback(callback, publicInstance);
      this._renderer._forcedUpdate = true;
      this._renderer.render(this._renderer._element, this._renderer._context);
    }
  
    enqueueReplaceState(publicInstance, completeState, callback, callerName) {
      this._enqueueCallback(callback, publicInstance);
      this._renderer._newState = completeState;
      this._renderer.render(this._renderer._element, this._renderer._context);
    }
  
    enqueueSetState(publicInstance, partialState, callback, callerName) {
      this._enqueueCallback(callback, publicInstance);
      const currentState = this._renderer._newState || publicInstance.state;
  
      if (typeof partialState === 'function') {
        partialState = partialState.call(
          publicInstance,
          currentState,
          publicInstance.props,
        );
      }
  
      // Null and undefined are treated as no-ops.
      if (partialState === null || partialState === undefined) {
        return;
      }
  
      this._renderer._newState = {
        ...currentState,
        ...partialState,
      };
  
      this._renderer.render(this._renderer._element, this._renderer._context);
    }
  }
  
  function createHook(): Hook {
    return {
      memoizedState: null,
      queue: null,
      next: null,
    };
  }
  
  function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
    return typeof action === 'function' ? action(state) : action;
  }
  
  class ReactShallowRenderer {
    static createRenderer = function() {
      return new ReactShallowRenderer();
    };
  
    constructor() {
      this._reset();
    }
  
    _reset() {
      this._context = null;
      this._element = null;
      this._instance = null;
      this._newState = null;
      this._rendered = null;
      this._rendering = false;
      this._forcedUpdate = false;
      this._updater = new Updater(this);
      this._dispatcher = this._createDispatcher();
      this._workInProgressHook = null;
      this._firstWorkInProgressHook = null;
      this._isReRender = false;
      this._didScheduleRenderPhaseUpdate = false;
      this._renderPhaseUpdates = null;
      this._numberOfReRenders = 0;
    }
  
    _context: null | Object;
    _newState: null | Object;
    _instance: any;
    _element: null | ReactElement;
    _rendered: null | mixed;
    _updater: Updater;
    _rendering: boolean;
    _forcedUpdate: boolean;
    _dispatcher: DispatcherType;
    _workInProgressHook: null | Hook;
    _firstWorkInProgressHook: null | Hook;
    _renderPhaseUpdates: Map<UpdateQueue<any>, Update<any>> | null;
    _isReRender: boolean;
    _didScheduleRenderPhaseUpdate: boolean;
    _numberOfReRenders: number;
  
    _validateCurrentlyRenderingComponent() {
      invariant(
        this._rendering && !this._instance,
        'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
          ' one of the following reasons:\n' +
          '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
          '2. You might be breaking the Rules of Hooks\n' +
          '3. You might have more than one copy of React in the same app\n' +
          'See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.',
      );
    }
  
    _createDispatcher(): DispatcherType {
      const useReducer = <S, I, A>(
        reducer: (S, A) => S,
        initialArg: I,
        init?: I => S,
      ): [S, Dispatch<A>] => {
        this._validateCurrentlyRenderingComponent();
        this._createWorkInProgressHook();
        const workInProgressHook: Hook = (this._workInProgressHook: any);
  
        if (this._isReRender) {
          // This is a re-render.
          const queue: UpdateQueue<A> = (workInProgressHook.queue: any);
          const dispatch: Dispatch<A> = (queue.dispatch: any);
          if (this._numberOfReRenders > 0) {
            // Apply the new render phase updates to the previous current hook.
            if (this._renderPhaseUpdates !== null) {
              // Render phase updates are stored in a map of queue -> linked list
              const firstRenderPhaseUpdate = this._renderPhaseUpdates.get(queue);
              if (firstRenderPhaseUpdate !== undefined) {
                (this._renderPhaseUpdates: any).delete(queue);
                let newState = workInProgressHook.memoizedState;
                let update = firstRenderPhaseUpdate;
                do {
                  const action = update.action;
                  newState = reducer(newState, action);
                  update = update.next;
                } while (update !== null);
                workInProgressHook.memoizedState = newState;
                return [newState, dispatch];
              }
            }
            return [workInProgressHook.memoizedState, dispatch];
          }
          // Process updates outside of render
          let newState = workInProgressHook.memoizedState;
          let update = queue.first;
          if (update !== null) {
            do {
              const action = update.action;
              newState = reducer(newState, action);
              update = update.next;
            } while (update !== null);
            queue.first = null;
            workInProgressHook.memoizedState = newState;
          }
          return [newState, dispatch];
        } else {
          let initialState;
          if (reducer === basicStateReducer) {
            // Special case for `useState`.
            initialState =
              typeof initialArg === 'function'
                ? ((initialArg: any): () => S)()
                : ((initialArg: any): S);
          } else {
            initialState =
              init !== undefined ? init(initialArg) : ((initialArg: any): S);
          }
          workInProgressHook.memoizedState = initialState;
          const queue: UpdateQueue<A> = (workInProgressHook.queue = {
            first: null,
            dispatch: null,
          });
          const dispatch: Dispatch<
            A,
          > = (queue.dispatch = (this._dispatchAction.bind(this, queue): any));
          return [workInProgressHook.memoizedState, dispatch];
        }
      };
  
      const useState = <S>(
        initialState: (() => S) | S,
      ): [S, Dispatch<BasicStateAction<S>>] => {
        return useReducer(
          basicStateReducer,
          // useReducer has a special case to support lazy useState initializers
          (initialState: any),
        );
      };
  
      const useMemo = <T>(
        nextCreate: () => T,
        deps: Array<mixed> | void | null,
      ): T => {
        this._validateCurrentlyRenderingComponent();
        this._createWorkInProgressHook();
  
        const nextDeps = deps !== undefined ? deps : null;
  
        if (
          this._workInProgressHook !== null &&
          this._workInProgressHook.memoizedState !== null
        ) {
          const prevState = this._workInProgressHook.memoizedState;
          const prevDeps = prevState[1];
          if (nextDeps !== null) {
            if (areHookInputsEqual(nextDeps, prevDeps)) {
              return prevState[0];
            }
          }
        }
  
        const nextValue = nextCreate();
        (this._workInProgressHook: any).memoizedState = [nextValue, nextDeps];
        return nextValue;
      };
  
      const useRef = <T>(initialValue: T): {current: T} => {
        this._validateCurrentlyRenderingComponent();
        this._createWorkInProgressHook();
        const previousRef = (this._workInProgressHook: any).memoizedState;
        if (previousRef === null) {
          const ref = {current: initialValue};
          if (__DEV__) {
            Object.seal(ref);
          }
          (this._workInProgressHook: any).memoizedState = ref;
          return ref;
        } else {
          return previousRef;
        }
      };
  
      const readContext = <T>(
        context: ReactContext<T>,
        observedBits: void | number | boolean,
      ): T => {
        return context._currentValue;
      };
  
      const noOp = () => {
        this._validateCurrentlyRenderingComponent();
      };
  
      const identity = (fn: Function): Function => {
        return fn;
      };
  
      const useResponder = (
        responder,
        props,
      ): ReactEventResponderListener<any, any> => ({
        props: props,
        responder,
      });
  
      // TODO: implement if we decide to keep the shallow renderer
      const useTransition = (
        config,
      ): [(callback: () => void) => void, boolean] => {
        this._validateCurrentlyRenderingComponent();
        const startTransition = callback => {
          callback();
        };
        return [startTransition, false];
      };
  
      // TODO: implement if we decide to keep the shallow renderer
      const useDeferredValue = <T>(value: T, config): T => {
        this._validateCurrentlyRenderingComponent();
        return value;
      };
  
      return {
        readContext,
        useCallback: (identity: any),
        useContext: <T>(context: ReactContext<T>): T => {
          this._validateCurrentlyRenderingComponent();
          return readContext(context);
        },
        useDebugValue: noOp,
        useEffect: noOp,
        useImperativeHandle: noOp,
        useLayoutEffect: noOp,
        useMemo,
        useReducer,
        useRef,
        useState,
        useResponder,
        useTransition,
        useDeferredValue,
      };
    }
  
    _dispatchAction<A>(queue: UpdateQueue<A>, action: A) {
      invariant(
        this._numberOfReRenders < RE_RENDER_LIMIT,
        'Too many re-renders. React limits the number of renders to prevent ' +
          'an infinite loop.',
      );
  
      if (this._rendering) {
        // This is a render phase update. Stash it in a lazily-created map of
        // queue -> linked list of updates. After this render pass, we'll restart
        // and apply the stashed updates on top of the work-in-progress hook.
        this._didScheduleRenderPhaseUpdate = true;
        const update: Update<A> = {
          action,
          next: null,
        };
        let renderPhaseUpdates = this._renderPhaseUpdates;
        if (renderPhaseUpdates === null) {
          this._renderPhaseUpdates = renderPhaseUpdates = new Map();
        }
        const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
        if (firstRenderPhaseUpdate === undefined) {
          renderPhaseUpdates.set(queue, update);
        } else {
          // Append the update to the end of the list.
          let lastRenderPhaseUpdate = firstRenderPhaseUpdate;
          while (lastRenderPhaseUpdate.next !== null) {
            lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;
          }
          lastRenderPhaseUpdate.next = update;
        }
      } else {
        const update: Update<A> = {
          action,
          next: null,
        };
  
        // Append the update to the end of the list.
        let last = queue.first;
        if (last === null) {
          queue.first = update;
        } else {
          while (last.next !== null) {
            last = last.next;
          }
          last.next = update;
        }
  
        // Re-render now.
        this.render(this._element, this._context);
      }
    }
  
    _createWorkInProgressHook(): Hook {
      if (this._workInProgressHook === null) {
        // This is the first hook in the list
        if (this._firstWorkInProgressHook === null) {
          this._isReRender = false;
          this._firstWorkInProgressHook = this._workInProgressHook = createHook();
        } else {
          // There's already a work-in-progress. Reuse it.
          this._isReRender = true;
          this._workInProgressHook = this._firstWorkInProgressHook;
        }
      } else {
        if (this._workInProgressHook.next === null) {
          this._isReRender = false;
          // Append to the end of the list
          this._workInProgressHook = (this
            ._workInProgressHook: any).next = createHook();
        } else {
          // There's already a work-in-progress. Reuse it.
          this._isReRender = true;
          this._workInProgressHook = this._workInProgressHook.next;
        }
      }
      return this._workInProgressHook;
    }
  
    _finishHooks(element: ReactElement, context: null | Object) {
      if (this._didScheduleRenderPhaseUpdate) {
        // Updates were scheduled during the render phase. They are stored in
        // the `renderPhaseUpdates` map. Call the component again, reusing the
        // work-in-progress hooks and applying the additional updates on top. Keep
        // restarting until no more updates are scheduled.
        this._didScheduleRenderPhaseUpdate = false;
        this._numberOfReRenders += 1;
  
        // Start over from the beginning of the list
        this._workInProgressHook = null;
        this._rendering = false;
        this.render(element, context);
      } else {
        this._workInProgressHook = null;
        this._renderPhaseUpdates = null;
        this._numberOfReRenders = 0;
      }
    }
  
    getMountedInstance() {
      return this._instance;
    }
  
    getRenderOutput() {
      return this._rendered;
    }
  
    render(element: ReactElement | null, context: null | Object = emptyObject) {
      invariant(
        React.isValidElement(element),
        'ReactShallowRenderer render(): Invalid component element.%s',
        typeof element === 'function'
          ? ' Instead of passing a component class, make sure to instantiate ' +
            'it by passing it to React.createElement.'
          : '',
      );
      element = ((element: any): ReactElement);
      // Show a special message for host elements since it's a common case.
      invariant(
        typeof element.type !== 'string',
        'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
          'components, not primitives (%s). Instead of calling `.render(el)` and ' +
          'inspecting the rendered output, look at `el.props` directly instead.',
        element.type,
      );
      invariant(
        isForwardRef(element) ||
          (typeof element.type === 'function' || isMemo(element)),
        'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
          'components, but the provided element type was `%s`.',
        Array.isArray(element.type)
          ? 'array'
          : element.type === null
            ? 'null'
            : typeof element.type,
      );
  
      if (this._rendering) {
        return;
      }
      if (this._element != null && this._element.type !== element.type) {
        this._reset();
      }
  
      const elementType = isMemo(element) ? element.type.type : element.type;
      const previousElement = this._element;
  
      this._rendering = true;
      this._element = element;
      this._context = getMaskedContext(elementType.contextTypes, context);
  
      // Inner memo component props aren't currently validated in createElement.
      if (isMemo(element) && elementType.propTypes) {
        currentlyValidatingElement = element;
        checkPropTypes(
          elementType.propTypes,
          element.props,
          'prop',
          getComponentName(elementType),
          getStackAddendum,
        );
      }
  
      if (this._instance) {
        this._updateClassComponent(elementType, element, this._context);
      } else {
        if (shouldConstruct(elementType)) {
          this._instance = new elementType(
            element.props,
            this._context,
            this._updater,
          );
          if (typeof elementType.getDerivedStateFromProps === 'function') {
            const partialState = elementType.getDerivedStateFromProps.call(
              null,
              element.props,
              this._instance.state,
            );
            if (partialState != null) {
              this._instance.state = Object.assign(
                {},
                this._instance.state,
                partialState,
              );
            }
          }
  
          if (elementType.contextTypes) {
            currentlyValidatingElement = element;
            checkPropTypes(
              elementType.contextTypes,
              this._context,
              'context',
              getName(elementType, this._instance),
              getStackAddendum,
            );
  
            currentlyValidatingElement = null;
          }
  
          this._mountClassComponent(elementType, element, this._context);
        } else {
          let shouldRender = true;
          if (isMemo(element) && previousElement !== null) {
            // This is a Memo component that is being re-rendered.
            const compare = element.type.compare || shallowEqual;
            if (compare(previousElement.props, element.props)) {
              shouldRender = false;
            }
          }
          if (shouldRender) {
            const prevDispatcher = ReactCurrentDispatcher.current;
            ReactCurrentDispatcher.current = this._dispatcher;
            try {
              // elementType could still be a ForwardRef if it was
              // nested inside Memo.
              if (elementType.$$typeof === ForwardRef) {
                invariant(
                  typeof elementType.render === 'function',
                  'forwardRef requires a render function but was given %s.',
                  typeof elementType.render,
                );
                this._rendered = elementType.render.call(
                  undefined,
                  element.props,
                  element.ref,
                );
              } else {
                this._rendered = elementType(element.props, this._context);
              }
            } finally {
              ReactCurrentDispatcher.current = prevDispatcher;
            }
            this._finishHooks(element, context);
          }
        }
      }
  
      this._rendering = false;
      this._updater._invokeCallbacks();
  
      return this.getRenderOutput();
    }
  
    unmount() {
      if (this._instance) {
        if (typeof this._instance.componentWillUnmount === 'function') {
          this._instance.componentWillUnmount();
        }
      }
      this._reset();
    }
  
    _mountClassComponent(
      elementType: Function,
      element: ReactElement,
      context: null | Object,
    ) {
      this._instance.context = context;
      this._instance.props = element.props;
      this._instance.state = this._instance.state || null;
      this._instance.updater = this._updater;
  
      if (
        typeof this._instance.UNSAFE_componentWillMount === 'function' ||
        typeof this._instance.componentWillMount === 'function'
      ) {
        const beforeState = this._newState;
  
        // In order to support react-lifecycles-compat polyfilled components,
        // Unsafe lifecycles should not be invoked for components using the new APIs.
        if (
          typeof elementType.getDerivedStateFromProps !== 'function' &&
          typeof this._instance.getSnapshotBeforeUpdate !== 'function'
        ) {
          if (typeof this._instance.componentWillMount === 'function') {
            this._instance.componentWillMount();
          }
          if (typeof this._instance.UNSAFE_componentWillMount === 'function') {
            this._instance.UNSAFE_componentWillMount();
          }
        }
  
        // setState may have been called during cWM
        if (beforeState !== this._newState) {
          this._instance.state = this._newState || emptyObject;
        }
      }
  
      this._rendered = this._instance.render();
      // Intentionally do not call componentDidMount()
      // because DOM refs are not available.
    }
  
    _updateClassComponent(
      elementType: Function,
      element: ReactElement,
      context: null | Object,
    ) {
      const {props} = element;
  
      const oldState = this._instance.state || emptyObject;
      const oldProps = this._instance.props;
  
      if (oldProps !== props) {
        // In order to support react-lifecycles-compat polyfilled components,
        // Unsafe lifecycles should not be invoked for components using the new APIs.
        if (
          typeof elementType.getDerivedStateFromProps !== 'function' &&
          typeof this._instance.getSnapshotBeforeUpdate !== 'function'
        ) {
          if (typeof this._instance.componentWillReceiveProps === 'function') {
            this._instance.componentWillReceiveProps(props, context);
          }
          if (
            typeof this._instance.UNSAFE_componentWillReceiveProps === 'function'
          ) {
            this._instance.UNSAFE_componentWillReceiveProps(props, context);
          }
        }
      }
  
      // Read state after cWRP in case it calls setState
      let state = this._newState || oldState;
      if (typeof elementType.getDerivedStateFromProps === 'function') {
        const partialState = elementType.getDerivedStateFromProps.call(
          null,
          props,
          state,
        );
        if (partialState != null) {
          state = Object.assign({}, state, partialState);
        }
      }
  
      let shouldUpdate = true;
      if (this._forcedUpdate) {
        shouldUpdate = true;
        this._forcedUpdate = false;
      } else if (typeof this._instance.shouldComponentUpdate === 'function') {
        shouldUpdate = !!this._instance.shouldComponentUpdate(
          props,
          state,
          context,
        );
      } else if (
        elementType.prototype &&
        elementType.prototype.isPureReactComponent
      ) {
        shouldUpdate =
          !shallowEqual(oldProps, props) || !shallowEqual(oldState, state);
      }
  
      if (shouldUpdate) {
        // In order to support react-lifecycles-compat polyfilled components,
        // Unsafe lifecycles should not be invoked for components using the new APIs.
        if (
          typeof elementType.getDerivedStateFromProps !== 'function' &&
          typeof this._instance.getSnapshotBeforeUpdate !== 'function'
        ) {
          if (typeof this._instance.componentWillUpdate === 'function') {
            this._instance.componentWillUpdate(props, state, context);
          }
          if (typeof this._instance.UNSAFE_componentWillUpdate === 'function') {
            this._instance.UNSAFE_componentWillUpdate(props, state, context);
          }
        }
      }
  
      this._instance.context = context;
      this._instance.props = props;
      this._instance.state = state;
      this._newState = null;
  
      if (shouldUpdate) {
        this._rendered = this._instance.render();
      }
      // Intentionally do not call componentDidUpdate()
      // because DOM refs are not available.
    }
  }
  
  let currentlyValidatingElement = null;
  
  function getDisplayName(element) {
    if (element == null) {
      return '#empty';
    } else if (typeof element === 'string' || typeof element === 'number') {
      return '#text';
    } else if (typeof element.type === 'string') {
      return element.type;
    } else {
      const elementType = isMemo(element) ? element.type.type : element.type;
      return elementType.displayName || elementType.name || 'Unknown';
    }
  }
  
  function getStackAddendum() {
    let stack = '';
    if (currentlyValidatingElement) {
      const name = getDisplayName(currentlyValidatingElement);
      const owner = currentlyValidatingElement._owner;
      stack += describeComponentFrame(
        name,
        currentlyValidatingElement._source,
        owner && getComponentName(owner.type),
      );
    }
    return stack;
  }
  
  function getName(type, instance) {
    const constructor = instance && instance.constructor;
    return (
      type.displayName ||
      (constructor && constructor.displayName) ||
      type.name ||
      (constructor && constructor.name) ||
      null
    );
  }
  
  function shouldConstruct(Component) {
    return !!(Component.prototype && Component.prototype.isReactComponent);
  }
  
  function getMaskedContext(contextTypes, unmaskedContext) {
    if (!contextTypes || !unmaskedContext) {
      return emptyObject;
    }
    const context = {};
    for (let key in contextTypes) {
      context[key] = unmaskedContext[key];
    }
    return context;
  }
  
  export default ReactShallowRenderer;
  