html-react-parser 0.10.5 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +185 -203
- package/README.md +92 -2
- package/dist/html-react-parser.js +101 -81
- package/dist/html-react-parser.js.map +1 -1
- package/dist/html-react-parser.min.js +1 -1
- package/dist/html-react-parser.min.js.map +1 -1
- package/index.d.ts +20 -7
- package/index.js +14 -5
- package/lib/dom-to-react.d.ts +3 -3
- package/lib/dom-to-react.js +39 -26
- package/package.json +11 -10
package/README.md
CHANGED
|
@@ -106,11 +106,11 @@ parse(
|
|
|
106
106
|
|
|
107
107
|
The `replace` callback allows you to swap an element with another React element.
|
|
108
108
|
|
|
109
|
-
The first argument is an object with the same output as [htmlparser2](https://github.com/fb55/htmlparser2)'s [domhandler](https://github.com/fb55/domhandler#example):
|
|
109
|
+
The first argument is an object with the same output as [htmlparser2](https://github.com/fb55/htmlparser2/tree/v3.10.1)'s [domhandler](https://github.com/fb55/domhandler#example):
|
|
110
110
|
|
|
111
111
|
```js
|
|
112
112
|
parse('<br>', {
|
|
113
|
-
replace: function(domNode) {
|
|
113
|
+
replace: function (domNode) {
|
|
114
114
|
console.dir(domNode, { depth: null });
|
|
115
115
|
}
|
|
116
116
|
});
|
|
@@ -224,6 +224,60 @@ parse('<br>', {
|
|
|
224
224
|
});
|
|
225
225
|
```
|
|
226
226
|
|
|
227
|
+
### htmlparser2
|
|
228
|
+
|
|
229
|
+
This library passes the following options to [htmlparser2](https://github.com/fb55/htmlparser2/tree/v3.10.1) on the server-side:
|
|
230
|
+
|
|
231
|
+
```js
|
|
232
|
+
{
|
|
233
|
+
decodeEntities: true,
|
|
234
|
+
lowerCaseAttributeNames: false
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
By passing your own options, the default library options will be **replaced** (not merged).
|
|
239
|
+
|
|
240
|
+
As a result, to enable `decodeEntities` and `xmlMode`, you need to do the following:
|
|
241
|
+
|
|
242
|
+
```js
|
|
243
|
+
parse('<p /><p />', {
|
|
244
|
+
htmlparser2: {
|
|
245
|
+
decodeEntities: true,
|
|
246
|
+
xmlMode: true
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
See [htmlparser2 options](https://github.com/fb55/htmlparser2/wiki/Parser-options).
|
|
252
|
+
|
|
253
|
+
> **Warning**: By overriding htmlparser2 options, there's a chance of breaking universal rendering. Do this at your own risk.
|
|
254
|
+
|
|
255
|
+
### trim
|
|
256
|
+
|
|
257
|
+
Normally, whitespace is preserved:
|
|
258
|
+
|
|
259
|
+
```js
|
|
260
|
+
parse('<br>\n'); // [React.createElement('br'), '\n']
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
By enabling the `trim` option, whitespace text nodes will be skipped:
|
|
264
|
+
|
|
265
|
+
```js
|
|
266
|
+
parse('<br>\n', { trim: true }); // React.createElement('br')
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
This addresses the warning:
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
Warning: validateDOMNesting(...): Whitespace text nodes cannot appear as a child of <table>. Make sure you don't have any extra whitespace between tags on each line of your source code.
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
However, this option may strip out intentional whitespace:
|
|
276
|
+
|
|
277
|
+
```js
|
|
278
|
+
parse('<p> </p>', { trim: true }); // React.createElement('p')
|
|
279
|
+
```
|
|
280
|
+
|
|
227
281
|
## FAQ
|
|
228
282
|
|
|
229
283
|
#### Is this library XSS safe?
|
|
@@ -250,6 +304,41 @@ Check if your arguments are valid. Also, see ["Does this library sanitize invali
|
|
|
250
304
|
|
|
251
305
|
Yes, this library supports server-side rendering on Node.js. See [demo](https://repl.it/@remarkablemark/html-react-parser-SSR).
|
|
252
306
|
|
|
307
|
+
#### Why are my elements nested incorrectly?
|
|
308
|
+
|
|
309
|
+
Make sure your [HTML markup is valid](https://validator.w3.org/). The HTML to DOM parsing will be affected if you're using self-closing syntax (`/>`) on non-void elements:
|
|
310
|
+
|
|
311
|
+
```js
|
|
312
|
+
parse('<div /><div />'); // returns single element instead of array of elements
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
See [#158](https://github.com/remarkablemark/html-react-parser/issues/158).
|
|
316
|
+
|
|
317
|
+
#### I get "Warning: validateDOMNesting(...): Whitespace text nodes cannot appear as a child of table."
|
|
318
|
+
|
|
319
|
+
Enable the [trim](https://github.com/remarkablemark/html-react-parser#trim) option. See [#155](https://github.com/remarkablemark/html-react-parser/issues/155).
|
|
320
|
+
|
|
321
|
+
#### Don't change case of tags.
|
|
322
|
+
|
|
323
|
+
Tags are lowercased by default. To prevent that from happening, pass the [htmlparser2 option](https://github.com/remarkablemark/html-react-parser#htmlparser2):
|
|
324
|
+
|
|
325
|
+
```js
|
|
326
|
+
const options = {
|
|
327
|
+
htmlparser2: {
|
|
328
|
+
lowerCaseTags: false
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
parse('<CustomElement>', options); // React.createElement('CustomElement')
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
> **Warning**: By preserving case-sensitivity of the tags, you may get rendering warnings like:
|
|
335
|
+
>
|
|
336
|
+
> ```
|
|
337
|
+
> Warning: <CustomElement> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.
|
|
338
|
+
> ```
|
|
339
|
+
|
|
340
|
+
See [#62](https://github.com/remarkablemark/html-react-parser/issues/62) and [example](https://repl.it/@remarkablemark/html-react-parser-62).
|
|
341
|
+
|
|
253
342
|
## Benchmarks
|
|
254
343
|
|
|
255
344
|
```sh
|
|
@@ -297,6 +386,7 @@ Support this project with your organization. Your logo will show up here with a
|
|
|
297
386
|
|
|
298
387
|
## Support
|
|
299
388
|
|
|
389
|
+
- [GitHub Sponsors](https://b.remarkabl.org/github-sponsors)
|
|
300
390
|
- [Patreon](https://b.remarkabl.org/patreon)
|
|
301
391
|
- [Open Collective](https://b.remarkabl.org/open-collective-html-react-parser)
|
|
302
392
|
- [Ko-fi](https://b.remarkabl.org/ko-fi)
|
|
@@ -1124,6 +1124,8 @@
|
|
|
1124
1124
|
var replaceElement;
|
|
1125
1125
|
var props;
|
|
1126
1126
|
var children;
|
|
1127
|
+
var data;
|
|
1128
|
+
var trim = options.trim;
|
|
1127
1129
|
|
|
1128
1130
|
for (var i = 0, len = nodes.length; i < len; i++) {
|
|
1129
1131
|
node = nodes[i];
|
|
@@ -1133,7 +1135,7 @@
|
|
|
1133
1135
|
replaceElement = options.replace(node);
|
|
1134
1136
|
|
|
1135
1137
|
if (isValidElement(replaceElement)) {
|
|
1136
|
-
//
|
|
1138
|
+
// set "key" prop for sibling elements
|
|
1137
1139
|
// https://fb.me/react-warning-keys
|
|
1138
1140
|
if (len > 1) {
|
|
1139
1141
|
replaceElement = cloneElement(replaceElement, {
|
|
@@ -1146,45 +1148,54 @@
|
|
|
1146
1148
|
}
|
|
1147
1149
|
|
|
1148
1150
|
if (node.type === 'text') {
|
|
1149
|
-
|
|
1151
|
+
// if trim option is enabled, skip whitespace text nodes
|
|
1152
|
+
if (trim) {
|
|
1153
|
+
data = node.data.trim();
|
|
1154
|
+
if (data) {
|
|
1155
|
+
result.push(node.data);
|
|
1156
|
+
}
|
|
1157
|
+
} else {
|
|
1158
|
+
result.push(node.data);
|
|
1159
|
+
}
|
|
1150
1160
|
continue;
|
|
1151
1161
|
}
|
|
1152
1162
|
|
|
1153
1163
|
props = node.attribs;
|
|
1154
1164
|
if (!shouldPassAttributesUnaltered(node)) {
|
|
1155
|
-
// update values
|
|
1156
1165
|
props = attributesToProps_1(node.attribs);
|
|
1157
1166
|
}
|
|
1158
1167
|
|
|
1159
1168
|
children = null;
|
|
1160
1169
|
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1170
|
+
switch (node.type) {
|
|
1171
|
+
case 'script':
|
|
1172
|
+
case 'style':
|
|
1173
|
+
// prevent text in <script> or <style> from being escaped
|
|
1174
|
+
// https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
|
|
1175
|
+
if (node.children[0]) {
|
|
1176
|
+
props.dangerouslySetInnerHTML = {
|
|
1177
|
+
__html: node.children[0].data
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
break;
|
|
1181
|
+
|
|
1182
|
+
case 'tag':
|
|
1183
|
+
// setting textarea value in children is an antipattern in React
|
|
1184
|
+
// https://reactjs.org/docs/forms.html#the-textarea-tag
|
|
1185
|
+
if (node.name === 'textarea' && node.children[0]) {
|
|
1186
|
+
props.defaultValue = node.children[0].data;
|
|
1187
|
+
} else if (node.children && node.children.length) {
|
|
1188
|
+
// continue recursion of creating React elements (if applicable)
|
|
1189
|
+
children = domToReact(node.children, options);
|
|
1190
|
+
}
|
|
1191
|
+
break;
|
|
1181
1192
|
|
|
1182
1193
|
// skip all other cases (e.g., comment)
|
|
1183
|
-
|
|
1184
|
-
|
|
1194
|
+
default:
|
|
1195
|
+
continue;
|
|
1185
1196
|
}
|
|
1186
1197
|
|
|
1187
|
-
//
|
|
1198
|
+
// set "key" prop for sibling elements
|
|
1188
1199
|
// https://fb.me/react-warning-keys
|
|
1189
1200
|
if (len > 1) {
|
|
1190
1201
|
props.key = i;
|
|
@@ -1197,6 +1208,8 @@
|
|
|
1197
1208
|
}
|
|
1198
1209
|
|
|
1199
1210
|
/**
|
|
1211
|
+
* Determines whether attributes should be altered or not.
|
|
1212
|
+
*
|
|
1200
1213
|
* @param {React.ReactElement} node
|
|
1201
1214
|
* @return {Boolean}
|
|
1202
1215
|
*/
|
|
@@ -1428,13 +1441,24 @@
|
|
|
1428
1441
|
var isIE9 = utilities$1.isIE(9);
|
|
1429
1442
|
var isIE$1 = isIE9 || utilities$1.isIE();
|
|
1430
1443
|
|
|
1444
|
+
// falls back to `parseFromString` if `createHTMLDocument` cannot be used
|
|
1445
|
+
var parseFromDocument = function () {
|
|
1446
|
+
throw new Error(
|
|
1447
|
+
'This browser does not support `document.implementation.createHTMLDocument`'
|
|
1448
|
+
);
|
|
1449
|
+
};
|
|
1450
|
+
|
|
1451
|
+
var parseFromString = function () {
|
|
1452
|
+
throw new Error(
|
|
1453
|
+
'This browser does not support `DOMParser.prototype.parseFromString`'
|
|
1454
|
+
);
|
|
1455
|
+
};
|
|
1456
|
+
|
|
1431
1457
|
/**
|
|
1432
1458
|
* DOMParser (performance: slow).
|
|
1433
1459
|
*
|
|
1434
1460
|
* @see https://developer.mozilla.org/docs/Web/API/DOMParser#Parsing_an_SVG_or_HTML_document
|
|
1435
1461
|
*/
|
|
1436
|
-
var parseFromString;
|
|
1437
|
-
|
|
1438
1462
|
if (typeof window.DOMParser === 'function') {
|
|
1439
1463
|
var domParser = new window.DOMParser();
|
|
1440
1464
|
|
|
@@ -1449,7 +1473,7 @@
|
|
|
1449
1473
|
* @param {string} [tagName] - The element to render the HTML (with 'body' as fallback).
|
|
1450
1474
|
* @return {HTMLDocument}
|
|
1451
1475
|
*/
|
|
1452
|
-
parseFromString = function
|
|
1476
|
+
parseFromString = function (html, tagName) {
|
|
1453
1477
|
if (tagName) {
|
|
1454
1478
|
html = '<' + tagName + '>' + html + '</' + tagName + '>';
|
|
1455
1479
|
}
|
|
@@ -1461,6 +1485,8 @@
|
|
|
1461
1485
|
|
|
1462
1486
|
return domParser.parseFromString(html, mimeType);
|
|
1463
1487
|
};
|
|
1488
|
+
|
|
1489
|
+
parseFromDocument = parseFromString;
|
|
1464
1490
|
}
|
|
1465
1491
|
|
|
1466
1492
|
/**
|
|
@@ -1468,13 +1494,11 @@
|
|
|
1468
1494
|
*
|
|
1469
1495
|
* @see https://developer.mozilla.org/docs/Web/API/DOMImplementation/createHTMLDocument
|
|
1470
1496
|
*/
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
if (typeof document.implementation === 'object') {
|
|
1497
|
+
if (document.implementation) {
|
|
1474
1498
|
// title parameter is required in IE
|
|
1475
1499
|
// https://msdn.microsoft.com/en-us/library/ff975457(v=vs.85).aspx
|
|
1476
1500
|
var doc = document.implementation.createHTMLDocument(
|
|
1477
|
-
isIE$1 ? '
|
|
1501
|
+
isIE$1 ? 'html-dom-parser' : undefined
|
|
1478
1502
|
);
|
|
1479
1503
|
|
|
1480
1504
|
/**
|
|
@@ -1484,7 +1508,7 @@
|
|
|
1484
1508
|
* @param {string} [tagName] - The element to render the HTML (with 'body' as fallback).
|
|
1485
1509
|
* @return {HTMLDocument}
|
|
1486
1510
|
*/
|
|
1487
|
-
parseFromDocument = function
|
|
1511
|
+
parseFromDocument = function (html, tagName) {
|
|
1488
1512
|
if (tagName) {
|
|
1489
1513
|
doc.documentElement.getElementsByTagName(tagName)[0].innerHTML = html;
|
|
1490
1514
|
return doc;
|
|
@@ -1507,8 +1531,8 @@
|
|
|
1507
1531
|
*
|
|
1508
1532
|
* @see https://developer.mozilla.org/docs/Web/HTML/Element/template
|
|
1509
1533
|
*/
|
|
1510
|
-
var parseFromTemplate;
|
|
1511
1534
|
var template = document.createElement('template');
|
|
1535
|
+
var parseFromTemplate;
|
|
1512
1536
|
|
|
1513
1537
|
if (template.content) {
|
|
1514
1538
|
/**
|
|
@@ -1517,15 +1541,12 @@
|
|
|
1517
1541
|
* @param {string} html - The HTML string.
|
|
1518
1542
|
* @return {NodeList}
|
|
1519
1543
|
*/
|
|
1520
|
-
parseFromTemplate = function
|
|
1544
|
+
parseFromTemplate = function (html) {
|
|
1521
1545
|
template.innerHTML = html;
|
|
1522
1546
|
return template.content.childNodes;
|
|
1523
1547
|
};
|
|
1524
1548
|
}
|
|
1525
1549
|
|
|
1526
|
-
// fallback document parser
|
|
1527
|
-
var parseWithFallback = parseFromDocument || parseFromString;
|
|
1528
|
-
|
|
1529
1550
|
/**
|
|
1530
1551
|
* Parses HTML string to DOM nodes.
|
|
1531
1552
|
*
|
|
@@ -1546,42 +1567,35 @@
|
|
|
1546
1567
|
|
|
1547
1568
|
switch (firstTagName) {
|
|
1548
1569
|
case HTML:
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
element.parentNode.removeChild(element);
|
|
1558
|
-
}
|
|
1570
|
+
doc = parseFromString(html);
|
|
1571
|
+
|
|
1572
|
+
// the created document may come with filler head/body elements,
|
|
1573
|
+
// so make sure to remove them if they don't actually exist
|
|
1574
|
+
if (!HEAD_TAG_REGEX.test(html)) {
|
|
1575
|
+
element = doc.getElementsByTagName(HEAD)[0];
|
|
1576
|
+
if (element) {
|
|
1577
|
+
element.parentNode.removeChild(element);
|
|
1559
1578
|
}
|
|
1579
|
+
}
|
|
1560
1580
|
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
}
|
|
1581
|
+
if (!BODY_TAG_REGEX.test(html)) {
|
|
1582
|
+
element = doc.getElementsByTagName(BODY)[0];
|
|
1583
|
+
if (element) {
|
|
1584
|
+
element.parentNode.removeChild(element);
|
|
1566
1585
|
}
|
|
1567
|
-
|
|
1568
|
-
return doc.getElementsByTagName(HTML);
|
|
1569
1586
|
}
|
|
1570
|
-
|
|
1587
|
+
|
|
1588
|
+
return doc.getElementsByTagName(HTML);
|
|
1571
1589
|
|
|
1572
1590
|
case HEAD:
|
|
1573
1591
|
case BODY:
|
|
1574
|
-
|
|
1575
|
-
elements = parseWithFallback(html).getElementsByTagName(firstTagName);
|
|
1592
|
+
elements = parseFromDocument(html).getElementsByTagName(firstTagName);
|
|
1576
1593
|
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
}
|
|
1581
|
-
|
|
1582
|
-
return elements;
|
|
1594
|
+
// if there's a sibling element, then return both elements
|
|
1595
|
+
if (BODY_TAG_REGEX.test(html) && HEAD_TAG_REGEX.test(html)) {
|
|
1596
|
+
return elements[0].parentNode.childNodes;
|
|
1583
1597
|
}
|
|
1584
|
-
|
|
1598
|
+
return elements;
|
|
1585
1599
|
|
|
1586
1600
|
// low-level tag or text
|
|
1587
1601
|
default:
|
|
@@ -1589,15 +1603,9 @@
|
|
|
1589
1603
|
return parseFromTemplate(html);
|
|
1590
1604
|
}
|
|
1591
1605
|
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
.childNodes;
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
|
-
break;
|
|
1606
|
+
return parseFromDocument(html, BODY).getElementsByTagName(BODY)[0]
|
|
1607
|
+
.childNodes;
|
|
1598
1608
|
}
|
|
1599
|
-
|
|
1600
|
-
return [];
|
|
1601
1609
|
}
|
|
1602
1610
|
|
|
1603
1611
|
var domparser_1 = domparser;
|
|
@@ -1641,19 +1649,31 @@
|
|
|
1641
1649
|
|
|
1642
1650
|
var htmlToDomClient = parseDOM;
|
|
1643
1651
|
|
|
1652
|
+
// decode HTML entities by default for `htmlparser2`
|
|
1653
|
+
var domParserOptions = { decodeEntities: true, lowerCaseAttributeNames: false };
|
|
1654
|
+
|
|
1644
1655
|
/**
|
|
1645
1656
|
* Converts HTML string to React elements.
|
|
1646
1657
|
*
|
|
1647
|
-
* @param {String} html -
|
|
1648
|
-
* @param {Object} [options] -
|
|
1649
|
-
* @param {
|
|
1650
|
-
* @
|
|
1658
|
+
* @param {String} html - HTML string.
|
|
1659
|
+
* @param {Object} [options] - Parser options.
|
|
1660
|
+
* @param {Object} [options.htmlparser2] - htmlparser2 options.
|
|
1661
|
+
* @param {Object} [options.library] - Library for React, Preact, etc.
|
|
1662
|
+
* @param {Function} [options.replace] - Replace method.
|
|
1663
|
+
* @return {JSX.Element|JSX.Element[]|String} - React element(s), empty array, or string.
|
|
1651
1664
|
*/
|
|
1652
1665
|
function HTMLReactParser(html, options) {
|
|
1653
1666
|
if (typeof html !== 'string') {
|
|
1654
1667
|
throw new TypeError('First argument must be a string');
|
|
1655
1668
|
}
|
|
1656
|
-
|
|
1669
|
+
if (html === '') {
|
|
1670
|
+
return [];
|
|
1671
|
+
}
|
|
1672
|
+
options = options || {};
|
|
1673
|
+
return domToReact_1(
|
|
1674
|
+
htmlToDomClient(html, options.htmlparser2 || domParserOptions),
|
|
1675
|
+
options
|
|
1676
|
+
);
|
|
1657
1677
|
}
|
|
1658
1678
|
|
|
1659
1679
|
HTMLReactParser.domToReact = domToReact_1;
|
|
@@ -1661,8 +1681,8 @@
|
|
|
1661
1681
|
|
|
1662
1682
|
// support CommonJS and ES Modules
|
|
1663
1683
|
var htmlReactParser = HTMLReactParser;
|
|
1664
|
-
var
|
|
1665
|
-
htmlReactParser.default =
|
|
1684
|
+
var _default = HTMLReactParser;
|
|
1685
|
+
htmlReactParser.default = _default;
|
|
1666
1686
|
|
|
1667
1687
|
return htmlReactParser;
|
|
1668
1688
|
|