"use strict";

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

var _types = require("./util/types");

var _symbols = require("./util/symbols");

var _binding = _interopRequireDefault(require("./util/binding"));

var _component = _interopRequireDefault(require("./component"));

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

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }

function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

const {
  createTree
} = _binding.default;
/**
 * Used during a synchronization flow. Takes in a vTree and a context object
 * and renders the component as a class or a function. Calls standard lifecycle
 * methods.
 *
 * @param {VTree} vTree - tree to render
 *
 * @returns {VTree | null}
 */

function renderComponent(vTree) {
  const RawComponent = vTree.rawNodeName;
  const props = vTree.attributes;
  const isNewable = RawComponent.prototype && RawComponent.prototype.render;
  /** @type {VTree|null} */

  let renderedTree = null; // Existing class component rerender.

  if (_types.InstanceCache.has(vTree)) {
    const instance = _types.InstanceCache.get(vTree);

    if (instance.componentWillReceiveProps) {
      instance.componentWillReceiveProps(props);
    }

    if (instance.shouldComponentUpdate && instance.shouldComponentUpdate(props, instance.state)) {
      // Wipe out all old references before re-rendering.
      _types.ComponentTreeCache.forEach((parentTree, childTree) => {
        if (parentTree === vTree) {
          _types.ComponentTreeCache.delete(childTree);
        }
      });

      _types.ActiveRenderState.push(instance);

      renderedTree = createTree(instance.render(props, instance.state));
      _types.ActiveRenderState.length = 0;

      if (instance.componentDidUpdate && instance.componentDidUpdate) {
        instance.componentDidUpdate(instance.props, instance.state);
      }
    } else {
      return null;
    }
  } // New class instance.
  else if (isNewable) {
    const instance = new RawComponent(props); // Associate the instance to the vTree.

    _types.InstanceCache.set(vTree, instance); // Signal the component is about to mount.


    if (instance.componentWillMount) {
      instance.componentWillMount();
    }

    _types.ActiveRenderState.push(instance); // Initial render of the class component, this should not be called again
    // for the same component in the same position. It will be picked up
    // automatically as a class component and rendered via its own Transaction.


    const renderRetVal = instance.render(props, instance.state);
    _types.ActiveRenderState.length = 0;
    renderedTree = createTree(renderRetVal || '#text'); // Set up the HoC parent/child relationship.

    if (typeof renderedTree.rawNodeName === 'function') {
      _types.ComponentTreeCache.set(renderedTree, vTree);
    } // Associate the instance with the vTree.


    instance[_symbols.$$vTree] = vTree;
  } // Function component, upgrade to a class to make it reactive.
  else {
    let FunctionComponent = /*#__PURE__*/function (_Component) {
      _inherits(FunctionComponent, _Component);

      var _super = _createSuper(FunctionComponent);

      /**
       * @param {any} props
       */
      function FunctionComponent(props) {
        var _this;

        _classCallCheck(this, FunctionComponent);

        _this = _super.call(this, props);
        _this[_symbols.$$vTree] = null;
        return _this;
      }
      /**
       * @param {any} props
       * @param {any} state
       */


      _createClass(FunctionComponent, [{
        key: "render",
        value: function render(props, state) {
          return createTree(RawComponent(props, state));
        }
        /** @type {VTree | null} */

      }]);

      return FunctionComponent;
    }(_component.default);

    const instance = new FunctionComponent(props); // Associate the instance to the vTree.

    _types.InstanceCache.set(vTree, instance); // Allow hooks to "hook" into the active rendering component.


    _types.ActiveRenderState.push(instance);

    let renderRetVal = null;

    try {
      renderRetVal = instance.render(props, instance.state);
    } catch (e) {
      // Clean up after a potential failure.
      _types.ActiveRenderState.length = 0;
      throw e;
    }

    _types.ActiveRenderState.length = 0;
    renderedTree = createTree(renderRetVal || '#text'); // Set up the HoC parent/child relationship.

    if (typeof renderedTree.rawNodeName === 'function') {
      _types.ComponentTreeCache.set(renderedTree, vTree);
    } // Associate the instance with the vTree.


    instance[_symbols.$$vTree] = vTree;
  }

  if (renderedTree !== vTree) {
    // Map all top-level fragment elements to this parent node.
    if (renderedTree.nodeType === 11) {
      renderedTree.childNodes.forEach(childNode => {
        _types.ComponentTreeCache.set(childNode, vTree);
      });
    } // Otherwise directly map the rendered VTree to the component tree.
    else {
      _types.ComponentTreeCache.set(renderedTree, vTree);
    }
  }

  return renderedTree;
}

;
module.exports = exports.default;