text: /**
   * Copyright (c) 2014-present, Facebook, Inc.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   */
  
  /**
   * ReactElementValidator provides a wrapper around a element factory
   * which validates the props passed to the element. This is intended to be
   * used only in DEV and could be replaced by a static type checker for languages
   * that support it.
   */
  
  import lowPriorityWarning from 'shared/lowPriorityWarning';
  import describeComponentFrame from 'shared/describeComponentFrame';
  import isValidElementType from 'shared/isValidElementType';
  import getComponentName from 'shared/getComponentName';
  import {getIteratorFn, REACT_FRAGMENT_TYPE} from 'shared/ReactSymbols';
  import checkPropTypes from 'prop-types/checkPropTypes';
  import warning from 'fbjs/lib/warning';
  
  import ReactCurrentOwner from './ReactCurrentOwner';
  import {isValidElement, createElement, cloneElement} from './ReactElement';
  import ReactDebugCurrentFrame from './ReactDebugCurrentFrame';
  
  let currentlyValidatingElement;
  let propTypesMisspellWarningShown;
  
  let getDisplayName = () => {};
  let getStackAddendum = () => {};
  
  let VALID_FRAGMENT_PROPS;
  
  if (__DEV__) {
    currentlyValidatingElement = null;
  
    propTypesMisspellWarningShown = false;
  
    getDisplayName = function(element): string {
      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 if (element.type === REACT_FRAGMENT_TYPE) {
        return 'React.Fragment';
      } else {
        return element.type.displayName || element.type.name || 'Unknown';
      }
    };
  
    getStackAddendum = function(): string {
      let stack = '';
      if (currentlyValidatingElement) {
        const name = getDisplayName(currentlyValidatingElement);
        const owner = currentlyValidatingElement._owner;
        stack += describeComponentFrame(
          name,
          currentlyValidatingElement._source,
          owner && getComponentName(owner),
        );
      }
      stack += ReactDebugCurrentFrame.getStackAddendum() || '';
      return stack;
    };
  
    VALID_FRAGMENT_PROPS = new Map([['children', true], ['key', true]]);
  }
  
  function getDeclarationErrorAddendum() {
    if (ReactCurrentOwner.current) {
      const name = getComponentName(ReactCurrentOwner.current);
      if (name) {
        return '\n\nCheck the render method of `' + name + '`.';
      }
    }
    return '';
  }
  
  function getSourceInfoErrorAddendum(elementProps) {
    if (
      elementProps !== null &&
      elementProps !== undefined &&
      elementProps.__source !== undefined
    ) {
      const source = elementProps.__source;
      const fileName = source.fileName.replace(/^.*[\\\/]/, '');
      const lineNumber = source.lineNumber;
      return '\n\nCheck your code at ' + fileName + ':' + lineNumber + '.';
    }
    return '';
  }
  
  /**
   * Warn if there's no key explicitly set on dynamic arrays of children or
   * object keys are not valid. This allows us to keep track of children between
   * updates.
   */
  const ownerHasKeyUseWarning = {};
  
  function getCurrentComponentErrorInfo(parentType) {
    let info = getDeclarationErrorAddendum();
  
    if (!info) {
      const parentName =
        typeof parentType === 'string'
          ? parentType
          : parentType.displayName || parentType.name;
      if (parentName) {
        info = `\n\nCheck the top-level render call using <${parentName}>.`;
      }
    }
    return info;
  }
  
  /**
   * Warn if the element doesn't have an explicit key assigned to it.
   * This element is in an array. The array could grow and shrink or be
   * reordered. All children that haven't already been validated are required to
   * have a "key" property assigned to it. Error statuses are cached so a warning
   * will only be shown once.
   *
   * @internal
   * @param {ReactElement} element Element that requires a key.
   * @param {*} parentType element's parent's type.
   */
  function validateExplicitKey(element, parentType) {
    if (!element._store || element._store.validated || element.key != null) {
      return;
    }
    element._store.validated = true;
  
    const currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType);
    if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {
      return;
    }
    ownerHasKeyUseWarning[currentComponentErrorInfo] = true;
  
    // Usually the current owner is the offender, but if it accepts children as a
    // property, it may be the creator of the child that's responsible for
    // assigning it a key.
    let childOwner = '';
    if (
      element &&
      element._owner &&
      element._owner !== ReactCurrentOwner.current
    ) {
      // Give the component that originally created this child.
      childOwner = ` It was passed a child from ${getComponentName(
        element._owner,
      )}.`;
    }
  
    currentlyValidatingElement = element;
    if (__DEV__) {
      warning(
        false,
        'Each child in an array or iterator should have a unique "key" prop.' +
          '%s%s See https://fb.me/react-warning-keys for more information.%s',
        currentComponentErrorInfo,
        childOwner,
        getStackAddendum(),
      );
    }
    currentlyValidatingElement = null;
  }
  
  /**
   * Ensure that every element either is passed in a static location, in an
   * array with an explicit keys property defined, or in an object literal
   * with valid key property.
   *
   * @internal
   * @param {ReactNode} node Statically passed child of any type.
   * @param {*} parentType node's parent's type.
   */
  function validateChildKeys(node, parentType) {
    if (typeof node !== 'object') {
      return;
    }
    if (Array.isArray(node)) {
      for (let i = 0; i < node.length; i++) {
        const child = node[i];
        if (isValidElement(child)) {
          validateExplicitKey(child, parentType);
        }
      }
    } else if (isValidElement(node)) {
      // This element was passed in a valid location.
      if (node._store) {
        node._store.validated = true;
      }
    } else if (node) {
      const iteratorFn = getIteratorFn(node);
      if (typeof iteratorFn === 'function') {
        // Entry iterators used to provide implicit keys,
        // but now we print a separate warning for them later.
        if (iteratorFn !== node.entries) {
          const iterator = iteratorFn.call(node);
          let step;
          while (!(step = iterator.next()).done) {
            if (isValidElement(step.value)) {
              validateExplicitKey(step.value, parentType);
            }
          }
        }
      }
    }
  }
  
  /**
   * Given an element, validate that its props follow the propTypes definition,
   * provided by the type.
   *
   * @param {ReactElement} element
   */
  function validatePropTypes(element) {
    const componentClass = element.type;
    if (typeof componentClass !== 'function') {
      return;
    }
    const name = componentClass.displayName || componentClass.name;
    const propTypes = componentClass.propTypes;
    if (propTypes) {
      currentlyValidatingElement = element;
      checkPropTypes(propTypes, element.props, 'prop', name, getStackAddendum);
      currentlyValidatingElement = null;
    } else if (
      componentClass.PropTypes !== undefined &&
      !propTypesMisspellWarningShown
    ) {
      propTypesMisspellWarningShown = true;
      warning(
        false,
        'Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?',
        name || 'Unknown',
      );
    }
    if (typeof componentClass.getDefaultProps === 'function') {
      warning(
        componentClass.getDefaultProps.isReactClassApproved,
        'getDefaultProps is only used on classic React.createClass ' +
          'definitions. Use a static property named `defaultProps` instead.',
      );
    }
  }
  
  /**
   * Given a fragment, validate that it can only be provided with fragment props
   * @param {ReactElement} fragment
   */
  function validateFragmentProps(fragment) {
    currentlyValidatingElement = fragment;
  
    const keys = Object.keys(fragment.props);
    for (let i = 0; i < keys.length; i++) {
      const key = keys[i];
      if (!VALID_FRAGMENT_PROPS.has(key)) {
        warning(
          false,
          'Invalid prop `%s` supplied to `React.Fragment`. ' +
            'React.Fragment can only have `key` and `children` props.%s',
          key,
          getStackAddendum(),
        );
        break;
      }
    }
  
    if (fragment.ref !== null) {
      warning(
        false,
        'Invalid attribute `ref` supplied to `React.Fragment`.%s',
        getStackAddendum(),
      );
    }
  
    currentlyValidatingElement = null;
  }
  
  export function createElementWithValidation(type, props, children) {
    const validType = isValidElementType(type);
  
    // We warn in this case but don't throw. We expect the element creation to
    // succeed and there will likely be errors in render.
    if (!validType) {
      let info = '';
      if (
        type === undefined ||
        (typeof type === 'object' &&
          type !== null &&
          Object.keys(type).length === 0)
      ) {
        info +=
          ' You likely forgot to export your component from the file ' +
          "it's defined in, or you might have mixed up default and named imports.";
      }
  
      const sourceInfo = getSourceInfoErrorAddendum(props);
      if (sourceInfo) {
        info += sourceInfo;
      } else {
        info += getDeclarationErrorAddendum();
      }
  
      info += getStackAddendum() || '';
  
      let typeString;
      if (type === null) {
        typeString = 'null';
      } else if (Array.isArray(type)) {
        typeString = 'array';
      } else {
        typeString = typeof type;
      }
  
      warning(
        false,
        'React.createElement: type is invalid -- expected a string (for ' +
          'built-in components) or a class/function (for composite ' +
          'components) but got: %s.%s',
        typeString,
        info,
      );
    }
  
    const element = createElement.apply(this, arguments);
  
    // The result can be nullish if a mock or a custom function is used.
    // TODO: Drop this when these are no longer allowed as the type argument.
    if (element == null) {
      return element;
    }
  
    // Skip key warning if the type isn't valid since our key validation logic
    // doesn't expect a non-string/function type and can throw confusing errors.
    // We don't want exception behavior to differ between dev and prod.
    // (Rendering will throw with a helpful message and as soon as the type is
    // fixed, the key warnings will appear.)
    if (validType) {
      for (let i = 2; i < arguments.length; i++) {
        validateChildKeys(arguments[i], type);
      }
    }
  
    if (type === REACT_FRAGMENT_TYPE) {
      validateFragmentProps(element);
    } else {
      validatePropTypes(element);
    }
  
    return element;
  }
  
  export function createFactoryWithValidation(type) {
    const validatedFactory = createElementWithValidation.bind(null, type);
    validatedFactory.type = type;
    // Legacy hook: remove it
    if (__DEV__) {
      Object.defineProperty(validatedFactory, 'type', {
        enumerable: false,
        get: function() {
          lowPriorityWarning(
            false,
            'Factory.type is deprecated. Access the class directly ' +
              'before passing it to createFactory.',
          );
          Object.defineProperty(this, 'type', {
            value: type,
          });
          return type;
        },
      });
    }
  
    return validatedFactory;
  }
  
  export function cloneElementWithValidation(element, props, children) {
    const newElement = cloneElement.apply(this, arguments);
    for (let i = 2; i < arguments.length; i++) {
      validateChildKeys(arguments[i], newElement.type);
    }
    validatePropTypes(newElement);
    return newElement;
  }
  