All files / lib/node create.js

98.31% Statements 117/119
94.44% Branches 17/18
100% Functions 1/1
98.31% Lines 117/119

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 1202x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 635x 635x 5x 5x 635x 635x 635x 635x 635x 635x 635x 635x 635x 281x 281x 635x 635x 635x 635x 635x 635x 635x 635x 635x 635x 635x 635x 635x 635x 635x 635x 295x 295x 295x 1x 1x 635x 635x 635x 635x 635x     635x 635x 635x 635x 635x 635x 635x 635x 635x 635x 635x 635x 348x 133x 133x 348x 348x 1x 1x 348x 348x 8x 8x 206x 206x 206x 206x 348x 348x 348x 8x 8x 8x 8x 348x 349x 349x 349x 349x 349x 349x 635x 73x 73x 73x 73x 73x 73x 349x 349x 635x  
import process from '../util/process';
import globalThis from '../util/global';
import {
  NodeCache,
  CreateNodeHookCache,
  VTreeLike,
  VTree,
  ValidNode,
  EMPTY,
} from '../util/types';
import createTree from '../tree/create';
 
const namespace = 'http://www.w3.org/2000/svg';
 
/**
 * Takes in a Virtual Tree Element (VTree) and creates a DOM Node from it.
 * Sets the node into the Node cache. If this VTree already has an
 * associated node, it will reuse that.
 *
 * @param {VTreeLike} vTreeLike - A Virtual Tree Element or VTree-like element
 * @param {Document=} ownerDocument - Document to create Nodes in, defaults to document
 * @param {Boolean=} isSVG - Indicates if the root element is SVG
 * @return {ValidNode | null} A DOM Node matching the vTree
 */
export default function createNode(vTreeLike, ownerDocument = globalThis.document, isSVG) {
  if (process.env.NODE_ENV !== 'production') {
    if (!vTreeLike) {
      throw new Error('Missing VTree when trying to create DOM Node');
    }
  }
 
  const vTree = createTree(vTreeLike);
  const existingNode = NodeCache.get(vTree);
 
  // If the DOM Node was already created, reuse the existing node. This is
  // required to ensure that passed in DOM Nodes are preserved, and to ensure
  // that elements are not constantly created with the same VTree instance.
  if (existingNode) {
    return existingNode;
  }
 
  const {
    nodeName,
    rawNodeName = nodeName,
    childNodes = /** @type {VTree[]} */ ([]),
  } = vTree;
 
  isSVG = isSVG || nodeName === 'svg';
 
  /** @type {ValidNode | null} */
  let domNodeCheck = null;
 
  /** @type {ValidNode | null | void} */
  let retVal = null;
 
  CreateNodeHookCache.forEach((fn) => {
    // Invoke all the `createNodeHook` functions passing along the vTree as the
    // only argument. These functions must return a valid DOM Node value.
    if (retVal = fn(vTree)) {
      domNodeCheck = retVal;
    }
  });
 
  // It is not possible to continue if this object is falsy. By returning null,
  // patching can gracefully no-op.
  if (!ownerDocument) {
    return domNodeCheck;
  }
 
  /**
   * If no DOM Node was provided by CreateNode hooks, we must create it
   * ourselves.
   *
   * @type {ValidNode | null}
   */
  let domNode = domNodeCheck;
 
  // Create empty text elements. They will get filled in during the patch
  // process.
  if (!domNode) {
    if (nodeName === '#text') {
      domNode = ownerDocument.createTextNode(vTree.nodeValue || EMPTY.STR);
    }
    // Support dynamically creating document fragments.
    else if (nodeName === '#document-fragment') {
      domNode = ownerDocument.createDocumentFragment();
    }
    // Support SVG.
    else if (isSVG) {
      domNode = ownerDocument.createElementNS(namespace, rawNodeName);
    }
    // If not a Text or SVG Node, then create with the standard method.
    else {
      domNode = ownerDocument.createElement(rawNodeName);
    }
 
    // Do not execute scripts by default.
    if (nodeName === 'script') {
      // Set the type to 'no-execute'. This will be toggled after patching,
      // unless disabled by the consumer.
      /** @type {any} */(domNode).type = 'no-execute';
    }
  }
 
  // Add to the domNodes cache.
  NodeCache.set(vTree, domNode);
 
  // Append all the children into the domNode, making sure to run them
  // through this `createNode` function as well.
  for (let i = 0; i < childNodes.length; i++) {
    const validChildNode = createNode(childNodes[i], ownerDocument, isSVG);
 
    if (domNode && validChildNode) {
      domNode.appendChild(validChildNode);
    }
  }
 
  return domNode;
}