html-react-parser 6.0.1 → 6.1.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/README.md CHANGED
@@ -43,6 +43,7 @@ parse('<p>Hello, World!</p>'); // React.createElement('p', {}, 'Hello, World!')
43
43
  - [library](#library)
44
44
  - [htmlparser2](#htmlparser2)
45
45
  - [trim](#trim)
46
+ - [trustedTypePolicy](#trustedtypepolicy)
46
47
  - [Migration](#migration)
47
48
  - [v5](#v5)
48
49
  - [v4](#v4)
@@ -208,7 +209,7 @@ parse('<br>', {
208
209
 
209
210
  > [!NOTE]
210
211
  >
211
- > The index will restart at 0 when traversing the node's children so don't rely on index being a unique key. See [#1259](https://github.com/remarkablemark/html-react-parser/issues/1259#issuecomment-1889574133).
212
+ > The index will restart at 0 when traversing the node's children so don't rely on index being a unique key (see [#1259](https://github.com/remarkablemark/html-react-parser/issues/1259#issuecomment-1889574133)).
212
213
 
213
214
  #### replace with TypeScript
214
215
 
@@ -244,7 +245,7 @@ If you're having issues, take a look at our [Create React App example](./example
244
245
 
245
246
  #### replace element and children
246
247
 
247
- Replace the element and its children (see [demo](https://replit.com/@remarkablemark/html-react-parser-replace-example)):
248
+ Replace the element and its children:
248
249
 
249
250
  ```tsx
250
251
  import parse, { domToReact } from 'html-react-parser';
@@ -335,7 +336,7 @@ parse(html, options);
335
336
 
336
337
  #### replace and remove element
337
338
 
338
- [Exclude](https://replit.com/@remarkablemark/html-react-parser-56) an element from rendering by replacing it with `<React.Fragment>`:
339
+ Exclude an element from rendering by replacing it with `<React.Fragment>`:
339
340
 
340
341
  ```tsx
341
342
  parse('<p><br id="remove"></p>', {
@@ -443,6 +444,21 @@ However, intentional whitespace may be stripped out:
443
444
  parse('<p> </p>', { trim: true }); // React.createElement('p')
444
445
  ```
445
446
 
447
+ ### trustedTypePolicy
448
+
449
+ When running in a browser, you can pass a [Trusted Types](https://developer.mozilla.org/docs/Web/API/Trusted_Types_API) policy so the parser calls `trustedTypePolicy.createHTML` before assigning content to `innerHTML`:
450
+
451
+ ```ts
452
+ parse('<div>Hello</div>', {
453
+ trustedTypePolicy: window.trustedTypes?.createPolicy('my-policy', {
454
+ createHTML(input) {
455
+ // apply sanitization logic here
456
+ return DOMPurify.sanitize(input);
457
+ },
458
+ }),
459
+ });
460
+ ```
461
+
446
462
  ## Migration
447
463
 
448
464
  ### v6
@@ -511,27 +527,27 @@ Since [v1.1.1](https://github.com/remarkablemark/html-react-parser/releases/tag/
511
527
 
512
528
  ### Is this XSS safe?
513
529
 
514
- No, this library is _**not**_ [XSS (cross-site scripting)](https://wikipedia.org/wiki/Cross-site_scripting) safe. See [#94](https://github.com/remarkablemark/html-react-parser/issues/94).
530
+ No, this library is **not** [XSS (cross-site scripting)](https://wikipedia.org/wiki/Cross-site_scripting) safe (see [#94](https://github.com/remarkablemark/html-react-parser/issues/94)). However, you can mitigate this risk by enforcing a Content Security Policy (CSP) with [Trusted Types](#trustedtypepolicy).
515
531
 
516
532
  ### Does invalid HTML get sanitized?
517
533
 
518
- No, this library does _**not**_ sanitize HTML. See [#124](https://github.com/remarkablemark/html-react-parser/issues/124), [#125](https://github.com/remarkablemark/html-react-parser/issues/125), and [#141](https://github.com/remarkablemark/html-react-parser/issues/141).
534
+ No, this library does **not** sanitize HTML (see [#124](https://github.com/remarkablemark/html-react-parser/issues/124), [#125](https://github.com/remarkablemark/html-react-parser/issues/125), and [#141](https://github.com/remarkablemark/html-react-parser/issues/141)).
519
535
 
520
536
  ### Are `<script>` tags parsed?
521
537
 
522
- Although `<script>` tags and their contents are rendered on the server-side, they're not evaluated on the client-side. See [#98](https://github.com/remarkablemark/html-react-parser/issues/98).
538
+ Although `<script>` tags and their contents are rendered on the server-side, they're not evaluated on the client-side (see [#98](https://github.com/remarkablemark/html-react-parser/issues/98)).
523
539
 
524
540
  ### Attributes aren't getting called
525
541
 
526
- The reason why your HTML attributes aren't getting called is because [inline event handlers](https://developer.mozilla.org/docs/Web/Guide/Events/Event_handlers) (e.g., `onclick`) are parsed as a _string_ rather than a _function_. See [#73](https://github.com/remarkablemark/html-react-parser/issues/73).
542
+ The reason why your HTML attributes aren't getting called is because [inline event handlers](https://developer.mozilla.org/docs/Web/Guide/Events/Event_handlers) (e.g., `onclick`) are parsed as a _string_ rather than a _function_ (see [#73](https://github.com/remarkablemark/html-react-parser/issues/73)).
527
543
 
528
544
  ### Parser throws an error
529
545
 
530
- If the parser throws an error, check if your arguments are valid. See ["Does invalid HTML get sanitized?"](#does-invalid-html-get-sanitized).
546
+ If the parser throws an error, check if your arguments are valid (see ["Does invalid HTML get sanitized?"](#does-invalid-html-get-sanitized)).
531
547
 
532
548
  ### Is SSR supported?
533
549
 
534
- Yes, server-side rendering on Node.js is supported by this library. See [demo](https://replit.com/@remarkablemark/html-react-parser-SSR).
550
+ Yes, server-side rendering on Node.js is supported by this library:
535
551
 
536
552
  ### Elements aren't nested correctly
537
553
 
@@ -564,15 +580,15 @@ parse('<CustomElement>', options); // React.createElement('CustomElement')
564
580
  > Warning: <CustomElement> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.
565
581
  > ```
566
582
 
567
- See [#62](https://github.com/remarkablemark/html-react-parser/issues/62) and [example](https://replit.com/@remarkablemark/html-react-parser-62).
583
+ See [#62](https://github.com/remarkablemark/html-react-parser/issues/62).
568
584
 
569
585
  ### TS Error: Property 'attribs' does not exist on type 'DOMNode'
570
586
 
571
- The TypeScript error occurs because `DOMNode` needs to be an instance of domhandler's `Element`. See [migration](#migration) or [#199](https://github.com/remarkablemark/html-react-parser/issues/199).
587
+ The TypeScript error occurs because `DOMNode` needs to be an instance of domhandler's `Element` (see [migration](#migration) or [#199](https://github.com/remarkablemark/html-react-parser/issues/199)).
572
588
 
573
589
  ### Can I enable `trim` for certain elements?
574
590
 
575
- Yes, you can enable or disable [`trim`](#trim) for certain elements using the [`replace`](#replace) option. See [#205](https://github.com/remarkablemark/html-react-parser/issues/205).
591
+ Yes, you can enable or disable [`trim`](#trim) for certain elements using the [`replace`](#replace) option (see [#205](https://github.com/remarkablemark/html-react-parser/issues/205)).
576
592
 
577
593
  ### Webpack build warnings
578
594
 
@@ -607,7 +623,7 @@ node_modules/htmlparser2/lib/index.d.ts:2:23 - error TS1005: ',' expected.
607
623
  ~~~~~~~~~~~~~
608
624
  ```
609
625
 
610
- Then upgrade to the latest version of [typescript](https://www.npmjs.com/package/typescript). See [#748](https://github.com/remarkablemark/html-react-parser/issues/748).
626
+ Then upgrade to the latest version of [typescript](https://www.npmjs.com/package/typescript) (see [#748](https://github.com/remarkablemark/html-react-parser/issues/748)).
611
627
 
612
628
  ## Performance
613
629
 
@@ -674,13 +674,16 @@
674
674
  var HEAD = 'head';
675
675
  var BODY = 'body';
676
676
  var FIRST_TAG_REGEX = /<([a-zA-Z]+[0-9]?)/; // e.g., <h1>
677
+ function getHTMLForInnerHTML(html, trustedTypePolicy) {
678
+ return trustedTypePolicy ? trustedTypePolicy.createHTML(html) : html;
679
+ }
677
680
  // falls back to `parseFromString` if `createHTMLDocument` cannot be used
678
681
  /* eslint-disable @typescript-eslint/no-unused-vars */
679
682
  /* v8 ignore start */
680
- var parseFromDocument = function (html, tagName) {
683
+ var parseFromDocument = function (html, tagName, trustedTypePolicy) {
681
684
  throw new Error('This browser does not support `document.implementation.createHTMLDocument`');
682
685
  };
683
- var parseFromString = function (html, tagName) {
686
+ var parseFromString = function (html, tagName, trustedTypePolicy) {
684
687
  throw new Error('This browser does not support `DOMParser.prototype.parseFromString`');
685
688
  };
686
689
  var DOMParser = typeof window === 'object' && window.DOMParser;
@@ -699,7 +702,7 @@
699
702
  * @param tagName - The element to render the HTML (with 'body' as fallback).
700
703
  * @returns - Document.
701
704
  */
702
- parseFromString = function (html, tagName) {
705
+ parseFromString = function (html, tagName, trustedTypePolicy) {
703
706
  if (tagName) {
704
707
  html = "<".concat(tagName, ">").concat(html, "</").concat(tagName, ">");
705
708
  }
@@ -722,15 +725,15 @@
722
725
  * @param tagName - The element to render the HTML (with 'body' as fallback).
723
726
  * @returns - Document
724
727
  */
725
- parseFromDocument = function (html, tagName) {
728
+ parseFromDocument = function (html, tagName, trustedTypePolicy) {
726
729
  if (tagName) {
727
730
  var element = htmlDocument_1.documentElement.querySelector(tagName);
728
731
  if (element) {
729
- element.innerHTML = html;
732
+ element.innerHTML = getHTMLForInnerHTML(html, trustedTypePolicy);
730
733
  }
731
734
  return htmlDocument_1;
732
735
  }
733
- htmlDocument_1.documentElement.innerHTML = html;
736
+ htmlDocument_1.documentElement.innerHTML = getHTMLForInnerHTML(html, trustedTypePolicy);
734
737
  return htmlDocument_1;
735
738
  };
736
739
  }
@@ -749,8 +752,8 @@
749
752
  * @param html - HTML string.
750
753
  * @returns - Nodes.
751
754
  */
752
- parseFromTemplate = function (html) {
753
- template.innerHTML = html;
755
+ parseFromTemplate = function (html, trustedTypePolicy) {
756
+ template.innerHTML = getHTMLForInnerHTML(html, trustedTypePolicy);
754
757
  return template.content.childNodes;
755
758
  };
756
759
  }
@@ -760,9 +763,10 @@
760
763
  * Parses HTML string to DOM nodes.
761
764
  *
762
765
  * @param html - HTML markup.
766
+ * @param trustedTypePolicy - Trusted Types policy.
763
767
  * @returns - DOM nodes.
764
768
  */
765
- function domparser(html) {
769
+ function domparser(html, trustedTypePolicy) {
766
770
  var _a, _b, _c, _d, _e, _f;
767
771
  // Escape special characters before parsing
768
772
  html = escapeSpecialCharacters(html);
@@ -785,7 +789,7 @@
785
789
  }
786
790
  case HEAD:
787
791
  case BODY: {
788
- var elements = parseFromDocument(html).querySelectorAll(firstTagName);
792
+ var elements = parseFromDocument(html, undefined, trustedTypePolicy).querySelectorAll(firstTagName);
789
793
  // if there's a sibling element, then return both elements
790
794
  /* v8 ignore next */
791
795
  if (hasOpenTag(html, BODY) && hasOpenTag(html, HEAD)) {
@@ -798,9 +802,9 @@
798
802
  default: {
799
803
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
800
804
  if (parseFromTemplate) {
801
- return parseFromTemplate(html);
805
+ return parseFromTemplate(html, trustedTypePolicy);
802
806
  }
803
- var element = parseFromDocument(html, BODY).querySelector(BODY);
807
+ var element = parseFromDocument(html, BODY, trustedTypePolicy).querySelector(BODY);
804
808
  return (_f = element === null || element === void 0 ? void 0 : element.childNodes) !== null && _f !== void 0 ? _f : createNodeList();
805
809
  }
806
810
  /* v8 ignore stop */
@@ -812,9 +816,10 @@
812
816
  * Parses HTML string to DOM nodes in browser.
813
817
  *
814
818
  * @param html - HTML markup.
819
+ * @param options - Parser options.
815
820
  * @returns - DOM elements.
816
821
  */
817
- function HTMLDOMParser(html) {
822
+ function HTMLDOMParser(html, options) {
818
823
  if (typeof html !== 'string') {
819
824
  throw new TypeError('First argument must be a string');
820
825
  }
@@ -824,7 +829,7 @@
824
829
  // match directive
825
830
  var match = DIRECTIVE_REGEX.exec(html);
826
831
  var directive = match ? match[1] : undefined;
827
- return formatDOM(domparser(html), null, directive);
832
+ return formatDOM(domparser(html, options === null || options === void 0 ? void 0 : options.trustedTypePolicy), null, directive);
828
833
  }
829
834
 
830
835
  htmlToDom.default = HTMLDOMParser;
@@ -2285,14 +2290,14 @@
2285
2290
  function requireUtilities () {
2286
2291
  if (hasRequiredUtilities) return utilities$1;
2287
2292
  hasRequiredUtilities = 1;
2288
- (function (exports$1) {
2293
+ (function (exports) {
2289
2294
  var __importDefault = (utilities$1 && utilities$1.__importDefault) || function (mod) {
2290
2295
  return (mod && mod.__esModule) ? mod : { "default": mod };
2291
2296
  };
2292
- Object.defineProperty(exports$1, "__esModule", { value: true });
2293
- exports$1.returnFirstArg = exports$1.canTextBeChildOfNode = exports$1.ELEMENTS_WITH_NO_TEXT_CHILDREN = exports$1.PRESERVE_CUSTOM_ATTRIBUTES = void 0;
2294
- exports$1.isCustomComponent = isCustomComponent;
2295
- exports$1.setStyleProp = setStyleProp;
2297
+ Object.defineProperty(exports, "__esModule", { value: true });
2298
+ exports.returnFirstArg = exports.canTextBeChildOfNode = exports.ELEMENTS_WITH_NO_TEXT_CHILDREN = exports.PRESERVE_CUSTOM_ATTRIBUTES = void 0;
2299
+ exports.isCustomComponent = isCustomComponent;
2300
+ exports.setStyleProp = setStyleProp;
2296
2301
  const react_1 = require$$0;
2297
2302
  const style_to_js_1 = __importDefault(requireCjs());
2298
2303
  const RESERVED_SVG_MATHML_ELEMENTS = new Set([
@@ -2355,11 +2360,11 @@
2355
2360
  /**
2356
2361
  * @see https://reactjs.org/blog/2017/09/08/dom-attributes-in-react-16.html
2357
2362
  */
2358
- exports$1.PRESERVE_CUSTOM_ATTRIBUTES = Number(react_1.version.split('.')[0]) >= 16;
2363
+ exports.PRESERVE_CUSTOM_ATTRIBUTES = Number(react_1.version.split('.')[0]) >= 16;
2359
2364
  /**
2360
2365
  * @see https://github.com/facebook/react/blob/cae635054e17a6f107a39d328649137b83f25972/packages/react-dom/src/client/validateDOMNesting.js#L213
2361
2366
  */
2362
- exports$1.ELEMENTS_WITH_NO_TEXT_CHILDREN = new Set([
2367
+ exports.ELEMENTS_WITH_NO_TEXT_CHILDREN = new Set([
2363
2368
  'tr',
2364
2369
  'tbody',
2365
2370
  'thead',
@@ -2376,8 +2381,8 @@
2376
2381
  * @param node - Element node.
2377
2382
  * @returns - Whether the node can contain text nodes.
2378
2383
  */
2379
- const canTextBeChildOfNode = (node) => !exports$1.ELEMENTS_WITH_NO_TEXT_CHILDREN.has(node.name);
2380
- exports$1.canTextBeChildOfNode = canTextBeChildOfNode;
2384
+ const canTextBeChildOfNode = (node) => !exports.ELEMENTS_WITH_NO_TEXT_CHILDREN.has(node.name);
2385
+ exports.canTextBeChildOfNode = canTextBeChildOfNode;
2381
2386
  /**
2382
2387
  * Returns the first argument as is.
2383
2388
  *
@@ -2385,7 +2390,7 @@
2385
2390
  * @returns - The input argument `arg`.
2386
2391
  */
2387
2392
  const returnFirstArg = (arg) => arg;
2388
- exports$1.returnFirstArg = returnFirstArg;
2393
+ exports.returnFirstArg = returnFirstArg;
2389
2394
 
2390
2395
  } (utilities$1));
2391
2396
  return utilities$1;
@@ -3207,24 +3212,24 @@
3207
3212
  function requireLib () {
3208
3213
  if (hasRequiredLib) return lib$1;
3209
3214
  hasRequiredLib = 1;
3210
- (function (exports$1) {
3215
+ (function (exports) {
3211
3216
  var __importDefault = (lib$1 && lib$1.__importDefault) || function (mod) {
3212
3217
  return (mod && mod.__esModule) ? mod : { "default": mod };
3213
3218
  };
3214
- Object.defineProperty(exports$1, "__esModule", { value: true });
3215
- exports$1.htmlToDOM = exports$1.domToReact = exports$1.attributesToProps = exports$1.Text = exports$1.ProcessingInstruction = exports$1.Element = exports$1.Comment = void 0;
3216
- exports$1.default = HTMLReactParser;
3219
+ Object.defineProperty(exports, "__esModule", { value: true });
3220
+ exports.htmlToDOM = exports.domToReact = exports.attributesToProps = exports.Text = exports.ProcessingInstruction = exports.Element = exports.Comment = void 0;
3221
+ exports.default = HTMLReactParser;
3217
3222
  const html_dom_parser_1 = __importDefault(requireHtmlToDom());
3218
- exports$1.htmlToDOM = html_dom_parser_1.default;
3223
+ exports.htmlToDOM = html_dom_parser_1.default;
3219
3224
  const attributes_to_props_1 = __importDefault(requireAttributesToProps());
3220
- exports$1.attributesToProps = attributes_to_props_1.default;
3225
+ exports.attributesToProps = attributes_to_props_1.default;
3221
3226
  const dom_to_react_1 = __importDefault(requireDomToReact());
3222
- exports$1.domToReact = dom_to_react_1.default;
3227
+ exports.domToReact = dom_to_react_1.default;
3223
3228
  var domhandler_1 = require$$3;
3224
- Object.defineProperty(exports$1, "Comment", { enumerable: true, get: function () { return domhandler_1.Comment; } });
3225
- Object.defineProperty(exports$1, "Element", { enumerable: true, get: function () { return domhandler_1.Element; } });
3226
- Object.defineProperty(exports$1, "ProcessingInstruction", { enumerable: true, get: function () { return domhandler_1.ProcessingInstruction; } });
3227
- Object.defineProperty(exports$1, "Text", { enumerable: true, get: function () { return domhandler_1.Text; } });
3229
+ Object.defineProperty(exports, "Comment", { enumerable: true, get: function () { return domhandler_1.Comment; } });
3230
+ Object.defineProperty(exports, "Element", { enumerable: true, get: function () { return domhandler_1.Element; } });
3231
+ Object.defineProperty(exports, "ProcessingInstruction", { enumerable: true, get: function () { return domhandler_1.ProcessingInstruction; } });
3232
+ Object.defineProperty(exports, "Text", { enumerable: true, get: function () { return domhandler_1.Text; } });
3228
3233
  const domParserOptions = { lowerCaseAttributeNames: false };
3229
3234
  /**
3230
3235
  * Converts HTML string to React elements.
@@ -3241,7 +3246,8 @@
3241
3246
  if (!html) {
3242
3247
  return [];
3243
3248
  }
3244
- return (0, dom_to_react_1.default)((0, html_dom_parser_1.default)(html, (_a = options === null || options === void 0 ? void 0 : options.htmlparser2) !== null && _a !== void 0 ? _a : domParserOptions), options);
3249
+ const htmlToDOMOptions = Object.assign(Object.assign({}, ((_a = options === null || options === void 0 ? void 0 : options.htmlparser2) !== null && _a !== void 0 ? _a : domParserOptions)), { trustedTypePolicy: options === null || options === void 0 ? void 0 : options.trustedTypePolicy });
3250
+ return (0, dom_to_react_1.default)((0, html_dom_parser_1.default)(html, htmlToDOMOptions), options);
3245
3251
  }
3246
3252
 
3247
3253
  } (lib$1));