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 warning from 'shared/warning';
  import {
    getIteratorFn,
    REACT_ELEMENT_TYPE,
    REACT_PORTAL_TYPE,
  } from 'shared/ReactSymbols';
  
  import {isValidElement, cloneAndReplaceKey} from './ReactElement';
  import ReactDebugCurrentFrame from './ReactDebugCurrentFrame';
  
  const SEPARATOR = '.';
  const SUBSEPARATOR = ':';
  
  /**
   * Escape and wrap key so it is safe to use as a reactid
   *
   * @param {string} key to be escaped.
   * @return {string} the escaped key.
   */
  function escape(key) {
    const escapeRegex = /[=:]/g;
    const escaperLookup = {
      '=': '=0',
      ':': '=2',
    };
    const escapedString = ('' + key).replace(escapeRegex, function(match) {
      return escaperLookup[match];
    });
  
    return '$' + escapedString;
  }
  
  /**
   * TODO: Test that a single child and an array with one item have the same key
   * pattern.
   */
  
  let didWarnAboutMaps = false;
  
  const userProvidedKeyEscapeRegex = /\/+/g;
  function escapeUserProvidedKey(text) {
    return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/');
  }
  
  // 设置 traverseContextPool 是为了减少很多对象创建和gc的损耗
  const POOL_SIZE = 10;
  const traverseContextPool = [];
  function getPooledTraverseContext(
    mapResult,
    keyPrefix,
    mapFunction,
    mapContext,
  ) {
    if (traverseContextPool.length) {
      const traverseContext = traverseContextPool.pop();
      traverseContext.result = mapResult;
      traverseContext.keyPrefix = keyPrefix;
      traverseContext.func = mapFunction;
      traverseContext.context = mapContext;
      traverseContext.count = 0;
      return traverseContext;
    } else {
      return {
        result: mapResult,
        keyPrefix: keyPrefix,
        func: mapFunction,
        context: mapContext,
        count: 0,
      };
    }
  }
  // 把当前的context对象清空然后放回到pool中
  function releaseTraverseContext(traverseContext) {
    traverseContext.result = null;
    traverseContext.keyPrefix = null;
    traverseContext.func = null;
    traverseContext.context = null;
    traverseContext.count = 0;
    if (traverseContextPool.length < POOL_SIZE) {
      traverseContextPool.push(traverseContext);
    }
  }
  
  /**
   * @param {?*} children Children tree container.
   * @param {!string} nameSoFar Name of the key path so far.
   * @param {!function} callback Callback to invoke with each child found.
   * @param {?*} traverseContext Used to pass information throughout the traversal
   * process.
   * @return {!number} The number of children in this subtree.
   */
  function traverseAllChildrenImpl(
    children,
    nameSoFar,
    callback,
    traverseContext,
  ) {
    const type = typeof children;
  
    if (type === 'undefined' || type === 'boolean') {
      // All of the above are perceived as null.
      children = null;
    }
  
    let invokeCallback = false;
  
    if (children === null) {
      invokeCallback = true;
    } else {
      switch (type) {
        case 'string':
        case 'number':
          invokeCallback = true;
          break;
        case 'object':
          switch (children.$$typeof) {
            case REACT_ELEMENT_TYPE:
            case REACT_PORTAL_TYPE:
              invokeCallback = true;
          }
      }
    }
  
    // 如果 children 是简单节点（null、字符串、数字、react组件），直接执行回调函数
    if (invokeCallback) {
      callback(   // map 函数中执行的是 mapSingleChildIntoContext
        traverseContext,
        children,
        // If it's the only child, treat the name as if it was wrapped in an array
        // so that it's consistent if the number of children grows.
        nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,
      );
      return 1;
    }
  
    let child;
    let nextName;
    let subtreeCount = 0; // Count of children found in the current subtree.
    const nextNamePrefix =
      nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
  
    // children 是 array
    if (Array.isArray(children)) {
      for (let i = 0; i < children.length; i++) {
        child = children[i];
        nextName = nextNamePrefix + getComponentKey(child, i);
        subtreeCount += traverseAllChildrenImpl(    // 递归遍历
          child,
          nextName,
          callback,
          traverseContext,
        );
      }
    } else {
      // children 是iterator，遍历iterator
      const iteratorFn = getIteratorFn(children);
      if (typeof iteratorFn === 'function') {
        if (__DEV__) {
          // Warn about using Maps as children
          if (iteratorFn === children.entries) {
            warning(
              didWarnAboutMaps,
              'Using Maps as children is unsupported and will likely yield ' +
                'unexpected results. Convert it to a sequence/iterable of keyed ' +
                'ReactElements instead.',
            );
            didWarnAboutMaps = true;
          }
        }
  
        const iterator = iteratorFn.call(children);
        let step;
        let ii = 0;
        while (!(step = iterator.next()).done) {
          child = step.value;
          nextName = nextNamePrefix + getComponentKey(child, ii++);
          subtreeCount += traverseAllChildrenImpl(
            child,
            nextName,
            callback,
            traverseContext,
          );
        }
      } else if (type === 'object') {   // children 是个对象，报错
        let addendum = '';
        if (__DEV__) {
          addendum =
            ' If you meant to render a collection of children, use an array ' +
            'instead.' +
            ReactDebugCurrentFrame.getStackAddendum();
        }
        const childrenString = '' + children;
        invariant(
          false,
          'Objects are not valid as a React child (found: %s).%s',
          childrenString === '[object Object]'
            ? 'object with keys {' + Object.keys(children).join(', ') + '}'
            : childrenString,
          addendum,
        );
      }
    }
  
    // 返回遍历次数
    return subtreeCount;
  }
  
  /**
   * Traverses children that are typically specified as `props.children`, but
   * might also be specified through attributes:
   *
   * - `traverseAllChildren(this.props.children, ...)`
   * - `traverseAllChildren(this.props.leftPanelChildren, ...)`
   *
   * The `traverseContext` is an optional argument that is passed through the
   * entire traversal. It can be used to store accumulations or anything else that
   * the callback might find relevant.
   *
   * @param {?*} children Children tree object.
   * @param {!function} callback To invoke upon traversing each child.
   * @param {?*} traverseContext Context for traversal.
   * @return {!number} The number of children in this subtree.
   */
  function traverseAllChildren(children, callback, traverseContext) {
    if (children == null) {
      return 0;
    }
  
    return traverseAllChildrenImpl(children, '', callback, traverseContext);
  }
  
  /**
   * Generate a key string that identifies a component within a set.
   *
   * @param {*} component A component that could contain a manual key.
   * @param {number} index Index that is used if a manual key is not provided.
   * @return {string}
   */
  function getComponentKey(component, index) {
    // Do some typechecking here since we call this blindly. We want to ensure
    // that we don't block potential future ES APIs.
    if (
      typeof component === 'object' &&
      component !== null &&
      component.key != null
    ) {
      // Explicit key
      return escape(component.key);
    }
    // Implicit key determined by the index in the set
    return index.toString(36);
  }
  
  function forEachSingleChild(bookKeeping, child, name) {
    const {func, context} = bookKeeping;
    func.call(context, child, bookKeeping.count++);
  }
  
  /**
   * Iterates through children that are typically specified as `props.children`.
   *
   * See https://reactjs.org/docs/react-api.html#reactchildrenforeach
   *
   * The provided forEachFunc(child, index) will be called for each
   * leaf child.
   *
   * @param {?*} children Children tree container.
   * @param {function(*, int)} forEachFunc
   * @param {*} forEachContext Context for forEachContext.
   */
  function forEachChildren(children, forEachFunc, forEachContext) {
    if (children == null) {
      return children;
    }
    const traverseContext = getPooledTraverseContext(
      null,
      null,
      forEachFunc,
      forEachContext,
    );
    traverseAllChildren(children, forEachSingleChild, traverseContext);
    releaseTraverseContext(traverseContext);
  }
  
  function mapSingleChildIntoContext(bookKeeping, child, childKey) {
    const {result, keyPrefix, func, context} = bookKeeping;
  
    // func 即为React.Children.map(props.children, callback)中传入的callback
    let mappedChild = func.call(context, child, bookKeeping.count++);
    if (Array.isArray(mappedChild)) {   // 如果执行结果是数组，将其结果打平为一维数组（即c => c），然后存入result
      mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c => c);
    } else if (mappedChild != null) {
      if (isValidElement(mappedChild)) {
        mappedChild = cloneAndReplaceKey(
          mappedChild,
          // Keep both the (mapped) and old keys if they differ, just as
          // traverseAllChildren used to do for objects as children
          keyPrefix +
            (mappedChild.key && (!child || child.key !== mappedChild.key)
              ? escapeUserProvidedKey(mappedChild.key) + '/'
              : '') +
            childKey,
        );
      }
      result.push(mappedChild);
    }
  }
  
  function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
    let escapedPrefix = '';
    if (prefix != null) {
      escapedPrefix = escapeUserProvidedKey(prefix) + '/';
    }
    const traverseContext = getPooledTraverseContext(
      array,
      escapedPrefix,
      func,
      context,
    );
    traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
    releaseTraverseContext(traverseContext);
  }
  
  /**
   * Maps children that are typically specified as `props.children`.
   *
   * See https://reactjs.org/docs/react-api.html#reactchildrenmap
   *
   * The provided mapFunction(child, key, index) will be called for each
   * leaf child.
   *
   * @param {?*} children Children tree container.
   * @param {function(*, int)} func The map function.
   * @param {*} context Context for mapFunction.
   * @return {object} Object containing the ordered map of results.
   */
  function mapChildren(children, func, context) {
    if (children == null) {
      return children;
    }
    const result = [];
    mapIntoWithKeyPrefixInternal(children, result, null, func, context);
    return result;
  }
  
  /**
   * Count the number of children that are typically specified as
   * `props.children`.
   *
   * See https://reactjs.org/docs/react-api.html#reactchildrencount
   *
   * @param {?*} children Children tree container.
   * @return {number} The number of children.
   */
  function countChildren(children) {
    return traverseAllChildren(children, () => null, null);
  }
  
  /**
   * Flatten a children object (typically specified as `props.children`) and
   * return an array with appropriately re-keyed children.
   *
   * See https://reactjs.org/docs/react-api.html#reactchildrentoarray
   */
  function toArray(children) {
    const result = [];
    mapIntoWithKeyPrefixInternal(children, result, null, child => child);
    return result;
  }
  
  /**
   * Returns the first child in a collection of children and verifies that there
   * is only one child in the collection.
   *
   * See https://reactjs.org/docs/react-api.html#reactchildrenonly
   *
   * The current implementation of this function assumes that a single child gets
   * passed without a wrapper, but the purpose of this helper function is to
   * abstract away the particular structure of children.
   *
   * @param {?object} children Child collection structure.
   * @return {ReactElement} The first and only `ReactElement` contained in the
   * structure.
   */
  function onlyChild(children) {
    invariant(
      isValidElement(children),
      'React.Children.only expected to receive a single React element child.',
    );
    return children;
  }
  
  export {
    forEachChildren as forEach,
    mapChildren as map,
    countChildren as count,
    onlyChild as only,
    toArray,
  };
  