"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.createMockReact = createMockReact;

var _babylon = require("babylon");

var _index = require("../../domains/index.js");

var _index2 = require("../../values/index.js");

var _index3 = require("../../methods/index.js");

var _singletons = require("../../singletons.js");

var _utils = require("../../react/utils.js");

var _elements = require("../../react/elements.js");

var _babelTypes = require("babel-types");

var t = _interopRequireWildcard(_babelTypes);

var _invariant = require("../../invariant");

var _invariant2 = _interopRequireDefault(_invariant);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

// most of the code here was taken from https://github.com/facebook/react/blob/master/packages/react/src/ReactElement.js
let reactCode = `
  function createReact(REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE, REACT_PORTAL_TYPE, ReactCurrentOwner) {
    function makeEmptyFunction(arg) {
      return function() {
        return arg;
      };
    }
    var emptyFunction = function() {};
    
    emptyFunction.thatReturns = makeEmptyFunction;
    emptyFunction.thatReturnsFalse = makeEmptyFunction(false);
    emptyFunction.thatReturnsTrue = makeEmptyFunction(true);
    emptyFunction.thatReturnsNull = makeEmptyFunction(null);
    emptyFunction.thatReturnsThis = function() { return this; };
    emptyFunction.thatReturnsArgument = function(arg) { return arg; };

    var hasOwnProperty = Object.prototype.hasOwnProperty;
    var RESERVED_PROPS = {
      key: true,
      ref: true,
      __self: true,
      __source: true,
    };

    var ReactElement = function(type, key, ref, self, source, owner, props) {
      return {
        // This tag allow 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,
      };
    };

    function hasValidRef(config) {
      return config.ref !== undefined;
    }
    
    function hasValidKey(config) {
      return config.key !== undefined;
    }

    function Component(props, context) {
      this.props = props;
      this.context = context;
      this.refs = {};
      this.setState = function () {}; // NO-OP
      this.setState.__PREPACK_MOCK__ = true;
    }
    
    Component.prototype.isReactComponent = {};

    function PureComponent(props, context) {
      this.props = props;
      this.context = context;
      this.refs = {};
      this.setState = function () {}; // NO-OP
      this.setState.__PREPACK_MOCK__ = true;
    }

    PureComponent.prototype.isReactComponent = {};
    PureComponent.prototype.isPureReactComponent = true;

    var userProvidedKeyEscapeRegex = /\/+/g;

    function escapeUserProvidedKey(text) {
      return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/');
    }

    function escape(key) {
      const escapeRegex = /[=:]/g;
      const escaperLookup = {
        '=': '=0',
        ':': '=2',
      };
      const escapedString = ('' + key).replace(escapeRegex, function(match) {
        return escaperLookup[match];
      });
    
      return '$' + escapedString;
    }

    var SEPARATOR = '.';
    var SUBSEPARATOR = ':';
    var POOL_SIZE = 10;
    var 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,
        };
      }
    }

    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);
      }
    }

    function traverseAllChildren(children, callback, traverseContext) {
      if (children == null) {
        return 0;
      }
    
      return traverseAllChildrenImpl(children, '', callback, traverseContext);
    }

    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 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;
            }
        }
      }
    
      if (invokeCallback) {
        callback(
          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;
    
      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 {
        const iteratorFn = getIteratorFn(children);
        if (typeof iteratorFn === 'function') {    
          var 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') {
          let addendum = '';
          var childrenString = '' + children;
        }
      }
    
      return subtreeCount;
    }

    function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
      var escapedPrefix = '';
      if (prefix != null) {
        escapedPrefix = escapeUserProvidedKey(prefix) + '/';
      }
      const traverseContext = getPooledTraverseContext(
        array,
        escapedPrefix,
        func,
        context,
      );
      traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
      releaseTraverseContext(traverseContext);
    }

    function forEachSingleChild(bookKeeping, child, name) {
      const {func, context} = bookKeeping;
      func.call(context, child, bookKeeping.count++);
    }

    function forEachChildren(children, forEachFunc, forEachContext) {
      if (children == null) {
        return children;
      }
      var traverseContext = getPooledTraverseContext(
        null,
        null,
        forEachFunc,
        forEachContext,
      );
      traverseAllChildren(children, forEachSingleChild, traverseContext);
      releaseTraverseContext(traverseContext);
    }

    function mapChildren(children, func, context) {
      if (children == null) {
        return children;
      }
      var result = [];
      mapIntoWithKeyPrefixInternal(children, result, null, func, context);
      return result;
    }

    function countChildren(children) {
      return traverseAllChildren(children, emptyFunction.thatReturnsNull, null);
    }

    function onlyChild(children) {
      return children;
    }

    function toArray(children) {
      var result = [];
      mapIntoWithKeyPrefixInternal(
        children,
        result,
        null,
        emptyFunction.thatReturnsArgument,
      );
      return result;
    }

    function cloneElement(element, config, children) {
      var propName;
      
      // Original props are copied
      var props = Object.assign({}, element.props);
    
      // Reserved names are extracted
      var key = element.key;
      var ref = element.ref;
      // Self is preserved since the owner is preserved.
      var 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.
      var source = element._source;
    
      // Owner will be preserved, unless ref is overridden
      var 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
        var 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.
      var childrenLength = arguments.length - 2;
      if (childrenLength === 1) {
        props.children = children;
      } else if (childrenLength > 1) {
        var childArray = Array(childrenLength);
        for (var i = 0; i < childrenLength; i++) {
          childArray[i] = arguments[i + 2];
        }
        props.children = childArray;
      }
    
      return ReactElement(element.type, key, ref, self, source, owner, props);
    }

    function isValidElement(object) {
      return (
        typeof object === 'object' &&
        object !== null &&
        object.$$typeof === REACT_ELEMENT_TYPE
      );
    }

    function shim() {

    }
    shim.isRequired = shim;

    function getShim() {
      return shim;
    };

    var ReactPropTypes = {
      array: shim,
      bool: shim,
      func: shim,
      number: shim,
      object: shim,
      string: shim,
      symbol: shim,
  
      any: shim,
      arrayOf: getShim,
      element: shim,
      instanceOf: getShim,
      node: shim,
      objectOf: getShim,
      oneOf: getShim,
      oneOfType: getShim,
      shape: getShim,
      exact: getShim
    };

    ReactPropTypes.checkPropTypes = shim;
    ReactPropTypes.PropTypes = ReactPropTypes;

    return {
      Children: {
        forEach: forEachChildren,
        map: mapChildren,
        count: countChildren,
        only: onlyChild,
        toArray,
      },
      Component,
      PureComponent,
      Fragment: REACT_FRAGMENT_TYPE,
      cloneElement,
      isValidElement,
      version: "16.2.0",
      PropTypes: ReactPropTypes,
    };
  }
`; /**
    * Copyright (c) 2017-present, Facebook, Inc.
    * All rights reserved.
    *
    * This source code is licensed under the BSD-style license found in the
    * LICENSE file in the root directory of this source tree. An additional grant
    * of patent rights can be found in the PATENTS file in the same directory.
    */

