@b9g/crank 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +203 -138
- package/_css.cjs +80 -0
- package/_css.cjs.map +1 -0
- package/_css.d.ts +21 -0
- package/_css.js +76 -0
- package/_css.js.map +1 -0
- package/_utils.cjs +106 -0
- package/_utils.cjs.map +1 -0
- package/_utils.d.ts +14 -0
- package/_utils.js +99 -0
- package/_utils.js.map +1 -0
- package/async.cjs +236 -0
- package/async.cjs.map +1 -0
- package/async.d.ts +104 -0
- package/async.js +233 -0
- package/async.js.map +1 -0
- package/crank.cjs +1609 -1345
- package/crank.cjs.map +1 -1
- package/crank.d.ts +461 -228
- package/crank.js +1602 -1339
- package/crank.js.map +1 -1
- package/dom.cjs +365 -231
- package/dom.cjs.map +1 -1
- package/dom.d.ts +5 -5
- package/dom.js +365 -231
- package/dom.js.map +1 -1
- package/event-target.cjs +254 -0
- package/event-target.cjs.map +1 -0
- package/event-target.d.ts +26 -0
- package/event-target.js +249 -0
- package/event-target.js.map +1 -0
- package/html.cjs +11 -25
- package/html.cjs.map +1 -1
- package/html.d.ts +3 -3
- package/html.js +11 -25
- package/html.js.map +1 -1
- package/jsx-tag.cjs +2 -2
- package/jsx-tag.cjs.map +1 -1
- package/jsx-tag.js +2 -2
- package/jsx-tag.js.map +1 -1
- package/package.json +18 -2
- package/standalone.cjs +1 -0
- package/standalone.cjs.map +1 -1
- package/standalone.js +1 -1
- package/umd.js +2361 -1569
- package/umd.js.map +1 -1
package/dom.cjs
CHANGED
|
@@ -1,311 +1,442 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var crank = require('./crank.cjs');
|
|
4
|
+
var _css = require('./_css.cjs');
|
|
4
5
|
|
|
5
6
|
const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
6
|
-
const
|
|
7
|
-
|
|
7
|
+
const MATHML_NAMESPACE = "http://www.w3.org/1998/Math/MathML";
|
|
8
|
+
function isWritableProperty(element, name) {
|
|
9
|
+
// walk up the object's prototype chain to find the owner
|
|
10
|
+
let propOwner = element;
|
|
11
|
+
do {
|
|
12
|
+
if (Object.prototype.hasOwnProperty.call(propOwner, name)) {
|
|
13
|
+
break;
|
|
14
|
+
}
|
|
15
|
+
} while ((propOwner = Object.getPrototypeOf(propOwner)));
|
|
16
|
+
if (propOwner === null) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
// get the descriptor for the named property and check whether it implies
|
|
20
|
+
// that the property is writable
|
|
21
|
+
const descriptor = Object.getOwnPropertyDescriptor(propOwner, name);
|
|
22
|
+
if (descriptor != null &&
|
|
23
|
+
(descriptor.writable === true || descriptor.set !== undefined)) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
function emitHydrationWarning(propName, quietProps, expectedValue, actualValue, element, displayName) {
|
|
29
|
+
const checkName = propName;
|
|
30
|
+
const showName = displayName || propName;
|
|
31
|
+
if (!quietProps || !quietProps.has(checkName)) {
|
|
32
|
+
if (expectedValue === null || expectedValue === false) {
|
|
33
|
+
console.warn(`Expected "${showName}" to be missing but found ${String(actualValue)} while hydrating:`, element);
|
|
34
|
+
}
|
|
35
|
+
else if (expectedValue === true || expectedValue === "") {
|
|
36
|
+
console.warn(`Expected "${showName}" to be ${expectedValue === true ? "present" : '""'} but found ${String(actualValue)} while hydrating:`, element);
|
|
37
|
+
}
|
|
38
|
+
else if (typeof window !== "undefined" &&
|
|
39
|
+
window.location &&
|
|
40
|
+
new URL(expectedValue, window.location.origin).href ===
|
|
41
|
+
new URL(actualValue, window.location.origin).href) ;
|
|
42
|
+
else {
|
|
43
|
+
console.warn(`Expected "${showName}" to be "${String(expectedValue)}" but found ${String(actualValue)} while hydrating:`, element);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const adapter = {
|
|
48
|
+
scope({ scope: xmlns, tag, props, }) {
|
|
8
49
|
switch (tag) {
|
|
9
50
|
case crank.Portal:
|
|
51
|
+
// TODO: read the namespace from the portal root element
|
|
10
52
|
xmlns = undefined;
|
|
11
53
|
break;
|
|
12
54
|
case "svg":
|
|
13
55
|
xmlns = SVG_NAMESPACE;
|
|
14
56
|
break;
|
|
57
|
+
case "math":
|
|
58
|
+
xmlns = MATHML_NAMESPACE;
|
|
59
|
+
break;
|
|
15
60
|
}
|
|
16
61
|
return props.xmlns || xmlns;
|
|
17
62
|
},
|
|
18
|
-
create(tag,
|
|
63
|
+
create({ tag, tagName, scope: xmlns, }) {
|
|
19
64
|
if (typeof tag !== "string") {
|
|
20
|
-
throw new Error(`Unknown tag: ${
|
|
65
|
+
throw new Error(`Unknown tag: ${tagName}`);
|
|
21
66
|
}
|
|
22
67
|
else if (tag.toLowerCase() === "svg") {
|
|
23
68
|
xmlns = SVG_NAMESPACE;
|
|
24
69
|
}
|
|
70
|
+
else if (tag.toLowerCase() === "math") {
|
|
71
|
+
xmlns = MATHML_NAMESPACE;
|
|
72
|
+
}
|
|
25
73
|
return xmlns
|
|
26
74
|
? document.createElementNS(xmlns, tag)
|
|
27
75
|
: document.createElement(tag);
|
|
28
76
|
},
|
|
29
|
-
|
|
77
|
+
adopt({ tag, tagName, node, }) {
|
|
30
78
|
if (typeof tag !== "string" && tag !== crank.Portal) {
|
|
31
|
-
throw new Error(`Unknown tag: ${
|
|
79
|
+
throw new Error(`Unknown tag: ${tagName}`);
|
|
32
80
|
}
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
81
|
+
if (node === document.body ||
|
|
82
|
+
node === document.head ||
|
|
83
|
+
node === document.documentElement ||
|
|
84
|
+
node === document) {
|
|
85
|
+
console.warn(`Hydrating ${node.nodeName.toLowerCase()} is discouraged as it is destructive and may remove unknown nodes.`);
|
|
38
86
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
else if (child.nodeType === Node.ELEMENT_NODE) {
|
|
46
|
-
children.push(child);
|
|
47
|
-
}
|
|
87
|
+
if (node == null ||
|
|
88
|
+
(typeof tag === "string" &&
|
|
89
|
+
(node.nodeType !== Node.ELEMENT_NODE ||
|
|
90
|
+
tag.toLowerCase() !== node.tagName.toLowerCase()))) {
|
|
91
|
+
console.warn(`Expected <${tagName}> while hydrating but found: `, node);
|
|
92
|
+
return;
|
|
48
93
|
}
|
|
49
|
-
|
|
50
|
-
return { props, children };
|
|
94
|
+
return Array.from(node.childNodes);
|
|
51
95
|
},
|
|
52
|
-
patch(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
96
|
+
patch({ tagName, node, props, oldProps, scope: xmlns, copyProps, quietProps, isHydrating, }) {
|
|
97
|
+
if (node.nodeType !== Node.ELEMENT_NODE) {
|
|
98
|
+
throw new TypeError(`Cannot patch node: ${String(node)}`);
|
|
99
|
+
}
|
|
100
|
+
else if (props.class && props.className) {
|
|
101
|
+
console.error(`Both "class" and "className" set in props for <${tagName}>. Use one or the other.`);
|
|
102
|
+
}
|
|
103
|
+
const element = node;
|
|
57
104
|
const isSVG = xmlns === SVG_NAMESPACE;
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
105
|
+
const isMathML = xmlns === MATHML_NAMESPACE;
|
|
106
|
+
for (let name in { ...oldProps, ...props }) {
|
|
107
|
+
let value = props[name];
|
|
108
|
+
const oldValue = oldProps ? oldProps[name] : undefined;
|
|
109
|
+
{
|
|
110
|
+
if (copyProps != null && copyProps.has(name)) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
// handle prop:name or attr:name properties
|
|
114
|
+
const colonIndex = name.indexOf(":");
|
|
115
|
+
if (colonIndex !== -1) {
|
|
116
|
+
const [ns, name1] = [
|
|
117
|
+
name.slice(0, colonIndex),
|
|
118
|
+
name.slice(colonIndex + 1),
|
|
119
|
+
];
|
|
120
|
+
switch (ns) {
|
|
121
|
+
case "prop":
|
|
122
|
+
node[name1] = value;
|
|
123
|
+
continue;
|
|
124
|
+
case "attr":
|
|
125
|
+
if (value == null || value === false) {
|
|
126
|
+
if (isHydrating && element.hasAttribute(name1)) {
|
|
127
|
+
emitHydrationWarning(name, quietProps, value, element.getAttribute(name1), element);
|
|
128
|
+
}
|
|
129
|
+
element.removeAttribute(name1);
|
|
130
|
+
}
|
|
131
|
+
else if (value === true) {
|
|
132
|
+
if (isHydrating && !element.hasAttribute(name1)) {
|
|
133
|
+
emitHydrationWarning(name, quietProps, value, null, element);
|
|
134
|
+
}
|
|
135
|
+
element.setAttribute(name1, "");
|
|
136
|
+
}
|
|
137
|
+
else if (typeof value !== "string") {
|
|
138
|
+
value = String(value);
|
|
139
|
+
}
|
|
140
|
+
if (isHydrating && element.getAttribute(name1) !== value) {
|
|
141
|
+
emitHydrationWarning(name, quietProps, value, element.getAttribute(name1), element);
|
|
142
|
+
}
|
|
143
|
+
element.setAttribute(name1, String(value));
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
switch (name) {
|
|
149
|
+
// TODO: fix hydration warnings for the style prop
|
|
150
|
+
case "style": {
|
|
151
|
+
const style = element.style;
|
|
67
152
|
if (value == null || value === false) {
|
|
68
|
-
|
|
153
|
+
if (isHydrating && style.cssText !== "") {
|
|
154
|
+
emitHydrationWarning(name, quietProps, value, style.cssText, element);
|
|
155
|
+
}
|
|
156
|
+
element.removeAttribute("style");
|
|
69
157
|
}
|
|
70
158
|
else if (value === true) {
|
|
71
|
-
|
|
159
|
+
if (isHydrating && style.cssText !== "") {
|
|
160
|
+
emitHydrationWarning(name, quietProps, "", style.cssText, element);
|
|
161
|
+
}
|
|
162
|
+
element.setAttribute("style", "");
|
|
72
163
|
}
|
|
73
164
|
else if (typeof value === "string") {
|
|
74
|
-
|
|
165
|
+
if (style.cssText !== value) {
|
|
166
|
+
// TODO: Fix hydration warnings for styles
|
|
167
|
+
//if (isHydrating) {
|
|
168
|
+
// emitHydrationWarning(
|
|
169
|
+
// name,
|
|
170
|
+
// quietProps,
|
|
171
|
+
// value,
|
|
172
|
+
// style.cssText,
|
|
173
|
+
// element,
|
|
174
|
+
// );
|
|
175
|
+
//}
|
|
176
|
+
style.cssText = value;
|
|
177
|
+
}
|
|
75
178
|
}
|
|
76
179
|
else {
|
|
77
|
-
|
|
180
|
+
if (typeof oldValue === "string") {
|
|
181
|
+
// if the old value was a string, we need to clear the style
|
|
182
|
+
// TODO: only clear the styles enumerated in the old value
|
|
183
|
+
style.cssText = "";
|
|
184
|
+
}
|
|
185
|
+
for (const styleName in { ...oldValue, ...value }) {
|
|
186
|
+
const cssName = _css.camelToKebabCase(styleName);
|
|
187
|
+
const styleValue = value && value[styleName];
|
|
188
|
+
if (styleValue == null) {
|
|
189
|
+
if (isHydrating && style.getPropertyValue(cssName) !== "") {
|
|
190
|
+
emitHydrationWarning(name, quietProps, null, style.getPropertyValue(cssName), element, `style.${styleName}`);
|
|
191
|
+
}
|
|
192
|
+
style.removeProperty(cssName);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
const formattedValue = _css.formatStyleValue(cssName, styleValue);
|
|
196
|
+
if (style.getPropertyValue(cssName) !== formattedValue) {
|
|
197
|
+
// TODO: hydration warnings for style props
|
|
198
|
+
//if (isHydrating) {
|
|
199
|
+
// emitHydrationWarning(
|
|
200
|
+
// name,
|
|
201
|
+
// quietProps,
|
|
202
|
+
// formattedValue,
|
|
203
|
+
// style.getPropertyValue(cssName),
|
|
204
|
+
// element,
|
|
205
|
+
// `style.${styleName}`,
|
|
206
|
+
// );
|
|
207
|
+
//}
|
|
208
|
+
style.setProperty(cssName, formattedValue);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
78
212
|
}
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
switch (name) {
|
|
83
|
-
case "style": {
|
|
84
|
-
const style = node.style;
|
|
85
|
-
if (style == null) {
|
|
86
|
-
node.setAttribute("style", value);
|
|
213
|
+
break;
|
|
87
214
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (style.cssText !== value) {
|
|
96
|
-
style.cssText = value;
|
|
215
|
+
case "class":
|
|
216
|
+
case "className":
|
|
217
|
+
if (value === true) {
|
|
218
|
+
if (isHydrating && element.getAttribute("class") !== "") {
|
|
219
|
+
emitHydrationWarning(name, quietProps, "", element.getAttribute("class"), element);
|
|
220
|
+
}
|
|
221
|
+
element.setAttribute("class", "");
|
|
97
222
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
223
|
+
else if (value == null) {
|
|
224
|
+
if (isHydrating && element.hasAttribute("class")) {
|
|
225
|
+
emitHydrationWarning(name, quietProps, value, element.getAttribute("class"), element);
|
|
226
|
+
}
|
|
227
|
+
element.removeAttribute("class");
|
|
102
228
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (
|
|
106
|
-
|
|
229
|
+
else if (typeof value === "object") {
|
|
230
|
+
// class={{"included-class": true, "excluded-class": false}} syntax
|
|
231
|
+
if (typeof oldValue === "string") {
|
|
232
|
+
// if the old value was a string, we need to clear all classes
|
|
233
|
+
element.setAttribute("class", "");
|
|
234
|
+
}
|
|
235
|
+
let shouldIssueWarning = false;
|
|
236
|
+
const hydratingClasses = isHydrating
|
|
237
|
+
? new Set(Array.from(element.classList))
|
|
238
|
+
: undefined;
|
|
239
|
+
const hydratingClassName = isHydrating
|
|
240
|
+
? element.getAttribute("class")
|
|
241
|
+
: undefined;
|
|
242
|
+
for (const className in { ...oldValue, ...value }) {
|
|
243
|
+
const classValue = value && value[className];
|
|
244
|
+
if (classValue) {
|
|
245
|
+
element.classList.add(className);
|
|
246
|
+
if (hydratingClasses && hydratingClasses.has(className)) {
|
|
247
|
+
hydratingClasses.delete(className);
|
|
248
|
+
}
|
|
249
|
+
else if (isHydrating) {
|
|
250
|
+
shouldIssueWarning = true;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
element.classList.remove(className);
|
|
255
|
+
}
|
|
107
256
|
}
|
|
108
|
-
|
|
109
|
-
|
|
257
|
+
if (shouldIssueWarning ||
|
|
258
|
+
(hydratingClasses && hydratingClasses.size > 0)) {
|
|
259
|
+
emitHydrationWarning(name, quietProps, Object.keys(value)
|
|
260
|
+
.filter((k) => value[k])
|
|
261
|
+
.join(" "), hydratingClassName || "", element);
|
|
110
262
|
}
|
|
111
263
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
else if (value == null) {
|
|
121
|
-
node.removeAttribute("class");
|
|
122
|
-
}
|
|
123
|
-
else if (!isSVG) {
|
|
124
|
-
if (node.className !== value) {
|
|
125
|
-
node["className"] = value;
|
|
264
|
+
else if (!isSVG && !isMathML) {
|
|
265
|
+
if (element.className !== value) {
|
|
266
|
+
if (isHydrating) {
|
|
267
|
+
emitHydrationWarning(name, quietProps, value, element.className, element);
|
|
268
|
+
}
|
|
269
|
+
element.className = value;
|
|
270
|
+
}
|
|
126
271
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
break;
|
|
132
|
-
case "innerHTML":
|
|
133
|
-
if (value !== oldValue) {
|
|
134
|
-
node.innerHTML = value;
|
|
135
|
-
}
|
|
136
|
-
break;
|
|
137
|
-
default: {
|
|
138
|
-
if (name[0] === "o" &&
|
|
139
|
-
name[1] === "n" &&
|
|
140
|
-
name[2] === name[2].toUpperCase() &&
|
|
141
|
-
typeof value === "function") {
|
|
142
|
-
// Support React-style event names (onClick, onChange, etc.)
|
|
143
|
-
name = name.toLowerCase();
|
|
144
|
-
}
|
|
145
|
-
if (name in node &&
|
|
146
|
-
// boolean properties will coerce strings, but sometimes they map to
|
|
147
|
-
// enumerated attributes, where truthy strings ("false", "no") map to
|
|
148
|
-
// falsy properties, so we use attributes in this case.
|
|
149
|
-
!(typeof value === "string" &&
|
|
150
|
-
typeof node[name] === "boolean")) {
|
|
151
|
-
// walk up the object's prototype chain to find the owner of the
|
|
152
|
-
// named property
|
|
153
|
-
let obj = node;
|
|
154
|
-
do {
|
|
155
|
-
if (Object.prototype.hasOwnProperty.call(obj, name)) {
|
|
156
|
-
break;
|
|
272
|
+
else if (element.getAttribute("class") !== value) {
|
|
273
|
+
if (isHydrating) {
|
|
274
|
+
emitHydrationWarning(name, quietProps, value, element.getAttribute("class"), element);
|
|
157
275
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (
|
|
163
|
-
(
|
|
164
|
-
|
|
165
|
-
node[name] = value;
|
|
276
|
+
element.setAttribute("class", value);
|
|
277
|
+
}
|
|
278
|
+
break;
|
|
279
|
+
case "innerHTML":
|
|
280
|
+
if (value !== oldValue) {
|
|
281
|
+
if (isHydrating) {
|
|
282
|
+
emitHydrationWarning(name, quietProps, value, element.innerHTML, element);
|
|
166
283
|
}
|
|
167
|
-
|
|
284
|
+
element.innerHTML = value;
|
|
168
285
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
if (node.getAttribute(name) !== value) {
|
|
180
|
-
node.setAttribute(name, value);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
arrange(tag, node, props, children, _oldProps, oldChildren) {
|
|
186
|
-
if (tag === crank.Portal && (node == null || typeof node.nodeType !== "number")) {
|
|
187
|
-
throw new TypeError(`Portal root is not a node. Received: ${JSON.stringify(node && node.toString())}`);
|
|
188
|
-
}
|
|
189
|
-
if (!("innerHTML" in props) &&
|
|
190
|
-
// We don’t want to update elements without explicit children (<div/>),
|
|
191
|
-
// because these elements sometimes have child nodes added via raw
|
|
192
|
-
// DOM manipulations.
|
|
193
|
-
// However, if an element has previously rendered children, we clear the
|
|
194
|
-
// them because it would be surprising not to clear Crank managed
|
|
195
|
-
// children, even if the new element does not have explicit children.
|
|
196
|
-
("children" in props || (oldChildren && oldChildren.length))) {
|
|
197
|
-
if (children.length === 0) {
|
|
198
|
-
node.textContent = "";
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
let oldChild = node.firstChild;
|
|
202
|
-
let i = 0;
|
|
203
|
-
while (oldChild !== null && i < children.length) {
|
|
204
|
-
const newChild = children[i];
|
|
205
|
-
if (oldChild === newChild) {
|
|
206
|
-
oldChild = oldChild.nextSibling;
|
|
207
|
-
i++;
|
|
286
|
+
break;
|
|
287
|
+
default: {
|
|
288
|
+
if (name[0] === "o" &&
|
|
289
|
+
name[1] === "n" &&
|
|
290
|
+
name[2] === name[2].toUpperCase() &&
|
|
291
|
+
typeof value === "function") {
|
|
292
|
+
// Support React-style event names (onClick, onChange, etc.)
|
|
293
|
+
name = name.toLowerCase();
|
|
208
294
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
295
|
+
// try to set the property directly
|
|
296
|
+
if (name in element &&
|
|
297
|
+
// boolean properties will coerce strings, but sometimes they map to
|
|
298
|
+
// enumerated attributes, where truthy strings ("false", "no") map to
|
|
299
|
+
// falsy properties, so we force using setAttribute.
|
|
300
|
+
!(typeof value === "string" &&
|
|
301
|
+
typeof element[name] === "boolean") &&
|
|
302
|
+
isWritableProperty(element, name)) {
|
|
303
|
+
if (element[name] !== value || oldValue === undefined) {
|
|
304
|
+
if (isHydrating &&
|
|
305
|
+
typeof element[name] === "string" &&
|
|
306
|
+
element[name] !== value) {
|
|
307
|
+
emitHydrationWarning(name, quietProps, value, element[name], element);
|
|
213
308
|
}
|
|
214
|
-
|
|
309
|
+
// if the property is writable, assign it directly
|
|
310
|
+
element[name] = value;
|
|
215
311
|
}
|
|
216
|
-
|
|
217
|
-
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (value === true) {
|
|
315
|
+
value = "";
|
|
316
|
+
}
|
|
317
|
+
else if (value == null || value === false) {
|
|
318
|
+
if (isHydrating && element.hasAttribute(name)) {
|
|
319
|
+
emitHydrationWarning(name, quietProps, value, element.getAttribute(name), element);
|
|
218
320
|
}
|
|
219
|
-
|
|
321
|
+
element.removeAttribute(name);
|
|
322
|
+
continue;
|
|
220
323
|
}
|
|
221
|
-
else if (
|
|
222
|
-
|
|
223
|
-
node.removeChild(oldChild);
|
|
224
|
-
oldChild = nextSibling;
|
|
324
|
+
else if (typeof value !== "string") {
|
|
325
|
+
value = String(value);
|
|
225
326
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
// TODO: This is an optimization but we need to think a little more about other cases like prepending.
|
|
230
|
-
if (oldChild !== children[i]) {
|
|
231
|
-
const nextSibling = oldChild.nextSibling;
|
|
232
|
-
node.removeChild(oldChild);
|
|
233
|
-
oldChild = nextSibling;
|
|
327
|
+
if (element.getAttribute(name) !== value) {
|
|
328
|
+
if (isHydrating) {
|
|
329
|
+
emitHydrationWarning(name, quietProps, value, element.getAttribute(name), element);
|
|
234
330
|
}
|
|
331
|
+
element.setAttribute(name, value);
|
|
235
332
|
}
|
|
236
333
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
arrange({ tag, node, props, children, }) {
|
|
338
|
+
if (tag === crank.Portal && (node == null || typeof node.nodeType !== "number")) {
|
|
339
|
+
throw new TypeError(`<Portal> root is not a node. Received: ${String(node)}`);
|
|
340
|
+
}
|
|
341
|
+
if (!("innerHTML" in props)) {
|
|
342
|
+
let oldChild = node.firstChild;
|
|
343
|
+
for (let i = 0; i < children.length; i++) {
|
|
344
|
+
const newChild = children[i];
|
|
345
|
+
if (oldChild === newChild) {
|
|
346
|
+
// the child is already in the right place, so we can skip it
|
|
347
|
+
oldChild = oldChild.nextSibling;
|
|
242
348
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
349
|
+
else {
|
|
350
|
+
node.insertBefore(newChild, oldChild);
|
|
351
|
+
if (tag !== crank.Portal &&
|
|
352
|
+
oldChild &&
|
|
353
|
+
i + 1 < children.length &&
|
|
354
|
+
oldChild !== children[i + 1]) {
|
|
355
|
+
oldChild = oldChild.nextSibling;
|
|
356
|
+
}
|
|
249
357
|
}
|
|
250
358
|
}
|
|
251
359
|
}
|
|
252
360
|
},
|
|
253
|
-
|
|
254
|
-
if (
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
361
|
+
remove({ node, parentNode, isNested, }) {
|
|
362
|
+
if (!isNested && node.parentNode === parentNode) {
|
|
363
|
+
parentNode.removeChild(node);
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
text({ value, oldNode, hydrationNodes, }) {
|
|
367
|
+
if (hydrationNodes != null) {
|
|
368
|
+
let node = hydrationNodes.shift();
|
|
369
|
+
if (!node || node.nodeType !== Node.TEXT_NODE) {
|
|
370
|
+
console.warn(`Expected "${value}" while hydrating but found:`, node);
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
// value is a text node, check if it matches the expected text
|
|
374
|
+
const textData = node.data;
|
|
375
|
+
if (textData.length > value.length) {
|
|
376
|
+
if (textData.startsWith(value)) {
|
|
377
|
+
// the text node is longer than the expected text, so we
|
|
378
|
+
// reuse the existing text node, but truncate it and unshift the rest
|
|
379
|
+
node.data = value;
|
|
380
|
+
hydrationNodes.unshift(document.createTextNode(textData.slice(value.length)));
|
|
381
|
+
return node;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
else if (textData === value) {
|
|
385
|
+
return node;
|
|
386
|
+
}
|
|
387
|
+
// We log textData and not node because node will be mutated
|
|
388
|
+
console.warn(`Expected "${value}" while hydrating but found:`, textData);
|
|
389
|
+
oldNode = node;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (oldNode != null) {
|
|
393
|
+
if (oldNode.data !== value) {
|
|
394
|
+
oldNode.data = value;
|
|
260
395
|
}
|
|
396
|
+
return oldNode;
|
|
261
397
|
}
|
|
262
|
-
return
|
|
398
|
+
return document.createTextNode(value);
|
|
263
399
|
},
|
|
264
|
-
raw(value, xmlns,
|
|
265
|
-
let
|
|
400
|
+
raw({ value, scope: xmlns, hydrationNodes, }) {
|
|
401
|
+
let nodes;
|
|
266
402
|
if (typeof value === "string") {
|
|
267
403
|
const el = xmlns == null
|
|
268
404
|
? document.createElement("div")
|
|
269
|
-
:
|
|
405
|
+
: xmlns === SVG_NAMESPACE
|
|
406
|
+
? document.createElementNS(xmlns, "svg")
|
|
407
|
+
: document.createElementNS(xmlns, "math");
|
|
270
408
|
el.innerHTML = value;
|
|
271
|
-
|
|
272
|
-
result = undefined;
|
|
273
|
-
}
|
|
274
|
-
else if (el.childNodes.length === 1) {
|
|
275
|
-
result = el.childNodes[0];
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
result = Array.from(el.childNodes);
|
|
279
|
-
}
|
|
409
|
+
nodes = Array.from(el.childNodes);
|
|
280
410
|
}
|
|
281
411
|
else {
|
|
282
|
-
|
|
412
|
+
nodes = value == null ? [] : Array.isArray(value) ? [...value] : [value];
|
|
283
413
|
}
|
|
284
|
-
if (
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
414
|
+
if (hydrationNodes != null) {
|
|
415
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
416
|
+
const node = nodes[i];
|
|
417
|
+
// check if node is equal to the next node in the hydration array
|
|
418
|
+
const hydrationNode = hydrationNodes.shift();
|
|
419
|
+
if (hydrationNode &&
|
|
420
|
+
typeof hydrationNode === "object" &&
|
|
421
|
+
typeof hydrationNode.nodeType === "number" &&
|
|
422
|
+
node.isEqualNode(hydrationNode)) {
|
|
423
|
+
nodes[i] = hydrationNode;
|
|
294
424
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (result.nodeType === Node.ELEMENT_NODE ||
|
|
298
|
-
result.nodeType === Node.TEXT_NODE) {
|
|
299
|
-
hydrationData.children.shift();
|
|
425
|
+
else {
|
|
426
|
+
console.warn(`Expected <Raw value="${String(value)}"> while hydrating but found:`, hydrationNode);
|
|
300
427
|
}
|
|
301
428
|
}
|
|
302
429
|
}
|
|
303
|
-
return
|
|
430
|
+
return nodes.length === 0
|
|
431
|
+
? undefined
|
|
432
|
+
: nodes.length === 1
|
|
433
|
+
? nodes[0]
|
|
434
|
+
: nodes;
|
|
304
435
|
},
|
|
305
436
|
};
|
|
306
437
|
class DOMRenderer extends crank.Renderer {
|
|
307
438
|
constructor() {
|
|
308
|
-
super(
|
|
439
|
+
super(adapter);
|
|
309
440
|
}
|
|
310
441
|
render(children, root, ctx) {
|
|
311
442
|
validateRoot(root);
|
|
@@ -317,14 +448,17 @@ class DOMRenderer extends crank.Renderer {
|
|
|
317
448
|
}
|
|
318
449
|
}
|
|
319
450
|
function validateRoot(root) {
|
|
320
|
-
if (root
|
|
451
|
+
if (root == null ||
|
|
321
452
|
(typeof root === "object" && typeof root.nodeType !== "number")) {
|
|
322
|
-
throw new TypeError(`Render root is not a node. Received: ${
|
|
453
|
+
throw new TypeError(`Render root is not a node. Received: ${String(root)}`);
|
|
454
|
+
}
|
|
455
|
+
else if (root.nodeType !== Node.ELEMENT_NODE) {
|
|
456
|
+
throw new TypeError(`Render root must be an element node. Received: ${String(root)}`);
|
|
323
457
|
}
|
|
324
458
|
}
|
|
325
459
|
const renderer = new DOMRenderer();
|
|
326
460
|
|
|
327
461
|
exports.DOMRenderer = DOMRenderer;
|
|
328
|
-
exports.
|
|
462
|
+
exports.adapter = adapter;
|
|
329
463
|
exports.renderer = renderer;
|
|
330
464
|
//# sourceMappingURL=dom.cjs.map
|