@b9g/crank 0.5.0-debug.0 → 0.5.0
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 +6 -17
- package/crank.cjs +679 -392
- package/crank.cjs.map +1 -1
- package/crank.d.ts +57 -77
- package/crank.js +678 -391
- package/crank.js.map +1 -1
- package/dom.cjs +103 -27
- package/dom.cjs.map +1 -1
- package/dom.d.ts +1 -0
- package/dom.js +103 -25
- package/dom.js.map +1 -1
- package/html.cjs +1 -3
- package/html.cjs.map +1 -1
- package/html.d.ts +2 -1
- package/html.js +1 -1
- package/html.js.map +1 -1
- package/jsx-runtime.cjs +18 -0
- package/jsx-runtime.cjs.map +1 -0
- package/jsx-runtime.d.ts +5 -0
- package/jsx-runtime.js +15 -0
- package/jsx-runtime.js.map +1 -0
- package/{xm.cjs → jsx-tag.cjs} +8 -10
- package/jsx-tag.cjs.map +1 -0
- package/jsx-tag.d.ts +2 -0
- package/{xm.js → jsx-tag.js} +9 -9
- package/jsx-tag.js.map +1 -0
- package/package.json +41 -33
- package/{mod.cjs → standalone.cjs} +3 -5
- package/standalone.cjs.map +1 -0
- package/standalone.d.ts +2 -0
- package/{mod.js → standalone.js} +3 -3
- package/standalone.js.map +1 -0
- package/umd.js +784 -421
- package/umd.js.map +1 -1
- package/mod.cjs.map +0 -1
- package/mod.d.ts +0 -2
- package/mod.js.map +0 -1
- package/xm.cjs.map +0 -1
- package/xm.d.ts +0 -2
- package/xm.js.map +0 -1
package/umd.js
CHANGED
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
: typeof value === "string" ||
|
|
28
28
|
typeof value[Symbol.iterator] !== "function"
|
|
29
29
|
? [value]
|
|
30
|
-
:
|
|
30
|
+
: // TODO: inference broke in TypeScript 3.9.
|
|
31
|
+
[...value];
|
|
31
32
|
}
|
|
32
33
|
function isIteratorLike(value) {
|
|
33
34
|
return value != null && typeof value.next === "function";
|
|
@@ -76,8 +77,7 @@
|
|
|
76
77
|
/**
|
|
77
78
|
* A special tag for injecting raw nodes or strings via a value prop.
|
|
78
79
|
*
|
|
79
|
-
*
|
|
80
|
-
* the string and the result will be set as the element’s value.
|
|
80
|
+
* Renderer.prototype.raw() is called with the value prop.
|
|
81
81
|
*/
|
|
82
82
|
const Raw = Symbol.for("crank.Raw");
|
|
83
83
|
const ElementSymbol = Symbol.for("crank.Element");
|
|
@@ -163,22 +163,6 @@
|
|
|
163
163
|
else if (children.length === 1) {
|
|
164
164
|
props1.children = children[0];
|
|
165
165
|
}
|
|
166
|
-
// string aliases for the special tags
|
|
167
|
-
// TODO: Does this logic belong here, or in the Element constructor
|
|
168
|
-
switch (tag) {
|
|
169
|
-
case "$FRAGMENT":
|
|
170
|
-
tag = Fragment;
|
|
171
|
-
break;
|
|
172
|
-
case "$PORTAL":
|
|
173
|
-
tag = Portal;
|
|
174
|
-
break;
|
|
175
|
-
case "$COPY":
|
|
176
|
-
tag = Copy;
|
|
177
|
-
break;
|
|
178
|
-
case "$RAW":
|
|
179
|
-
tag = Raw;
|
|
180
|
-
break;
|
|
181
|
-
}
|
|
182
166
|
return new Element(tag, props1, key, ref, static_);
|
|
183
167
|
}
|
|
184
168
|
/** Clones a given element, shallowly copying the props object. */
|
|
@@ -257,13 +241,13 @@
|
|
|
257
241
|
class Retainer {
|
|
258
242
|
constructor(el) {
|
|
259
243
|
this.el = el;
|
|
260
|
-
this.value = undefined;
|
|
261
244
|
this.ctx = undefined;
|
|
262
245
|
this.children = undefined;
|
|
263
|
-
this.
|
|
264
|
-
this.
|
|
265
|
-
this.
|
|
266
|
-
this.
|
|
246
|
+
this.value = undefined;
|
|
247
|
+
this.cachedChildValues = undefined;
|
|
248
|
+
this.fallbackValue = undefined;
|
|
249
|
+
this.inflightValue = undefined;
|
|
250
|
+
this.onNextValues = undefined;
|
|
267
251
|
}
|
|
268
252
|
}
|
|
269
253
|
/**
|
|
@@ -272,10 +256,10 @@
|
|
|
272
256
|
* @returns The value of the element.
|
|
273
257
|
*/
|
|
274
258
|
function getValue(ret) {
|
|
275
|
-
if (typeof ret.
|
|
276
|
-
return typeof ret.
|
|
277
|
-
? getValue(ret.
|
|
278
|
-
: ret.
|
|
259
|
+
if (typeof ret.fallbackValue !== "undefined") {
|
|
260
|
+
return typeof ret.fallbackValue === "object"
|
|
261
|
+
? getValue(ret.fallbackValue)
|
|
262
|
+
: ret.fallbackValue;
|
|
279
263
|
}
|
|
280
264
|
else if (ret.el.tag === Portal) {
|
|
281
265
|
return;
|
|
@@ -291,8 +275,8 @@
|
|
|
291
275
|
* @returns A normalized array of nodes and strings.
|
|
292
276
|
*/
|
|
293
277
|
function getChildValues(ret) {
|
|
294
|
-
if (ret.
|
|
295
|
-
return wrap(ret.
|
|
278
|
+
if (ret.cachedChildValues) {
|
|
279
|
+
return wrap(ret.cachedChildValues);
|
|
296
280
|
}
|
|
297
281
|
const values = [];
|
|
298
282
|
const children = wrap(ret.children);
|
|
@@ -305,7 +289,7 @@
|
|
|
305
289
|
const values1 = normalize(values);
|
|
306
290
|
const tag = ret.el.tag;
|
|
307
291
|
if (typeof tag === "function" || (tag !== Fragment && tag !== Raw)) {
|
|
308
|
-
ret.
|
|
292
|
+
ret.cachedChildValues = unwrap(values1);
|
|
309
293
|
}
|
|
310
294
|
return values1;
|
|
311
295
|
}
|
|
@@ -313,16 +297,19 @@
|
|
|
313
297
|
create() {
|
|
314
298
|
throw new Error("Not implemented");
|
|
315
299
|
},
|
|
300
|
+
hydrate() {
|
|
301
|
+
throw new Error("Not implemented");
|
|
302
|
+
},
|
|
316
303
|
scope: IDENTITY,
|
|
317
304
|
read: IDENTITY,
|
|
318
|
-
|
|
319
|
-
|
|
305
|
+
text: IDENTITY,
|
|
306
|
+
raw: IDENTITY,
|
|
320
307
|
patch: NOOP,
|
|
321
308
|
arrange: NOOP,
|
|
322
309
|
dispose: NOOP,
|
|
323
310
|
flush: NOOP,
|
|
324
311
|
};
|
|
325
|
-
const
|
|
312
|
+
const _RendererImpl = Symbol.for("crank.RendererImpl");
|
|
326
313
|
/**
|
|
327
314
|
* An abstract class which is subclassed to render to different target
|
|
328
315
|
* environments. This class is responsible for kicking off the rendering
|
|
@@ -336,7 +323,7 @@
|
|
|
336
323
|
class Renderer {
|
|
337
324
|
constructor(impl) {
|
|
338
325
|
this.cache = new WeakMap();
|
|
339
|
-
this[
|
|
326
|
+
this[_RendererImpl] = {
|
|
340
327
|
...defaultRendererImpl,
|
|
341
328
|
...impl,
|
|
342
329
|
};
|
|
@@ -348,7 +335,7 @@
|
|
|
348
335
|
* used root to delete the previously rendered element tree from the cache.
|
|
349
336
|
* @param root - The node to be rendered into. The renderer will cache
|
|
350
337
|
* element trees per root.
|
|
351
|
-
* @param
|
|
338
|
+
* @param bridge - An optional context that will be the ancestor context of all
|
|
352
339
|
* elements in the tree. Useful for connecting different renderers so that
|
|
353
340
|
* events/provisions properly propagate. The context for a given root must be
|
|
354
341
|
* the same or an error will be thrown.
|
|
@@ -358,7 +345,7 @@
|
|
|
358
345
|
*/
|
|
359
346
|
render(children, root, bridge) {
|
|
360
347
|
let ret;
|
|
361
|
-
const ctx = bridge && bridge[
|
|
348
|
+
const ctx = bridge && bridge[_ContextImpl];
|
|
362
349
|
if (typeof root === "object" && root !== null) {
|
|
363
350
|
ret = this.cache.get(root);
|
|
364
351
|
}
|
|
@@ -381,8 +368,32 @@
|
|
|
381
368
|
this.cache.delete(root);
|
|
382
369
|
}
|
|
383
370
|
}
|
|
384
|
-
const impl = this[
|
|
385
|
-
const childValues = diffChildren(impl, root, ret, ctx, impl.scope(undefined, Portal, ret.el.props), ret, children);
|
|
371
|
+
const impl = this[_RendererImpl];
|
|
372
|
+
const childValues = diffChildren(impl, root, ret, ctx, impl.scope(undefined, Portal, ret.el.props), ret, children, undefined);
|
|
373
|
+
// We return the child values of the portal because portal elements
|
|
374
|
+
// themselves have no readable value.
|
|
375
|
+
if (isPromiseLike(childValues)) {
|
|
376
|
+
return childValues.then((childValues) => commitRootRender(impl, root, ctx, ret, childValues, oldProps));
|
|
377
|
+
}
|
|
378
|
+
return commitRootRender(impl, root, ctx, ret, childValues, oldProps);
|
|
379
|
+
}
|
|
380
|
+
hydrate(children, root, bridge) {
|
|
381
|
+
const impl = this[_RendererImpl];
|
|
382
|
+
const ctx = bridge && bridge[_ContextImpl];
|
|
383
|
+
let ret;
|
|
384
|
+
ret = this.cache.get(root);
|
|
385
|
+
if (ret !== undefined) {
|
|
386
|
+
// If there is a retainer for the root, hydration is not necessary.
|
|
387
|
+
return this.render(children, root, bridge);
|
|
388
|
+
}
|
|
389
|
+
let oldProps;
|
|
390
|
+
ret = new Retainer(createElement(Portal, { children, root }));
|
|
391
|
+
ret.value = root;
|
|
392
|
+
if (typeof root === "object" && root !== null && children != null) {
|
|
393
|
+
this.cache.set(root, ret);
|
|
394
|
+
}
|
|
395
|
+
const hydrationData = impl.hydrate(Portal, root, {});
|
|
396
|
+
const childValues = diffChildren(impl, root, ret, ctx, impl.scope(undefined, Portal, ret.el.props), ret, children, hydrationData);
|
|
386
397
|
// We return the child values of the portal because portal elements
|
|
387
398
|
// themselves have no readable value.
|
|
388
399
|
if (isPromiseLike(childValues)) {
|
|
@@ -394,17 +405,17 @@
|
|
|
394
405
|
/*** PRIVATE RENDERER FUNCTIONS ***/
|
|
395
406
|
function commitRootRender(renderer, root, ctx, ret, childValues, oldProps) {
|
|
396
407
|
// element is a host or portal element
|
|
397
|
-
if (root
|
|
398
|
-
renderer.arrange(Portal, root, ret.el.props, childValues, oldProps, wrap(ret.
|
|
408
|
+
if (root != null) {
|
|
409
|
+
renderer.arrange(Portal, root, ret.el.props, childValues, oldProps, wrap(ret.cachedChildValues));
|
|
399
410
|
flush(renderer, root);
|
|
400
411
|
}
|
|
401
|
-
ret.
|
|
412
|
+
ret.cachedChildValues = unwrap(childValues);
|
|
402
413
|
if (root == null) {
|
|
403
414
|
unmount(renderer, ret, ctx, ret);
|
|
404
415
|
}
|
|
405
|
-
return renderer.read(ret.
|
|
416
|
+
return renderer.read(ret.cachedChildValues);
|
|
406
417
|
}
|
|
407
|
-
function diffChildren(renderer, root, host, ctx, scope, parent, children) {
|
|
418
|
+
function diffChildren(renderer, root, host, ctx, scope, parent, children, hydrationData) {
|
|
408
419
|
const oldRetained = wrap(parent.children);
|
|
409
420
|
const newRetained = [];
|
|
410
421
|
const newChildren = arrayify(children);
|
|
@@ -413,14 +424,15 @@
|
|
|
413
424
|
let childrenByKey;
|
|
414
425
|
let seenKeys;
|
|
415
426
|
let isAsync = false;
|
|
416
|
-
let
|
|
427
|
+
let hydrationBlock;
|
|
428
|
+
let oi = 0;
|
|
429
|
+
let oldLength = oldRetained.length;
|
|
417
430
|
for (let ni = 0, newLength = newChildren.length; ni < newLength; ni++) {
|
|
418
|
-
//
|
|
419
|
-
// deoptimizations.
|
|
431
|
+
// length checks to prevent index out of bounds deoptimizations.
|
|
420
432
|
let ret = oi >= oldLength ? undefined : oldRetained[oi];
|
|
421
433
|
let child = narrow(newChildren[ni]);
|
|
422
434
|
{
|
|
423
|
-
//
|
|
435
|
+
// aligning new children with old retainers
|
|
424
436
|
let oldKey = typeof ret === "object" ? ret.el.key : undefined;
|
|
425
437
|
let newKey = typeof child === "object" ? child.key : undefined;
|
|
426
438
|
if (newKey !== undefined && seenKeys && seenKeys.has(newKey)) {
|
|
@@ -455,18 +467,19 @@
|
|
|
455
467
|
// Updating
|
|
456
468
|
let value;
|
|
457
469
|
if (typeof child === "object") {
|
|
458
|
-
if (
|
|
459
|
-
ret.el = child;
|
|
460
|
-
value = getInflightValue(ret);
|
|
461
|
-
}
|
|
462
|
-
else if (child.tag === Copy) {
|
|
470
|
+
if (child.tag === Copy) {
|
|
463
471
|
value = getInflightValue(ret);
|
|
464
472
|
}
|
|
465
473
|
else {
|
|
466
474
|
let oldProps;
|
|
475
|
+
let static_ = false;
|
|
467
476
|
if (typeof ret === "object" && ret.el.tag === child.tag) {
|
|
468
477
|
oldProps = ret.el.props;
|
|
469
478
|
ret.el = child;
|
|
479
|
+
if (child.static_) {
|
|
480
|
+
value = getInflightValue(ret);
|
|
481
|
+
static_ = true;
|
|
482
|
+
}
|
|
470
483
|
}
|
|
471
484
|
else {
|
|
472
485
|
if (typeof ret === "object") {
|
|
@@ -474,19 +487,28 @@
|
|
|
474
487
|
}
|
|
475
488
|
const fallback = ret;
|
|
476
489
|
ret = new Retainer(child);
|
|
477
|
-
ret.
|
|
490
|
+
ret.fallbackValue = fallback;
|
|
478
491
|
}
|
|
479
|
-
if (
|
|
480
|
-
|
|
492
|
+
if (static_) ;
|
|
493
|
+
else if (child.tag === Raw) {
|
|
494
|
+
value = hydrationBlock
|
|
495
|
+
? hydrationBlock.then(() => updateRaw(renderer, ret, scope, oldProps, hydrationData))
|
|
496
|
+
: updateRaw(renderer, ret, scope, oldProps, hydrationData);
|
|
481
497
|
}
|
|
482
498
|
else if (child.tag === Fragment) {
|
|
483
|
-
value =
|
|
499
|
+
value = hydrationBlock
|
|
500
|
+
? hydrationBlock.then(() => updateFragment(renderer, root, host, ctx, scope, ret, hydrationData))
|
|
501
|
+
: updateFragment(renderer, root, host, ctx, scope, ret, hydrationData);
|
|
484
502
|
}
|
|
485
503
|
else if (typeof child.tag === "function") {
|
|
486
|
-
value =
|
|
504
|
+
value = hydrationBlock
|
|
505
|
+
? hydrationBlock.then(() => updateComponent(renderer, root, host, ctx, scope, ret, oldProps, hydrationData))
|
|
506
|
+
: updateComponent(renderer, root, host, ctx, scope, ret, oldProps, hydrationData);
|
|
487
507
|
}
|
|
488
508
|
else {
|
|
489
|
-
value =
|
|
509
|
+
value = hydrationBlock
|
|
510
|
+
? hydrationBlock.then(() => updateHost(renderer, root, ctx, scope, ret, oldProps, hydrationData))
|
|
511
|
+
: updateHost(renderer, root, ctx, scope, ret, oldProps, hydrationData);
|
|
490
512
|
}
|
|
491
513
|
}
|
|
492
514
|
const ref = child.ref;
|
|
@@ -498,9 +520,14 @@
|
|
|
498
520
|
return value;
|
|
499
521
|
});
|
|
500
522
|
}
|
|
523
|
+
if (hydrationData !== undefined) {
|
|
524
|
+
hydrationBlock = value;
|
|
525
|
+
}
|
|
501
526
|
}
|
|
502
|
-
else
|
|
503
|
-
ref
|
|
527
|
+
else {
|
|
528
|
+
if (typeof ref === "function") {
|
|
529
|
+
ref(renderer.read(value));
|
|
530
|
+
}
|
|
504
531
|
}
|
|
505
532
|
}
|
|
506
533
|
else {
|
|
@@ -509,7 +536,7 @@
|
|
|
509
536
|
(graveyard = graveyard || []).push(ret);
|
|
510
537
|
}
|
|
511
538
|
if (typeof child === "string") {
|
|
512
|
-
value = ret = renderer.
|
|
539
|
+
value = ret = renderer.text(child, scope, hydrationData);
|
|
513
540
|
}
|
|
514
541
|
else {
|
|
515
542
|
ret = undefined;
|
|
@@ -542,27 +569,29 @@
|
|
|
542
569
|
childValues1,
|
|
543
570
|
new Promise((resolve) => (onChildValues = resolve)),
|
|
544
571
|
]);
|
|
545
|
-
if (parent.
|
|
546
|
-
parent.
|
|
572
|
+
if (parent.onNextValues) {
|
|
573
|
+
parent.onNextValues(childValues1);
|
|
547
574
|
}
|
|
548
|
-
parent.
|
|
575
|
+
parent.onNextValues = onChildValues;
|
|
549
576
|
return childValues1.then((childValues) => {
|
|
550
|
-
parent.
|
|
577
|
+
parent.inflightValue = parent.fallbackValue = undefined;
|
|
551
578
|
return normalize(childValues);
|
|
552
579
|
});
|
|
553
580
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
581
|
+
else {
|
|
582
|
+
if (graveyard) {
|
|
583
|
+
for (let i = 0; i < graveyard.length; i++) {
|
|
584
|
+
unmount(renderer, host, ctx, graveyard[i]);
|
|
585
|
+
}
|
|
557
586
|
}
|
|
587
|
+
if (parent.onNextValues) {
|
|
588
|
+
parent.onNextValues(values);
|
|
589
|
+
parent.onNextValues = undefined;
|
|
590
|
+
}
|
|
591
|
+
parent.inflightValue = parent.fallbackValue = undefined;
|
|
592
|
+
// We can assert there are no promises in the array because isAsync is false
|
|
593
|
+
return normalize(values);
|
|
558
594
|
}
|
|
559
|
-
if (parent.onCommit) {
|
|
560
|
-
parent.onCommit(values);
|
|
561
|
-
parent.onCommit = undefined;
|
|
562
|
-
}
|
|
563
|
-
parent.inflight = parent.fallback = undefined;
|
|
564
|
-
// We can assert there are no promises in the array because isAsync is false
|
|
565
|
-
return normalize(values);
|
|
566
595
|
}
|
|
567
596
|
function createChildrenByKey(children, offset) {
|
|
568
597
|
const childrenByKey = new Map();
|
|
@@ -582,58 +611,69 @@
|
|
|
582
611
|
if (ctx && ctx.f & IsUpdating && ctx.inflightValue) {
|
|
583
612
|
return ctx.inflightValue;
|
|
584
613
|
}
|
|
585
|
-
else if (child.
|
|
586
|
-
return child.
|
|
614
|
+
else if (child.inflightValue) {
|
|
615
|
+
return child.inflightValue;
|
|
587
616
|
}
|
|
588
617
|
return getValue(child);
|
|
589
618
|
}
|
|
590
|
-
function updateRaw(renderer, ret, scope, oldProps) {
|
|
619
|
+
function updateRaw(renderer, ret, scope, oldProps, hydrationData) {
|
|
591
620
|
const props = ret.el.props;
|
|
592
|
-
if (
|
|
593
|
-
|
|
594
|
-
ret.value = renderer.parse(props.value, scope);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
else {
|
|
598
|
-
ret.value = props.value;
|
|
621
|
+
if (!oldProps || oldProps.value !== props.value) {
|
|
622
|
+
ret.value = renderer.raw(props.value, scope, hydrationData);
|
|
599
623
|
}
|
|
600
624
|
return ret.value;
|
|
601
625
|
}
|
|
602
|
-
function updateFragment(renderer, root, host, ctx, scope, ret) {
|
|
603
|
-
const childValues = diffChildren(renderer, root, host, ctx, scope, ret, ret.el.props.children);
|
|
626
|
+
function updateFragment(renderer, root, host, ctx, scope, ret, hydrationData) {
|
|
627
|
+
const childValues = diffChildren(renderer, root, host, ctx, scope, ret, ret.el.props.children, hydrationData);
|
|
604
628
|
if (isPromiseLike(childValues)) {
|
|
605
|
-
ret.
|
|
606
|
-
return ret.
|
|
629
|
+
ret.inflightValue = childValues.then((childValues) => unwrap(childValues));
|
|
630
|
+
return ret.inflightValue;
|
|
607
631
|
}
|
|
608
632
|
return unwrap(childValues);
|
|
609
633
|
}
|
|
610
|
-
function updateHost(renderer, root, ctx, scope, ret, oldProps) {
|
|
634
|
+
function updateHost(renderer, root, ctx, scope, ret, oldProps, hydrationData) {
|
|
611
635
|
const el = ret.el;
|
|
612
636
|
const tag = el.tag;
|
|
637
|
+
let hydrationValue;
|
|
613
638
|
if (el.tag === Portal) {
|
|
614
639
|
root = ret.value = el.props.root;
|
|
615
640
|
}
|
|
616
|
-
else
|
|
617
|
-
|
|
618
|
-
|
|
641
|
+
else {
|
|
642
|
+
if (hydrationData !== undefined) {
|
|
643
|
+
const value = hydrationData.children.shift();
|
|
644
|
+
hydrationValue = value;
|
|
645
|
+
}
|
|
619
646
|
}
|
|
620
647
|
scope = renderer.scope(scope, tag, el.props);
|
|
621
|
-
|
|
648
|
+
let childHydrationData;
|
|
649
|
+
if (hydrationValue != null && typeof hydrationValue !== "string") {
|
|
650
|
+
childHydrationData = renderer.hydrate(tag, hydrationValue, el.props);
|
|
651
|
+
if (childHydrationData === undefined) {
|
|
652
|
+
hydrationValue = undefined;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
const childValues = diffChildren(renderer, root, ret, ctx, scope, ret, ret.el.props.children, childHydrationData);
|
|
622
656
|
if (isPromiseLike(childValues)) {
|
|
623
|
-
ret.
|
|
624
|
-
return ret.
|
|
657
|
+
ret.inflightValue = childValues.then((childValues) => commitHost(renderer, scope, ret, childValues, oldProps, hydrationValue));
|
|
658
|
+
return ret.inflightValue;
|
|
625
659
|
}
|
|
626
|
-
return commitHost(renderer, scope, ret, childValues, oldProps);
|
|
660
|
+
return commitHost(renderer, scope, ret, childValues, oldProps, hydrationValue);
|
|
627
661
|
}
|
|
628
|
-
function commitHost(renderer, scope, ret, childValues, oldProps) {
|
|
662
|
+
function commitHost(renderer, scope, ret, childValues, oldProps, hydrationValue) {
|
|
629
663
|
const tag = ret.el.tag;
|
|
630
|
-
|
|
664
|
+
let value = hydrationValue || ret.value;
|
|
631
665
|
let props = ret.el.props;
|
|
632
666
|
let copied;
|
|
633
667
|
if (tag !== Portal) {
|
|
668
|
+
if (value == null) {
|
|
669
|
+
// This assumes that renderer.create does not return nullish values.
|
|
670
|
+
value = ret.value = renderer.create(tag, props, scope);
|
|
671
|
+
}
|
|
634
672
|
for (const propName in { ...oldProps, ...props }) {
|
|
635
673
|
const propValue = props[propName];
|
|
636
674
|
if (propValue === Copy) {
|
|
675
|
+
// TODO: The Copy tag doubles as a way to skip the patching of a prop.
|
|
676
|
+
// Not sure about this feature. Should probably be removed.
|
|
637
677
|
(copied = copied || new Set()).add(propName);
|
|
638
678
|
}
|
|
639
679
|
else if (propName !== "children") {
|
|
@@ -648,8 +688,8 @@
|
|
|
648
688
|
}
|
|
649
689
|
ret.el = new Element(tag, props, ret.el.key, ret.el.ref);
|
|
650
690
|
}
|
|
651
|
-
renderer.arrange(tag, value, props, childValues, oldProps, wrap(ret.
|
|
652
|
-
ret.
|
|
691
|
+
renderer.arrange(tag, value, props, childValues, oldProps, wrap(ret.cachedChildValues));
|
|
692
|
+
ret.cachedChildValues = unwrap(childValues);
|
|
653
693
|
if (tag === Portal) {
|
|
654
694
|
flush(renderer, ret.value);
|
|
655
695
|
return;
|
|
@@ -696,7 +736,7 @@
|
|
|
696
736
|
}
|
|
697
737
|
else if (ret.el.tag === Portal) {
|
|
698
738
|
host = ret;
|
|
699
|
-
renderer.arrange(Portal, host.value, host.el.props, [], host.el.props, wrap(host.
|
|
739
|
+
renderer.arrange(Portal, host.value, host.el.props, [], host.el.props, wrap(host.cachedChildValues));
|
|
700
740
|
flush(renderer, host.value);
|
|
701
741
|
}
|
|
702
742
|
else if (ret.el.tag !== Fragment) {
|
|
@@ -720,38 +760,43 @@
|
|
|
720
760
|
}
|
|
721
761
|
/*** CONTEXT FLAGS ***/
|
|
722
762
|
/**
|
|
723
|
-
* A flag which is
|
|
724
|
-
*
|
|
725
|
-
*
|
|
763
|
+
* A flag which is true when the component is initialized or updated by an
|
|
764
|
+
* ancestor component or the root render call.
|
|
765
|
+
*
|
|
766
|
+
* Used to determine things like whether the nearest host ancestor needs to be
|
|
767
|
+
* rearranged.
|
|
726
768
|
*/
|
|
727
769
|
const IsUpdating = 1 << 0;
|
|
728
770
|
/**
|
|
729
|
-
* A flag which is
|
|
730
|
-
*
|
|
731
|
-
*
|
|
732
|
-
* overflow or a generator error.
|
|
771
|
+
* A flag which is true when the component is synchronously executing.
|
|
772
|
+
*
|
|
773
|
+
* Used to guard against components triggering stack overflow or generator error.
|
|
733
774
|
*/
|
|
734
|
-
const
|
|
775
|
+
const IsSyncExecuting = 1 << 1;
|
|
735
776
|
/**
|
|
736
|
-
* A flag
|
|
737
|
-
* iterators without a yield.
|
|
777
|
+
* A flag which is true when the component is in a for...of loop.
|
|
738
778
|
*/
|
|
739
|
-
const
|
|
779
|
+
const IsInForOfLoop = 1 << 2;
|
|
740
780
|
/**
|
|
741
|
-
* A flag
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
781
|
+
* A flag which is true when the component is in a for await...of loop.
|
|
782
|
+
*/
|
|
783
|
+
const IsInForAwaitOfLoop = 1 << 3;
|
|
784
|
+
/**
|
|
785
|
+
* A flag which is true when the component starts the render loop but has not
|
|
786
|
+
* yielded yet.
|
|
787
|
+
*
|
|
788
|
+
* Used to make sure that components yield at least once per loop.
|
|
745
789
|
*/
|
|
746
|
-
const
|
|
790
|
+
const NeedsToYield = 1 << 4;
|
|
747
791
|
/**
|
|
748
|
-
* A flag
|
|
749
|
-
*
|
|
750
|
-
*
|
|
792
|
+
* A flag used by async generator components in conjunction with the
|
|
793
|
+
* onAvailable callback to mark whether new props can be pulled via the context
|
|
794
|
+
* async iterator. See the Symbol.asyncIterator method and the
|
|
795
|
+
* resumeCtxIterator function.
|
|
751
796
|
*/
|
|
752
|
-
const
|
|
797
|
+
const PropsAvailable = 1 << 5;
|
|
753
798
|
/**
|
|
754
|
-
* A flag which is set when a
|
|
799
|
+
* A flag which is set when a component errors.
|
|
755
800
|
*
|
|
756
801
|
* NOTE: This is mainly used to prevent some false positives in component
|
|
757
802
|
* yields or returns undefined warnings. The reason we’re using this versus
|
|
@@ -759,28 +804,28 @@
|
|
|
759
804
|
* sync generator child) where synchronous code causes a stack overflow error
|
|
760
805
|
* in a non-deterministic way. Deeply disturbing stuff.
|
|
761
806
|
*/
|
|
762
|
-
const IsErrored = 1 <<
|
|
807
|
+
const IsErrored = 1 << 6;
|
|
763
808
|
/**
|
|
764
809
|
* A flag which is set when the component is unmounted. Unmounted components
|
|
765
810
|
* are no longer in the element tree and cannot refresh or rerender.
|
|
766
811
|
*/
|
|
767
|
-
const IsUnmounted = 1 <<
|
|
812
|
+
const IsUnmounted = 1 << 7;
|
|
768
813
|
/**
|
|
769
814
|
* A flag which indicates that the component is a sync generator component.
|
|
770
815
|
*/
|
|
771
|
-
const IsSyncGen = 1 <<
|
|
816
|
+
const IsSyncGen = 1 << 8;
|
|
772
817
|
/**
|
|
773
818
|
* A flag which indicates that the component is an async generator component.
|
|
774
819
|
*/
|
|
775
|
-
const IsAsyncGen = 1 <<
|
|
820
|
+
const IsAsyncGen = 1 << 9;
|
|
776
821
|
/**
|
|
777
822
|
* A flag which is set while schedule callbacks are called.
|
|
778
823
|
*/
|
|
779
|
-
const IsScheduling = 1 <<
|
|
824
|
+
const IsScheduling = 1 << 10;
|
|
780
825
|
/**
|
|
781
826
|
* A flag which is set when a schedule callback calls refresh.
|
|
782
827
|
*/
|
|
783
|
-
const IsSchedulingRefresh = 1 <<
|
|
828
|
+
const IsSchedulingRefresh = 1 << 11;
|
|
784
829
|
const provisionMaps = new WeakMap();
|
|
785
830
|
const scheduleMap = new WeakMap();
|
|
786
831
|
const cleanupMap = new WeakMap();
|
|
@@ -788,12 +833,12 @@
|
|
|
788
833
|
const flushMaps = new WeakMap();
|
|
789
834
|
/**
|
|
790
835
|
* @internal
|
|
791
|
-
* The internal class which holds
|
|
836
|
+
* The internal class which holds context data.
|
|
792
837
|
*/
|
|
793
838
|
class ContextImpl {
|
|
794
839
|
constructor(renderer, root, host, parent, scope, ret) {
|
|
795
840
|
this.f = 0;
|
|
796
|
-
this.
|
|
841
|
+
this.owner = new Context(this);
|
|
797
842
|
this.renderer = renderer;
|
|
798
843
|
this.root = root;
|
|
799
844
|
this.host = host;
|
|
@@ -805,10 +850,11 @@
|
|
|
805
850
|
this.inflightValue = undefined;
|
|
806
851
|
this.enqueuedBlock = undefined;
|
|
807
852
|
this.enqueuedValue = undefined;
|
|
808
|
-
this.
|
|
853
|
+
this.onProps = undefined;
|
|
854
|
+
this.onPropsRequested = undefined;
|
|
809
855
|
}
|
|
810
856
|
}
|
|
811
|
-
const
|
|
857
|
+
const _ContextImpl = Symbol.for("crank.ContextImpl");
|
|
812
858
|
/**
|
|
813
859
|
* A class which is instantiated and passed to every component as its this
|
|
814
860
|
* value. Contexts form a tree just like elements and all components in the
|
|
@@ -822,8 +868,10 @@
|
|
|
822
868
|
* schedule and cleanup callbacks.
|
|
823
869
|
*/
|
|
824
870
|
class Context {
|
|
871
|
+
// TODO: If we could make the constructor function take a nicer value, it
|
|
872
|
+
// would be useful for testing purposes.
|
|
825
873
|
constructor(impl) {
|
|
826
|
-
this[
|
|
874
|
+
this[_ContextImpl] = impl;
|
|
827
875
|
}
|
|
828
876
|
/**
|
|
829
877
|
* The current props of the associated element.
|
|
@@ -833,7 +881,7 @@
|
|
|
833
881
|
* plugins or utilities which wrap contexts.
|
|
834
882
|
*/
|
|
835
883
|
get props() {
|
|
836
|
-
return this[
|
|
884
|
+
return this[_ContextImpl].ret.el.props;
|
|
837
885
|
}
|
|
838
886
|
// TODO: Should we rename this???
|
|
839
887
|
/**
|
|
@@ -844,45 +892,64 @@
|
|
|
844
892
|
* mainly for plugins or utilities which wrap contexts.
|
|
845
893
|
*/
|
|
846
894
|
get value() {
|
|
847
|
-
return this[
|
|
895
|
+
return this[_ContextImpl].renderer.read(getValue(this[_ContextImpl].ret));
|
|
848
896
|
}
|
|
849
897
|
*[Symbol.iterator]() {
|
|
850
|
-
const
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
898
|
+
const ctx = this[_ContextImpl];
|
|
899
|
+
try {
|
|
900
|
+
ctx.f |= IsInForOfLoop;
|
|
901
|
+
while (!(ctx.f & IsUnmounted)) {
|
|
902
|
+
if (ctx.f & NeedsToYield) {
|
|
903
|
+
throw new Error("Context iterated twice without a yield");
|
|
904
|
+
}
|
|
905
|
+
else {
|
|
906
|
+
ctx.f |= NeedsToYield;
|
|
907
|
+
}
|
|
908
|
+
yield ctx.ret.el.props;
|
|
857
909
|
}
|
|
858
|
-
|
|
859
|
-
|
|
910
|
+
}
|
|
911
|
+
finally {
|
|
912
|
+
ctx.f &= ~IsInForOfLoop;
|
|
860
913
|
}
|
|
861
914
|
}
|
|
862
915
|
async *[Symbol.asyncIterator]() {
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
916
|
+
const ctx = this[_ContextImpl];
|
|
917
|
+
if (ctx.f & IsSyncGen) {
|
|
918
|
+
throw new Error("Use for...of in sync generator components");
|
|
919
|
+
}
|
|
920
|
+
try {
|
|
921
|
+
ctx.f |= IsInForAwaitOfLoop;
|
|
922
|
+
while (!(ctx.f & IsUnmounted)) {
|
|
923
|
+
if (ctx.f & NeedsToYield) {
|
|
924
|
+
throw new Error("Context iterated twice without a yield");
|
|
925
|
+
}
|
|
926
|
+
else {
|
|
927
|
+
ctx.f |= NeedsToYield;
|
|
928
|
+
}
|
|
929
|
+
if (ctx.f & PropsAvailable) {
|
|
930
|
+
ctx.f &= ~PropsAvailable;
|
|
931
|
+
yield ctx.ret.el.props;
|
|
932
|
+
}
|
|
933
|
+
else {
|
|
934
|
+
const props = await new Promise((resolve) => (ctx.onProps = resolve));
|
|
935
|
+
if (ctx.f & IsUnmounted) {
|
|
936
|
+
break;
|
|
937
|
+
}
|
|
938
|
+
yield props;
|
|
939
|
+
}
|
|
940
|
+
if (ctx.onPropsRequested) {
|
|
941
|
+
ctx.onPropsRequested();
|
|
942
|
+
ctx.onPropsRequested = undefined;
|
|
882
943
|
}
|
|
883
944
|
}
|
|
884
|
-
|
|
885
|
-
|
|
945
|
+
}
|
|
946
|
+
finally {
|
|
947
|
+
ctx.f &= ~IsInForAwaitOfLoop;
|
|
948
|
+
if (ctx.onPropsRequested) {
|
|
949
|
+
ctx.onPropsRequested();
|
|
950
|
+
ctx.onPropsRequested = undefined;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
886
953
|
}
|
|
887
954
|
/**
|
|
888
955
|
* Re-executes a component.
|
|
@@ -897,32 +964,31 @@
|
|
|
897
964
|
* async iterator to suspend.
|
|
898
965
|
*/
|
|
899
966
|
refresh() {
|
|
900
|
-
const
|
|
901
|
-
if (
|
|
967
|
+
const ctx = this[_ContextImpl];
|
|
968
|
+
if (ctx.f & IsUnmounted) {
|
|
902
969
|
console.error("Component is unmounted");
|
|
903
|
-
return
|
|
970
|
+
return ctx.renderer.read(undefined);
|
|
904
971
|
}
|
|
905
|
-
else if (
|
|
972
|
+
else if (ctx.f & IsSyncExecuting) {
|
|
906
973
|
console.error("Component is already executing");
|
|
907
974
|
return this.value;
|
|
908
975
|
}
|
|
909
|
-
|
|
910
|
-
const value = runComponent(impl);
|
|
976
|
+
const value = enqueueComponentRun(ctx);
|
|
911
977
|
if (isPromiseLike(value)) {
|
|
912
|
-
return value.then((value) =>
|
|
978
|
+
return value.then((value) => ctx.renderer.read(value));
|
|
913
979
|
}
|
|
914
|
-
return
|
|
980
|
+
return ctx.renderer.read(value);
|
|
915
981
|
}
|
|
916
982
|
/**
|
|
917
983
|
* Registers a callback which fires when the component commits. Will only
|
|
918
984
|
* fire once per callback and update.
|
|
919
985
|
*/
|
|
920
986
|
schedule(callback) {
|
|
921
|
-
const
|
|
922
|
-
let callbacks = scheduleMap.get(
|
|
987
|
+
const ctx = this[_ContextImpl];
|
|
988
|
+
let callbacks = scheduleMap.get(ctx);
|
|
923
989
|
if (!callbacks) {
|
|
924
990
|
callbacks = new Set();
|
|
925
|
-
scheduleMap.set(
|
|
991
|
+
scheduleMap.set(ctx, callbacks);
|
|
926
992
|
}
|
|
927
993
|
callbacks.add(callback);
|
|
928
994
|
}
|
|
@@ -931,19 +997,19 @@
|
|
|
931
997
|
* rendered into the root. Will only fire once per callback and render.
|
|
932
998
|
*/
|
|
933
999
|
flush(callback) {
|
|
934
|
-
const
|
|
935
|
-
if (typeof
|
|
1000
|
+
const ctx = this[_ContextImpl];
|
|
1001
|
+
if (typeof ctx.root !== "object" || ctx.root === null) {
|
|
936
1002
|
return;
|
|
937
1003
|
}
|
|
938
|
-
let flushMap = flushMaps.get(
|
|
1004
|
+
let flushMap = flushMaps.get(ctx.root);
|
|
939
1005
|
if (!flushMap) {
|
|
940
1006
|
flushMap = new Map();
|
|
941
|
-
flushMaps.set(
|
|
1007
|
+
flushMaps.set(ctx.root, flushMap);
|
|
942
1008
|
}
|
|
943
|
-
let callbacks = flushMap.get(
|
|
1009
|
+
let callbacks = flushMap.get(ctx);
|
|
944
1010
|
if (!callbacks) {
|
|
945
1011
|
callbacks = new Set();
|
|
946
|
-
flushMap.set(
|
|
1012
|
+
flushMap.set(ctx, callbacks);
|
|
947
1013
|
}
|
|
948
1014
|
callbacks.add(callback);
|
|
949
1015
|
}
|
|
@@ -952,45 +1018,45 @@
|
|
|
952
1018
|
* fire once per callback.
|
|
953
1019
|
*/
|
|
954
1020
|
cleanup(callback) {
|
|
955
|
-
const
|
|
956
|
-
let callbacks = cleanupMap.get(
|
|
1021
|
+
const ctx = this[_ContextImpl];
|
|
1022
|
+
let callbacks = cleanupMap.get(ctx);
|
|
957
1023
|
if (!callbacks) {
|
|
958
1024
|
callbacks = new Set();
|
|
959
|
-
cleanupMap.set(
|
|
1025
|
+
cleanupMap.set(ctx, callbacks);
|
|
960
1026
|
}
|
|
961
1027
|
callbacks.add(callback);
|
|
962
1028
|
}
|
|
963
1029
|
consume(key) {
|
|
964
|
-
for (let
|
|
965
|
-
const provisions = provisionMaps.get(
|
|
1030
|
+
for (let ctx = this[_ContextImpl].parent; ctx !== undefined; ctx = ctx.parent) {
|
|
1031
|
+
const provisions = provisionMaps.get(ctx);
|
|
966
1032
|
if (provisions && provisions.has(key)) {
|
|
967
1033
|
return provisions.get(key);
|
|
968
1034
|
}
|
|
969
1035
|
}
|
|
970
1036
|
}
|
|
971
1037
|
provide(key, value) {
|
|
972
|
-
const
|
|
973
|
-
let provisions = provisionMaps.get(
|
|
1038
|
+
const ctx = this[_ContextImpl];
|
|
1039
|
+
let provisions = provisionMaps.get(ctx);
|
|
974
1040
|
if (!provisions) {
|
|
975
1041
|
provisions = new Map();
|
|
976
|
-
provisionMaps.set(
|
|
1042
|
+
provisionMaps.set(ctx, provisions);
|
|
977
1043
|
}
|
|
978
1044
|
provisions.set(key, value);
|
|
979
1045
|
}
|
|
980
1046
|
addEventListener(type, listener, options) {
|
|
981
|
-
const
|
|
1047
|
+
const ctx = this[_ContextImpl];
|
|
982
1048
|
let listeners;
|
|
983
1049
|
if (!isListenerOrListenerObject(listener)) {
|
|
984
1050
|
return;
|
|
985
1051
|
}
|
|
986
1052
|
else {
|
|
987
|
-
const listeners1 = listenersMap.get(
|
|
1053
|
+
const listeners1 = listenersMap.get(ctx);
|
|
988
1054
|
if (listeners1) {
|
|
989
1055
|
listeners = listeners1;
|
|
990
1056
|
}
|
|
991
1057
|
else {
|
|
992
1058
|
listeners = [];
|
|
993
|
-
listenersMap.set(
|
|
1059
|
+
listenersMap.set(ctx, listeners);
|
|
994
1060
|
}
|
|
995
1061
|
}
|
|
996
1062
|
options = normalizeListenerOptions(options);
|
|
@@ -1001,7 +1067,7 @@
|
|
|
1001
1067
|
else {
|
|
1002
1068
|
callback = listener;
|
|
1003
1069
|
}
|
|
1004
|
-
const record = { type,
|
|
1070
|
+
const record = { type, listener, callback, options };
|
|
1005
1071
|
if (options.once) {
|
|
1006
1072
|
record.callback = function () {
|
|
1007
1073
|
const i = listeners.indexOf(record);
|
|
@@ -1018,15 +1084,15 @@
|
|
|
1018
1084
|
}
|
|
1019
1085
|
listeners.push(record);
|
|
1020
1086
|
// TODO: is it possible to separate out the EventTarget delegation logic
|
|
1021
|
-
for (const value of getChildValues(
|
|
1087
|
+
for (const value of getChildValues(ctx.ret)) {
|
|
1022
1088
|
if (isEventTarget(value)) {
|
|
1023
1089
|
value.addEventListener(record.type, record.callback, record.options);
|
|
1024
1090
|
}
|
|
1025
1091
|
}
|
|
1026
1092
|
}
|
|
1027
1093
|
removeEventListener(type, listener, options) {
|
|
1028
|
-
const
|
|
1029
|
-
const listeners = listenersMap.get(
|
|
1094
|
+
const ctx = this[_ContextImpl];
|
|
1095
|
+
const listeners = listenersMap.get(ctx);
|
|
1030
1096
|
if (listeners == null || !isListenerOrListenerObject(listener)) {
|
|
1031
1097
|
return;
|
|
1032
1098
|
}
|
|
@@ -1040,16 +1106,16 @@
|
|
|
1040
1106
|
const record = listeners[i];
|
|
1041
1107
|
listeners.splice(i, 1);
|
|
1042
1108
|
// TODO: is it possible to separate out the EventTarget delegation logic
|
|
1043
|
-
for (const value of getChildValues(
|
|
1109
|
+
for (const value of getChildValues(ctx.ret)) {
|
|
1044
1110
|
if (isEventTarget(value)) {
|
|
1045
1111
|
value.removeEventListener(record.type, record.callback, record.options);
|
|
1046
1112
|
}
|
|
1047
1113
|
}
|
|
1048
1114
|
}
|
|
1049
1115
|
dispatchEvent(ev) {
|
|
1050
|
-
const
|
|
1116
|
+
const ctx = this[_ContextImpl];
|
|
1051
1117
|
const path = [];
|
|
1052
|
-
for (let parent =
|
|
1118
|
+
for (let parent = ctx.parent; parent !== undefined; parent = parent.parent) {
|
|
1053
1119
|
path.push(parent);
|
|
1054
1120
|
}
|
|
1055
1121
|
// We patch the stopImmediatePropagation method because ev.cancelBubble
|
|
@@ -1061,7 +1127,7 @@
|
|
|
1061
1127
|
immediateCancelBubble = true;
|
|
1062
1128
|
return stopImmediatePropagation.call(ev);
|
|
1063
1129
|
});
|
|
1064
|
-
setEventProperty(ev, "target",
|
|
1130
|
+
setEventProperty(ev, "target", ctx.owner);
|
|
1065
1131
|
// The only possible errors in this block are errors thrown by callbacks,
|
|
1066
1132
|
// and dispatchEvent will only log these errors rather than throwing
|
|
1067
1133
|
// them. Therefore, we place all code in a try block, log errors in the
|
|
@@ -1070,18 +1136,21 @@
|
|
|
1070
1136
|
// Each early return within the try block returns true because while the
|
|
1071
1137
|
// return value is overridden in the finally block, TypeScript
|
|
1072
1138
|
// (justifiably) does not recognize the unsafe return statement.
|
|
1073
|
-
//
|
|
1074
|
-
// TODO: Run all callbacks even if one of them errors
|
|
1075
1139
|
try {
|
|
1076
1140
|
setEventProperty(ev, "eventPhase", CAPTURING_PHASE);
|
|
1077
1141
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
1078
1142
|
const target = path[i];
|
|
1079
1143
|
const listeners = listenersMap.get(target);
|
|
1080
1144
|
if (listeners) {
|
|
1081
|
-
setEventProperty(ev, "currentTarget", target.
|
|
1145
|
+
setEventProperty(ev, "currentTarget", target.owner);
|
|
1082
1146
|
for (const record of listeners) {
|
|
1083
1147
|
if (record.type === ev.type && record.options.capture) {
|
|
1084
|
-
|
|
1148
|
+
try {
|
|
1149
|
+
record.callback.call(target.owner, ev);
|
|
1150
|
+
}
|
|
1151
|
+
catch (err) {
|
|
1152
|
+
console.error(err);
|
|
1153
|
+
}
|
|
1085
1154
|
if (immediateCancelBubble) {
|
|
1086
1155
|
return true;
|
|
1087
1156
|
}
|
|
@@ -1093,13 +1162,25 @@
|
|
|
1093
1162
|
}
|
|
1094
1163
|
}
|
|
1095
1164
|
{
|
|
1096
|
-
|
|
1165
|
+
setEventProperty(ev, "eventPhase", AT_TARGET);
|
|
1166
|
+
setEventProperty(ev, "currentTarget", ctx.owner);
|
|
1167
|
+
const propCallback = ctx.ret.el.props["on" + ev.type];
|
|
1168
|
+
if (propCallback != null) {
|
|
1169
|
+
propCallback(ev);
|
|
1170
|
+
if (immediateCancelBubble || ev.cancelBubble) {
|
|
1171
|
+
return true;
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
const listeners = listenersMap.get(ctx);
|
|
1097
1175
|
if (listeners) {
|
|
1098
|
-
setEventProperty(ev, "eventPhase", AT_TARGET);
|
|
1099
|
-
setEventProperty(ev, "currentTarget", impl.ctx);
|
|
1100
1176
|
for (const record of listeners) {
|
|
1101
1177
|
if (record.type === ev.type) {
|
|
1102
|
-
|
|
1178
|
+
try {
|
|
1179
|
+
record.callback.call(ctx.owner, ev);
|
|
1180
|
+
}
|
|
1181
|
+
catch (err) {
|
|
1182
|
+
console.error(err);
|
|
1183
|
+
}
|
|
1103
1184
|
if (immediateCancelBubble) {
|
|
1104
1185
|
return true;
|
|
1105
1186
|
}
|
|
@@ -1116,10 +1197,15 @@
|
|
|
1116
1197
|
const target = path[i];
|
|
1117
1198
|
const listeners = listenersMap.get(target);
|
|
1118
1199
|
if (listeners) {
|
|
1119
|
-
setEventProperty(ev, "currentTarget", target.
|
|
1200
|
+
setEventProperty(ev, "currentTarget", target.owner);
|
|
1120
1201
|
for (const record of listeners) {
|
|
1121
1202
|
if (record.type === ev.type && !record.options.capture) {
|
|
1122
|
-
|
|
1203
|
+
try {
|
|
1204
|
+
record.callback.call(target.owner, ev);
|
|
1205
|
+
}
|
|
1206
|
+
catch (err) {
|
|
1207
|
+
console.error(err);
|
|
1208
|
+
}
|
|
1123
1209
|
if (immediateCancelBubble) {
|
|
1124
1210
|
return true;
|
|
1125
1211
|
}
|
|
@@ -1132,10 +1218,6 @@
|
|
|
1132
1218
|
}
|
|
1133
1219
|
}
|
|
1134
1220
|
}
|
|
1135
|
-
catch (err) {
|
|
1136
|
-
// TODO: Use setTimeout to rethrow the error.
|
|
1137
|
-
console.error(err);
|
|
1138
|
-
}
|
|
1139
1221
|
finally {
|
|
1140
1222
|
setEventProperty(ev, "eventPhase", NONE);
|
|
1141
1223
|
setEventProperty(ev, "currentTarget", null);
|
|
@@ -1153,42 +1235,47 @@
|
|
|
1153
1235
|
}
|
|
1154
1236
|
return false;
|
|
1155
1237
|
}
|
|
1156
|
-
function updateComponent(renderer, root, host, parent, scope, ret, oldProps) {
|
|
1238
|
+
function updateComponent(renderer, root, host, parent, scope, ret, oldProps, hydrationData) {
|
|
1157
1239
|
let ctx;
|
|
1158
1240
|
if (oldProps) {
|
|
1159
1241
|
ctx = ret.ctx;
|
|
1160
|
-
if (ctx.f &
|
|
1242
|
+
if (ctx.f & IsSyncExecuting) {
|
|
1161
1243
|
console.error("Component is already executing");
|
|
1162
|
-
return ret.
|
|
1244
|
+
return ret.cachedChildValues;
|
|
1163
1245
|
}
|
|
1164
1246
|
}
|
|
1165
1247
|
else {
|
|
1166
1248
|
ctx = ret.ctx = new ContextImpl(renderer, root, host, parent, scope, ret);
|
|
1167
1249
|
}
|
|
1168
1250
|
ctx.f |= IsUpdating;
|
|
1169
|
-
|
|
1170
|
-
return runComponent(ctx);
|
|
1251
|
+
return enqueueComponentRun(ctx, hydrationData);
|
|
1171
1252
|
}
|
|
1172
|
-
function updateComponentChildren(ctx, children) {
|
|
1173
|
-
if (ctx.f & IsUnmounted
|
|
1253
|
+
function updateComponentChildren(ctx, children, hydrationData) {
|
|
1254
|
+
if (ctx.f & IsUnmounted) {
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
else if (ctx.f & IsErrored) {
|
|
1258
|
+
// This branch is necessary for some race conditions where this function is
|
|
1259
|
+
// called after iterator.throw() in async generator components.
|
|
1174
1260
|
return;
|
|
1175
1261
|
}
|
|
1176
1262
|
else if (children === undefined) {
|
|
1177
1263
|
console.error("A component has returned or yielded undefined. If this was intentional, return or yield null instead.");
|
|
1178
1264
|
}
|
|
1179
1265
|
let childValues;
|
|
1180
|
-
// We set the isExecuting flag in case a child component dispatches an event
|
|
1181
|
-
// which bubbles to this component and causes a synchronous refresh().
|
|
1182
|
-
ctx.f |= IsExecuting;
|
|
1183
1266
|
try {
|
|
1184
|
-
|
|
1267
|
+
// TODO: WAT
|
|
1268
|
+
// We set the isExecuting flag in case a child component dispatches an event
|
|
1269
|
+
// which bubbles to this component and causes a synchronous refresh().
|
|
1270
|
+
ctx.f |= IsSyncExecuting;
|
|
1271
|
+
childValues = diffChildren(ctx.renderer, ctx.root, ctx.host, ctx, ctx.scope, ctx.ret, narrow(children), hydrationData);
|
|
1185
1272
|
}
|
|
1186
1273
|
finally {
|
|
1187
|
-
ctx.f &= ~
|
|
1274
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1188
1275
|
}
|
|
1189
1276
|
if (isPromiseLike(childValues)) {
|
|
1190
|
-
ctx.ret.
|
|
1191
|
-
return ctx.ret.
|
|
1277
|
+
ctx.ret.inflightValue = childValues.then((childValues) => commitComponent(ctx, childValues));
|
|
1278
|
+
return ctx.ret.inflightValue;
|
|
1192
1279
|
}
|
|
1193
1280
|
return commitComponent(ctx, childValues);
|
|
1194
1281
|
}
|
|
@@ -1208,8 +1295,8 @@
|
|
|
1208
1295
|
}
|
|
1209
1296
|
}
|
|
1210
1297
|
}
|
|
1211
|
-
const oldValues = wrap(ctx.ret.
|
|
1212
|
-
let value = (ctx.ret.
|
|
1298
|
+
const oldValues = wrap(ctx.ret.cachedChildValues);
|
|
1299
|
+
let value = (ctx.ret.cachedChildValues = unwrap(values));
|
|
1213
1300
|
if (ctx.f & IsScheduling) {
|
|
1214
1301
|
ctx.f |= IsSchedulingRefresh;
|
|
1215
1302
|
}
|
|
@@ -1217,7 +1304,7 @@
|
|
|
1217
1304
|
// If we’re not updating the component, which happens when components are
|
|
1218
1305
|
// refreshed, or when async generator components iterate, we have to do a
|
|
1219
1306
|
// little bit housekeeping when a component’s child values have changed.
|
|
1220
|
-
if (!
|
|
1307
|
+
if (!arrayEqual(oldValues, values)) {
|
|
1221
1308
|
const records = getListenerRecords(ctx.parent, ctx.host);
|
|
1222
1309
|
if (records.length) {
|
|
1223
1310
|
for (let i = 0; i < values.length; i++) {
|
|
@@ -1232,7 +1319,7 @@
|
|
|
1232
1319
|
}
|
|
1233
1320
|
// rearranging the nearest ancestor host element
|
|
1234
1321
|
const host = ctx.host;
|
|
1235
|
-
const oldHostValues = wrap(host.
|
|
1322
|
+
const oldHostValues = wrap(host.cachedChildValues);
|
|
1236
1323
|
invalidate(ctx, host);
|
|
1237
1324
|
const hostValues = getChildValues(host);
|
|
1238
1325
|
ctx.renderer.arrange(host.el.tag, host.value, host.el.props, hostValues,
|
|
@@ -1261,50 +1348,79 @@
|
|
|
1261
1348
|
}
|
|
1262
1349
|
function invalidate(ctx, host) {
|
|
1263
1350
|
for (let parent = ctx.parent; parent !== undefined && parent.host === host; parent = parent.parent) {
|
|
1264
|
-
parent.ret.
|
|
1351
|
+
parent.ret.cachedChildValues = undefined;
|
|
1265
1352
|
}
|
|
1266
|
-
host.
|
|
1353
|
+
host.cachedChildValues = undefined;
|
|
1267
1354
|
}
|
|
1268
|
-
function
|
|
1269
|
-
if (
|
|
1355
|
+
function arrayEqual(arr1, arr2) {
|
|
1356
|
+
if (arr1.length !== arr2.length) {
|
|
1270
1357
|
return false;
|
|
1271
1358
|
}
|
|
1272
|
-
for (let i = 0; i <
|
|
1273
|
-
const value1 =
|
|
1274
|
-
const value2 =
|
|
1359
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
1360
|
+
const value1 = arr1[i];
|
|
1361
|
+
const value2 = arr2[i];
|
|
1275
1362
|
if (value1 !== value2) {
|
|
1276
1363
|
return false;
|
|
1277
1364
|
}
|
|
1278
1365
|
}
|
|
1279
1366
|
return true;
|
|
1280
1367
|
}
|
|
1281
|
-
/**
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1368
|
+
/** Enqueues and executes the component associated with the context. */
|
|
1369
|
+
function enqueueComponentRun(ctx, hydrationData) {
|
|
1370
|
+
if (ctx.f & IsAsyncGen && !(ctx.f & IsInForOfLoop)) {
|
|
1371
|
+
if (hydrationData !== undefined) {
|
|
1372
|
+
throw new Error("Hydration error");
|
|
1373
|
+
}
|
|
1374
|
+
// This branch will run for non-initial renders of async generator
|
|
1375
|
+
// components when they are not in for...of loops. When in a for...of loop,
|
|
1376
|
+
// async generator components will behave normally.
|
|
1377
|
+
//
|
|
1378
|
+
// Async gen componennts can be in one of three states:
|
|
1379
|
+
//
|
|
1380
|
+
// 1. propsAvailable flag is true: "available"
|
|
1381
|
+
//
|
|
1382
|
+
// The component is suspended somewhere in the loop. When the component
|
|
1383
|
+
// reaches the bottom of the loop, it will run again with the next props.
|
|
1384
|
+
//
|
|
1385
|
+
// 2. onAvailable callback is defined: "suspended"
|
|
1386
|
+
//
|
|
1387
|
+
// The component has suspended at the bottom of the loop and is waiting
|
|
1388
|
+
// for new props.
|
|
1389
|
+
//
|
|
1390
|
+
// 3. neither 1 or 2: "Running"
|
|
1391
|
+
//
|
|
1392
|
+
// The component is suspended somewhere in the loop. When the component
|
|
1393
|
+
// reaches the bottom of the loop, it will suspend.
|
|
1394
|
+
//
|
|
1395
|
+
// Components will never be both available and suspended at
|
|
1396
|
+
// the same time.
|
|
1397
|
+
//
|
|
1398
|
+
// If the component is at the loop bottom, this means that the next value
|
|
1399
|
+
// produced by the component will have the most up to date props, so we can
|
|
1400
|
+
// simply return the current inflight value. Otherwise, we have to wait for
|
|
1401
|
+
// the bottom of the loop to be reached before returning the inflight
|
|
1402
|
+
// value.
|
|
1403
|
+
const isAtLoopbottom = ctx.f & IsInForAwaitOfLoop && !ctx.onProps;
|
|
1404
|
+
resumePropsIterator(ctx);
|
|
1405
|
+
if (isAtLoopbottom) {
|
|
1406
|
+
if (ctx.inflightBlock == null) {
|
|
1407
|
+
ctx.inflightBlock = new Promise((resolve) => (ctx.onPropsRequested = resolve));
|
|
1408
|
+
}
|
|
1409
|
+
return ctx.inflightBlock.then(() => {
|
|
1410
|
+
ctx.inflightBlock = undefined;
|
|
1411
|
+
return ctx.inflightValue;
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1414
|
+
return ctx.inflightValue;
|
|
1415
|
+
}
|
|
1416
|
+
else if (!ctx.inflightBlock) {
|
|
1299
1417
|
try {
|
|
1300
|
-
const [block, value] =
|
|
1418
|
+
const [block, value] = runComponent(ctx, hydrationData);
|
|
1301
1419
|
if (block) {
|
|
1302
1420
|
ctx.inflightBlock = block
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
}
|
|
1307
|
-
})
|
|
1421
|
+
// TODO: there is some fuckery going on here related to async
|
|
1422
|
+
// generator components resuming when they’re meant to be returned.
|
|
1423
|
+
.then((v) => v)
|
|
1308
1424
|
.finally(() => advanceComponent(ctx));
|
|
1309
1425
|
// stepComponent will only return a block if the value is asynchronous
|
|
1310
1426
|
ctx.inflightValue = value;
|
|
@@ -1318,38 +1434,46 @@
|
|
|
1318
1434
|
throw err;
|
|
1319
1435
|
}
|
|
1320
1436
|
}
|
|
1321
|
-
else if (ctx.f & IsAsyncGen) {
|
|
1322
|
-
return ctx.inflightValue;
|
|
1323
|
-
}
|
|
1324
1437
|
else if (!ctx.enqueuedBlock) {
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1438
|
+
if (hydrationData !== undefined) {
|
|
1439
|
+
throw new Error("Hydration error");
|
|
1440
|
+
}
|
|
1441
|
+
// We need to assign enqueuedBlock and enqueuedValue synchronously, hence
|
|
1442
|
+
// the Promise constructor call here.
|
|
1443
|
+
let resolveEnqueuedBlock;
|
|
1444
|
+
ctx.enqueuedBlock = new Promise((resolve) => (resolveEnqueuedBlock = resolve));
|
|
1445
|
+
ctx.enqueuedValue = ctx.inflightBlock.then(() => {
|
|
1328
1446
|
try {
|
|
1329
|
-
const [block, value] =
|
|
1330
|
-
resolve(value);
|
|
1447
|
+
const [block, value] = runComponent(ctx);
|
|
1331
1448
|
if (block) {
|
|
1332
|
-
|
|
1333
|
-
if (!(ctx.f & IsUpdating)) {
|
|
1334
|
-
return propagateError(ctx.parent, err);
|
|
1335
|
-
}
|
|
1336
|
-
});
|
|
1449
|
+
resolveEnqueuedBlock(block.finally(() => advanceComponent(ctx)));
|
|
1337
1450
|
}
|
|
1451
|
+
return value;
|
|
1338
1452
|
}
|
|
1339
1453
|
catch (err) {
|
|
1340
1454
|
if (!(ctx.f & IsUpdating)) {
|
|
1341
1455
|
return propagateError(ctx.parent, err);
|
|
1342
1456
|
}
|
|
1457
|
+
throw err;
|
|
1343
1458
|
}
|
|
1344
|
-
})
|
|
1345
|
-
.finally(() => advanceComponent(ctx));
|
|
1346
|
-
ctx.enqueuedValue = new Promise((resolve1) => (resolve = resolve1));
|
|
1459
|
+
});
|
|
1347
1460
|
}
|
|
1348
1461
|
return ctx.enqueuedValue;
|
|
1349
1462
|
}
|
|
1463
|
+
/** Called when the inflight block promise settles. */
|
|
1464
|
+
function advanceComponent(ctx) {
|
|
1465
|
+
if (ctx.f & IsAsyncGen && !(ctx.f & IsInForOfLoop)) {
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
ctx.inflightBlock = ctx.enqueuedBlock;
|
|
1469
|
+
ctx.inflightValue = ctx.enqueuedValue;
|
|
1470
|
+
ctx.enqueuedBlock = undefined;
|
|
1471
|
+
ctx.enqueuedValue = undefined;
|
|
1472
|
+
}
|
|
1350
1473
|
/**
|
|
1351
1474
|
* This function is responsible for executing the component and handling all
|
|
1352
|
-
* the different component types.
|
|
1475
|
+
* the different component types. We cannot identify whether a component is a
|
|
1476
|
+
* generator or async without calling it and inspecting the return value.
|
|
1353
1477
|
*
|
|
1354
1478
|
* @returns {[block, value]} A tuple where
|
|
1355
1479
|
* block - A possible promise which represents the duration during which the
|
|
@@ -1364,25 +1488,23 @@
|
|
|
1364
1488
|
* - Sync generator components block while any children are executing, because
|
|
1365
1489
|
* they are expected to only resume when they’ve actually rendered.
|
|
1366
1490
|
*/
|
|
1367
|
-
function
|
|
1491
|
+
function runComponent(ctx, hydrationData) {
|
|
1368
1492
|
const ret = ctx.ret;
|
|
1369
|
-
if (ctx.f & IsDone) {
|
|
1370
|
-
return [undefined, getValue(ret)];
|
|
1371
|
-
}
|
|
1372
1493
|
const initial = !ctx.iterator;
|
|
1373
1494
|
if (initial) {
|
|
1374
|
-
ctx
|
|
1495
|
+
resumePropsIterator(ctx);
|
|
1496
|
+
ctx.f |= IsSyncExecuting;
|
|
1375
1497
|
clearEventListeners(ctx);
|
|
1376
1498
|
let result;
|
|
1377
1499
|
try {
|
|
1378
|
-
result = ret.el.tag.call(ctx.
|
|
1500
|
+
result = ret.el.tag.call(ctx.owner, ret.el.props);
|
|
1379
1501
|
}
|
|
1380
1502
|
catch (err) {
|
|
1381
1503
|
ctx.f |= IsErrored;
|
|
1382
1504
|
throw err;
|
|
1383
1505
|
}
|
|
1384
1506
|
finally {
|
|
1385
|
-
ctx.f &= ~
|
|
1507
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1386
1508
|
}
|
|
1387
1509
|
if (isIteratorLike(result)) {
|
|
1388
1510
|
ctx.iterator = result;
|
|
@@ -1390,124 +1512,241 @@
|
|
|
1390
1512
|
else if (isPromiseLike(result)) {
|
|
1391
1513
|
// async function component
|
|
1392
1514
|
const result1 = result instanceof Promise ? result : Promise.resolve(result);
|
|
1393
|
-
const value = result1.then((result) => updateComponentChildren(ctx, result), (err) => {
|
|
1515
|
+
const value = result1.then((result) => updateComponentChildren(ctx, result, hydrationData), (err) => {
|
|
1394
1516
|
ctx.f |= IsErrored;
|
|
1395
1517
|
throw err;
|
|
1396
1518
|
});
|
|
1397
|
-
return [result1, value];
|
|
1519
|
+
return [result1.catch(NOOP), value];
|
|
1398
1520
|
}
|
|
1399
1521
|
else {
|
|
1400
1522
|
// sync function component
|
|
1401
|
-
return [
|
|
1523
|
+
return [
|
|
1524
|
+
undefined,
|
|
1525
|
+
updateComponentChildren(ctx, result, hydrationData),
|
|
1526
|
+
];
|
|
1402
1527
|
}
|
|
1403
1528
|
}
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
// The argument passed to the first call to next is ignored.
|
|
1407
|
-
oldValue = undefined;
|
|
1408
|
-
}
|
|
1409
|
-
else if (ctx.ret.inflight) {
|
|
1410
|
-
// The value passed back into the generator as the argument to the next
|
|
1411
|
-
// method is a promise if an async generator component has async children.
|
|
1412
|
-
// Sync generator components only resume when their children have fulfilled
|
|
1413
|
-
// so the element’s inflight child values will never be defined.
|
|
1414
|
-
oldValue = ctx.ret.inflight.then((value) => ctx.renderer.read(value), () => ctx.renderer.read(undefined));
|
|
1415
|
-
}
|
|
1416
|
-
else {
|
|
1417
|
-
oldValue = ctx.renderer.read(getValue(ret));
|
|
1529
|
+
else if (hydrationData !== undefined) {
|
|
1530
|
+
throw new Error("Hydration error");
|
|
1418
1531
|
}
|
|
1419
1532
|
let iteration;
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1533
|
+
if (initial) {
|
|
1534
|
+
try {
|
|
1535
|
+
ctx.f |= IsSyncExecuting;
|
|
1536
|
+
iteration = ctx.iterator.next();
|
|
1537
|
+
}
|
|
1538
|
+
catch (err) {
|
|
1539
|
+
ctx.f |= IsErrored;
|
|
1540
|
+
throw err;
|
|
1541
|
+
}
|
|
1542
|
+
finally {
|
|
1543
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1544
|
+
}
|
|
1545
|
+
if (isPromiseLike(iteration)) {
|
|
1546
|
+
ctx.f |= IsAsyncGen;
|
|
1547
|
+
}
|
|
1548
|
+
else {
|
|
1549
|
+
ctx.f |= IsSyncGen;
|
|
1550
|
+
}
|
|
1427
1551
|
}
|
|
1428
|
-
|
|
1429
|
-
ctx.f &= ~
|
|
1552
|
+
if (ctx.f & IsSyncGen) {
|
|
1553
|
+
ctx.f &= ~NeedsToYield;
|
|
1554
|
+
// sync generator component
|
|
1555
|
+
if (!initial) {
|
|
1556
|
+
try {
|
|
1557
|
+
ctx.f |= IsSyncExecuting;
|
|
1558
|
+
iteration = ctx.iterator.next(ctx.renderer.read(getValue(ret)));
|
|
1559
|
+
}
|
|
1560
|
+
catch (err) {
|
|
1561
|
+
ctx.f |= IsErrored;
|
|
1562
|
+
throw err;
|
|
1563
|
+
}
|
|
1564
|
+
finally {
|
|
1565
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
if (isPromiseLike(iteration)) {
|
|
1569
|
+
throw new Error("Mixed generator component");
|
|
1570
|
+
}
|
|
1571
|
+
if (iteration.done) {
|
|
1572
|
+
ctx.f &= ~IsSyncGen;
|
|
1573
|
+
ctx.iterator = undefined;
|
|
1574
|
+
}
|
|
1575
|
+
let value;
|
|
1576
|
+
try {
|
|
1577
|
+
value = updateComponentChildren(ctx,
|
|
1578
|
+
// Children can be void so we eliminate that here
|
|
1579
|
+
iteration.value, hydrationData);
|
|
1580
|
+
if (isPromiseLike(value)) {
|
|
1581
|
+
value = value.catch((err) => handleChildError(ctx, err));
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
catch (err) {
|
|
1585
|
+
value = handleChildError(ctx, err);
|
|
1586
|
+
}
|
|
1587
|
+
const block = isPromiseLike(value) ? value.catch(NOOP) : undefined;
|
|
1588
|
+
return [block, value];
|
|
1430
1589
|
}
|
|
1431
|
-
if (
|
|
1432
|
-
//
|
|
1433
|
-
|
|
1434
|
-
|
|
1590
|
+
else if (ctx.f & IsInForOfLoop) {
|
|
1591
|
+
// TODO: does this need to be done async?
|
|
1592
|
+
ctx.f &= ~NeedsToYield;
|
|
1593
|
+
// we are in a for...of loop for async generator
|
|
1594
|
+
if (!initial) {
|
|
1595
|
+
try {
|
|
1596
|
+
ctx.f |= IsSyncExecuting;
|
|
1597
|
+
iteration = ctx.iterator.next(ctx.renderer.read(getValue(ret)));
|
|
1598
|
+
}
|
|
1599
|
+
catch (err) {
|
|
1600
|
+
ctx.f |= IsErrored;
|
|
1601
|
+
throw err;
|
|
1602
|
+
}
|
|
1603
|
+
finally {
|
|
1604
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1605
|
+
}
|
|
1435
1606
|
}
|
|
1607
|
+
if (!isPromiseLike(iteration)) {
|
|
1608
|
+
throw new Error("Mixed generator component");
|
|
1609
|
+
}
|
|
1610
|
+
const block = iteration.catch(NOOP);
|
|
1436
1611
|
const value = iteration.then((iteration) => {
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
ctx.f &= ~IsIterating;
|
|
1441
|
-
if (iteration.done) {
|
|
1442
|
-
ctx.f |= IsDone;
|
|
1612
|
+
let value;
|
|
1613
|
+
if (!(ctx.f & IsInForOfLoop)) {
|
|
1614
|
+
runAsyncGenComponent(ctx, Promise.resolve(iteration), hydrationData);
|
|
1443
1615
|
}
|
|
1444
1616
|
try {
|
|
1445
|
-
|
|
1617
|
+
value = updateComponentChildren(ctx,
|
|
1618
|
+
// Children can be void so we eliminate that here
|
|
1619
|
+
iteration.value, hydrationData);
|
|
1446
1620
|
if (isPromiseLike(value)) {
|
|
1447
|
-
|
|
1621
|
+
value = value.catch((err) => handleChildError(ctx, err));
|
|
1448
1622
|
}
|
|
1449
|
-
return value;
|
|
1450
1623
|
}
|
|
1451
1624
|
catch (err) {
|
|
1452
|
-
|
|
1625
|
+
value = handleChildError(ctx, err);
|
|
1453
1626
|
}
|
|
1627
|
+
return value;
|
|
1454
1628
|
}, (err) => {
|
|
1455
|
-
ctx.f |=
|
|
1629
|
+
ctx.f |= IsErrored;
|
|
1456
1630
|
throw err;
|
|
1457
1631
|
});
|
|
1458
|
-
return [
|
|
1632
|
+
return [block, value];
|
|
1459
1633
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
ctx.f &= ~IsIterating;
|
|
1465
|
-
if (iteration.done) {
|
|
1466
|
-
ctx.f |= IsDone;
|
|
1634
|
+
else {
|
|
1635
|
+
runAsyncGenComponent(ctx, iteration, hydrationData);
|
|
1636
|
+
// async generator component
|
|
1637
|
+
return [ctx.inflightBlock, ctx.inflightValue];
|
|
1467
1638
|
}
|
|
1468
|
-
|
|
1639
|
+
}
|
|
1640
|
+
async function runAsyncGenComponent(ctx, iterationP, hydrationData) {
|
|
1641
|
+
let done = false;
|
|
1469
1642
|
try {
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1643
|
+
while (!done) {
|
|
1644
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1645
|
+
break;
|
|
1646
|
+
}
|
|
1647
|
+
// inflightValue must be set synchronously.
|
|
1648
|
+
let onValue;
|
|
1649
|
+
ctx.inflightValue = new Promise((resolve) => (onValue = resolve));
|
|
1650
|
+
if (ctx.f & IsUpdating) {
|
|
1651
|
+
// We should not swallow unhandled promise rejections if the component is
|
|
1652
|
+
// updating independently.
|
|
1653
|
+
// TODO: Does this handle this.refresh() calls?
|
|
1654
|
+
ctx.inflightValue.catch(NOOP);
|
|
1655
|
+
}
|
|
1656
|
+
let iteration;
|
|
1657
|
+
try {
|
|
1658
|
+
iteration = await iterationP;
|
|
1659
|
+
}
|
|
1660
|
+
catch (err) {
|
|
1661
|
+
done = true;
|
|
1662
|
+
ctx.f |= IsErrored;
|
|
1663
|
+
onValue(Promise.reject(err));
|
|
1664
|
+
break;
|
|
1665
|
+
}
|
|
1666
|
+
finally {
|
|
1667
|
+
ctx.f &= ~NeedsToYield;
|
|
1668
|
+
if (!(ctx.f & IsInForAwaitOfLoop)) {
|
|
1669
|
+
ctx.f &= ~PropsAvailable;
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
done = !!iteration.done;
|
|
1673
|
+
let value;
|
|
1674
|
+
try {
|
|
1675
|
+
value = updateComponentChildren(ctx, iteration.value, hydrationData);
|
|
1676
|
+
hydrationData = undefined;
|
|
1677
|
+
if (isPromiseLike(value)) {
|
|
1678
|
+
value = value.catch((err) => handleChildError(ctx, err));
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
catch (err) {
|
|
1682
|
+
// Do we need to catch potential errors here in the case of unhandled
|
|
1683
|
+
// promise rejections?
|
|
1684
|
+
value = handleChildError(ctx, err);
|
|
1685
|
+
}
|
|
1686
|
+
finally {
|
|
1687
|
+
onValue(value);
|
|
1688
|
+
}
|
|
1689
|
+
// TODO: this can be done more elegantly
|
|
1690
|
+
let oldValue;
|
|
1691
|
+
if (ctx.ret.inflightValue) {
|
|
1692
|
+
// The value passed back into the generator as the argument to the next
|
|
1693
|
+
// method is a promise if an async generator component has async
|
|
1694
|
+
// children. Sync generator components only resume when their children
|
|
1695
|
+
// have fulfilled so the element’s inflight child values will never be
|
|
1696
|
+
// defined.
|
|
1697
|
+
oldValue = ctx.ret.inflightValue.then((value) => ctx.renderer.read(value), () => ctx.renderer.read(undefined));
|
|
1698
|
+
}
|
|
1699
|
+
else {
|
|
1700
|
+
oldValue = ctx.renderer.read(getValue(ctx.ret));
|
|
1701
|
+
}
|
|
1702
|
+
if (ctx.f & IsUnmounted) {
|
|
1703
|
+
if (ctx.f & IsInForAwaitOfLoop) {
|
|
1704
|
+
try {
|
|
1705
|
+
ctx.f |= IsSyncExecuting;
|
|
1706
|
+
iterationP = ctx.iterator.next(oldValue);
|
|
1707
|
+
}
|
|
1708
|
+
finally {
|
|
1709
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
else {
|
|
1713
|
+
returnComponent(ctx);
|
|
1714
|
+
break;
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
else if (!done && !(ctx.f & IsInForOfLoop)) {
|
|
1718
|
+
try {
|
|
1719
|
+
ctx.f |= IsSyncExecuting;
|
|
1720
|
+
iterationP = ctx.iterator.next(oldValue);
|
|
1721
|
+
}
|
|
1722
|
+
finally {
|
|
1723
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1473
1726
|
}
|
|
1474
1727
|
}
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
}
|
|
1481
|
-
return [undefined, value];
|
|
1482
|
-
}
|
|
1483
|
-
/**
|
|
1484
|
-
* Called when the inflight block promise settles.
|
|
1485
|
-
*/
|
|
1486
|
-
function advanceComponent(ctx) {
|
|
1487
|
-
ctx.inflightBlock = ctx.enqueuedBlock;
|
|
1488
|
-
ctx.inflightValue = ctx.enqueuedValue;
|
|
1489
|
-
ctx.enqueuedBlock = undefined;
|
|
1490
|
-
ctx.enqueuedValue = undefined;
|
|
1491
|
-
if (ctx.f & IsAsyncGen && !(ctx.f & IsDone) && !(ctx.f & IsUnmounted)) {
|
|
1492
|
-
runComponent(ctx);
|
|
1728
|
+
finally {
|
|
1729
|
+
if (done) {
|
|
1730
|
+
ctx.f &= ~IsAsyncGen;
|
|
1731
|
+
ctx.iterator = undefined;
|
|
1732
|
+
}
|
|
1493
1733
|
}
|
|
1494
1734
|
}
|
|
1495
1735
|
/**
|
|
1496
|
-
* Called to
|
|
1497
|
-
* generator components.
|
|
1736
|
+
* Called to resume the props async iterator for async generator components.
|
|
1498
1737
|
*/
|
|
1499
|
-
function
|
|
1500
|
-
if (ctx.
|
|
1501
|
-
ctx.
|
|
1502
|
-
ctx.
|
|
1738
|
+
function resumePropsIterator(ctx) {
|
|
1739
|
+
if (ctx.onProps) {
|
|
1740
|
+
ctx.onProps(ctx.ret.el.props);
|
|
1741
|
+
ctx.onProps = undefined;
|
|
1742
|
+
ctx.f &= ~PropsAvailable;
|
|
1503
1743
|
}
|
|
1504
1744
|
else {
|
|
1505
|
-
ctx.f |=
|
|
1745
|
+
ctx.f |= PropsAvailable;
|
|
1506
1746
|
}
|
|
1507
1747
|
}
|
|
1508
1748
|
// TODO: async unmounting
|
|
1509
1749
|
function unmountComponent(ctx) {
|
|
1510
|
-
ctx.f |= IsUnmounted;
|
|
1511
1750
|
clearEventListeners(ctx);
|
|
1512
1751
|
const callbacks = cleanupMap.get(ctx);
|
|
1513
1752
|
if (callbacks) {
|
|
@@ -1517,23 +1756,71 @@
|
|
|
1517
1756
|
callback(value);
|
|
1518
1757
|
}
|
|
1519
1758
|
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
ctx.f
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1759
|
+
ctx.f |= IsUnmounted;
|
|
1760
|
+
if (ctx.iterator) {
|
|
1761
|
+
if (ctx.f & IsSyncGen) {
|
|
1762
|
+
let value;
|
|
1763
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1764
|
+
value = enqueueComponentRun(ctx);
|
|
1765
|
+
}
|
|
1766
|
+
if (isPromiseLike(value)) {
|
|
1767
|
+
value.then(() => {
|
|
1768
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1769
|
+
unmountComponent(ctx);
|
|
1770
|
+
}
|
|
1771
|
+
else {
|
|
1772
|
+
returnComponent(ctx);
|
|
1773
|
+
}
|
|
1774
|
+
}, (err) => {
|
|
1775
|
+
propagateError(ctx.parent, err);
|
|
1776
|
+
});
|
|
1777
|
+
}
|
|
1778
|
+
else {
|
|
1779
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1780
|
+
unmountComponent(ctx);
|
|
1781
|
+
}
|
|
1782
|
+
else {
|
|
1783
|
+
returnComponent(ctx);
|
|
1529
1784
|
}
|
|
1530
1785
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1786
|
+
}
|
|
1787
|
+
else if (ctx.f & IsAsyncGen) {
|
|
1788
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1789
|
+
const value = enqueueComponentRun(ctx);
|
|
1790
|
+
value.then(() => {
|
|
1791
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1792
|
+
unmountComponent(ctx);
|
|
1793
|
+
}
|
|
1794
|
+
else {
|
|
1795
|
+
returnComponent(ctx);
|
|
1796
|
+
}
|
|
1797
|
+
}, (err) => {
|
|
1798
|
+
propagateError(ctx.parent, err);
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
else {
|
|
1802
|
+
// The logic for unmounting async generator components is in the
|
|
1803
|
+
// runAsyncGenComponent function.
|
|
1804
|
+
resumePropsIterator(ctx);
|
|
1533
1805
|
}
|
|
1534
1806
|
}
|
|
1535
1807
|
}
|
|
1536
1808
|
}
|
|
1809
|
+
function returnComponent(ctx) {
|
|
1810
|
+
resumePropsIterator(ctx);
|
|
1811
|
+
if (ctx.iterator && typeof ctx.iterator.return === "function") {
|
|
1812
|
+
try {
|
|
1813
|
+
ctx.f |= IsSyncExecuting;
|
|
1814
|
+
const iteration = ctx.iterator.return();
|
|
1815
|
+
if (isPromiseLike(iteration)) {
|
|
1816
|
+
iteration.catch((err) => propagateError(ctx.parent, err));
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
finally {
|
|
1820
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1537
1824
|
/*** EVENT TARGET UTILITIES ***/
|
|
1538
1825
|
// EVENT PHASE CONSTANTS
|
|
1539
1826
|
// https://developer.mozilla.org/en-US/docs/Web/API/Event/eventPhase
|
|
@@ -1602,39 +1889,39 @@
|
|
|
1602
1889
|
}
|
|
1603
1890
|
}
|
|
1604
1891
|
/*** ERROR HANDLING UTILITIES ***/
|
|
1605
|
-
// TODO: generator components which throw errors should be recoverable
|
|
1606
1892
|
function handleChildError(ctx, err) {
|
|
1607
|
-
if (ctx.
|
|
1608
|
-
!ctx.iterator ||
|
|
1609
|
-
typeof ctx.iterator.throw !== "function") {
|
|
1893
|
+
if (!ctx.iterator || typeof ctx.iterator.throw !== "function") {
|
|
1610
1894
|
throw err;
|
|
1611
1895
|
}
|
|
1612
|
-
|
|
1896
|
+
resumePropsIterator(ctx);
|
|
1613
1897
|
let iteration;
|
|
1614
1898
|
try {
|
|
1615
|
-
ctx.f |=
|
|
1899
|
+
ctx.f |= IsSyncExecuting;
|
|
1616
1900
|
iteration = ctx.iterator.throw(err);
|
|
1617
1901
|
}
|
|
1618
1902
|
catch (err) {
|
|
1619
|
-
ctx.f |=
|
|
1903
|
+
ctx.f |= IsErrored;
|
|
1620
1904
|
throw err;
|
|
1621
1905
|
}
|
|
1622
1906
|
finally {
|
|
1623
|
-
ctx.f &= ~
|
|
1907
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1624
1908
|
}
|
|
1625
1909
|
if (isPromiseLike(iteration)) {
|
|
1626
1910
|
return iteration.then((iteration) => {
|
|
1627
1911
|
if (iteration.done) {
|
|
1628
|
-
ctx.f
|
|
1912
|
+
ctx.f &= ~IsAsyncGen;
|
|
1913
|
+
ctx.iterator = undefined;
|
|
1629
1914
|
}
|
|
1630
1915
|
return updateComponentChildren(ctx, iteration.value);
|
|
1631
1916
|
}, (err) => {
|
|
1632
|
-
ctx.f |=
|
|
1917
|
+
ctx.f |= IsErrored;
|
|
1633
1918
|
throw err;
|
|
1634
1919
|
});
|
|
1635
1920
|
}
|
|
1636
1921
|
if (iteration.done) {
|
|
1637
|
-
ctx.f
|
|
1922
|
+
ctx.f &= ~IsSyncGen;
|
|
1923
|
+
ctx.f &= ~IsAsyncGen;
|
|
1924
|
+
ctx.iterator = undefined;
|
|
1638
1925
|
}
|
|
1639
1926
|
return updateComponentChildren(ctx, iteration.value);
|
|
1640
1927
|
}
|
|
@@ -1657,44 +1944,58 @@
|
|
|
1657
1944
|
|
|
1658
1945
|
const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
1659
1946
|
const impl$1 = {
|
|
1660
|
-
|
|
1661
|
-
if (typeof document.createRange === "function") {
|
|
1662
|
-
const fragment = document.createRange().createContextualFragment(text);
|
|
1663
|
-
return Array.from(fragment.childNodes);
|
|
1664
|
-
}
|
|
1665
|
-
else {
|
|
1666
|
-
const childNodes = new DOMParser().parseFromString(text, "text/html").body
|
|
1667
|
-
.childNodes;
|
|
1668
|
-
return Array.from(childNodes);
|
|
1669
|
-
}
|
|
1670
|
-
},
|
|
1671
|
-
scope(scope, tag) {
|
|
1947
|
+
scope(xmlns, tag) {
|
|
1672
1948
|
// TODO: Should we handle xmlns???
|
|
1673
1949
|
switch (tag) {
|
|
1674
1950
|
case Portal:
|
|
1675
1951
|
case "foreignObject":
|
|
1676
|
-
|
|
1952
|
+
xmlns = undefined;
|
|
1953
|
+
break;
|
|
1677
1954
|
case "svg":
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
return scope;
|
|
1955
|
+
xmlns = SVG_NAMESPACE;
|
|
1956
|
+
break;
|
|
1681
1957
|
}
|
|
1958
|
+
return xmlns;
|
|
1682
1959
|
},
|
|
1683
|
-
create(tag, _props,
|
|
1960
|
+
create(tag, _props, xmlns) {
|
|
1684
1961
|
if (typeof tag !== "string") {
|
|
1685
1962
|
throw new Error(`Unknown tag: ${tag.toString()}`);
|
|
1686
1963
|
}
|
|
1687
1964
|
else if (tag.toLowerCase() === "svg") {
|
|
1688
|
-
|
|
1965
|
+
xmlns = SVG_NAMESPACE;
|
|
1966
|
+
}
|
|
1967
|
+
return xmlns
|
|
1968
|
+
? document.createElementNS(xmlns, tag)
|
|
1969
|
+
: document.createElement(tag);
|
|
1970
|
+
},
|
|
1971
|
+
hydrate(tag, node, props) {
|
|
1972
|
+
if (typeof tag !== "string" && tag !== Portal) {
|
|
1973
|
+
throw new Error(`Unknown tag: ${tag.toString()}`);
|
|
1689
1974
|
}
|
|
1690
|
-
|
|
1975
|
+
if (typeof tag === "string" &&
|
|
1976
|
+
tag.toUpperCase() !== node.tagName) {
|
|
1977
|
+
console.error(`Expected <${tag}> while hydrating but found:`, node);
|
|
1978
|
+
return undefined;
|
|
1979
|
+
}
|
|
1980
|
+
const children = [];
|
|
1981
|
+
for (let i = 0; i < node.childNodes.length; i++) {
|
|
1982
|
+
const child = node.childNodes[i];
|
|
1983
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
1984
|
+
children.push(child.data);
|
|
1985
|
+
}
|
|
1986
|
+
else if (child.nodeType === Node.ELEMENT_NODE) {
|
|
1987
|
+
children.push(child);
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
// TODO: extract props from nodes
|
|
1991
|
+
return { props, children };
|
|
1691
1992
|
},
|
|
1692
1993
|
patch(_tag,
|
|
1693
1994
|
// TODO: Why does this assignment work?
|
|
1694
1995
|
node, name,
|
|
1695
1996
|
// TODO: Stricter typings?
|
|
1696
|
-
value, oldValue,
|
|
1697
|
-
const isSVG =
|
|
1997
|
+
value, oldValue, xmlns) {
|
|
1998
|
+
const isSVG = xmlns === SVG_NAMESPACE;
|
|
1698
1999
|
switch (name) {
|
|
1699
2000
|
case "style": {
|
|
1700
2001
|
const style = node.style;
|
|
@@ -1770,7 +2071,9 @@
|
|
|
1770
2071
|
const descriptor = Object.getOwnPropertyDescriptor(obj, name);
|
|
1771
2072
|
if (descriptor != null &&
|
|
1772
2073
|
(descriptor.writable === true || descriptor.set !== undefined)) {
|
|
1773
|
-
node[name]
|
|
2074
|
+
if (node[name] !== value) {
|
|
2075
|
+
node[name] = value;
|
|
2076
|
+
}
|
|
1774
2077
|
return;
|
|
1775
2078
|
}
|
|
1776
2079
|
// if the property wasn't writable, fall through to the code below
|
|
@@ -1857,24 +2160,86 @@
|
|
|
1857
2160
|
}
|
|
1858
2161
|
}
|
|
1859
2162
|
},
|
|
2163
|
+
text(text, _scope, hydrationData) {
|
|
2164
|
+
if (hydrationData != null) {
|
|
2165
|
+
let value = hydrationData.children.shift();
|
|
2166
|
+
if (typeof value !== "string" || !value.startsWith(text)) {
|
|
2167
|
+
console.error(`Expected "${text}" while hydrating but found:`, value);
|
|
2168
|
+
}
|
|
2169
|
+
else if (text.length < value.length) {
|
|
2170
|
+
value = value.slice(text.length);
|
|
2171
|
+
hydrationData.children.unshift(value);
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
return text;
|
|
2175
|
+
},
|
|
2176
|
+
raw(value, xmlns, hydrationData) {
|
|
2177
|
+
let result;
|
|
2178
|
+
if (typeof value === "string") {
|
|
2179
|
+
const el = xmlns == null
|
|
2180
|
+
? document.createElement("div")
|
|
2181
|
+
: document.createElementNS(xmlns, "svg");
|
|
2182
|
+
el.innerHTML = value;
|
|
2183
|
+
if (el.childNodes.length === 0) {
|
|
2184
|
+
result = undefined;
|
|
2185
|
+
}
|
|
2186
|
+
else if (el.childNodes.length === 1) {
|
|
2187
|
+
result = el.childNodes[0];
|
|
2188
|
+
}
|
|
2189
|
+
else {
|
|
2190
|
+
result = Array.from(el.childNodes);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
else {
|
|
2194
|
+
result = value;
|
|
2195
|
+
}
|
|
2196
|
+
if (hydrationData != null) {
|
|
2197
|
+
// TODO: maybe we should warn on incorrect values
|
|
2198
|
+
if (Array.isArray(result)) {
|
|
2199
|
+
for (let i = 0; i < result.length; i++) {
|
|
2200
|
+
const node = result[i];
|
|
2201
|
+
if (typeof node !== "string" &&
|
|
2202
|
+
(node.nodeType === Node.ELEMENT_NODE ||
|
|
2203
|
+
node.nodeType === Node.TEXT_NODE)) {
|
|
2204
|
+
hydrationData.children.shift();
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
else if (result != null && typeof result !== "string") {
|
|
2209
|
+
if (result.nodeType === Node.ELEMENT_NODE ||
|
|
2210
|
+
result.nodeType === Node.TEXT_NODE) {
|
|
2211
|
+
hydrationData.children.shift();
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
return result;
|
|
2216
|
+
},
|
|
1860
2217
|
};
|
|
1861
2218
|
class DOMRenderer extends Renderer {
|
|
1862
2219
|
constructor() {
|
|
1863
2220
|
super(impl$1);
|
|
1864
2221
|
}
|
|
1865
2222
|
render(children, root, ctx) {
|
|
1866
|
-
|
|
1867
|
-
throw new TypeError(`Render root is not a node. Received: ${JSON.stringify(root && root.toString())}`);
|
|
1868
|
-
}
|
|
2223
|
+
validateRoot(root);
|
|
1869
2224
|
return super.render(children, root, ctx);
|
|
1870
2225
|
}
|
|
2226
|
+
hydrate(children, root, ctx) {
|
|
2227
|
+
validateRoot(root);
|
|
2228
|
+
return super.hydrate(children, root, ctx);
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
function validateRoot(root) {
|
|
2232
|
+
if (root === null ||
|
|
2233
|
+
(typeof root === "object" && typeof root.nodeType !== "number")) {
|
|
2234
|
+
throw new TypeError(`Render root is not a node. Received: ${JSON.stringify(root && root.toString())}`);
|
|
2235
|
+
}
|
|
1871
2236
|
}
|
|
1872
2237
|
const renderer$1 = new DOMRenderer();
|
|
1873
2238
|
|
|
1874
2239
|
var dom = /*#__PURE__*/Object.freeze({
|
|
1875
2240
|
__proto__: null,
|
|
1876
|
-
impl: impl$1,
|
|
1877
2241
|
DOMRenderer: DOMRenderer,
|
|
2242
|
+
impl: impl$1,
|
|
1878
2243
|
renderer: renderer$1
|
|
1879
2244
|
});
|
|
1880
2245
|
|
|
@@ -1971,7 +2336,7 @@
|
|
|
1971
2336
|
create() {
|
|
1972
2337
|
return { value: "" };
|
|
1973
2338
|
},
|
|
1974
|
-
|
|
2339
|
+
text(text) {
|
|
1975
2340
|
return escape(text);
|
|
1976
2341
|
},
|
|
1977
2342
|
read(value) {
|
|
@@ -2018,8 +2383,8 @@
|
|
|
2018
2383
|
|
|
2019
2384
|
var html = /*#__PURE__*/Object.freeze({
|
|
2020
2385
|
__proto__: null,
|
|
2021
|
-
impl: impl,
|
|
2022
2386
|
HTMLRenderer: HTMLRenderer,
|
|
2387
|
+
impl: impl,
|
|
2023
2388
|
renderer: renderer
|
|
2024
2389
|
});
|
|
2025
2390
|
|
|
@@ -2036,7 +2401,5 @@
|
|
|
2036
2401
|
exports.html = html;
|
|
2037
2402
|
exports.isElement = isElement;
|
|
2038
2403
|
|
|
2039
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2040
|
-
|
|
2041
2404
|
}));
|
|
2042
2405
|
//# sourceMappingURL=umd.js.map
|