createNodesFromMarkup.js.flow 2.38 KB
Newer Older
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
/**
 * Copyright (c) 2013-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @providesModule createNodesFromMarkup
 * @typechecks
 */

/*eslint-disable fb-www/unsafe-html*/
const ExecutionEnvironment = require("./ExecutionEnvironment");

const createArrayFromMixed = require("./createArrayFromMixed");

const getMarkupWrap = require("./getMarkupWrap");

const invariant = require("./invariant");
/**
 * Dummy container used to render all markup.
 */


const dummyNode = ExecutionEnvironment.canUseDOM ? document.createElement('div') : null;
/**
 * Pattern used by `getNodeName`.
 */

const nodeNamePattern = /^\s*<(\w+)/;
/**
 * Extracts the `nodeName` of the first element in a string of markup.
 *
 * @param {string} markup String of markup.
 * @return {?string} Node name of the supplied markup.
 */

function getNodeName(markup) {
  const nodeNameMatch = markup.match(nodeNamePattern);
  return nodeNameMatch && nodeNameMatch[1].toLowerCase();
}
/**
 * Creates an array containing the nodes rendered from the supplied markup. The
 * optionally supplied `handleScript` function will be invoked once for each
 * <script> element that is rendered. If no `handleScript` function is supplied,
 * an exception is thrown if any <script> elements are rendered.
 *
 * @param {string} markup A string of valid HTML markup.
 * @param {?function} handleScript Invoked once for each rendered <script>.
 * @return {array<DOMElement|DOMTextNode>} An array of rendered nodes.
 */


function createNodesFromMarkup(markup, handleScript) {
  let node = dummyNode;
  invariant(!!dummyNode, 'createNodesFromMarkup dummy not initialized');
  const nodeName = getNodeName(markup);
  const wrap = nodeName && getMarkupWrap(nodeName);

  if (wrap) {
    node.innerHTML = wrap[1] + markup + wrap[2];
    let wrapDepth = wrap[0];

    while (wrapDepth--) {
      node = node.lastChild;
    }
  } else {
    node.innerHTML = markup;
  }

  const scripts = node.getElementsByTagName('script');

  if (scripts.length) {
    invariant(handleScript, 'createNodesFromMarkup(...): Unexpected <script> element rendered.');
    createArrayFromMixed(scripts).forEach(handleScript);
  }

  const nodes = Array.from(node.childNodes);

  while (node.lastChild) {
    node.removeChild(node.lastChild);
  }

  return nodes;
}

module.exports = createNodesFromMarkup;