html-react-parser 1.4.0 → 1.4.4

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/README.md CHANGED
@@ -5,7 +5,6 @@
5
5
  [![NPM version](https://img.shields.io/npm/v/html-react-parser.svg)](https://www.npmjs.com/package/html-react-parser)
6
6
  [![Build Status](https://github.com/remarkablemark/html-react-parser/workflows/build/badge.svg?branch=master)](https://github.com/remarkablemark/html-react-parser/actions?query=workflow%3Abuild)
7
7
  [![codecov](https://codecov.io/gh/remarkablemark/html-react-parser/branch/master/graph/badge.svg?token=wosFd1DBIR)](https://codecov.io/gh/remarkablemark/html-react-parser)
8
- [![Dependency status](https://david-dm.org/remarkablemark/html-react-parser.svg)](https://david-dm.org/remarkablemark/html-react-parser)
9
8
  [![NPM downloads](https://img.shields.io/npm/dm/html-react-parser.svg?style=flat-square)](https://www.npmjs.com/package/html-react-parser)
10
9
  [![Discord](https://img.shields.io/discord/422421589582282752.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/njExwXdrRJ)
11
10
 
@@ -26,7 +25,7 @@ const parse = require('html-react-parser');
26
25
  parse('<p>Hello, World!</p>'); // React.createElement('p', {}, 'Hello, World!')
27
26
  ```
28
27
 
29
- [Repl.it](https://repl.it/@remarkablemark/html-react-parser) | [JSFiddle](https://jsfiddle.net/remarkablemark/7v86d800/) | [CodeSandbox](https://codesandbox.io/s/940pov1l4w) | [TypeScript](https://codesandbox.io/s/html-react-parser-z0kp6) | [Examples](https://github.com/remarkablemark/html-react-parser/tree/master/examples)
28
+ [Replit](https://replit.com/@remarkablemark/html-react-parser) | [JSFiddle](https://jsfiddle.net/remarkablemark/7v86d800/) | [CodeSandbox](https://codesandbox.io/s/940pov1l4w) | [TypeScript](https://codesandbox.io/s/html-react-parser-z0kp6) | [Examples](https://github.com/remarkablemark/html-react-parser/tree/master/examples)
30
29
 
31
30
  <details>
32
31
  <summary>Table of Contents</summary>
@@ -51,7 +50,6 @@ parse('<p>Hello, World!</p>'); // React.createElement('p', {}, 'Hello, World!')
51
50
  - [Parser throws an error](#parser-throws-an-error)
52
51
  - [Is SSR supported?](#is-ssr-supported)
53
52
  - [Elements aren't nested correctly](#elements-arent-nested-correctly)
54
- - [Warning: validateDOMNesting(...): Whitespace text nodes cannot appear as a child of table](#warning-validatedomnesting-whitespace-text-nodes-cannot-appear-as-a-child-of-table)
55
53
  - [Don't change case of tags](#dont-change-case-of-tags)
56
54
  - [TS Error: Property 'attribs' does not exist on type 'DOMNode'](#ts-error-property-attribs-does-not-exist-on-type-domnode)
57
55
  - [Can I enable `trim` for certain elements?](#can-i-enable-trim-for-certain-elements)
@@ -203,7 +201,7 @@ If you're having issues take a look at our [Create React App example](./examples
203
201
 
204
202
  #### replace element and children
205
203
 
206
- Replace the element and its children (see [demo](https://repl.it/@remarkablemark/html-react-parser-replace-example)):
204
+ Replace the element and its children (see [demo](https://replit.com/@remarkablemark/html-react-parser-replace-example)):
207
205
 
208
206
  ```jsx
209
207
  import parse, { domToReact } from 'html-react-parser';
@@ -284,7 +282,7 @@ HTML output:
284
282
 
285
283
  #### replace and remove element
286
284
 
287
- [Exclude](https://repl.it/@remarkablemark/html-react-parser-56) an element from rendering by replacing it with `<React.Fragment>`:
285
+ [Exclude](https://replit.com/@remarkablemark/html-react-parser-56) an element from rendering by replacing it with `<React.Fragment>`:
288
286
 
289
287
  ```jsx
290
288
  parse('<p><br id="remove"></p>', {
@@ -352,16 +350,16 @@ By default, whitespace is preserved:
352
350
  parse('<br>\n'); // [React.createElement('br'), '\n']
353
351
  ```
354
352
 
355
- To remove whitespace, enable the `trim` option:
353
+ But certain elements like `<table>` will strip out invalid whitespace:
356
354
 
357
355
  ```js
358
- parse('<br>\n', { trim: true }); // React.createElement('br')
356
+ parse('<table>\n</table>'); // React.createElement('table')
359
357
  ```
360
358
 
361
- This fixes the warning:
359
+ To remove whitespace, enable the `trim` option:
362
360
 
363
- ```
364
- 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.
361
+ ```js
362
+ parse('<br>\n', { trim: true }); // React.createElement('br')
365
363
  ```
366
364
 
367
365
  However, intentional whitespace may be stripped out:
@@ -416,7 +414,7 @@ If the parser throws an erorr, check if your arguments are valid. See ["Does inv
416
414
 
417
415
  ### Is SSR supported?
418
416
 
419
- Yes, server-side rendering on Node.js is supported by this library. See [demo](https://repl.it/@remarkablemark/html-react-parser-SSR).
417
+ Yes, server-side rendering on Node.js is supported by this library. See [demo](https://replit.com/@remarkablemark/html-react-parser-SSR).
420
418
 
421
419
  ### Elements aren't nested correctly
422
420
 
@@ -428,10 +426,6 @@ parse('<div /><div />'); // returns single element instead of array of elements
428
426
 
429
427
  See [#158](https://github.com/remarkablemark/html-react-parser/issues/158).
430
428
 
431
- ### Warning: validateDOMNesting(...): Whitespace text nodes cannot appear as a child of table
432
-
433
- Enable the [trim](#trim) option. See [#155](https://github.com/remarkablemark/html-react-parser/issues/155).
434
-
435
429
  ### Don't change case of tags
436
430
 
437
431
  Tags are lowercased by default. To prevent that from happening, pass the [htmlparser2 option](#htmlparser2):
@@ -451,7 +445,7 @@ parse('<CustomElement>', options); // React.createElement('CustomElement')
451
445
  > Warning: <CustomElement> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.
452
446
  > ```
453
447
 
454
- See [#62](https://github.com/remarkablemark/html-react-parser/issues/62) and [example](https://repl.it/@remarkablemark/html-react-parser-62).
448
+ See [#62](https://github.com/remarkablemark/html-react-parser/issues/62) and [example](https://replit.com/@remarkablemark/html-react-parser-62).
455
449
 
456
450
  ### TS Error: Property 'attribs' does not exist on type 'DOMNode'
457
451
 
@@ -1279,11 +1279,37 @@
1279
1279
  */
1280
1280
  var PRESERVE_CUSTOM_ATTRIBUTES = React$1.version.split('.')[0] >= 16;
1281
1281
 
1282
+ // Taken from
1283
+ // https://github.com/facebook/react/blob/cae635054e17a6f107a39d328649137b83f25972/packages/react-dom/src/client/validateDOMNesting.js#L213
1284
+ var elementsWithNoTextChildren = new Set([
1285
+ 'tr',
1286
+ 'tbody',
1287
+ 'thead',
1288
+ 'tfoot',
1289
+ 'colgroup',
1290
+ 'table',
1291
+ 'head',
1292
+ 'html',
1293
+ 'frameset'
1294
+ ]);
1295
+
1296
+ /**
1297
+ * Checks if the given node can contain text nodes
1298
+ *
1299
+ * @param {DomElement} node
1300
+ * @returns {boolean}
1301
+ */
1302
+ function canTextBeChildOfNode$1(node) {
1303
+ return !elementsWithNoTextChildren.has(node.name);
1304
+ }
1305
+
1282
1306
  var utilities$3 = {
1283
1307
  PRESERVE_CUSTOM_ATTRIBUTES: PRESERVE_CUSTOM_ATTRIBUTES,
1284
1308
  invertObject: invertObject,
1285
1309
  isCustomComponent: isCustomComponent,
1286
- setStyleProp: setStyleProp$1
1310
+ setStyleProp: setStyleProp$1,
1311
+ canTextBeChildOfNode: canTextBeChildOfNode$1,
1312
+ elementsWithNoTextChildren: elementsWithNoTextChildren
1287
1313
  };
1288
1314
 
1289
1315
  var reactProperty = lib$1;
@@ -1316,11 +1342,19 @@
1316
1342
 
1317
1343
  // convert HTML/SVG attribute to React prop
1318
1344
  attributeNameLowerCased = attributeName.toLowerCase();
1319
- propName = reactProperty.possibleStandardNames[attributeNameLowerCased];
1345
+ propName = getPropName(attributeNameLowerCased);
1320
1346
 
1321
1347
  if (propName) {
1322
- props[propName] = attributeValue;
1323
1348
  propertyInfo = reactProperty.getPropertyInfo(propName);
1349
+
1350
+ // convert attribute to uncontrolled component prop (e.g., `value` to `defaultValue`)
1351
+ // https://reactjs.org/docs/uncontrolled-components.html
1352
+ if (propName === 'checked' || propName === 'value') {
1353
+ propName = getPropName('default' + attributeNameLowerCased);
1354
+ }
1355
+
1356
+ props[propName] = attributeValue;
1357
+
1324
1358
  switch (propertyInfo && propertyInfo.type) {
1325
1359
  case reactProperty.BOOLEAN:
1326
1360
  props[propName] = true;
@@ -1346,11 +1380,22 @@
1346
1380
  return props;
1347
1381
  };
1348
1382
 
1383
+ /**
1384
+ * Gets prop name from lowercased attribute name.
1385
+ *
1386
+ * @param {string} attributeName - Lowercased attribute name.
1387
+ * @return {string}
1388
+ */
1389
+ function getPropName(attributeName) {
1390
+ return reactProperty.possibleStandardNames[attributeName];
1391
+ }
1392
+
1349
1393
  var React = require$$0__default["default"];
1350
1394
  var attributesToProps$1 = attributesToProps$2;
1351
1395
  var utilities$1 = utilities$3;
1352
1396
 
1353
1397
  var setStyleProp = utilities$1.setStyleProp;
1398
+ var canTextBeChildOfNode = utilities$1.canTextBeChildOfNode;
1354
1399
 
1355
1400
  /**
1356
1401
  * Converts DOM nodes to JSX element(s).
@@ -1371,11 +1416,11 @@
1371
1416
 
1372
1417
  var result = [];
1373
1418
  var node;
1419
+ var isWhitespace;
1374
1420
  var hasReplace = typeof options.replace === 'function';
1375
1421
  var replaceElement;
1376
1422
  var props;
1377
1423
  var children;
1378
- var data;
1379
1424
  var trim = options.trim;
1380
1425
 
1381
1426
  for (var i = 0, len = nodes.length; i < len; i++) {
@@ -1399,15 +1444,23 @@
1399
1444
  }
1400
1445
 
1401
1446
  if (node.type === 'text') {
1402
- // if trim option is enabled, skip whitespace text nodes
1403
- if (trim) {
1404
- data = node.data.trim();
1405
- if (data) {
1406
- result.push(node.data);
1407
- }
1408
- } else {
1409
- result.push(node.data);
1447
+ isWhitespace = !node.data.trim().length;
1448
+
1449
+ if (isWhitespace && node.parent && !canTextBeChildOfNode(node.parent)) {
1450
+ // We have a whitespace node that can't be nested in its parent
1451
+ // so skip it
1452
+ continue;
1410
1453
  }
1454
+
1455
+ if (trim && isWhitespace) {
1456
+ // Trim is enabled and we have a whitespace node
1457
+ // so skip it
1458
+ continue;
1459
+ }
1460
+
1461
+ // We have a text node that's not whitespace and it can be nested
1462
+ // in its parent so add it to the results
1463
+ result.push(node.data);
1411
1464
  continue;
1412
1465
  }
1413
1466
 
@@ -1645,6 +1698,10 @@
1645
1698
  }
1646
1699
  Object.defineProperty(Node.prototype, "nodeType", {
1647
1700
  // Read-only aliases
1701
+ /**
1702
+ * [DOM spec](https://dom.spec.whatwg.org/#dom-node-nodetype)-compatible
1703
+ * node {@link type}.
1704
+ */
1648
1705
  get: function () {
1649
1706
  var _a;
1650
1707
  return (_a = nodeTypes.get(this.type)) !== null && _a !== void 0 ? _a : 1;
@@ -1654,6 +1711,10 @@
1654
1711
  });
1655
1712
  Object.defineProperty(Node.prototype, "parentNode", {
1656
1713
  // Read-write aliases for properties
1714
+ /**
1715
+ * Same as {@link parent}.
1716
+ * [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
1717
+ */
1657
1718
  get: function () {
1658
1719
  return this.parent;
1659
1720
  },
@@ -1664,6 +1725,10 @@
1664
1725
  configurable: true
1665
1726
  });
1666
1727
  Object.defineProperty(Node.prototype, "previousSibling", {
1728
+ /**
1729
+ * Same as {@link prev}.
1730
+ * [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
1731
+ */
1667
1732
  get: function () {
1668
1733
  return this.prev;
1669
1734
  },
@@ -1674,6 +1739,10 @@
1674
1739
  configurable: true
1675
1740
  });
1676
1741
  Object.defineProperty(Node.prototype, "nextSibling", {
1742
+ /**
1743
+ * Same as {@link next}.
1744
+ * [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
1745
+ */
1677
1746
  get: function () {
1678
1747
  return this.next;
1679
1748
  },
@@ -1711,6 +1780,10 @@
1711
1780
  return _this;
1712
1781
  }
1713
1782
  Object.defineProperty(DataNode.prototype, "nodeValue", {
1783
+ /**
1784
+ * Same as {@link data}.
1785
+ * [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
1786
+ */
1714
1787
  get: function () {
1715
1788
  return this.data;
1716
1789
  },
@@ -1774,6 +1847,7 @@
1774
1847
  }
1775
1848
  Object.defineProperty(NodeWithChildren.prototype, "firstChild", {
1776
1849
  // Aliases
1850
+ /** First child of the node. */
1777
1851
  get: function () {
1778
1852
  var _a;
1779
1853
  return (_a = this.children[0]) !== null && _a !== void 0 ? _a : null;
@@ -1782,6 +1856,7 @@
1782
1856
  configurable: true
1783
1857
  });
1784
1858
  Object.defineProperty(NodeWithChildren.prototype, "lastChild", {
1859
+ /** Last child of the node. */
1785
1860
  get: function () {
1786
1861
  return this.children.length > 0
1787
1862
  ? this.children[this.children.length - 1]
@@ -1791,6 +1866,10 @@
1791
1866
  configurable: true
1792
1867
  });
1793
1868
  Object.defineProperty(NodeWithChildren.prototype, "childNodes", {
1869
+ /**
1870
+ * Same as {@link children}.
1871
+ * [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
1872
+ */
1794
1873
  get: function () {
1795
1874
  return this.children;
1796
1875
  },
@@ -1838,6 +1917,10 @@
1838
1917
  }
1839
1918
  Object.defineProperty(Element.prototype, "tagName", {
1840
1919
  // DOM Level 1 aliases
1920
+ /**
1921
+ * Same as {@link name}.
1922
+ * [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
1923
+ */
1841
1924
  get: function () {
1842
1925
  return this.name;
1843
1926
  },
@@ -1941,6 +2024,9 @@
1941
2024
  var children = recursive ? cloneChildren(node.children) : [];
1942
2025
  var clone_1 = new Element$1(node.name, __assign({}, node.attribs), children);
1943
2026
  children.forEach(function (child) { return (child.parent = clone_1); });
2027
+ if (node.namespace != null) {
2028
+ clone_1.namespace = node.namespace;
2029
+ }
1944
2030
  if (node["x-attribsNamespace"]) {
1945
2031
  clone_1["x-attribsNamespace"] = __assign({}, node["x-attribsNamespace"]);
1946
2032
  }
@@ -1974,10 +2060,13 @@
1974
2060
  result = instruction;
1975
2061
  }
1976
2062
  else {
1977
- throw new Error("Not implemented yet: " + node.type);
2063
+ throw new Error("Not implemented yet: ".concat(node.type));
1978
2064
  }
1979
2065
  result.startIndex = node.startIndex;
1980
2066
  result.endIndex = node.endIndex;
2067
+ if (node.sourceCodeLocation != null) {
2068
+ result.sourceCodeLocation = node.sourceCodeLocation;
2069
+ }
1981
2070
  return result;
1982
2071
  }
1983
2072
  node.cloneNode = cloneNode;