# All files / lib middleware.js

100% Statements 148/148
96.77% Branches 30/31
100% Functions 7/7
100% Lines 148/148

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    1492x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    132x
    132x
    132x
    132x
    132x
    132x
    12x
    12x
    12x
    12x
    12x
    3x
    3x
    12x
    132x
    132x
    132x
    132x
    123x
    123x
    132x
    132x
    132x
    132x
    132x
    132x
    132x
    132x
    132x
    132x
    132x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    1138x
    1138x
    1138x
    1138x
    167x
    167x
    2x
    2x
    2x
    2x
    2x
    2x
    394x
    394x
    22x
    22x
    22x
    394x
    394x
    394x
    22x
    22x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    802x
    802x
    802x
    802x
    1x
    1x
    1x
    802x
    1x
    1x
    801x
    802x
    26x
    26x
    802x
    802x
    802x
    591x
    591x
    591x
    591x
    591x
    591x
    131x
    131x
    131x
    131x
    3x
    3x
    127x
    127x
    127x
    127x
    127x
    127x
    127x
    127x
    56x
    56x
    56x
    13x
    13x
    56x
    56x
    56x
    56x
    56x
    56x
    56x
    127x
    131x
    591x
    2x
    2x
    2x
    127x
    127x
    127x
    127x
    127x
    127x
    127x
    127x
    2x
     import { EMPTY, ComponentTreeCache, VTree, Transaction } from './util/types';
    import globalThis from './util/global';
    import onceEnded from './once-ended';
    import componentWillUnmount from './lifecycle/component-will-unmount';
    import renderComponent from './render-component';

    const { assign } = Object;

    /**
     * @param {VTree} oldTree
     * @param {VTree} newTree
     *
     * @returns {VTree | null}
     */
    function render(oldTree, newTree) {
      let 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
     */
    const releaseHook = vTree => componentWillUnmount(vTree);

    /**
     * @param {VTree} vTree
     */
    const createTreeHook = vTree => {
      const { customElements } = globalThis;
      const Constructor = customElements && customElements.get(vTree.nodeName);

      if (typeof vTree.rawNodeName === 'function' || Constructor) {
        vTree.attributes.children = vTree.attributes.children || vTree.childNodes;
      }
    };

    /**
     * @param {VTree} vTree
     */
    const createNodeHook = vTree => {
      // Only look up elements with a dash in the name.
      if (!vTree.nodeName.includes('-')) return;

      // Convert this to globalThis
      const { customElements } = globalThis;
      const Constructor = customElements && customElements.get(vTree.nodeName);

      if (Constructor) {
        return new Constructor(vTree.attributes);
      }
    };

    /**
     * @param {VTree} oldTree
     * @param {VTree} newTree
     */
    const syncTreeHook = (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 (let i = 0; i < newTree.childNodes.length; i++) {
        const newChildTree = newTree.childNodes[i];
        const 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') {
          const 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 {
                newTree.childNodes.splice(i, 1, ...renderTree.childNodes);
              }
            }
          }
        }
      }
    };

    export default () => assign(
      (/** @type {Transaction} */transaction) => transaction.onceEnded(onceEnded),
      {
        displayName: 'componentTask',
        syncTreeHook,
        createNodeHook,
        createTreeHook,
        releaseHook,
      },
    );