Skip to content

Commit 4db43eb

Browse files
Merge pull request #161 from remarkablemark/feat/options-htmlparser2
feat: add option htmlparser2
2 parents 0078b2a + 5944dd8 commit 4db43eb

File tree

6 files changed

+88
-14
lines changed

6 files changed

+88
-14
lines changed

README.md

+29-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ parse(
106106

107107
The `replace` callback allows you to swap an element with another React element.
108108

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):
110110

111111
```js
112112
parse('<br>', {
@@ -224,6 +224,34 @@ parse('<br>', {
224224
});
225225
```
226226

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+
227255
## FAQ
228256

229257
#### Is this library XSS safe?

index.d.ts

+10-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
// TypeScript Version: 3.3
22

3-
import { DomElement } from 'domhandler';
3+
import { DomElement, ParserOptions } from 'htmlparser2';
44
import domToReact from './lib/dom-to-react';
55
import htmlToDOM from 'html-dom-parser';
66

77
export interface HTMLReactParserOptions {
8-
replace?: (
9-
domNode: DomElement
10-
) => JSX.Element | object | void | undefined | null | false;
8+
htmlparser2?: ParserOptions;
9+
1110
library?: {
1211
cloneElement: (
1312
element: JSX.Element,
@@ -18,20 +17,24 @@ export interface HTMLReactParserOptions {
1817
isValidElement: (element: any) => boolean;
1918
[key: string]: any;
2019
};
20+
21+
replace?: (
22+
domNode: DomElement
23+
) => JSX.Element | object | void | undefined | null | false;
2124
}
2225

2326
/**
2427
* Converts HTML string to JSX element(s).
2528
*
26-
* @param html - HTML string to parse to JSX element(s).
29+
* @param html - HTML string.
2730
* @param options - Parser options.
28-
* @return - JSX element(s).
31+
* @return - JSX element(s), empty array, or string.
2932
*/
3033
declare function HTMLReactParser(
3134
html: string,
3235
options?: HTMLReactParserOptions
3336
): ReturnType<typeof domToReact>;
3437

35-
export { DomElement, domToReact, htmlToDOM };
38+
export { DomElement, ParserOptions, domToReact, htmlToDOM };
3639

3740
export default HTMLReactParser;

index.js

+11-5
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ var domParserOptions = { decodeEntities: true, lowerCaseAttributeNames: false };
77
/**
88
* Converts HTML string to React elements.
99
*
10-
* @param {String} html - The HTML string to parse to React.
11-
* @param {Object} [options] - The parser options.
12-
* @param {Function} [options.replace] - The replace method.
13-
* @return {JSX.Element|JSX.Element[]|String} - Returns React element(s), string, or empty array.
10+
* @param {String} html - HTML string.
11+
* @param {Object} [options] - Parser options.
12+
* @param {Object} [options.htmlparser2] - htmlparser2 options.
13+
* @param {Object} [options.library] - Library for React, Preact, etc.
14+
* @param {Function} [options.replace] - Replace method.
15+
* @return {JSX.Element|JSX.Element[]|String} - React element(s), empty array, or string.
1416
*/
1517
function HTMLReactParser(html, options) {
1618
if (typeof html !== 'string') {
@@ -19,7 +21,11 @@ function HTMLReactParser(html, options) {
1921
if (html === '') {
2022
return [];
2123
}
22-
return domToReact(htmlToDOM(html, domParserOptions), options);
24+
options = options || {};
25+
return domToReact(
26+
htmlToDOM(html, options.htmlparser2 || domParserOptions),
27+
options
28+
);
2329
}
2430

2531
HTMLReactParser.domToReact = domToReact;

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"dom"
3333
],
3434
"dependencies": {
35-
"@types/domhandler": "2.4.1",
35+
"@types/htmlparser2": "3.10.1",
3636
"html-dom-parser": "0.3.0",
3737
"react-property": "1.0.1",
3838
"style-to-object": "0.3.0"

test/html-to-react.js

+25
Original file line numberDiff line numberDiff line change
@@ -127,5 +127,30 @@ describe('HTML to React', () => {
127127
);
128128
});
129129
});
130+
131+
describe('library', () => {
132+
it('converts with Preact instead of React', () => {
133+
const Preact = require('preact');
134+
const html = data.html.single;
135+
const options = { library: Preact };
136+
const preactElement = parse(html, options);
137+
assert.deepEqual(preactElement, Preact.createElement('p', {}, 'foo'));
138+
});
139+
});
140+
141+
describe('htmlparser2', () => {
142+
it('parses XHTML with xmlMode enabled', () => {
143+
// using self-closing syntax (`/>`) for non-void elements is invalid
144+
// which causes elements to nest instead of being rendered correctly
145+
// enabling htmlparser2 option xmlMode resolves this issue
146+
const html = '<ul><li/><li/></ul>';
147+
const options = { htmlparser2: { xmlMode: true } };
148+
const reactElements = parse(html, options);
149+
assert.strictEqual(
150+
render(reactElements),
151+
'<ul><li></li><li></li></ul>'
152+
);
153+
});
154+
});
130155
});
131156
});

test/types/index.test.tsx

+12
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,18 @@ parse('<hr>', {
6060
}
6161
});
6262

63+
// $ExpectType Element | Element[]
64+
parse('<p/><p/>', {
65+
htmlparser2: {
66+
xmlMode: true,
67+
decodeEntities: true,
68+
lowerCaseTags: false,
69+
lowerCaseAttributeNames: false,
70+
recognizeCDATA: true,
71+
recognizeSelfClosing: true
72+
}
73+
});
74+
6375
// $ExpectType DomElement[]
6476
const domNodes = htmlToDOM('<div>text</div>');
6577

0 commit comments

Comments
 (0)