# All files / lib middleware.js

98.72% Statements 155/157
94.28% Branches 33/35
100% Functions 7/7
98.72% Lines 155/157

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
    149
    150
    151
    152
    153
    154
    155
    156
    157
    1582x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    118x
    118x
    118x
    118x
    118x
    118x
    11x
    11x
    11x
    11x
    11x
    3x
    3x
    11x
    118x
    118x
    118x
    118x
    110x
    110x
    8x
    8x
    8x
    8x
    8x
    8x
    8x
    8x

    118x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    1056x
    1056x
    1056x
    1056x
    153x
    153x
    2x
    2x
    2x
    2x
    2x
    2x
    381x
    381x
    22x
    22x
    22x
    381x
    381x
    381x
    22x
    22x
    2x
    2x
    2x
    2x
    2x
    2x
    2x
    769x
    769x
    769x
    769x
    1x
    1x
    1x
    769x
    1x
    1x
    769x
    769x
    26x
    26x
    742x
    742x
    769x
    574x
    574x
    574x
    574x
    574x
    574x
    574x
    574x
    574x
    3x
    3x
    574x
    574x
    574x
    574x
    117x
    117x
    117x
    117x
    3x
    3x
    117x
    117x
    117x
    117x
    117x
    117x
    117x
    117x
    56x
    56x
    56x
    13x
    13x
    56x
    56x
    56x
    56x
    56x
    56x
    56x
    117x
    117x
    574x
    2x
    2x
    2x
    112x
    112x
    112x
    112x
    112x
    112x
    112x
    112x
    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;

        const isOldFunction = Boolean(ComponentTreeCache.get(oldChildTree));
        const isNewFunction = typeof newChildTree.rawNodeName === 'function';

        // Previous slot was a component and the new slot is not, therefore we
        // should be indicating this component is unmounting.
        if (isOldFunction && !isNewFunction) {
            releaseHook(oldChildTree);
        }

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