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.
   */
  
  import invariant from 'shared/invariant';
  import warningWithoutStack from 'shared/warningWithoutStack';
  import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
  
  import ReactCurrentOwner from './ReactCurrentOwner';
  
  // 这个文件直接从 ReactElement 开始看起
  // 上面的这些代码没啥阅读意义
  
  const hasOwnProperty = Object.prototype.hasOwnProperty;
  
  const RESERVED_PROPS = {
    key: true,
    ref: true,
    __self: true,
    __source: true,
  };
  
  let specialPropKeyWarningShown, specialPropRefWarningShown;
  
  function hasValidRef(config) {
    if (__DEV__) {
      if (hasOwnProperty.call(config, 'ref')) {
        const getter = Object.getOwnPropertyDescriptor(config, 'ref').get;
        if (getter && getter.isReactWarning) {
          return false;
        }
      }
    }
    return config.ref !== undefined;
  }
  
  function hasValidKey(config) {
    if (__DEV__) {
      if (hasOwnProperty.call(config, 'key')) {
        const getter = Object.getOwnPropertyDescriptor(config, 'key').get;
        if (getter && getter.isReactWarning) {
          return false;
        }
      }
    }
    return config.key !== undefined;
  }
  
  function defineKeyPropWarningGetter(props, displayName) {
    const warnAboutAccessingKey = function() {
      if (!specialPropKeyWarningShown) {
        specialPropKeyWarningShown = true;
        warningWithoutStack(
          false,
          '%s: `key` is not a prop. Trying to access it will result ' +
          'in `undefined` being returned. If you need to access the same ' +
          'value within the child component, you should pass it as a different ' +
          'prop. (https://fb.me/react-special-props)',
          displayName,
        );
      }
    };
    warnAboutAccessingKey.isReactWarning = true;
    Object.defineProperty(props, 'key', {
      get: warnAboutAccessingKey,
      configurable: true,
    });
  }
  
  function defineRefPropWarningGetter(props, displayName) {
    const warnAboutAccessingRef = function() {
      if (!specialPropRefWarningShown) {
        specialPropRefWarningShown = true;
        warningWithoutStack(
          false,
          '%s: `ref` is not a prop. Trying to access it will result ' +
          'in `undefined` being returned. If you need to access the same ' +
          'value within the child component, you should pass it as a different ' +
          'prop. (https://fb.me/react-special-props)',
          displayName,
        );
      }
    };
    warnAboutAccessingRef.isReactWarning = true;
    Object.defineProperty(props, 'ref', {
      get: warnAboutAccessingRef,
      configurable: true,
    });
  }
  
  /**
   * Factory method to create a new React element. This no longer adheres to
   * the class pattern, so do not use new to call it. Also, no instanceof check
   * will work. Instead test $$typeof field against Symbol.for('react.element') to check
   * if something is a React Element.
   *
   * @param {*} type
   * @param {*} key
   * @param {string|object} ref
   * @param {*} self A *temporary* helper to detect places where `this` is
   * different from the `owner` when React.createElement is called, so that we
   * can warn. We want to get rid of owner and replace string `ref`s with arrow
   * functions, and as long as `this` and owner are the same, there will be no
   * change in behavior.
   * @param {*} source An annotation object (added by a transpiler or otherwise)
   * indicating filename, line number, and/or other information.
   * @param {*} owner
   * @param {*} props
   * @internal
   */
  // 这就是个工厂函数，帮助我们创建 React Element 的
  // 内部代码很简单，无非多了一个 $$typeof 帮助我们标识
  // 这是一个 React Element
  const ReactElement = function(type, key, ref, self, source, owner, props) {
    const element = {
      // This tag allows us to uniquely identify this as a React Element
      $$typeof: REACT_ELEMENT_TYPE,
  
      // Built-in properties that belong on the element
      type: type,
      key: key,
      ref: ref,
      props: props,
  
      // Record the component responsible for creating this element.
      _owner: owner,
    };
  
    if (__DEV__) {
      // The validation flag is currently mutative. We put it on
      // an external backing store so that we can freeze the whole object.
      // This can be replaced with a WeakMap once they are implemented in
      // commonly used development environments.
      element._store = {};
  
      // To make comparing ReactElements easier for testing purposes, we make
      // the validation flag non-enumerable (where possible, which should
      // include every environment we run tests in), so the test framework
      // ignores it.
      Object.defineProperty(element._store, 'validated', {
        configurable: false,
        enumerable: false,
        writable: true,
        value: false,
      });
      // self and source are DEV only properties.
      Object.defineProperty(element, '_self', {
        configurable: false,
        enumerable: false,
        writable: false,
        value: self,
      });
      // Two elements created in two different places should be considered
      // equal for testing purposes and therefore we hide it from enumeration.
      Object.defineProperty(element, '_source', {
        configurable: false,
        enumerable: false,
        writable: false,
        value: source,
      });
      if (Object.freeze) {
        Object.freeze(element.props);
        Object.freeze(element);
      }
    }
  
    return element;
  };
  
  /**
   * Create and return a new ReactElement of the given type.
   * 根据 type 返回一个新的 ReactElement
   * See https://reactjs.org/docs/react-api.html#createelement
   */
    export function createElement(type, config, children) {
    let propName;
  
    // Reserved names are extracted
    const props = {};
  
    let key = null;
    let ref = null;
    let self = null;
    let source = null;
    // 判断是否传入配置，比如 <div className='11'></div> 中的 className 会被解析到配置中
    if (config != null) {
      // 验证 ref 和 key，只在开发环境下
      if (hasValidRef(config)) {
        ref = config.ref;
      }
      if (hasValidKey(config)) {
        key = '' + config.key;
      }
      // 赋值操作
      // self 是为了以后正确获取 this
      // source 基本来说没啥用，内部有一些 filename, line number 这种
      self = config.__self === undefined ? null : config.__self;
      source = config.__source === undefined ? null : config.__source;
      // Remaining properties are added to a new props object
      // 遍历配置，把内建的几个属性剔除后丢到 props 中
      for (propName in config) {
        if (
          // JavaScript 并没有保护 hasOwnProperty 属性名，因此某个对象是有可能存在使用这个属性名的属性，
          // 使用外部的 hasOwnProperty 获得正确的结果是需要的
  
          // var foo = {
          //   hasOwnProperty: function() {
          //       return false;
          //   },
          //   bar: 'Here be dragons'
          // };
          // foo.hasOwnProperty('bar'); // 始终返回 false
          // // 如果担心这种情况，可以直接使用原型链上真正的 hasOwnProperty 方法
          // ({}).hasOwnProperty.call(foo, 'bar'); // true
          // // 也可以使用 Object 原型上的 hasOwnProperty 属性
          // Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
          hasOwnProperty.call(config, propName) &&
          !RESERVED_PROPS.hasOwnProperty(propName)
        ) {
          props[propName] = config[propName];
        }
      }
    }
  
    // Children can be more than one argument, and those are transferred onto
    // the newly allocated props object.
    // 处理 children 的几个操作，很简单
    const childrenLength = arguments.length - 2;
    if (childrenLength === 1) {
      props.children = children;
    } else if (childrenLength > 1) {
      const childArray = Array(childrenLength);
      for (let i = 0; i < childrenLength; i++) {
        childArray[i] = arguments[i + 2];
      }
      if (__DEV__) {
        if (Object.freeze) {
          Object.freeze(childArray);
        }
      }
      props.children = childArray;
    }
  
    // Resolve default props
    // 判断是否有给组件设置 defaultProps，有的话判断是否有给 props 赋值，只有当值为
    // undefined 时，才会设置默认值
    if (type && type.defaultProps) {
      const defaultProps = type.defaultProps;
      for (propName in defaultProps) {
        if (props[propName] === undefined) {
          props[propName] = defaultProps[propName];
        }
      }
    }
    if (__DEV__) {
      if (key || ref) {
        const displayName =
          typeof type === 'function'
            ? type.displayName || type.name || 'Unknown'
            : type;
        if (key) {
          defineKeyPropWarningGetter(props, displayName);
        }
        if (ref) {
          defineRefPropWarningGetter(props, displayName);
        }
      }
    }
    return ReactElement(
      type,
      key,
      ref,
      self,
      source,
      ReactCurrentOwner.current,
      props,
    );
  }
  
  /**
   * Return a function that produces ReactElements of a given type.
   * See https://reactjs.org/docs/react-api.html#createfactory
   */
  export function createFactory(type) {
    const factory = createElement.bind(null, type);
    // Expose the type on the factory and the prototype so that it can be
    // easily accessed on elements. E.g. `<Foo />.type === Foo`.
    // This should not be named `constructor` since this may not be the function
    // that created the element, and it may not even be a constructor.
    // Legacy hook: remove it
    factory.type = type;
    return factory;
  }
  
  export function cloneAndReplaceKey(oldElement, newKey) {
    const newElement = ReactElement(
      oldElement.type,
      newKey,
      oldElement.ref,
      oldElement._self,
      oldElement._source,
      oldElement._owner,
      oldElement.props,
    );
  
    return newElement;
  }
  
  /**
   * Clone and return a new ReactElement using element as the starting point.
   * See https://reactjs.org/docs/react-api.html#cloneelement
   */
  export function cloneElement(element, config, children) {
    invariant(
      !(element === null || element === undefined),
      'React.cloneElement(...): The argument must be a React element, but you passed %s.',
      element,
    );
  
    let propName;
  
    // Original props are copied
    const props = Object.assign({}, element.props);
  
    // Reserved names are extracted
    let key = element.key;
    let ref = element.ref;
    // Self is preserved since the owner is preserved.
    const self = element._self;
    // Source is preserved since cloneElement is unlikely to be targeted by a
    // transpiler, and the original source is probably a better indicator of the
    // true owner.
    const source = element._source;
  
    // Owner will be preserved, unless ref is overridden
    let owner = element._owner;
  
    if (config != null) {
      if (hasValidRef(config)) {
        // Silently steal the ref from the parent.
        ref = config.ref;
        owner = ReactCurrentOwner.current;
      }
      if (hasValidKey(config)) {
        key = '' + config.key;
      }
  
      // Remaining properties override existing props
      let defaultProps;
      if (element.type && element.type.defaultProps) {
        defaultProps = element.type.defaultProps;
      }
      for (propName in config) {
        if (
          hasOwnProperty.call(config, propName) &&
          !RESERVED_PROPS.hasOwnProperty(propName)
        ) {
          if (config[propName] === undefined && defaultProps !== undefined) {
            // Resolve default props
            props[propName] = defaultProps[propName];
          } else {
            props[propName] = config[propName];
          }
        }
      }
    }
  
    // Children can be more than one argument, and those are transferred onto
    // the newly allocated props object.
    const childrenLength = arguments.length - 2;
    if (childrenLength === 1) {
      props.children = children;
    } else if (childrenLength > 1) {
      const childArray = Array(childrenLength);
      for (let i = 0; i < childrenLength; i++) {
        childArray[i] = arguments[i + 2];
      }
      props.children = childArray;
    }
  
    return ReactElement(element.type, key, ref, self, source, owner, props);
  }
  
  /**
   * Verifies the object is a ReactElement.
   * See https://reactjs.org/docs/react-api.html#isvalidelement
   * @param {?object} object
   * @return {boolean} True if `object` is a ReactElement.
   * @final
   */
  export function isValidElement(object) {
    return (
      typeof object === 'object' &&
      object !== null &&
      object.$$typeof === REACT_ELEMENT_TYPE
    );
  }
  