From dda59e06d667935244b3e5379bb31f955f125f53 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 3 Jun 2020 17:59:43 -0400 Subject: [PATCH 1/9] docs(index): tidy JSDoc comments for default export in index.d.ts --- index.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.d.ts b/index.d.ts index 4bcc997d..09fb9fe3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -23,9 +23,9 @@ export interface HTMLReactParserOptions { /** * Converts HTML string to JSX element(s). * - * @param html - HTML string to parse to JSX element(s). + * @param html - HTML string. * @param options - Parser options. - * @return - JSX element(s). + * @return - JSX element(s), empty array, or string. */ declare function HTMLReactParser( html: string, From bcab3a8a64e0dec7ea7bf0844729305d77b28c2d Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 3 Jun 2020 18:22:40 -0400 Subject: [PATCH 2/9] docs(index): tidy and update JSDoc comments for index.js Document missing param for `options.library`. --- index.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index b6fb44b9..a805ed5a 100644 --- a/index.js +++ b/index.js @@ -7,10 +7,11 @@ var domParserOptions = { decodeEntities: true, lowerCaseAttributeNames: false }; /** * Converts HTML string to React elements. * - * @param {String} html - The HTML string to parse to React. - * @param {Object} [options] - The parser options. - * @param {Function} [options.replace] - The replace method. - * @return {JSX.Element|JSX.Element[]|String} - Returns React element(s), string, or empty array. + * @param {String} html - HTML string. + * @param {Object} [options] - Parser options. + * @param {Object} [options.library] - Library for React, Preact, etc. + * @param {Function} [options.replace] - Replace method. + * @return {JSX.Element|JSX.Element[]|String} - React element(s), empty array, or string. */ function HTMLReactParser(html, options) { if (typeof html !== 'string') { From 466688bb1c3412a107b3311b6d70e85639364457 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 3 Jun 2020 19:01:02 -0400 Subject: [PATCH 3/9] build(package): replace @types/domhandler with @types/htmlparser2 --- index.d.ts | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.d.ts b/index.d.ts index 09fb9fe3..d4ab2a34 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,6 +1,6 @@ // TypeScript Version: 3.3 -import { DomElement } from 'domhandler'; +import { DomElement } from 'htmlparser2'; import domToReact from './lib/dom-to-react'; import htmlToDOM from 'html-dom-parser'; diff --git a/package.json b/package.json index aa28ec36..2b6a1d3e 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "dom" ], "dependencies": { - "@types/domhandler": "2.4.1", + "@types/htmlparser2": "3.10.1", "html-dom-parser": "0.3.0", "react-property": "1.0.1", "style-to-object": "0.3.0" From cfb4ec890c7e99fdc1c0b0222359e73675ba88b1 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 3 Jun 2020 19:04:26 -0400 Subject: [PATCH 4/9] test(html-to-react): add test for option library Although we have a test in dom-to-react, adding one here helps with completeness. --- test/html-to-react.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/html-to-react.js b/test/html-to-react.js index 31e59410..506126d6 100644 --- a/test/html-to-react.js +++ b/test/html-to-react.js @@ -127,5 +127,15 @@ describe('HTML to React', () => { ); }); }); + + describe('library', () => { + it('converts with Preact instead of React', () => { + const Preact = require('preact'); + const html = data.html.single; + const options = { library: Preact }; + const preactElement = parse(html, options); + assert.deepEqual(preactElement, Preact.createElement('p', {}, 'foo')); + }); + }); }); }); From c4ecf64a3a51978c4088da10c6d9d1602d669ac2 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 3 Jun 2020 19:08:23 -0400 Subject: [PATCH 5/9] feat(index): add options.htmlparser2 https://github.com/fb55/htmlparser2/wiki/Parser-options --- index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index a805ed5a..a4e859ca 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ var domParserOptions = { decodeEntities: true, lowerCaseAttributeNames: false }; * * @param {String} html - HTML string. * @param {Object} [options] - Parser options. + * @param {Object} [options.htmlparser2] - htmlparser2 options. * @param {Object} [options.library] - Library for React, Preact, etc. * @param {Function} [options.replace] - Replace method. * @return {JSX.Element|JSX.Element[]|String} - React element(s), empty array, or string. @@ -20,7 +21,11 @@ function HTMLReactParser(html, options) { if (html === '') { return []; } - return domToReact(htmlToDOM(html, domParserOptions), options); + options = options || {}; + return domToReact( + htmlToDOM(html, options.htmlparser2 || domParserOptions), + options + ); } HTMLReactParser.domToReact = domToReact; From c20f222217dcd9c726f50cbaa83175248d48acc6 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 3 Jun 2020 19:12:14 -0400 Subject: [PATCH 6/9] test(html-to-react): add test for option htmlparser2 --- test/html-to-react.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/html-to-react.js b/test/html-to-react.js index 506126d6..15bf7085 100644 --- a/test/html-to-react.js +++ b/test/html-to-react.js @@ -137,5 +137,20 @@ describe('HTML to React', () => { assert.deepEqual(preactElement, Preact.createElement('p', {}, 'foo')); }); }); + + describe('htmlparser2', () => { + it('parses XHTML with xmlMode enabled', () => { + // using self-closing syntax (`/>`) for non-void elements is invalid + // which causes elements to nest instead of being rendered correctly + // enabling htmlparser2 option xmlMode resolves this issue + const html = '
'; + const options = { htmlparser2: { xmlMode: true } }; + const reactElements = parse(html, options); + assert.strictEqual( + render(reactElements), + '
' + ); + }); + }); }); }); From 81f74fbd93b737ca7a74d93a835dadde559ed00b Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 3 Jun 2020 19:14:54 -0400 Subject: [PATCH 7/9] feat(index): add htmlparser2 type to HTMLReactParserOptions --- index.d.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/index.d.ts b/index.d.ts index d4ab2a34..28b40dc3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,13 +1,12 @@ // TypeScript Version: 3.3 -import { DomElement } from 'htmlparser2'; +import { DomElement, ParserOptions } from 'htmlparser2'; import domToReact from './lib/dom-to-react'; import htmlToDOM from 'html-dom-parser'; export interface HTMLReactParserOptions { - replace?: ( - domNode: DomElement - ) => JSX.Element | object | void | undefined | null | false; + htmlparser2?: ParserOptions; + library?: { cloneElement: ( element: JSX.Element, @@ -18,6 +17,10 @@ export interface HTMLReactParserOptions { isValidElement: (element: any) => boolean; [key: string]: any; }; + + replace?: ( + domNode: DomElement + ) => JSX.Element | object | void | undefined | null | false; } /** @@ -32,6 +35,6 @@ declare function HTMLReactParser( options?: HTMLReactParserOptions ): ReturnType; -export { DomElement, domToReact, htmlToDOM }; +export { DomElement, ParserOptions, domToReact, htmlToDOM }; export default HTMLReactParser; From 8a057ede0a79de87e11459fa4830f91b1d02f1ab Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 3 Jun 2020 19:26:45 -0400 Subject: [PATCH 8/9] test(types): add test that checks type for option htmlparser2 --- test/types/index.test.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/types/index.test.tsx b/test/types/index.test.tsx index 30314694..9fbef9b4 100644 --- a/test/types/index.test.tsx +++ b/test/types/index.test.tsx @@ -60,6 +60,18 @@ parse('
', { } }); +// $ExpectType Element | Element[] +parse('

', { + htmlparser2: { + xmlMode: true, + decodeEntities: true, + lowerCaseTags: false, + lowerCaseAttributeNames: false, + recognizeCDATA: true, + recognizeSelfClosing: true + } +}); + // $ExpectType DomElement[] const domNodes = htmlToDOM('

text
'); From 5944dd851ad10f62a4ecc425dab24128e6adbbd1 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 3 Jun 2020 19:45:05 -0400 Subject: [PATCH 9/9] docs(readme): document option `htmlparser2` and any risks --- README.md | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dd9a0d43..a242442d 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ parse( The `replace` callback allows you to swap an element with another React element. -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): +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): ```js parse('
', { @@ -224,6 +224,34 @@ parse('
', { }); ``` +### htmlparser2 + +This library passes the following options to [htmlparser2](https://github.com/fb55/htmlparser2/tree/v3.10.1) on the server-side: + +```js +{ + decodeEntities: true, + lowerCaseAttributeNames: false +} +``` + +By passing your own options, the default library options will be **replaced** (not merged). + +As a result, to enable `decodeEntities` and `xmlMode`, you need to do the following: + +```js +parse('

', { + htmlparser2: { + decodeEntities: true, + xmlMode: true + } +}); +``` + +See [htmlparser2 options](https://github.com/fb55/htmlparser2/wiki/Parser-options). + +> **Warning**: By overriding htmlparser2 options, there's a chance of breaking universal rendering. Do this at your own risk. + ## FAQ #### Is this library XSS safe?