interpolateAndFlatten = (childNode, supplemental) => {
  let match = null;
  let newNodeName = childNode.rawNodeName;

  // Comments
  if (childNode.nodeType === NODE_TYPE.COMMENT) {
    const parts = childNode.nodeValue.split(tokenEx);
    let newValue = '';

    for (let i = 0; i < parts.length; i++) {
      const isDynamic = i % 2 !== 0;

      if (isDynamic) {
        newValue += supplemental.attributes[parts[i]];
      }
      else {
        newValue += parts[i];
      }
    }

    childNode.nodeValue = newValue;
  }

  // Node name
  if (match = tokenEx.exec(childNode.rawNodeName)) {
    newNodeName = supplemental.tags[match[1]];

    childNode = createTree(
      newNodeName,
      childNode.attributes,
      childNode.childNodes,
    );
  }

  // Attributes
  for (const keyName of getOwnPropertyNames(childNode.attributes)) {
    keyName.split(' ').forEach(keyName => {
      const value = childNode.attributes[keyName];
      let newValue = value;
      let newKey = keyName;

      // Check for dynamic value and assign to newValue.
      if (match = tokenEx.exec(value)) {
        const parts = value.split(tokenEx);
        newValue = '';

        // A value is always split between two other elements.
        if (parts.length === 3 && parts[0] === EMPTY.STR && parts[2] === EMPTY.STR) {
          newValue = supplemental.attributes[parts[1]];
        }
        else {
          for (let i = 0; i < parts.length; i++) {
            const isDynamic = i % 2 !== 0;

            if (isDynamic) {
              newValue += supplemental.attributes[parts[i]];
            }
            else {
              newValue += parts[i];
            }
          }
        }
      }

      // Check for dynamic key and assign to newKey.
      if (match = tokenEx.exec(keyName)) {
        const parts = keyName.split(tokenEx);

        for (let i = 0; i < parts.length; i++) {
          if (i % 2 !== 0) {
            newKey = supplemental.attributes[parts[i]];
          }
        }
      }

      if (newKey) {
        if (typeof newKey !== 'string') {
          if (Array.isArray(newKey)) {
            if (internalProcess.env.NODE_ENV !== 'production') {
              throw new Error('Arrays cannot be spread as attributes');
            }

            delete childNode.attributes[keyName];
          }
          else {
            delete childNode.attributes[keyName];
            Object.assign(childNode.attributes, newKey);
          }
        }
        else {
          delete childNode.attributes[keyName];

          if (newKey === 'childNodes') {
            childNode.childNodes.length = 0;
            if (typeof newValue !== 'string') {
              childNode.childNodes.push(createTree(newValue));
            }
            else {
              childNode.childNodes.push(createTree('#text', newValue));
            }
          }

          childNode.attributes[newKey] = newValue === undefined ? true : newValue;
        }
      }

      if (childNode.attributes.key) {
        childNode.key = childNode.attributes.key;
      }

      if (childNode.nodeName === 'script' && childNode.attributes.src) {
        childNode.key = childNode.attributes.src;
      }
    });
  }

  // Node value
  if (match = tokenEx.exec(childNode.nodeValue)) {
    const parts = childNode.nodeValue.split(tokenEx);
    const fragment = createTree();

    for (let i = 0; i < parts.length; i++) {
      if (i % 2 !== 0) {
        fragment.childNodes.push(
          createTree(supplemental.children[parts[i]])
        );
      }
      else if (parts[i]) {
        fragment.childNodes.push(createTree('#text', parts[i]));
      }
    }

    return fragment;
  }

  // Children
  for (let i = 0; i < childNode.childNodes.length; i++) {
    // Flatten fragments.
    const vTree = interpolateAndFlatten(childNode.childNodes[i], supplemental);

    if (vTree.nodeName === '#document-fragment' && vTree.rawNodeName === vTree.nodeName) {
      childNode.childNodes.splice(i, 1, ...vTree.childNodes);
      i -= 1;
    }
    else {
      childNode.childNodes[i] = vTree;
    }
  }

  return childNode;
}
Iterates over the result from the parser and interpolates the supplemental
dynamic data into place. This allows parsers to only worry about strings
and returning minimal structures.

This also flattens fragments.

@param {VTree} childNode
@param {Supplemental} supplemental
@return {VTree}