react-markdown
Advanced tools
Comparing version 3.6.0 to 4.0.0
@@ -5,2 +5,20 @@ # Change Log | ||
## 4.0.0 - 2018-10-03 | ||
### BREAKING | ||
* `text` is now a first-class node + renderer - if you are using `allowedNodes`, it needs to be included in this list. Since it is now a React component, it will be passed an object of props instead of the old approach where a string was passed. `children` will contain the actual text string. | ||
* On React >= 16.2, if no `className` prop is provided, a fragment will be used instead of a div. To always render a div, pass `'div'` as the `root` renderer. | ||
* On React >= 16.2, escaped HTML will no longer be rendered with div/span containers | ||
* The UMD bundle now exports the component as `window.ReactMarkdown` instead of `window.reactMarkdown` | ||
### Added | ||
* HTML parser plugin for full HTML compatibility (Espen Hovlandsdal) | ||
### Fixes | ||
* URI transformer allows uppercase http/https URLs (Liam Kennedy) | ||
* [TypeScript] Strongly type the keys of `renderers` (Linus Unnebäck) | ||
## 3.6.0 - 2018-09-05 | ||
@@ -7,0 +25,0 @@ |
@@ -82,3 +82,3 @@ // Type definitions for react-markdown > v3.3.0 | ||
readonly unwrapDisallowed?: boolean | ||
readonly renderers?: {[nodeType: string]: ReactType} | ||
readonly renderers?: {[nodeType: NodeType]: ReactType} | ||
readonly astPlugins?: MdastPlugin[] | ||
@@ -85,0 +85,0 @@ readonly plugins?: any[] | (() => void) |
'use strict'; | ||
var React = require('react'); | ||
var xtend = require('xtend'); | ||
@@ -9,18 +10,11 @@ | ||
var index = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; | ||
var renderer = options.renderers[node.type]; | ||
var pos = node.position.start; | ||
var key = [node.type, pos.line, pos.column].join('-'); | ||
if (node.type === 'text') { | ||
return renderer ? renderer(node.value, key) : node.value; | ||
} | ||
if (typeof renderer !== 'function' && typeof renderer !== 'string' && !isReactFragment(renderer)) { | ||
throw new Error('Renderer for type `' + node.type + '` not defined or is not renderable'); | ||
throw new Error("Renderer for type `".concat(node.type, "` not defined or is not renderable")); | ||
} | ||
var nodeProps = getNodeProps(node, key, options, renderer, parent, index); | ||
return React.createElement(renderer, nodeProps, nodeProps.children || resolveChildren() || undefined); | ||
@@ -30,3 +24,6 @@ | ||
return node.children && node.children.map(function (childNode, i) { | ||
return astToReact(childNode, options, { node: node, props: nodeProps }, i); | ||
return astToReact(childNode, options, { | ||
node: node, | ||
props: nodeProps | ||
}, i); | ||
}); | ||
@@ -38,11 +35,11 @@ } | ||
return React.Fragment && React.Fragment === renderer; | ||
} | ||
} // eslint-disable-next-line max-params, complexity | ||
// eslint-disable-next-line max-params, complexity | ||
function getNodeProps(node, key, opts, renderer, parent, index) { | ||
var props = { key: key }; | ||
var props = { | ||
key: key | ||
}; | ||
var isTagRenderer = typeof renderer === 'string'; // `sourcePos` is true if the user wants source information (line/column info from markdown source) | ||
var isTagRenderer = typeof renderer === 'string'; | ||
// `sourcePos` is true if the user wants source information (line/column info from markdown source) | ||
if (opts.sourcePos && node.position) { | ||
@@ -54,5 +51,5 @@ props['data-sourcepos'] = flattenPosition(node.position); | ||
props.sourcePosition = node.position; | ||
} | ||
} // If `includeNodeIndex` is true, pass node index info to all non-tag renderers | ||
// If `includeNodeIndex` is true, pass node index info to all non-tag renderers | ||
if (opts.includeNodeIndex && parent.node && parent.node.children && !isTagRenderer) { | ||
@@ -67,7 +64,16 @@ props.index = parent.node.children.indexOf(node); | ||
case 'root': | ||
assignDefined(props, { className: opts.className }); | ||
assignDefined(props, { | ||
className: opts.className | ||
}); | ||
break; | ||
case 'text': | ||
props.nodeKey = key; | ||
props.children = node.value; | ||
break; | ||
case 'heading': | ||
props.level = node.depth; | ||
break; | ||
case 'list': | ||
@@ -79,2 +85,3 @@ props.start = node.start; | ||
break; | ||
case 'listItem': | ||
@@ -86,11 +93,23 @@ props.checked = node.checked; | ||
props.children = (props.tight ? unwrapParagraphs(node) : node.children).map(function (childNode, i) { | ||
return astToReact(childNode, opts, { node: node, props: props }, i); | ||
return astToReact(childNode, opts, { | ||
node: node, | ||
props: props | ||
}, i); | ||
}); | ||
break; | ||
case 'definition': | ||
assignDefined(props, { identifier: node.identifier, title: node.title, url: node.url }); | ||
assignDefined(props, { | ||
identifier: node.identifier, | ||
title: node.title, | ||
url: node.url | ||
}); | ||
break; | ||
case 'code': | ||
assignDefined(props, { language: node.lang && node.lang.split(/\s/, 1)[0] }); | ||
assignDefined(props, { | ||
language: node.lang && node.lang.split(/\s/, 1)[0] | ||
}); | ||
break; | ||
case 'inlineCode': | ||
@@ -100,2 +119,3 @@ props.children = node.value; | ||
break; | ||
case 'link': | ||
@@ -108,2 +128,3 @@ assignDefined(props, { | ||
break; | ||
case 'image': | ||
@@ -116,2 +137,3 @@ assignDefined(props, { | ||
break; | ||
case 'linkReference': | ||
@@ -122,2 +144,3 @@ assignDefined(props, xtend(ref, { | ||
break; | ||
case 'imageReference': | ||
@@ -130,2 +153,3 @@ assignDefined(props, { | ||
break; | ||
case 'table': | ||
@@ -136,2 +160,3 @@ case 'tableHead': | ||
break; | ||
case 'tableRow': | ||
@@ -141,2 +166,3 @@ props.isHeader = parent.node.type === 'tableHead'; | ||
break; | ||
case 'tableCell': | ||
@@ -148,5 +174,7 @@ assignDefined(props, { | ||
break; | ||
case 'virtualHtml': | ||
props.tag = node.tag; | ||
break; | ||
case 'html': | ||
@@ -158,2 +186,14 @@ // @todo find a better way than this | ||
break; | ||
case 'parsedHtml': | ||
props.escapeHtml = opts.escapeHtml; | ||
props.skipHtml = opts.skipHtml; | ||
props.element = mergeNodeChildren(node, (node.children || []).map(function (child, i) { | ||
return astToReact(child, opts, { | ||
node: node, | ||
props: props | ||
}, i); | ||
})); | ||
break; | ||
default: | ||
@@ -182,2 +222,14 @@ assignDefined(props, xtend(node, { | ||
function mergeNodeChildren(node, parsedChildren) { | ||
var el = node.element; | ||
if (Array.isArray(el)) { | ||
var Fragment = React.Fragment || 'div'; | ||
return React.createElement(Fragment, null, el); | ||
} | ||
var children = (el.props.children || []).concat(parsedChildren); | ||
return React.cloneElement(el, null, children); | ||
} | ||
function flattenPosition(pos) { | ||
@@ -184,0 +236,0 @@ return [pos.start.line, ':', pos.start.column, '-', pos.end.line, ':', pos.end.column].map(String).join(''); |
@@ -5,3 +5,2 @@ 'use strict'; | ||
var defs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
return (node.children || []).reduce(function (definitions, child) { | ||
@@ -8,0 +7,0 @@ if (child.type === 'definition') { |
@@ -1,2 +0,2 @@ | ||
'use strict'; | ||
"use strict"; | ||
@@ -3,0 +3,0 @@ var visit = require('unist-util-visit'); |
@@ -1,2 +0,2 @@ | ||
'use strict'; | ||
"use strict"; | ||
@@ -15,4 +15,4 @@ /** | ||
module.exports = function (tree) { | ||
var open = void 0; | ||
var currentParent = void 0; | ||
var open; | ||
var currentParent; | ||
visit(tree, 'html', function (node, index, parent) { | ||
@@ -25,2 +25,3 @@ if (currentParent !== parent) { | ||
var selfClosing = getSelfClosing(node); | ||
if (selfClosing) { | ||
@@ -36,2 +37,3 @@ parent.children.splice(index, 1, { | ||
var current = getSimpleTag(node, parent); | ||
if (!current) { | ||
@@ -52,3 +54,2 @@ return true; | ||
); | ||
return tree; | ||
@@ -59,2 +60,3 @@ }; | ||
var i = open.length; | ||
while (i--) { | ||
@@ -71,3 +73,7 @@ if (open[i].tag === matchingTag) { | ||
var match = node.value.match(simpleTagRe); | ||
return match ? { tag: match[2], opening: !match[1], node: node } : false; | ||
return match ? { | ||
tag: match[2], | ||
opening: !match[1], | ||
node: node | ||
} : false; | ||
} | ||
@@ -83,3 +89,2 @@ | ||
var toIndex = parent.children.indexOf(toNode.node); | ||
var extracted = parent.children.splice(fromIndex, toIndex - fromIndex + 1); | ||
@@ -86,0 +91,0 @@ var children = extracted.slice(1, -1); |
'use strict'; | ||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } | ||
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } | ||
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); } | ||
function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); } | ||
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } } | ||
var xtend = require('xtend'); | ||
var unified = require('unified'); | ||
var parse = require('remark-parse'); | ||
var PropTypes = require('prop-types'); | ||
var addListMetadata = require('mdast-add-list-metadata'); | ||
var naiveHtml = require('./plugins/naive-html'); | ||
var disallowNode = require('./plugins/disallow-node'); | ||
var astToReact = require('./ast-to-react'); | ||
var wrapTableRows = require('./wrap-table-rows'); | ||
var getDefinitions = require('./get-definitions'); | ||
var uriTransformer = require('./uriTransformer'); | ||
var uriTransformer = require('./uri-transformer'); | ||
var defaultRenderers = require('./renderers'); | ||
var symbols = require('./symbols'); | ||
var allTypes = Object.keys(defaultRenderers); | ||
@@ -28,6 +47,4 @@ | ||
var renderers = xtend(defaultRenderers, props.renderers); | ||
var plugins = [parse].concat(props.plugins || []); | ||
var parser = plugins.reduce(applyParserPlugin, unified()); | ||
var rawAst = parser.parse(src); | ||
@@ -38,3 +55,2 @@ var renderProps = xtend(props, { | ||
}); | ||
var astPlugins = determineAstPlugins(props); | ||
@@ -44,3 +60,2 @@ var ast = astPlugins.reduce(function (node, plugin) { | ||
}, rawAst); | ||
return astToReact(ast, renderProps); | ||
@@ -55,4 +70,4 @@ }; | ||
var plugins = [wrapTableRows, addListMetadata()]; | ||
var disallowedTypes = props.disallowedTypes; | ||
var disallowedTypes = props.disallowedTypes; | ||
if (props.allowedTypes) { | ||
@@ -65,2 +80,3 @@ disallowedTypes = allTypes.filter(function (type) { | ||
var removalMethod = props.unwrapDisallowed ? 'unwrap' : 'remove'; | ||
if (disallowedTypes && disallowedTypes.length > 0) { | ||
@@ -75,3 +91,8 @@ plugins.push(disallowNode.ofType(disallowedTypes, removalMethod)); | ||
var renderHtml = !props.escapeHtml && !props.skipHtml; | ||
if (renderHtml) { | ||
var hasHtmlParser = (props.astPlugins || []).some(function (item) { | ||
var plugin = Array.isArray(item) ? item[0] : item; | ||
return plugin.identity === symbols.HtmlParser; | ||
}); | ||
if (renderHtml && !hasHtmlParser) { | ||
plugins.push(naiveHtml); | ||
@@ -93,3 +114,2 @@ } | ||
}; | ||
ReactMarkdown.propTypes = { | ||
@@ -114,7 +134,5 @@ className: PropTypes.string, | ||
}; | ||
ReactMarkdown.types = allTypes; | ||
ReactMarkdown.renderers = defaultRenderers; | ||
ReactMarkdown.uriTransformer = uriTransformer; | ||
module.exports = ReactMarkdown; |
@@ -5,7 +5,7 @@ /* eslint-disable react/prop-types, react/no-multi-comp */ | ||
var xtend = require('xtend'); | ||
var React = require('react'); | ||
var createElement = React.createElement; | ||
module.exports = { | ||
root: 'div', | ||
break: 'br', | ||
@@ -27,3 +27,4 @@ paragraph: 'p', | ||
tableCell: TableCell, | ||
root: Root, | ||
text: TextRenderer, | ||
list: List, | ||
@@ -36,5 +37,16 @@ listItem: ListItem, | ||
html: Html, | ||
virtualHtml: VirtualHtml | ||
virtualHtml: VirtualHtml, | ||
parsedHtml: ParsedHtml | ||
}; | ||
function TextRenderer(props) { | ||
return props.children; | ||
} | ||
function Root(props) { | ||
var useFragment = !props.className; | ||
var root = useFragment ? React.Fragment || 'div' : 'div'; | ||
return createElement(root, useFragment ? null : props, props.children); | ||
} | ||
function SimpleRenderer(tag, props) { | ||
@@ -45,9 +57,13 @@ return createElement(tag, getCoreProps(props), props.children); | ||
function TableCell(props) { | ||
var style = props.align ? { textAlign: props.align } : undefined; | ||
var style = props.align ? { | ||
textAlign: props.align | ||
} : undefined; | ||
var coreProps = getCoreProps(props); | ||
return createElement(props.isHeader ? 'th' : 'td', style ? xtend({ style: style }, coreProps) : coreProps, props.children); | ||
return createElement(props.isHeader ? 'th' : 'td', style ? xtend({ | ||
style: style | ||
}, coreProps) : coreProps, props.children); | ||
} | ||
function Heading(props) { | ||
return createElement('h' + props.level, getCoreProps(props), props.children); | ||
return createElement("h".concat(props.level), getCoreProps(props), props.children); | ||
} | ||
@@ -57,2 +73,3 @@ | ||
var attrs = getCoreProps(props); | ||
if (props.start !== null && props.start !== 1) { | ||
@@ -67,5 +84,10 @@ attrs.start = props.start.toString(); | ||
var checkbox = null; | ||
if (props.checked !== null) { | ||
var checked = props.checked; | ||
checkbox = createElement('input', { type: 'checkbox', checked: checked, readOnly: true }); | ||
checkbox = createElement('input', { | ||
type: 'checkbox', | ||
checked: checked, | ||
readOnly: true | ||
}); | ||
} | ||
@@ -77,4 +99,6 @@ | ||
function CodeBlock(props) { | ||
var className = props.language && 'language-' + props.language; | ||
var code = createElement('code', className ? { className: className } : null, props.value); | ||
var className = props.language && "language-".concat(props.language); | ||
var code = createElement('code', className ? { | ||
className: className | ||
} : null, props.value); | ||
return createElement('pre', getCoreProps(props), code); | ||
@@ -93,11 +117,22 @@ } | ||
var tag = props.isBlock ? 'div' : 'span'; | ||
if (props.escapeHtml) { | ||
// @todo when fiber lands, we can simply render props.value | ||
return createElement(tag, null, props.value); | ||
var comp = React.Fragment || tag; | ||
return createElement(comp, null, props.value); | ||
} | ||
var nodeProps = { dangerouslySetInnerHTML: { __html: props.value } }; | ||
var nodeProps = { | ||
dangerouslySetInnerHTML: { | ||
__html: props.value | ||
} | ||
}; | ||
return createElement(tag, nodeProps); | ||
} | ||
function ParsedHtml(props) { | ||
return props['data-sourcepos'] ? React.cloneElement(props.element, { | ||
'data-sourcepos': props['data-sourcepos'] | ||
}) : props.element; | ||
} | ||
function VirtualHtml(props) { | ||
@@ -112,3 +147,5 @@ return createElement(props.tag, getCoreProps(props), props.children); | ||
function getCoreProps(props) { | ||
return props['data-sourcepos'] ? { 'data-sourcepos': props['data-sourcepos'] } : {}; | ||
return props['data-sourcepos'] ? { | ||
'data-sourcepos': props['data-sourcepos'] | ||
} : {}; | ||
} |
@@ -18,2 +18,3 @@ 'use strict'; | ||
}]; | ||
if (children.length > 1) { | ||
@@ -20,0 +21,0 @@ table.children.push({ |
{ | ||
"name": "react-markdown", | ||
"description": "Renders Markdown as React components", | ||
"version": "3.6.0", | ||
"version": "4.0.0", | ||
"keywords": [ | ||
@@ -13,2 +13,3 @@ "markdown", | ||
"scripts": { | ||
"analyze": "npm run clean && npm run compile && NODE_ENV=production ANALYZE_BUNDLE=1 webpack -p", | ||
"build": "npm run clean && npm run compile && NODE_ENV=production webpack -p && npm run build:demo", | ||
@@ -24,3 +25,4 @@ "build:demo": "NODE_ENV=production webpack -p --config webpack.config.demo.js", | ||
"test": "jest --coverage", | ||
"watch": "webpack --watch" | ||
"watch": "webpack --watch", | ||
"watch:demo": "webpack --watch --config webpack.config.demo.js" | ||
}, | ||
@@ -41,2 +43,3 @@ "repository": { | ||
"dependencies": { | ||
"html-to-react": "^1.3.3", | ||
"mdast-add-list-metadata": "1.0.1", | ||
@@ -50,24 +53,28 @@ "prop-types": "^15.6.1", | ||
"devDependencies": { | ||
"babel-cli": "^6.26.0", | ||
"babel-loader": "^7.1.3", | ||
"babel-plugin-transform-react-remove-prop-types": "^0.4.13", | ||
"babel-preset-env": "^1.6.1", | ||
"babel-preset-react": "^6.24.1", | ||
"eslint": "^4.18.2", | ||
"eslint-config-prettier": "^2.9.0", | ||
"@babel/cli": "^7.1.0", | ||
"@babel/core": "^7.1.0", | ||
"@babel/preset-env": "^7.1.0", | ||
"@babel/preset-react": "^7.0.0", | ||
"babel-core": "^7.0.0-bridge.0", | ||
"babel-jest": "^23.6.0", | ||
"babel-loader": "^8.0.2", | ||
"babel-plugin-transform-react-remove-prop-types": "^0.4.18", | ||
"eslint": "^5.6.0", | ||
"eslint-config-prettier": "^3.1.0", | ||
"eslint-config-sanity": "^4.0.2", | ||
"eslint-plugin-react": "^7.7.0", | ||
"gh-pages-deploy": "^0.4.2", | ||
"jest": "^22.4.2", | ||
"prettier": "^1.11.1", | ||
"react": "^16.2.0", | ||
"gh-pages-deploy": "^0.5.0", | ||
"jest": "^23.6.0", | ||
"prettier": "^1.14.3", | ||
"react": "^16.5.2", | ||
"react-addons-test-utils": "^15.6.2", | ||
"react-dom": "^16.2.0", | ||
"react-test-renderer": "^16.2.0", | ||
"react-dom": "^16.5.2", | ||
"react-test-renderer": "^16.5.2", | ||
"remark-breaks": "^1.0.0", | ||
"remark-shortcodes": "^0.1.5", | ||
"remark-shortcodes": "^0.2.1", | ||
"rimraf": "^2.6.2", | ||
"uglify-js": "^3.4.5", | ||
"webpack": "^4.1.0", | ||
"webpack-cli": "^2.0.10" | ||
"webpack": "^4.20.2", | ||
"webpack-bundle-analyzer": "^3.0.2", | ||
"webpack-cli": "^3.1.1" | ||
}, | ||
@@ -74,0 +81,0 @@ "peerDependencies": { |
@@ -41,2 +41,10 @@ # react-markdown | ||
## Upgrading to 4.0 | ||
Should be straightforward. You might need to alter you code slightly if you: | ||
- Are using `allowedTypes` (add `text` to the list) | ||
- Rely on there being a container `<div>` without a class name around your rendered markdown | ||
See [CHANGELOG](CHANGELOG.md) for more details. | ||
## Notes | ||
@@ -47,13 +55,7 @@ | ||
## Inline HTML is broken | ||
Inline HTML is currently broken for any tags that include attributes. A vague idea of how to fix | ||
this has been planned, but if you're feeling up to the task, create an issue and let us know! | ||
## Options | ||
* `source` or `children` - _string_ The Markdown source to parse (**required**) | ||
* `className` - _string_ Class name of the container element (default: `''`). | ||
* `escapeHtml` - _boolean_ Setting to `false` will cause HTML to be rendered (see note above about | ||
broken HTML, though). Be aware that setting this to `false` might cause security issues if the | ||
* `className` - _string_ Class name of the container element. If none is passed, a container will not be rendered. | ||
* `escapeHtml` - _boolean_ Setting to `false` will cause HTML to be rendered (see notes below about proper HTML support). Be aware that setting this to `false` might cause security issues if the | ||
input is user-generated. Use at your own risk. (default: `true`). | ||
@@ -93,6 +95,41 @@ * `skipHtml` - _boolean_ Setting to `true` will skip inlined and blocks of HTML (default: `false`). | ||
varies based on the type of node. | ||
* With one exception: if the key is `text`, the value should be a function that takes the literal text and returns a new string or React element. | ||
* `plugins` - _array_ An array of unified/remark parser plugins. If you need to pass options to the plugin, pass an array with two elements, the first being the plugin and the second being the options - for instance: `{plugins: [[require('remark-shortcodes'), {your: 'options'}]]`. (default: `[]`) | ||
## Parsing HTML | ||
If you are in a trusted environment and want to parse and render HTML, you will want to use the `html-parser` plugin. For a default configuration, import `react-markdown/with-html` instead of the default: | ||
```js | ||
const ReactMarkdown = require('react-markdown/with-html') | ||
const markdown = ` | ||
This block of Markdown contains <a href="https://en.wikipedia.org/wiki/HTML">HTML</a>, and will require the <code>html-parser</code> AST plugin to be loaded, in addition to setting the <code class="prop">escapeHtml</code> property to false. | ||
` | ||
<ReactMarkdown | ||
source={markdown} | ||
escapeHtml={false} | ||
> | ||
``` | ||
If you want to specify options for the HTML parsing step, you can instead import the HTML parser plugin directly: | ||
```js | ||
const ReactMarkdown = require('react-markdown') | ||
const htmlParser = require('react-markdown/plugins/html-parser') | ||
// See https://github.com/aknuds1/html-to-react#with-custom-processing-instructions | ||
// for more info on the processing instructions | ||
const parseHtml = htmlParser({ | ||
isValidNode: node => node.type !== 'script', | ||
processingInstructions: [/* ... */] | ||
}) | ||
<ReactMarkdown | ||
source={markdown} | ||
escapeHtml={false} | ||
astPlugins={[parseHtml]} | ||
> | ||
``` | ||
## Node types | ||
@@ -128,2 +165,4 @@ | ||
* `html` - HTML node (Best-effort rendering) | ||
* `virtualHtml` - When not using the HTML parser plugin, a cheap and dirty approach to supporting simple HTML elements without a complete parser. | ||
* `parsedHtml` - When using the HTML parser plugin, HTML parsed to a React element. | ||
@@ -130,0 +169,0 @@ Note: Disallowing a node will also prevent the rendering of any children of that node, unless the |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
21
955
181
114442
8
26
+ Addedhtml-to-react@^1.3.3
+ Addeddom-serializer@2.0.0(transitive)
+ Addeddomelementtype@2.3.0(transitive)
+ Addeddomhandler@5.0.3(transitive)
+ Addeddomutils@3.1.0(transitive)
+ Addedentities@4.5.0(transitive)
+ Addedhtml-to-react@1.7.0(transitive)
+ Addedhtmlparser2@9.1.0(transitive)
+ Addedlodash.camelcase@4.3.0(transitive)