(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
  (global = global || self, factory(global.components = {}));
}(this, (function (exports) { 'use strict';

  /**
   * @type {{ [type: string]: any }}
   */
  var EMPTY = {
    OBJ: {},
    ARR: [],
    BOOL: true,
    FUN: function () {}
  };
  /**
   * @typedef {any[]} ActiveTreeCache
   * @type {ActiveTreeCache}
   */

  var ActiveRenderState = [];
  var ComponentTreeCache = new Map();
  /**
   * @typedef {Map<VTree, any>} InstanceCache
   * @type {InstanceCache}
   */

  var InstanceCache = new Map();

  var caches = {
    ComponentTreeCache: ComponentTreeCache,
    InstanceCache: InstanceCache
  };

  var internals = /*#__PURE__*/Object.freeze({
    __proto__: null,
    caches: caches
  });

  function ownKeys(object, enumerableOnly) {
    var keys = Object.keys(object);

    if (Object.getOwnPropertySymbols) {
      var symbols = Object.getOwnPropertySymbols(object);

      if (enumerableOnly) {
        symbols = symbols.filter(function (sym) {
          return Object.getOwnPropertyDescriptor(object, sym).enumerable;
        });
      }

      keys.push.apply(keys, symbols);
    }

    return keys;
  }

  function _objectSpread2(target) {
    for (var i = 1; i < arguments.length; i++) {
      var source = arguments[i] != null ? arguments[i] : {};

      if (i % 2) {
        ownKeys(Object(source), true).forEach(function (key) {
          _defineProperty(target, key, source[key]);
        });
      } else if (Object.getOwnPropertyDescriptors) {
        Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
      } else {
        ownKeys(Object(source)).forEach(function (key) {
          Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
        });
      }
    }

    return target;
  }

  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 _defineProperty(obj, key, value) {
    if (key in obj) {
      Object.defineProperty(obj, key, {
        value: value,
        enumerable: true,
        configurable: true,
        writable: true
      });
    } else {
      obj[key] = value;
    }

    return obj;
  }

  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 _getPrototypeOf(o) {
    _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
      return o.__proto__ || Object.getPrototypeOf(o);
    };
    return _getPrototypeOf(o);
  }

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

    return _setPrototypeOf(o, p);
  }

  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 _assertThisInitialized(self) {
    if (self === void 0) {
      throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }

    return self;
  }

  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 _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 _slicedToArray(arr, i) {
    return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
  }

  function _toConsumableArray(arr) {
    return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
  }

  function _arrayWithoutHoles(arr) {
    if (Array.isArray(arr)) return _arrayLikeToArray(arr);
  }

  function _arrayWithHoles(arr) {
    if (Array.isArray(arr)) return arr;
  }

  function _iterableToArray(iter) {
    if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
  }

  function _iterableToArrayLimit(arr, i) {
    var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];

    if (_i == null) return;
    var _arr = [];
    var _n = true;
    var _d = false;

    var _s, _e;

    try {
      for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
        _arr.push(_s.value);

        if (i && _arr.length === i) break;
      }
    } catch (err) {
      _d = true;
      _e = err;
    } finally {
      try {
        if (!_n && _i["return"] != null) _i["return"]();
      } finally {
        if (_d) throw _e;
      }
    }

    return _arr;
  }

  function _unsupportedIterableToArray(o, minLen) {
    if (!o) return;
    if (typeof o === "string") return _arrayLikeToArray(o, minLen);
    var n = Object.prototype.toString.call(o).slice(8, -1);
    if (n === "Object" && o.constructor) n = o.constructor.name;
    if (n === "Map" || n === "Set") return Array.from(o);
    if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
  }

  function _arrayLikeToArray(arr, len) {
    if (len == null || len > arr.length) len = arr.length;

    for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];

    return arr2;
  }

  function _nonIterableSpread() {
    throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
  }

  function _nonIterableRest() {
    throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
  }

  var $$render = Symbol.for('diff.render');
  var $$vTree = Symbol.for('diff.vTree');
  var $$unsubscribe = Symbol.for('diff.unsubscribe');
  var $$type = Symbol.for('diff.type');
  var $$hooks = Symbol.for('diff.hooks');
  var $$insertAfter = Symbol.for('diff.after');

  /**
   * Create state allows a class or function component to create state from the
   * render function. Any other attempt to use this will fail. All createState
   * invocations must be in the exact same order, every render.
   *
   * Borrowed from the concept introduced by React, useState. This will be used by
   * the React Compatibility later renamed as `useState`. The reason this is named
   * `createState` is due to no guarentees of parity with React hooks.
   *
   * @param {any} defaultValue
   *
   * @returns {any[]}
   */

  function createState() {
    var defaultValue = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

    if (ActiveRenderState.length === 0) {
      throw new Error('Cannot create state unless in render function');
    }

    var _ActiveRenderState = _slicedToArray(ActiveRenderState, 1),
        activeComponent = _ActiveRenderState[0];

    var activeHook = activeComponent[$$hooks].shift();
    var currentValue = activeHook ? activeHook[0] : defaultValue;
    var retVal = activeHook || [currentValue];
    /**
     * @param {any} newValue
     */

    var setState = function (newValue) {
      retVal[0] = newValue;
      return activeComponent.forceUpdate();
    };

    if (retVal.length === 1) {
      retVal.push(setState);
    } // Return currentValue and setState.


    activeComponent[$$hooks].push(retVal);
    return retVal;
  }

  /**
   * Allow a function component to hook into lifecycle methods in a manner
   * consistent with class components.
   *
   * @param {Function} sideEffectFn - A function that is called whenever the
   * component is mounted or updated. To invoke cleanup pass a second function
   * which will run whenever the component is removed.
   *
   * @returns {void}
   */

  function createSideEffect(sideEffectFn) {
    if (ActiveRenderState.length === 0) {
      throw new Error('Cannot create side effect unless in render function');
    }

    if (typeof sideEffectFn !== 'function') {
      throw new Error('Missing function for side effect');
    }

    var _ActiveRenderState = _slicedToArray(ActiveRenderState, 1),
        activeComponent = _ActiveRenderState[0];

    var activeHook = activeComponent[$$hooks].shift(); // Only do this the first time.

    if (!activeHook) {
      // First schedule a componentDidMount
      activeComponent.componentDidMount = activeComponent.componentDidUpdate = function () {
        var unMount = sideEffectFn() || EMPTY.FUN;

        if (typeof unMount === 'function') {
          activeComponent.componentWillUnmount = function () {
            return unMount();
          };
        }
      }; // Return currentValue and setState.


      activeComponent[$$hooks].push(sideEffectFn);
    }
  }

  /** @type {unknown} */
  var globalThis = typeof global === 'object' ? global : (typeof window === 'object' ? window : self) || {};

  /**
   * @returns {DIFF_BINDING}
   */

  var diff = /** @type {DIFF_BINDING} */

  /** @type {any} */
  globalThis[Symbol.for('diffHTML')];

  var Internals = diff.Internals;
  /**
   * @param {any} target
   * @param {VTree} vTree
   */

  var invokeRef = function () {
    var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : EMPTY.OBJ;
    var vTree = arguments.length > 1 ? arguments[1] : undefined;

    var _ref = target.props || target,
        ref = _ref.ref; // Allow refs to be passed to HTML elements. When in a DOM environment
    // a Node will be passed to the ref function and assigned.


    if (!ref) {
      target = Internals.NodeCache.get(vTree);
      ref = vTree.attributes.ref;
    }

    if (typeof ref === 'function') {
      ref(target);
    } else if (typeof ref === 'object' && ref) {
      ref.current = target;
    } else if (typeof ref === 'string') {
      target.refs = _objectSpread2(_objectSpread2({}, target.refs), {}, _defineProperty({}, ref, target));
    }
  };

  var invokeRefsForVTrees = function () {
    for (var i = 0; i < arguments.length; i++) {
      var vTree = i < 0 || arguments.length <= i ? undefined : arguments[i];
      if (!vTree) continue;
      var componentTree = ComponentTreeCache.get(vTree);
      var instance = InstanceCache.get(componentTree || vTree);

      if (vTree.childNodes.length) {
        invokeRefsForVTrees.apply(void 0, _toConsumableArray(vTree.childNodes));
      }

      if (!instance) {
        invokeRef(Internals.NodeCache.get(vTree), vTree);
        continue;
      } // If any instances exist, loop through them and invoke the respective `ref`
      // logic.


      invokeRef(instance, vTree);
    }
  };
  /**
   * @param {VTree} vTree
   *
   * @returns {void}
   */


  function componentDidMount(vTree) {
    var componentTree = ComponentTreeCache.get(vTree);
    /** @type {VTree[]} */

    var childTrees = [];
    ComponentTreeCache.forEach(function (parentTree, childTree) {
      if (parentTree === componentTree) {
        childTrees.push(childTree);
      }
    });
    invokeRefsForVTrees(vTree); // Only trigger a mount for the first element.

    if (childTrees.length === 0 || childTrees[0] !== vTree) {
      return;
    } // If there is a parent, ensure it is called recursively.


    if (ComponentTreeCache.has(componentTree)) {
      componentDidMount(componentTree);
    }

    var instance = InstanceCache.get(componentTree);
    instance && instance.componentDidMount && instance.componentDidMount();
  }

  var release = diff.release,
      Internals$1 = diff.Internals;
  /**
   * @param {any} target
   * @param {VTree} vTree
   */

  var invokeRef$1 = function () {
    var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : EMPTY.OBJ;
    var vTree = arguments.length > 1 ? arguments[1] : undefined;

    var _ref = target.props || target,
        ref = _ref.ref; // Allow refs to be passed to HTML elements. When in a DOM environment
    // a Node will be passed to the ref function and assigned.


    if (!ref) {
      ref = vTree.attributes.ref;
    }

    if (typeof ref === 'function') {
      ref(null);
    } else if (typeof ref === 'object' && ref) {
      ref.current = null;
    } else if (typeof ref === 'string') {
      target.refs = _objectSpread2(_objectSpread2({}, target.refs), {}, _defineProperty({}, ref, null));
    }
  };

  var invokeRefsForVTrees$1 = function () {
    for (var i = 0; i < arguments.length; i++) {
      var vTree = i < 0 || arguments.length <= i ? undefined : arguments[i];
      if (!vTree) continue;
      var componentTree = ComponentTreeCache.get(vTree);
      var instance = InstanceCache.get(componentTree || vTree);

      if (vTree.childNodes.length) {
        invokeRefsForVTrees$1.apply(void 0, _toConsumableArray(vTree.childNodes));
      }

      if (!instance) {
        invokeRef$1(Internals$1.NodeCache.get(vTree), vTree);
        continue;
      } // If any instances exist, loop through them and invoke the respective `ref`
      // logic.


      invokeRef$1(instance, vTree);
    }
  };
  /**
   * Called whenever a component is being removed.
   *
   * @param {VTree} vTree - The respecting tree pointing to the component
   */


  function componentWillUnmount(vTree) {
    var componentTree = ComponentTreeCache.get(vTree);
    ComponentTreeCache.forEach(function (parentTree, childTree) {
    });
    invokeRefsForVTrees$1(vTree);
    var domNode = Internals$1.NodeCache.get(vTree); // Clean up attached Shadow DOM.

    if (domNode &&
    /** @type {any} */
    domNode.shadowRoot) {
      release(
      /** @type {any} */
      domNode.shadowRoot);
    }

    if (!InstanceCache.has(componentTree)) {
      return;
    }

    var instance = InstanceCache.get(componentTree);
    InstanceCache.delete(componentTree); // Empty out all hooks for gc. If using a stateless class or function, they
    // may not have this value set.

    if (instance[$$hooks]) {
      instance[$$hooks].length = 0;
    }

    ComponentTreeCache.delete(vTree); // If there is a parent, ensure it is called recursively.

    if (ComponentTreeCache.has(componentTree)) {
      componentWillUnmount(componentTree);
    } // Ensure this is a stateful component. Stateless components do not get
    // lifecycle events yet.


    instance && instance.componentWillUnmount && instance.componentWillUnmount();
  }

  var _diff$Internals = diff.Internals,
      NodeCache = _diff$Internals.NodeCache,
      PATCH_TYPE = _diff$Internals.PATCH_TYPE,
      decodeEntities = _diff$Internals.decodeEntities;
  var uppercaseEx = /[A-Z]/g;
  /**
   * Once the transaction has ended we can know for certain that DOM operations
   * have completed and can trigger lifecycle methods like componentDidMount or
   * componentWillUnmount.
   *
   * @param {Transaction} transaction
   */

  var onceEnded = (function (transaction) {
    if (transaction.aborted) {
      return;
    }

    var patches = transaction.patches;
    var length = patches.length;
    var i = 0;

    while (true) {
      var patchType = patches[i]; // Exhausted remaining patches.

      if (i === length) {
        break;
      }

      switch (patchType) {
        case PATCH_TYPE.SET_ATTRIBUTE:
          {
            var vTree = patches[i + 1];
            var name = patches[i + 2];
            var value = patches[i + 3];
            uppercaseEx.lastIndex = 0; // Normalize uppercase attributes.

            if (uppercaseEx.test(name)) {
              uppercaseEx.lastIndex = 0;
              var newName = name.replace(uppercaseEx, function (
              /** @type {string} */
              ch) {
                return "-".concat(ch.toLowerCase());
              });

              if (value && typeof value === 'string') {
                var decodedValue = decodeEntities(value);

                if (NodeCache.has(vTree)) {
                  /** @type {HTMLElement} */
                  NodeCache.get(vTree).setAttribute(newName, decodedValue);
                }
              }
            }

            i += 4;
            break;
          }

        case PATCH_TYPE.REMOVE_ATTRIBUTE:
          {
            i += 3;
            break;
          }

        case PATCH_TYPE.NODE_VALUE:
          {
            i += 4;
            break;
          }

        case PATCH_TYPE.INSERT_BEFORE:
          {
            var newTree = patches[i + 2];
            componentDidMount(newTree);
            i += 4;
            break;
          }

        case PATCH_TYPE.REPLACE_CHILD:
          {
            var _newTree = patches[i + 1];
            var oldTree = patches[i + 2];
            componentWillUnmount(oldTree);
            componentDidMount(_newTree);
            i += 3;
            break;
          }

        case PATCH_TYPE.REMOVE_CHILD:
          {
            var _vTree = patches[i + 1];
            componentWillUnmount(_vTree);
            i += 2;
            break;
          }
      }
    }
  });

  var createTree = diff.createTree;
  /**
   * 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) {
    var RawComponent = vTree.rawNodeName;
    var props = vTree.attributes;
    var isNewable = RawComponent.prototype && RawComponent.prototype.render;
    /** @type {VTree|null} */

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

    if (InstanceCache.has(vTree)) {
      var instance = 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.
        ComponentTreeCache.forEach(function (parentTree, childTree) {
          if (parentTree === vTree) {
            ComponentTreeCache.delete(childTree);
          }
        });
        ActiveRenderState.push(instance);
        renderedTree = createTree(instance.render(props, instance.state));
        ActiveRenderState.length = 0;

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


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

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

      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.

      var renderRetVal = _instance.render(props, _instance.state);

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

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


      _instance[$$vTree] = vTree;
    } // Function component, upgrade to a class to make it reactive.
    else {
      var 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[$$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);

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


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

      ActiveRenderState.push(_instance2);
      var _renderRetVal = null;

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

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

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


      _instance2[$$vTree] = vTree;
    }

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

    return renderedTree;
  }

  var assign = Object.assign;
  /**
   * @param {VTree} oldTree
   * @param {VTree} newTree
   *
   * @returns {VTree | null}
   */

  function render(oldTree, newTree) {
    var oldComponentTree = null; // When there is an oldTree and it has childNodes, attempt to look up first
    // by the top-level element, or by the first element.

    if (oldTree && oldTree.childNodes) {
      // First try and lookup the old tree as a component.
      oldComponentTree = ComponentTreeCache.get(oldTree); // If that fails, try looking up it's first child.

      if (!oldComponentTree) {
        oldComponentTree = ComponentTreeCache.get(oldTree.childNodes[0]);
      }
    } // If there is no old component, or if the components do not match, then we
    // are rendering a brand new component.


    if (!oldComponentTree || oldComponentTree.rawNodeName !== newTree.rawNodeName) {
      return renderComponent(newTree);
    } // Otherwise re-use the existing component if the constructors are the same.


    if (oldComponentTree) {
      // Update the incoming props/attrs.
      assign(oldComponentTree.attributes, newTree.attributes);
      return renderComponent(oldComponentTree);
    }

    return oldTree;
  }
  /**
   * @param {VTree} vTree
   */


  var releaseHook = function (vTree) {
    return componentWillUnmount(vTree);
  };
  /**
   * @param {VTree} vTree
   */


  var createTreeHook = function (vTree) {
    var customElements = globalThis.customElements;
    var Constructor = customElements && customElements.get(vTree.nodeName);

    if (typeof vTree.rawNodeName === 'function' || Constructor) {
      vTree.attributes.children = vTree.attributes.children || vTree.childNodes;
    }
  };
  /**
   * @param {VTree} vTree
   */


  var createNodeHook = function (vTree) {
    // Only look up elements with a dash in the name.
    if (!vTree.nodeName.includes('-')) return; // Convert this to globalThis

    var customElements = globalThis.customElements;
    var Constructor = customElements && customElements.get(vTree.nodeName);

    if (Constructor) {
      return new Constructor(vTree.attributes);
    }
  };
  /**
   * @param {VTree} oldTree
   * @param {VTree} newTree
   */


  var syncTreeHook = function (oldTree, newTree) {
    // Render components during synchronization.
    if ( // When child is a Component
    typeof newTree.rawNodeName === 'function' && ( // If there is an oldTree and it's not the existing component, trigger a
    // render.
    oldTree && oldTree.rawNodeName ? oldTree.rawNodeName !== newTree.rawNodeName : false)) {
      return render(oldTree, newTree);
    }

    if (!newTree.childNodes) {
      return oldTree;
    } // Loop through childNodes seeking out components to render.


    for (var i = 0; i < newTree.childNodes.length; i++) {
      var newChildTree = newTree.childNodes[i];
      var oldChildTree = oldTree.childNodes && oldTree.childNodes[i] || EMPTY.OBJ; // If the old slot was not a component, and the new tree is, then we are
      // rendering a brand new component.

      if (typeof newChildTree.rawNodeName === 'function') {
        var renderTree = render(oldChildTree, newChildTree); // If nothing was rendered, return the oldTree.

        if (!renderTree) {
          return oldTree;
        } // Inject the rendered tree into the position.


        if (renderTree) {
          newTree.childNodes[i] = renderTree; // If the rendered tree is a fragment, splice in the children, as this
          // is simply a container for the nodes.

          if (renderTree.nodeType === 11) {
            // If a function was returned, re-run the inspection over this
            // element.
            if (typeof renderTree.rawNodeName === 'function') {
              i = i - 1;
            } // Replace the fragment with the rendered elements. This reduces and
            // flattens the fragments into their respective nodes. If there are
            // none, then they are removed from the DOM and nothing is rendered.
            else {
              var _newTree$childNodes;

              (_newTree$childNodes = newTree.childNodes).splice.apply(_newTree$childNodes, [i, 1].concat(_toConsumableArray(renderTree.childNodes)));
            }
          }
        }
      }
    }
  };

  var middleware = (function () {
    return assign(function (
    /** @type {Transaction} */
    transaction) {
      return transaction.onceEnded(onceEnded);
    }, {
      displayName: 'componentTask',
      syncTreeHook: syncTreeHook,
      createNodeHook: createNodeHook,
      createTreeHook: createTreeHook,
      releaseHook: releaseHook
    });
  });

  var outerHTML = diff.outerHTML,
      innerHTML = diff.innerHTML,
      createTree$1 = diff.createTree,
      release$1 = diff.release,
      Internals$2 = diff.Internals;
  var isArray = Array.isArray;
  var setPrototypeOf = Object.setPrototypeOf,
      defineProperty = Object.defineProperty,
      keys = Object.keys,
      assign$1 = Object.assign;
  var RenderDebounce = new WeakMap();
  /**
   * @param {Props} defaultProps
   * @returns {string[]}
   */

  var getObserved = function (_ref) {
    var defaultProps = _ref.defaultProps;
    return defaultProps ? keys(defaultProps) : EMPTY.ARR;
  };
  /**
   * Creates the `component.props` object.
   *
   * @param {any} domNode
   * @param {Props} existingProps
   */


  var createProps = function (domNode) {
    var existingProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    return getObserved(domNode.constructor).reduce(function (props, attr) {
      return _objectSpread2(_defineProperty({}, attr, (domNode.hasAttribute(attr) ? domNode.getAttribute(attr) : domNode[attr]) || props[attr] || domNode.constructor.defaultProps[attr]), props);
    }, existingProps);
  };
  /**
   * Creates the `component.state` object.
   *
   * @param {Component} domNode
   * @param {State} newState
   */


  var createState$1 = function (domNode, newState) {
    return assign$1({}, domNode.state, newState);
  };
  /**
   * Finds all VTrees associated with a component.
   *
   * @param {VTree[]} childTrees
   * @param {VTree} vTree
   */


  var getChildTrees = function (childTrees, vTree) {
    ComponentTreeCache.forEach(function (parentTree, childTree) {
      if (parentTree === vTree) {
        ComponentTreeCache.delete(childTree);

        if (typeof childTree.rawNodeName !== 'function') {
          childTrees.push(childTree);
        } else {
          getChildTrees(childTrees, childTree);
        }
      }
    });
  };
  /**
   * Represents a vanilla JavaScript Component. This is a lightweight version of
   * what you'd find in most modern web application libraries. It provides a
   * React-inspired API.
   */


  var Component = /*#__PURE__*/function () {
    /**
     * Creates a lightweight stateful JavaScript component. Takes the static
     * default props and applies them to the incoming initial props.
     *
     * @param {Props} initialProps
     */
    function Component(initialProps) {
      _classCallCheck(this, Component);

      this.state = {};
      this[$$type] = 'class';
      this[$$vTree] = null;
      this[$$hooks] = [];
      var instance = this; // This is how we detect if this is a Web Component or not. The class
      // must be registered ahead of time within the customElements global
      // registry. This will allow us to construct the HTMLElement and attach the
      // shadow root.

      try {
        instance = Reflect.construct(HTMLElement, [], this instanceof Component ? this.constructor : void 0);
        /** @type {any} */

        instance.attachShadow({
          mode: 'open'
        });
        /** @type {any} */

        instance[$$type] = 'web';
      } catch (e) {
        // Not a Web Component.

        /** @type {any} */
        instance[$$type] = 'class';
      }

      var props = instance.props = assign$1({}, initialProps);
      var _instance$constructor =
      /** @type {any} */
      instance.constructor.defaultProps,
          defaultProps = _instance$constructor === void 0 ? EMPTY.OBJ : _instance$constructor; // Merge default props into props object.

      if (typeof defaultProps === 'object' && !isArray(defaultProps)) {
        keys(defaultProps || EMPTY.OBJ).forEach(function (prop) {
          if (prop in props && props[prop] !== undefined) {
            return;
          }

          instance.props[prop] = defaultProps[prop];
        });
      }

      return instance;
    }
    /**
     * @param {Props} props
     * @param {State} state
     *
     * @returns {VTree[] | VTree | undefined}
     */
    // @ts-ignore


    _createClass(Component, [{
      key: "render",
      value: function render(props, state) {
        return undefined;
      }
      /**
       * Changes the component state synchronously by shallow merging the incoming
       * state into the previous state. Rendering will then be scheduled to the
       * next reachable tick. If this is called multiple times on a single tick
       * all that will happen is the state will be updated by no rendering will
       * occur until the next tick.
       *
       * You can await this method to know when rendering has completed.
       *
       * @param {State} state
       */

    }, {
      key: "setState",
      value: function setState(state) {
        var _this = this;

        this.state = assign$1({}, this.state, state);

        if (!RenderDebounce.has(this)) {
          RenderDebounce.set(this, new Promise(function (resolve) {
            return setTimeout(function () {
              RenderDebounce.delete(_this);

              _this.componentWillReceiveProps(_this.props, _this.state);

              if (_this.shouldComponentUpdate(_this.props, _this.state)) {
                var _this$$$render;

                (_this$$$render = _this[$$render]()) === null || _this$$$render === void 0 ? void 0 : _this$$$render.then(resolve);
              } else {
                resolve(null);
              }
            });
          }));
        }

        return RenderDebounce.get(this);
      }
      /**
       * Schedule a render to happen, bypassing shouldComponentUpdate. Bound to the
       * same tick logic as setState, allowing this to be called as many times as
       * you want.
       */

    }, {
      key: "forceUpdate",
      value: function forceUpdate() {
        var _this2 = this;

        if (!RenderDebounce.has(this)) {
          RenderDebounce.set(this, new Promise(function (resolve) {
            return setTimeout(function () {
              var _this2$$$render;

              RenderDebounce.delete(_this2);

              _this2.componentWillReceiveProps(_this2.props, _this2.state);

              (_this2$$$render = _this2[$$render]()) === null || _this2$$$render === void 0 ? void 0 : _this2$$$render.then(resolve);
            });
          }));
        }

        return RenderDebounce.get(this);
      }
      /** @type {State} */

    }, {
      key: $$render,
      value:
      /**
       * Stateful render. Used when a component changes and needs to re-render
       * itself. This is triggered on `setState` and `forceUpdate` calls.
       *
       * @return {Promise<Transaction> | undefined}
       */
      function () {
        var _this3 = this;

        // This is a WebComponent, so do something different.
        if (this[$$type] === 'web') {
          var oldProps = this.props;
          var oldState = this.state;
          this.props = createProps(this, this.props);
          this.state = createState$1(this, this.state);
          ActiveRenderState.push(this);
          /** @type {Promise<Transaction>} */

          var _promise = innerHTML(
          /** @type {any} */
          this.shadowRoot, this.render(this.props, this.state));

          ActiveRenderState.length = 0;
          this.componentDidUpdate(oldProps, oldState);
          return _promise;
        } // Get the fragment tree associated with this component. This is used to
        // lookup rendered children.


        var vTree = this[$$vTree];
        /**
         * Find all previously rendered top-level children associated to this
         * component. This will be used to diff against the newly rendered
         * elements.
         *
         * @type {VTree[]}
         */

        var childTrees = []; // Lookup all DOM nodes that were associated at the top level with this
        // component.

        vTree && getChildTrees(childTrees, vTree); // Render directly from the Component.

        ActiveRenderState.push(this);
        var renderTree = this.render(this.props, this.state);
        ActiveRenderState.length = 0; // Do not render.

        if (!renderTree) {
          return;
        }

        var renderTreeAsVTree =
        /** @type {VTree} */
        renderTree; // Always compare a fragment to a fragment. If the renderTree was not
        // wrapped, ensure it is here.

        if (renderTreeAsVTree.nodeType !== 11) {
          var isList = ('length' in renderTree);
          var renderTreeAsList =
          /** @type {VTree[]} */
          renderTree;
          renderTree = createTree$1(isList ? renderTreeAsList : [renderTreeAsVTree]);
        } // Put all the nodes together into a fragment for diffing.


        var fragment = createTree$1(childTrees);

        var tasks = _toConsumableArray(Internals$2.defaultTasks);

        var syncTreesIndex = tasks.indexOf(Internals$2.tasks.syncTrees); // Inject a custom task after syncing has finished, but before patching has
        // occured. This gives us time to add additional patch logic per render.

        tasks.splice(syncTreesIndex + 1, 0, function (
        /** @type {Transaction} */
        transaction) {
          var lastTree = null; // Reconcile all top-level replacements and additions.

          for (var i = 0; i < fragment.childNodes.length; i++) {
            var childTree = fragment.childNodes[i]; // Replace if the nodes are different.

            if (childTree && childTrees[i] && childTrees[i] !== childTree) {
              transaction.patches.push(Internals$2.PATCH_TYPE.REPLACE_CHILD, childTree, childTrees[i]);
              lastTree = childTree;
            } // Add if there is no old Node.
            else if (lastTree && !childTrees[i]) {
              transaction.patches.push(Internals$2.PATCH_TYPE.INSERT_BEFORE, $$insertAfter, childTree, lastTree);
              lastTree = childTree;
            } // Keep the old node.
            else {
              lastTree = childTrees[i];
            }

            ComponentTreeCache.set(childTree, vTree);
          }

          return transaction;
        });
        /**
         * Compare the existing component node(s) to the new node(s).
         *
         * @type {Promise<Transaction>}
         */

        var promise = outerHTML(fragment, renderTree, {
          tasks: tasks
        });
        return promise.then(function (transaction) {
          // Empty the fragment after using.
          fragment.childNodes.length = 0;
          release$1(fragment);

          _this3.componentDidUpdate(_this3.props, _this3.state);

          return transaction;
        });
      }
    }, {
      key: "connectedCallback",
      value: function connectedCallback() {
        var _this4 = this;

        var _this$constructor$def =
        /** @type {any} */
        this.constructor.defaultProps,
            defaultProps = _this$constructor$def === void 0 ? {} : _this$constructor$def; // This callback gets called during replace operations, there is no point
        // in re-rendering or creating a new shadow root due to this.
        // Always do a full render when mounting, so that something is visible.

        this[$$render]();
        this.componentDidMount();
        keys(defaultProps).forEach(function (propName) {
          defineProperty(_this4, propName, {
            get: function () {
              return _this4.props[propName];
            },
            set: function (value) {
              _this4.props[propName] = value;

              _this4.componentWillReceiveProps(_this4.props, _this4.state);

              if (_this4.shouldComponentUpdate(_this4.props, _this4.state)) {
                _this4[$$render]();
              }
            }
          });
        });
      }
    }, {
      key: "disconnectedCallback",
      value: function disconnectedCallback() {
        this.componentWillUnmount();
      }
    }, {
      key: "attributeChangedCallback",
      value: function attributeChangedCallback() {
        var nextProps = createProps(this, this.props);
        var nextState = createState$1(this, this.state);
        this.componentWillReceiveProps(nextProps, nextState);

        if (this.shouldComponentUpdate(nextProps, nextState)) {
          this[$$render]();
        }
      }
      /**
       * @param {Props} props
       * @param {State} state
       *
       * @returns {boolean}
       */
      // @ts-ignore

    }, {
      key: "shouldComponentUpdate",
      value: function shouldComponentUpdate(props, state) {
        return true;
      }
    }, {
      key: "componentWillMount",
      value: function componentWillMount() {}
    }, {
      key: "componentDidMount",
      value: function componentDidMount() {}
      /**
       * @param {Props} props
       * @param {State} state
       */
      // @ts-ignore

    }, {
      key: "componentWillReceiveProps",
      value: function componentWillReceiveProps(props, state) {}
      /**
       * @param {Props} props
       * @param {State} state
       *
       * @returns {void}
       */
      // @ts-ignore

    }, {
      key: "componentDidUpdate",
      value: function componentDidUpdate(props, state) {}
    }, {
      key: "componentWillUnmount",
      value: function componentWillUnmount() {}
    }], [{
      key: "observedAttributes",
      get: function () {
        return getObserved(this).map(function (key) {
          return key.toLowerCase();
        });
      }
      /**
       * Allow tests to unbind this task, you would not typically need to do this in
       * a web application, as this code loads once and is not reloaded.
       *
       * @type {Function | null}
       */

    }, {
      key: "subscribeMiddleware",
      value:
      /**
       * Adds the Component middleware, but first unregisters any previous
       * Component middleware.
       *
       * @returns {void}
       */
      function subscribeMiddleware() {
        var unsubscribe = Component[$$unsubscribe];
        unsubscribe && unsubscribe();
        Component[$$unsubscribe] = diff.use(middleware());
      }
      /**
       * Removes the Component middleware and clears the internal caches.
       *
       * @returns {void}
       */

    }, {
      key: "unsubscribeMiddleware",
      value: function unsubscribeMiddleware() {
        var unsubscribe = Component[$$unsubscribe];
        unsubscribe && unsubscribe();
        ComponentTreeCache.clear();
        InstanceCache.clear();
      }
    }]);

    return Component;
  }(); // TODO: Can this be done inside the constructor so that class components do not
  // need to inherit from HTMLElement?


  Component[$$unsubscribe] = null;

  try {
    setPrototypeOf(Component.prototype, HTMLElement.prototype);
  } catch (_unused) {} // Automatically subscribe the Component middleware.


  Component.subscribeMiddleware();

  exports.Component = Component;
  exports.Internals = internals;
  exports.createSideEffect = createSideEffect;
  exports.createState = createState;

  Object.defineProperty(exports, '__esModule', { value: true });

})));
