Skip to content

Commit 523e292

Browse files
committed
fix(dom-to-react): trim whitespaces if it is not valid in parent
1 parent ec61f9e commit 523e292

File tree

3 files changed

+49
-12
lines changed

3 files changed

+49
-12
lines changed

lib/dom-to-react.js

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ var attributesToProps = require('./attributes-to-props');
33
var utilities = require('./utilities');
44

55
var setStyleProp = utilities.setStyleProp;
6+
var canTextBeChildOfNode = utilities.canTextBeChildOfNode;
67

78
/**
89
* Converts DOM nodes to JSX element(s).
@@ -23,11 +24,11 @@ function domToReact(nodes, options) {
2324

2425
var result = [];
2526
var node;
27+
var isWhitespace;
2628
var hasReplace = typeof options.replace === 'function';
2729
var replaceElement;
2830
var props;
2931
var children;
30-
var data;
3132
var trim = options.trim;
3233

3334
for (var i = 0, len = nodes.length; i < len; i++) {
@@ -51,15 +52,23 @@ function domToReact(nodes, options) {
5152
}
5253

5354
if (node.type === 'text') {
54-
// if trim option is enabled, skip whitespace text nodes
55-
if (trim) {
56-
data = node.data.trim();
57-
if (data) {
58-
result.push(node.data);
59-
}
60-
} else {
61-
result.push(node.data);
55+
isWhitespace = !node.data.trim().length;
56+
57+
if (isWhitespace && node.parent && !canTextBeChildOfNode(node.parent)) {
58+
// We have a whitespace node that can't be nested in it's parent
59+
// so skip it
60+
continue;
6261
}
62+
63+
if (trim && isWhitespace) {
64+
// Trim is enabled and we have a whitespace node
65+
// so skip it
66+
continue;
67+
}
68+
69+
// We have a text node thats not whitespace and it can be nested
70+
// in its parent so add it to the results
71+
result.push(node.data);
6372
continue;
6473
}
6574

lib/utilities.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,34 @@ function setStyleProp(style, props) {
9696
*/
9797
var PRESERVE_CUSTOM_ATTRIBUTES = React.version.split('.')[0] >= 16;
9898

99+
// Taken from
100+
// https://github.com/facebook/react/blob/cae635054e17a6f107a39d328649137b83f25972/packages/react-dom/src/client/validateDOMNesting.js#L213
101+
var elementsWithNoTextChildren = new Set([
102+
'tr',
103+
'tbody',
104+
'thead',
105+
'tfoot',
106+
'colgroup',
107+
'table',
108+
'head',
109+
'html',
110+
'frameset',
111+
'#document'
112+
]);
113+
114+
/**
115+
* Returns True if the the given node can contains text nodes
116+
* @param {DomElement} node
117+
* @returns {boolean}
118+
*/
119+
function canTextBeChildOfNode(node) {
120+
return !elementsWithNoTextChildren.has(node.name);
121+
}
122+
99123
module.exports = {
100124
PRESERVE_CUSTOM_ATTRIBUTES: PRESERVE_CUSTOM_ATTRIBUTES,
101125
invertObject: invertObject,
102126
isCustomComponent: isCustomComponent,
103-
setStyleProp: setStyleProp
127+
setStyleProp: setStyleProp,
128+
canTextBeChildOfNode: canTextBeChildOfNode
104129
};

test/index.test.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,16 @@ describe('htmlparser2 option', () => {
138138
});
139139

140140
describe('trim option', () => {
141-
it('preserves whitespace text nodes when disabled (default)', () => {
141+
it('preserves whitespace text nodes when disabled if valid in parent (default)', () => {
142142
const html = `<table>
143143
<tbody>
144+
<tr><td>\n</td>\t</tr>\r
144145
</tbody>
145146
</table>`;
146147
const reactElement = parse(html);
147-
expect(render(reactElement)).toBe(html);
148+
expect(render(reactElement)).toBe(
149+
'<table><tbody><tr><td>\n</td></tr></tbody></table>'
150+
);
148151
});
149152

150153
it('removes whitespace text nodes when enabled', () => {

0 commit comments

Comments
 (0)