createNodesFromMarkup.js.flow 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. /**
  2. * Copyright (c) 2013-present, Facebook, Inc.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @providesModule createNodesFromMarkup
  8. * @typechecks
  9. */
  10. /*eslint-disable fb-www/unsafe-html*/
  11. const ExecutionEnvironment = require('./ExecutionEnvironment');
  12. const createArrayFromMixed = require('./createArrayFromMixed');
  13. const getMarkupWrap = require('./getMarkupWrap');
  14. const invariant = require('./invariant');
  15. /**
  16. * Dummy container used to render all markup.
  17. */
  18. const dummyNode = ExecutionEnvironment.canUseDOM ? document.createElement('div') : null;
  19. /**
  20. * Pattern used by `getNodeName`.
  21. */
  22. const nodeNamePattern = /^\s*<(\w+)/;
  23. /**
  24. * Extracts the `nodeName` of the first element in a string of markup.
  25. *
  26. * @param {string} markup String of markup.
  27. * @return {?string} Node name of the supplied markup.
  28. */
  29. function getNodeName(markup) {
  30. const nodeNameMatch = markup.match(nodeNamePattern);
  31. return nodeNameMatch && nodeNameMatch[1].toLowerCase();
  32. }
  33. /**
  34. * Creates an array containing the nodes rendered from the supplied markup. The
  35. * optionally supplied `handleScript` function will be invoked once for each
  36. * <script> element that is rendered. If no `handleScript` function is supplied,
  37. * an exception is thrown if any <script> elements are rendered.
  38. *
  39. * @param {string} markup A string of valid HTML markup.
  40. * @param {?function} handleScript Invoked once for each rendered <script>.
  41. * @return {array<DOMElement|DOMTextNode>} An array of rendered nodes.
  42. */
  43. function createNodesFromMarkup(markup, handleScript) {
  44. let node = dummyNode;
  45. invariant(!!dummyNode, 'createNodesFromMarkup dummy not initialized');
  46. const nodeName = getNodeName(markup);
  47. const wrap = nodeName && getMarkupWrap(nodeName);
  48. if (wrap) {
  49. node.innerHTML = wrap[1] + markup + wrap[2];
  50. let wrapDepth = wrap[0];
  51. while (wrapDepth--) {
  52. node = node.lastChild;
  53. }
  54. } else {
  55. node.innerHTML = markup;
  56. }
  57. const scripts = node.getElementsByTagName('script');
  58. if (scripts.length) {
  59. invariant(handleScript, 'createNodesFromMarkup(...): Unexpected <script> element rendered.');
  60. createArrayFromMixed(scripts).forEach(handleScript);
  61. }
  62. const nodes = Array.from(node.childNodes);
  63. while (node.lastChild) {
  64. node.removeChild(node.lastChild);
  65. }
  66. return nodes;
  67. }
  68. module.exports = createNodesFromMarkup;