-
-
Notifications
You must be signed in to change notification settings - Fork 135
/
Copy pathdom-to-react.js
124 lines (107 loc) Β· 3.31 KB
/
dom-to-react.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
var attributesToProps = require('./attributes-to-props');
var utilities = require('./utilities');
/**
* Converts DOM nodes to React elements.
*
* @param {DomElement[]} nodes - The DOM nodes.
* @param {Object} [options={}] - The additional options.
* @param {Function} [options.replace] - The replacer.
* @param {Object} [options.library] - The library (React, Preact, etc.).
* @return {String|ReactElement|ReactElement[]}
*/
function domToReact(nodes, options) {
options = options || {};
var React = options.library || require('react');
var cloneElement = React.cloneElement;
var createElement = React.createElement;
var isValidElement = React.isValidElement;
var result = [];
var node;
var hasReplace = typeof options.replace === 'function';
var replaceElement;
var props;
var children;
var data;
var trim = options.trim;
for (var i = 0, len = nodes.length; i < len; i++) {
node = nodes[i];
// replace with custom React element (if present)
if (hasReplace) {
replaceElement = options.replace(node);
if (isValidElement(replaceElement)) {
// set "key" prop for sibling elements
// https://fb.me/react-warning-keys
if (len > 1) {
replaceElement = cloneElement(replaceElement, {
key: replaceElement.key || i
});
}
result.push(replaceElement);
continue;
}
}
if (node.type === 'text') {
// if trim option is enabled, skip whitespace text nodes
if (trim) {
data = node.data.trim();
if (data) {
result.push(node.data);
}
} else {
result.push(node.data);
}
continue;
}
props = node.attribs;
if (!shouldPassAttributesUnaltered(node)) {
props = attributesToProps(node.attribs);
}
children = null;
switch (node.type) {
case 'script':
case 'style':
// prevent text in <script> or <style> from being escaped
// https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
if (node.children[0]) {
props.dangerouslySetInnerHTML = {
__html: node.children[0].data
};
}
break;
case 'tag':
// setting textarea value in children is an antipattern in React
// https://reactjs.org/docs/forms.html#the-textarea-tag
if (node.name === 'textarea' && node.children[0]) {
props.defaultValue = node.children[0].data;
} else if (node.children && node.children.length) {
// continue recursion of creating React elements (if applicable)
children = domToReact(node.children, options);
}
break;
// skip all other cases (e.g., comment)
default:
continue;
}
// set "key" prop for sibling elements
// https://fb.me/react-warning-keys
if (len > 1) {
props.key = i;
}
result.push(createElement(node.name, props, children));
}
return result.length === 1 ? result[0] : result;
}
/**
* Determines whether attributes should be altered or not.
*
* @param {React.ReactElement} node
* @return {Boolean}
*/
function shouldPassAttributesUnaltered(node) {
return (
utilities.PRESERVE_CUSTOM_ATTRIBUTES &&
node.type === 'tag' &&
utilities.isCustomComponent(node.name, node.attribs)
);
}
module.exports = domToReact;