UNPKG

html-react-parser

Version:
125 lines (107 loc) 3.39 kB
var attributesToProps = require('./attributes-to-props'); var utilities = require('./utilities'); /** * Converts DOM nodes to React elements. * * @param {DomElement[]} nodes - The DOM nodes. * @param {Object} [options={}] - The additional options. * @param {Function} [options.replace] - The replacer. * @param {Object} [options.library] - The library (React, Preact, etc.). * @return {String|ReactElement|ReactElement[]} */ function domToReact(nodes, options) { options = options || {}; var React = options.library || require('react'); var cloneElement = React.cloneElement; var createElement = React.createElement; var isValidElement = React.isValidElement; var result = []; var node; var hasReplace = typeof options.replace === 'function'; var replaceElement; var props; var children; var data; var trim = options.trim; for (var i = 0, len = nodes.length; i < len; i++) { node = nodes[i]; // replace with custom React element (if present) if (hasReplace) { replaceElement = options.replace(node); if (isValidElement(replaceElement)) { // set "key" prop for sibling elements // https://fb.me/react-warning-keys if (len > 1) { replaceElement = cloneElement(replaceElement, { key: replaceElement.key || i }); } result.push(replaceElement); continue; } } if (node.type === 'text') { // if trim option is enabled, skip whitespace text nodes if (trim) { data = node.data.trim(); if (data) { result.push(node.data); } } else { result.push(node.data); } continue; } props = node.attribs; if (!shouldPassAttributesUnaltered(node)) { props = attributesToProps(node.attribs); } children = null; switch (node.type) { case 'script': case 'style': // prevent text in <script> or <style> from being escaped // https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml if (node.children[0]) { props.dangerouslySetInnerHTML = { __html: node.children[0].data }; } break; case 'tag': // setting textarea value in children is an antipattern in React // https://reactjs.org/docs/forms.html#the-textarea-tag if (node.name === 'textarea' && node.children[0]) { props.defaultValue = node.children[0].data; } else if (node.children && node.children.length) { // continue recursion of creating React elements (if applicable) children = domToReact(node.children, options); } break; // skip all other cases (e.g., comment) default: continue; } // set "key" prop for sibling elements // https://fb.me/react-warning-keys if (len > 1) { props.key = i; } result.push(createElement(node.name, props, children)); } return result.length === 1 ? result[0] : result; } /** * Determines whether attributes should be altered or not. * * @param {React.ReactElement} node * @return {Boolean} */ function shouldPassAttributesUnaltered(node) { return ( utilities.PRESERVE_CUSTOM_ATTRIBUTES && node.type === 'tag' && utilities.isCustomComponent(node.name, node.attribs) ); } module.exports = domToReact;