let reactAst = (0, _babylon.parseExpression)(reactCode, { plugins: ["flow"] });

function createMockReact(realm, reactRequireName) {
  let reactFactory = _singletons.Environment.GetValue(realm, realm.$GlobalEnv.evaluate(reactAst, false));
  (0, _invariant2.default)(reactFactory instanceof _index2.ECMAScriptSourceFunctionValue);

  let currentOwner = realm.react.currentOwner = new _index2.ObjectValue(realm, realm.intrinsics.ObjectPrototype, "currentOwner");
  // this is to get around Flow getting confused
  let factory = reactFactory.$Call;
  (0, _invariant2.default)(factory !== undefined);

  let reactValue = factory(realm.intrinsics.undefined, [(0, _utils.getReactSymbol)("react.element", realm), (0, _utils.getReactSymbol)("react.fragment", realm), (0, _utils.getReactSymbol)("react.portal", realm), currentOwner]);
  (0, _invariant2.default)(reactValue instanceof _index2.ObjectValue);
  reactValue.intrinsicName = `require("${reactRequireName}")`;
  (0, _invariant2.default)(reactValue instanceof _index2.ObjectValue);

  let reactComponentValue = (0, _index3.Get)(realm, reactValue, "Component");
  reactComponentValue.intrinsicName = `require("${reactRequireName}").Component`;
  (0, _invariant2.default)(reactComponentValue instanceof _index2.ECMAScriptFunctionValue);
  let reactPureComponentValue = (0, _index3.Get)(realm, reactValue, "PureComponent");
  reactPureComponentValue.intrinsicName = `require("${reactRequireName}").PureComponent`;
  (0, _invariant2.default)(reactPureComponentValue instanceof _index2.ECMAScriptFunctionValue);
  reactComponentValue.$FunctionKind = "normal";
  (0, _invariant2.default)(reactComponentValue instanceof _index2.ObjectValue);

  let reactComponentPrototypeValue = (0, _index3.Get)(realm, reactComponentValue, "prototype");
  reactComponentPrototypeValue.intrinsicName = `require("${reactRequireName}").Component.prototype`;

  let reactPureComponentPrototypeValue = (0, _index3.Get)(realm, reactPureComponentValue, "prototype");
  reactPureComponentPrototypeValue.intrinsicName = `require("${reactRequireName}").PureComponent.prototype`;

  let reactCloneElementValue = (0, _index3.Get)(realm, reactValue, "cloneElement");
  reactCloneElementValue.intrinsicName = `require("${reactRequireName}").cloneElement`;

  reactValue.refuseSerialization = true;
  let reactElementValue = new _index2.NativeFunctionValue(realm, undefined, `createElement`, 0, (context, [type, config, ...children]) => {
    (0, _invariant2.default)(type instanceof _index2.Value);
    (0, _invariant2.default)(config instanceof _index2.ObjectValue || config instanceof _index2.AbstractObjectValue || config instanceof _index2.AbstractValue || config instanceof _index2.NullValue);

    if (Array.isArray(children)) {
      if (children.length === 0) {
        children = realm.intrinsics.undefined;
      } else if (children.length === 1) {
        children = children[0];
      } else {
        let array = _singletons.Create.ArrayCreate(realm, 0);
        let length = children.length;

        for (let i = 0; i < length; i++) {
          _singletons.Create.CreateDataPropertyOrThrow(realm, array, "" + i, children[i]);
        }
        children = array;
        children.makeFinal();
      }
    }
    (0, _invariant2.default)(children instanceof _index2.Value);
    return (0, _elements.createReactElement)(realm, type, config, children);
  });
  reactValue.$DefineOwnProperty("createElement", {
    value: reactElementValue,
    writable: false,
    enumerable: false,
    configurable: true
  });
  reactElementValue.intrinsicName = `require("${reactRequireName}").createElement`;

  let reactContextValue = new _index2.NativeFunctionValue(realm, undefined, `createContext`, 0, (_, [defaultValue = realm.intrinsics.undefined]) => {
    (0, _invariant2.default)(defaultValue instanceof _index2.Value);
    let consumerObject = new _index2.ObjectValue(realm, realm.intrinsics.ObjectPrototype);
    let providerObject = new _index2.ObjectValue(realm, realm.intrinsics.ObjectPrototype);
    let consumer = _index2.AbstractValue.createTemporalFromBuildFunction(realm, _index2.ObjectValue, [reactContextValue, defaultValue], ([methodNode, defaultValueNode]) => {
      return t.callExpression(methodNode, [defaultValueNode]);
    });
    (0, _invariant2.default)(consumer instanceof _index2.AbstractObjectValue);
    consumer.values = new _index.ValuesDomain(new Set([consumerObject]));

    let provider = _index2.AbstractValue.createTemporalFromBuildFunction(realm, _index2.ObjectValue, [consumer], ([consumerNode]) => {
      return t.memberExpression(consumerNode, t.identifier("Provider"));
    });
    (0, _invariant2.default)(provider instanceof _index2.AbstractObjectValue);
    provider.values = new _index.ValuesDomain(new Set([providerObject]));

    _singletons.Properties.Set(realm, consumerObject, "$$typeof", (0, _utils.getReactSymbol)("react.context", realm), true);
    _singletons.Properties.Set(realm, consumerObject, "currentValue", defaultValue, true);
    _singletons.Properties.Set(realm, consumerObject, "defaultValue", defaultValue, true);
    _singletons.Properties.Set(realm, consumerObject, "changedBits", new _index2.NumberValue(realm, 0), true);
    _singletons.Properties.Set(realm, consumerObject, "Consumer", consumer, true);

    _singletons.Properties.Set(realm, providerObject, "$$typeof", (0, _utils.getReactSymbol)("react.provider", realm), true);
    _singletons.Properties.Set(realm, providerObject, "context", consumer, true);

    _singletons.Properties.Set(realm, consumerObject, "Provider", provider, true);

    return consumer;
  });
  reactValue.$DefineOwnProperty("createContext", {
    value: reactContextValue,
    writable: false,
    enumerable: false,
    configurable: true
  });
  reactContextValue.intrinsicName = `require("${reactRequireName}").createContext`;

  let reactIsValidElementValue = (0, _index3.Get)(realm, reactValue, "isValidElement");
  reactIsValidElementValue.intrinsicName = `require("${reactRequireName}").isValidElement`;

  let reactChildrenValue = (0, _index3.Get)(realm, reactValue, "Children");
  reactChildrenValue.intrinsicName = `require("${reactRequireName}").Children`;

  let reactPropTypesValue = (0, _index3.Get)(realm, reactValue, "PropTypes");
  reactPropTypesValue.intrinsicName = `require("${reactRequireName}").PropTypes`;

  reactValue.refuseSerialization = false;
  reactValue.makeFinal();
  return reactValue;
}
//# sourceMappingURL=react-mocks.js.map