Skip to content

Commit eca7e35

Browse files
Merge pull request #109 from remarkablemark/chore/improvements
chore: skip <script> tag, improve test coverage, tidy code
2 parents d941132 + b97f2e1 commit eca7e35

File tree

5 files changed

+53
-49
lines changed

5 files changed

+53
-49
lines changed

lib/dom-to-react.js

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ var React = require('react');
22
var attributesToProps = require('./attributes-to-props');
33
var utilities = require('./utilities');
44

5+
var cloneElement = React.cloneElement;
6+
var createElement = React.createElement;
7+
var isValidElement = React.isValidElement;
8+
59
/**
610
* Converts DOM nodes to React elements.
711
*
@@ -14,27 +18,27 @@ function domToReact(nodes, options) {
1418
options = options || {};
1519
var result = [];
1620
var node;
17-
var isReplacePresent = typeof options.replace === 'function';
18-
var replacement;
21+
var hasReplace = typeof options.replace === 'function';
22+
var replaceElement;
1923
var props;
2024
var children;
2125

2226
for (var i = 0, len = nodes.length; i < len; i++) {
2327
node = nodes[i];
2428

25-
// replace with custom React element (if applicable)
26-
if (isReplacePresent) {
27-
replacement = options.replace(node);
29+
// replace with custom React element (if present)
30+
if (hasReplace) {
31+
replaceElement = options.replace(node);
2832

29-
if (React.isValidElement(replacement)) {
33+
if (isValidElement(replaceElement)) {
3034
// specify a "key" prop if element has siblings
3135
// https://fb.me/react-warning-keys
3236
if (len > 1) {
33-
replacement = React.cloneElement(replacement, {
34-
key: replacement.key || i
37+
replaceElement = cloneElement(replaceElement, {
38+
key: replaceElement.key || i
3539
});
3640
}
37-
result.push(replacement);
41+
result.push(replaceElement);
3842
continue;
3943
}
4044
}
@@ -52,11 +56,11 @@ function domToReact(nodes, options) {
5256

5357
children = null;
5458

55-
// node type for <script> is "script"
5659
// node type for <style> is "style"
57-
if (node.type === 'script' || node.type === 'style') {
58-
// prevent text in <script> or <style> from being escaped
59-
// https://facebook.github.io/react/tips/dangerously-set-inner-html.html
60+
// skip node type "script" as react-dom does not render the contents
61+
if (node.type === 'style') {
62+
// prevent text in <style> from being escaped
63+
// https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
6064
if (node.children[0]) {
6165
props.dangerouslySetInnerHTML = {
6266
__html: node.children[0].data
@@ -84,7 +88,7 @@ function domToReact(nodes, options) {
8488
props.key = i;
8589
}
8690

87-
result.push(React.createElement(node.name, props, children));
91+
result.push(createElement(node.name, props, children));
8892
}
8993

9094
return result.length === 1 ? result[0] : result;

test/attributes-to-props.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,20 @@ describe('attributesToProps', () => {
217217
}
218218
);
219219
});
220+
221+
[Object, Array, Number, Date].forEach(type => {
222+
it(`throws an error when attributes.style=${type.name}`, () => {
223+
assert.throws(
224+
() => {
225+
attributesToProps({ style: type });
226+
},
227+
{
228+
name: 'TypeError',
229+
message: 'First argument must be a string.'
230+
}
231+
);
232+
});
233+
});
220234
});
221235

222236
describe('when utilties.PRESERVE_CUSTOM_ATTRIBUTES=false', () => {

test/dom-to-react.js

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,23 +56,9 @@ describe('dom-to-react parser', () => {
5656
);
5757
});
5858

59-
it('does not escape <script> content', () => {
59+
it('does not parse <script> content', () => {
6060
const html = data.html.script;
61-
const reactElement = domToReact(htmlToDOM(html));
62-
63-
assert(React.isValidElement(reactElement));
64-
assert.deepEqual(
65-
reactElement,
66-
React.createElement(
67-
'script',
68-
{
69-
dangerouslySetInnerHTML: {
70-
__html: 'alert(1 < 2);'
71-
}
72-
},
73-
null
74-
)
75-
);
61+
assert.deepEqual(domToReact(htmlToDOM(html)), []);
7662
});
7763

7864
it('does not escape <style> content', () => {

test/helpers/data.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"multiple": "<p>foo</p><p>bar</p>",
55
"nested": "<div><p>foo <em>bar</em></p></div>",
66
"attributes": "<hr id=\"foo\" class=\"bar baz\" style=\"background: #fff; text-align: center;\" data-foo=\"bar\" />",
7-
"complex": "<html><head><meta charSet=\"utf-8\"/><title>Title</title><link rel=\"stylesheet\" href=\"style.css\"/></head><body><header id=\"header\">Header</header><h1 style=\"color:#000;font-size:42px\">Heading</h1><hr/><p>Paragraph</p><img src=\"image.jpg\"/><div class=\"class1 class2\">Some <em>text</em>.</div><script>alert();</script></body></html>",
7+
"complex": "<html><head><meta charSet=\"utf-8\"/><title>Title</title><link rel=\"stylesheet\" href=\"style.css\"/></head><body><header id=\"header\">Header</header><h1 style=\"color:#000;font-size:42px\">Heading</h1><hr/><p>Paragraph</p><img src=\"image.jpg\"/><div class=\"class1 class2\">Some <em>text</em>.</div></body></html>",
88
"textarea": "<textarea>foo</textarea>",
99
"script": "<script>alert(1 < 2);</script>",
1010
"style": "<style>body > .foo { color: #f00; }</style>",

test/html-to-react.js

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,38 @@
11
const assert = require('assert');
22
const React = require('react');
3-
const Parser = require('../');
3+
const parse = require('../');
44
const { data, render } = require('./helpers/');
55

66
describe('html-to-react', () => {
77
describe('parser', () => {
88
[undefined, null, {}, [], 42].forEach(value => {
99
it(`throws an error if first argument is ${value}`, () => {
1010
assert.throws(() => {
11-
Parser(value);
11+
parse(value);
1212
}, TypeError);
1313
});
1414
});
1515

1616
it('returns string if it cannot be parsed as HTML', () => {
17-
assert.equal(Parser('foo'), 'foo');
17+
assert.equal(parse('foo'), 'foo');
1818
});
1919

2020
it('converts single HTML element to React', () => {
2121
const html = data.html.single;
22-
const reactElement = Parser(html);
22+
const reactElement = parse(html);
2323
assert.equal(render(reactElement), html);
2424
});
2525

2626
it('converts single HTML element and ignores comment', () => {
2727
const html = data.html.single;
2828
// comment should be ignored
29-
const reactElement = Parser(html + data.html.comment);
29+
const reactElement = parse(html + data.html.comment);
3030
assert.equal(render(reactElement), html);
3131
});
3232

3333
it('converts multiple HTML elements to React', () => {
3434
const html = data.html.multiple;
35-
const reactElements = Parser(html);
35+
const reactElements = parse(html);
3636
assert.equal(
3737
render(React.createElement('div', {}, reactElements)),
3838
'<div>' + html + '</div>'
@@ -41,32 +41,26 @@ describe('html-to-react', () => {
4141

4242
it('converts complex HTML to React', () => {
4343
const html = data.html.complex;
44-
const reactElement = Parser(data.html.doctype + html);
45-
assert.equal(render(reactElement), html);
46-
});
47-
48-
it('converts empty <script> to React', () => {
49-
const html = '<script></script>';
50-
const reactElement = Parser(html);
44+
const reactElement = parse(data.html.doctype + html);
5145
assert.equal(render(reactElement), html);
5246
});
5347

5448
it('converts empty <style> to React', () => {
5549
const html = '<style></style>';
56-
const reactElement = Parser(html);
50+
const reactElement = parse(html);
5751
assert.equal(render(reactElement), html);
5852
});
5953

6054
it('converts SVG to React', () => {
6155
const svg = data.svg.complex;
62-
const reactElement = Parser(svg);
56+
const reactElement = parse(svg);
6357
assert.equal(render(reactElement), svg);
6458
});
6559

6660
it('decodes HTML entities', () => {
6761
const encodedEntities = 'asdf &amp; &yuml; &uuml; &apos;';
6862
const decodedEntities = "asdf & ÿ ü '";
69-
const reactElement = Parser('<i>' + encodedEntities + '</i>');
63+
const reactElement = parse('<i>' + encodedEntities + '</i>');
7064
assert.equal(reactElement.props.children, decodedEntities);
7165
});
7266
});
@@ -75,7 +69,7 @@ describe('html-to-react', () => {
7569
describe('replace', () => {
7670
it('overrides the element if replace is valid', () => {
7771
const html = data.html.complex;
78-
const reactElement = Parser(html, {
72+
const reactElement = parse(html, {
7973
replace: node => {
8074
if (node.name === 'title') {
8175
return React.createElement('title', {}, 'Replaced Title');
@@ -90,7 +84,7 @@ describe('html-to-react', () => {
9084

9185
it('does not override the element if replace is invalid', () => {
9286
const html = data.html.complex;
93-
const reactElement = Parser(html, {
87+
const reactElement = parse(html, {
9488
replace: node => {
9589
if (node.attribs && node.attribs.id === 'header') {
9690
return {
@@ -111,3 +105,9 @@ describe('html-to-react', () => {
111105
});
112106
});
113107
});
108+
109+
describe('dom-to-react', () => {
110+
it('exports domToReact', () => {
111+
assert.equal(parse.domToReact, require('../lib/dom-to-react'));
112+
});
113+
});

0 commit comments

Comments
 (0)