@b9g/crank 0.5.0-debug.0 → 0.5.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 +6 -17
- package/crank.cjs +682 -392
- package/crank.cjs.map +1 -1
- package/crank.d.ts +57 -77
- package/crank.js +681 -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 +787 -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,72 @@
|
|
|
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 = ret.value;
|
|
665
|
+
if (hydrationValue != null) {
|
|
666
|
+
value = ret.value = hydrationValue;
|
|
667
|
+
}
|
|
631
668
|
let props = ret.el.props;
|
|
632
669
|
let copied;
|
|
633
670
|
if (tag !== Portal) {
|
|
671
|
+
if (value == null) {
|
|
672
|
+
// This assumes that renderer.create does not return nullish values.
|
|
673
|
+
value = ret.value = renderer.create(tag, props, scope);
|
|
674
|
+
}
|
|
634
675
|
for (const propName in { ...oldProps, ...props }) {
|
|
635
676
|
const propValue = props[propName];
|
|
636
677
|
if (propValue === Copy) {
|
|
678
|
+
// TODO: The Copy tag doubles as a way to skip the patching of a prop.
|
|
679
|
+
// Not sure about this feature. Should probably be removed.
|
|
637
680
|
(copied = copied || new Set()).add(propName);
|
|
638
681
|
}
|
|
639
682
|
else if (propName !== "children") {
|
|
@@ -648,8 +691,8 @@
|
|
|
648
691
|
}
|
|
649
692
|
ret.el = new Element(tag, props, ret.el.key, ret.el.ref);
|
|
650
693
|
}
|
|
651
|
-
renderer.arrange(tag, value, props, childValues, oldProps, wrap(ret.
|
|
652
|
-
ret.
|
|
694
|
+
renderer.arrange(tag, value, props, childValues, oldProps, wrap(ret.cachedChildValues));
|
|
695
|
+
ret.cachedChildValues = unwrap(childValues);
|
|
653
696
|
if (tag === Portal) {
|
|
654
697
|
flush(renderer, ret.value);
|
|
655
698
|
return;
|
|
@@ -696,7 +739,7 @@
|
|
|
696
739
|
}
|
|
697
740
|
else if (ret.el.tag === Portal) {
|
|
698
741
|
host = ret;
|
|
699
|
-
renderer.arrange(Portal, host.value, host.el.props, [], host.el.props, wrap(host.
|
|
742
|
+
renderer.arrange(Portal, host.value, host.el.props, [], host.el.props, wrap(host.cachedChildValues));
|
|
700
743
|
flush(renderer, host.value);
|
|
701
744
|
}
|
|
702
745
|
else if (ret.el.tag !== Fragment) {
|
|
@@ -720,38 +763,43 @@
|
|
|
720
763
|
}
|
|
721
764
|
/*** CONTEXT FLAGS ***/
|
|
722
765
|
/**
|
|
723
|
-
* A flag which is
|
|
724
|
-
*
|
|
725
|
-
*
|
|
766
|
+
* A flag which is true when the component is initialized or updated by an
|
|
767
|
+
* ancestor component or the root render call.
|
|
768
|
+
*
|
|
769
|
+
* Used to determine things like whether the nearest host ancestor needs to be
|
|
770
|
+
* rearranged.
|
|
726
771
|
*/
|
|
727
772
|
const IsUpdating = 1 << 0;
|
|
728
773
|
/**
|
|
729
|
-
* A flag which is
|
|
730
|
-
*
|
|
731
|
-
*
|
|
732
|
-
* overflow or a generator error.
|
|
774
|
+
* A flag which is true when the component is synchronously executing.
|
|
775
|
+
*
|
|
776
|
+
* Used to guard against components triggering stack overflow or generator error.
|
|
733
777
|
*/
|
|
734
|
-
const
|
|
778
|
+
const IsSyncExecuting = 1 << 1;
|
|
735
779
|
/**
|
|
736
|
-
* A flag
|
|
737
|
-
* iterators without a yield.
|
|
780
|
+
* A flag which is true when the component is in a for...of loop.
|
|
738
781
|
*/
|
|
739
|
-
const
|
|
782
|
+
const IsInForOfLoop = 1 << 2;
|
|
740
783
|
/**
|
|
741
|
-
* A flag
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
784
|
+
* A flag which is true when the component is in a for await...of loop.
|
|
785
|
+
*/
|
|
786
|
+
const IsInForAwaitOfLoop = 1 << 3;
|
|
787
|
+
/**
|
|
788
|
+
* A flag which is true when the component starts the render loop but has not
|
|
789
|
+
* yielded yet.
|
|
790
|
+
*
|
|
791
|
+
* Used to make sure that components yield at least once per loop.
|
|
745
792
|
*/
|
|
746
|
-
const
|
|
793
|
+
const NeedsToYield = 1 << 4;
|
|
747
794
|
/**
|
|
748
|
-
* A flag
|
|
749
|
-
*
|
|
750
|
-
*
|
|
795
|
+
* A flag used by async generator components in conjunction with the
|
|
796
|
+
* onAvailable callback to mark whether new props can be pulled via the context
|
|
797
|
+
* async iterator. See the Symbol.asyncIterator method and the
|
|
798
|
+
* resumeCtxIterator function.
|
|
751
799
|
*/
|
|
752
|
-
const
|
|
800
|
+
const PropsAvailable = 1 << 5;
|
|
753
801
|
/**
|
|
754
|
-
* A flag which is set when a
|
|
802
|
+
* A flag which is set when a component errors.
|
|
755
803
|
*
|
|
756
804
|
* NOTE: This is mainly used to prevent some false positives in component
|
|
757
805
|
* yields or returns undefined warnings. The reason we’re using this versus
|
|
@@ -759,28 +807,28 @@
|
|
|
759
807
|
* sync generator child) where synchronous code causes a stack overflow error
|
|
760
808
|
* in a non-deterministic way. Deeply disturbing stuff.
|
|
761
809
|
*/
|
|
762
|
-
const IsErrored = 1 <<
|
|
810
|
+
const IsErrored = 1 << 6;
|
|
763
811
|
/**
|
|
764
812
|
* A flag which is set when the component is unmounted. Unmounted components
|
|
765
813
|
* are no longer in the element tree and cannot refresh or rerender.
|
|
766
814
|
*/
|
|
767
|
-
const IsUnmounted = 1 <<
|
|
815
|
+
const IsUnmounted = 1 << 7;
|
|
768
816
|
/**
|
|
769
817
|
* A flag which indicates that the component is a sync generator component.
|
|
770
818
|
*/
|
|
771
|
-
const IsSyncGen = 1 <<
|
|
819
|
+
const IsSyncGen = 1 << 8;
|
|
772
820
|
/**
|
|
773
821
|
* A flag which indicates that the component is an async generator component.
|
|
774
822
|
*/
|
|
775
|
-
const IsAsyncGen = 1 <<
|
|
823
|
+
const IsAsyncGen = 1 << 9;
|
|
776
824
|
/**
|
|
777
825
|
* A flag which is set while schedule callbacks are called.
|
|
778
826
|
*/
|
|
779
|
-
const IsScheduling = 1 <<
|
|
827
|
+
const IsScheduling = 1 << 10;
|
|
780
828
|
/**
|
|
781
829
|
* A flag which is set when a schedule callback calls refresh.
|
|
782
830
|
*/
|
|
783
|
-
const IsSchedulingRefresh = 1 <<
|
|
831
|
+
const IsSchedulingRefresh = 1 << 11;
|
|
784
832
|
const provisionMaps = new WeakMap();
|
|
785
833
|
const scheduleMap = new WeakMap();
|
|
786
834
|
const cleanupMap = new WeakMap();
|
|
@@ -788,12 +836,12 @@
|
|
|
788
836
|
const flushMaps = new WeakMap();
|
|
789
837
|
/**
|
|
790
838
|
* @internal
|
|
791
|
-
* The internal class which holds
|
|
839
|
+
* The internal class which holds context data.
|
|
792
840
|
*/
|
|
793
841
|
class ContextImpl {
|
|
794
842
|
constructor(renderer, root, host, parent, scope, ret) {
|
|
795
843
|
this.f = 0;
|
|
796
|
-
this.
|
|
844
|
+
this.owner = new Context(this);
|
|
797
845
|
this.renderer = renderer;
|
|
798
846
|
this.root = root;
|
|
799
847
|
this.host = host;
|
|
@@ -805,10 +853,11 @@
|
|
|
805
853
|
this.inflightValue = undefined;
|
|
806
854
|
this.enqueuedBlock = undefined;
|
|
807
855
|
this.enqueuedValue = undefined;
|
|
808
|
-
this.
|
|
856
|
+
this.onProps = undefined;
|
|
857
|
+
this.onPropsRequested = undefined;
|
|
809
858
|
}
|
|
810
859
|
}
|
|
811
|
-
const
|
|
860
|
+
const _ContextImpl = Symbol.for("crank.ContextImpl");
|
|
812
861
|
/**
|
|
813
862
|
* A class which is instantiated and passed to every component as its this
|
|
814
863
|
* value. Contexts form a tree just like elements and all components in the
|
|
@@ -822,8 +871,10 @@
|
|
|
822
871
|
* schedule and cleanup callbacks.
|
|
823
872
|
*/
|
|
824
873
|
class Context {
|
|
874
|
+
// TODO: If we could make the constructor function take a nicer value, it
|
|
875
|
+
// would be useful for testing purposes.
|
|
825
876
|
constructor(impl) {
|
|
826
|
-
this[
|
|
877
|
+
this[_ContextImpl] = impl;
|
|
827
878
|
}
|
|
828
879
|
/**
|
|
829
880
|
* The current props of the associated element.
|
|
@@ -833,7 +884,7 @@
|
|
|
833
884
|
* plugins or utilities which wrap contexts.
|
|
834
885
|
*/
|
|
835
886
|
get props() {
|
|
836
|
-
return this[
|
|
887
|
+
return this[_ContextImpl].ret.el.props;
|
|
837
888
|
}
|
|
838
889
|
// TODO: Should we rename this???
|
|
839
890
|
/**
|
|
@@ -844,45 +895,64 @@
|
|
|
844
895
|
* mainly for plugins or utilities which wrap contexts.
|
|
845
896
|
*/
|
|
846
897
|
get value() {
|
|
847
|
-
return this[
|
|
898
|
+
return this[_ContextImpl].renderer.read(getValue(this[_ContextImpl].ret));
|
|
848
899
|
}
|
|
849
900
|
*[Symbol.iterator]() {
|
|
850
|
-
const
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
901
|
+
const ctx = this[_ContextImpl];
|
|
902
|
+
try {
|
|
903
|
+
ctx.f |= IsInForOfLoop;
|
|
904
|
+
while (!(ctx.f & IsUnmounted)) {
|
|
905
|
+
if (ctx.f & NeedsToYield) {
|
|
906
|
+
throw new Error("Context iterated twice without a yield");
|
|
907
|
+
}
|
|
908
|
+
else {
|
|
909
|
+
ctx.f |= NeedsToYield;
|
|
910
|
+
}
|
|
911
|
+
yield ctx.ret.el.props;
|
|
857
912
|
}
|
|
858
|
-
|
|
859
|
-
|
|
913
|
+
}
|
|
914
|
+
finally {
|
|
915
|
+
ctx.f &= ~IsInForOfLoop;
|
|
860
916
|
}
|
|
861
917
|
}
|
|
862
918
|
async *[Symbol.asyncIterator]() {
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
919
|
+
const ctx = this[_ContextImpl];
|
|
920
|
+
if (ctx.f & IsSyncGen) {
|
|
921
|
+
throw new Error("Use for...of in sync generator components");
|
|
922
|
+
}
|
|
923
|
+
try {
|
|
924
|
+
ctx.f |= IsInForAwaitOfLoop;
|
|
925
|
+
while (!(ctx.f & IsUnmounted)) {
|
|
926
|
+
if (ctx.f & NeedsToYield) {
|
|
927
|
+
throw new Error("Context iterated twice without a yield");
|
|
928
|
+
}
|
|
929
|
+
else {
|
|
930
|
+
ctx.f |= NeedsToYield;
|
|
931
|
+
}
|
|
932
|
+
if (ctx.f & PropsAvailable) {
|
|
933
|
+
ctx.f &= ~PropsAvailable;
|
|
934
|
+
yield ctx.ret.el.props;
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
const props = await new Promise((resolve) => (ctx.onProps = resolve));
|
|
938
|
+
if (ctx.f & IsUnmounted) {
|
|
939
|
+
break;
|
|
940
|
+
}
|
|
941
|
+
yield props;
|
|
942
|
+
}
|
|
943
|
+
if (ctx.onPropsRequested) {
|
|
944
|
+
ctx.onPropsRequested();
|
|
945
|
+
ctx.onPropsRequested = undefined;
|
|
882
946
|
}
|
|
883
947
|
}
|
|
884
|
-
|
|
885
|
-
|
|
948
|
+
}
|
|
949
|
+
finally {
|
|
950
|
+
ctx.f &= ~IsInForAwaitOfLoop;
|
|
951
|
+
if (ctx.onPropsRequested) {
|
|
952
|
+
ctx.onPropsRequested();
|
|
953
|
+
ctx.onPropsRequested = undefined;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
886
956
|
}
|
|
887
957
|
/**
|
|
888
958
|
* Re-executes a component.
|
|
@@ -897,32 +967,31 @@
|
|
|
897
967
|
* async iterator to suspend.
|
|
898
968
|
*/
|
|
899
969
|
refresh() {
|
|
900
|
-
const
|
|
901
|
-
if (
|
|
970
|
+
const ctx = this[_ContextImpl];
|
|
971
|
+
if (ctx.f & IsUnmounted) {
|
|
902
972
|
console.error("Component is unmounted");
|
|
903
|
-
return
|
|
973
|
+
return ctx.renderer.read(undefined);
|
|
904
974
|
}
|
|
905
|
-
else if (
|
|
975
|
+
else if (ctx.f & IsSyncExecuting) {
|
|
906
976
|
console.error("Component is already executing");
|
|
907
977
|
return this.value;
|
|
908
978
|
}
|
|
909
|
-
|
|
910
|
-
const value = runComponent(impl);
|
|
979
|
+
const value = enqueueComponentRun(ctx);
|
|
911
980
|
if (isPromiseLike(value)) {
|
|
912
|
-
return value.then((value) =>
|
|
981
|
+
return value.then((value) => ctx.renderer.read(value));
|
|
913
982
|
}
|
|
914
|
-
return
|
|
983
|
+
return ctx.renderer.read(value);
|
|
915
984
|
}
|
|
916
985
|
/**
|
|
917
986
|
* Registers a callback which fires when the component commits. Will only
|
|
918
987
|
* fire once per callback and update.
|
|
919
988
|
*/
|
|
920
989
|
schedule(callback) {
|
|
921
|
-
const
|
|
922
|
-
let callbacks = scheduleMap.get(
|
|
990
|
+
const ctx = this[_ContextImpl];
|
|
991
|
+
let callbacks = scheduleMap.get(ctx);
|
|
923
992
|
if (!callbacks) {
|
|
924
993
|
callbacks = new Set();
|
|
925
|
-
scheduleMap.set(
|
|
994
|
+
scheduleMap.set(ctx, callbacks);
|
|
926
995
|
}
|
|
927
996
|
callbacks.add(callback);
|
|
928
997
|
}
|
|
@@ -931,19 +1000,19 @@
|
|
|
931
1000
|
* rendered into the root. Will only fire once per callback and render.
|
|
932
1001
|
*/
|
|
933
1002
|
flush(callback) {
|
|
934
|
-
const
|
|
935
|
-
if (typeof
|
|
1003
|
+
const ctx = this[_ContextImpl];
|
|
1004
|
+
if (typeof ctx.root !== "object" || ctx.root === null) {
|
|
936
1005
|
return;
|
|
937
1006
|
}
|
|
938
|
-
let flushMap = flushMaps.get(
|
|
1007
|
+
let flushMap = flushMaps.get(ctx.root);
|
|
939
1008
|
if (!flushMap) {
|
|
940
1009
|
flushMap = new Map();
|
|
941
|
-
flushMaps.set(
|
|
1010
|
+
flushMaps.set(ctx.root, flushMap);
|
|
942
1011
|
}
|
|
943
|
-
let callbacks = flushMap.get(
|
|
1012
|
+
let callbacks = flushMap.get(ctx);
|
|
944
1013
|
if (!callbacks) {
|
|
945
1014
|
callbacks = new Set();
|
|
946
|
-
flushMap.set(
|
|
1015
|
+
flushMap.set(ctx, callbacks);
|
|
947
1016
|
}
|
|
948
1017
|
callbacks.add(callback);
|
|
949
1018
|
}
|
|
@@ -952,45 +1021,45 @@
|
|
|
952
1021
|
* fire once per callback.
|
|
953
1022
|
*/
|
|
954
1023
|
cleanup(callback) {
|
|
955
|
-
const
|
|
956
|
-
let callbacks = cleanupMap.get(
|
|
1024
|
+
const ctx = this[_ContextImpl];
|
|
1025
|
+
let callbacks = cleanupMap.get(ctx);
|
|
957
1026
|
if (!callbacks) {
|
|
958
1027
|
callbacks = new Set();
|
|
959
|
-
cleanupMap.set(
|
|
1028
|
+
cleanupMap.set(ctx, callbacks);
|
|
960
1029
|
}
|
|
961
1030
|
callbacks.add(callback);
|
|
962
1031
|
}
|
|
963
1032
|
consume(key) {
|
|
964
|
-
for (let
|
|
965
|
-
const provisions = provisionMaps.get(
|
|
1033
|
+
for (let ctx = this[_ContextImpl].parent; ctx !== undefined; ctx = ctx.parent) {
|
|
1034
|
+
const provisions = provisionMaps.get(ctx);
|
|
966
1035
|
if (provisions && provisions.has(key)) {
|
|
967
1036
|
return provisions.get(key);
|
|
968
1037
|
}
|
|
969
1038
|
}
|
|
970
1039
|
}
|
|
971
1040
|
provide(key, value) {
|
|
972
|
-
const
|
|
973
|
-
let provisions = provisionMaps.get(
|
|
1041
|
+
const ctx = this[_ContextImpl];
|
|
1042
|
+
let provisions = provisionMaps.get(ctx);
|
|
974
1043
|
if (!provisions) {
|
|
975
1044
|
provisions = new Map();
|
|
976
|
-
provisionMaps.set(
|
|
1045
|
+
provisionMaps.set(ctx, provisions);
|
|
977
1046
|
}
|
|
978
1047
|
provisions.set(key, value);
|
|
979
1048
|
}
|
|
980
1049
|
addEventListener(type, listener, options) {
|
|
981
|
-
const
|
|
1050
|
+
const ctx = this[_ContextImpl];
|
|
982
1051
|
let listeners;
|
|
983
1052
|
if (!isListenerOrListenerObject(listener)) {
|
|
984
1053
|
return;
|
|
985
1054
|
}
|
|
986
1055
|
else {
|
|
987
|
-
const listeners1 = listenersMap.get(
|
|
1056
|
+
const listeners1 = listenersMap.get(ctx);
|
|
988
1057
|
if (listeners1) {
|
|
989
1058
|
listeners = listeners1;
|
|
990
1059
|
}
|
|
991
1060
|
else {
|
|
992
1061
|
listeners = [];
|
|
993
|
-
listenersMap.set(
|
|
1062
|
+
listenersMap.set(ctx, listeners);
|
|
994
1063
|
}
|
|
995
1064
|
}
|
|
996
1065
|
options = normalizeListenerOptions(options);
|
|
@@ -1001,7 +1070,7 @@
|
|
|
1001
1070
|
else {
|
|
1002
1071
|
callback = listener;
|
|
1003
1072
|
}
|
|
1004
|
-
const record = { type,
|
|
1073
|
+
const record = { type, listener, callback, options };
|
|
1005
1074
|
if (options.once) {
|
|
1006
1075
|
record.callback = function () {
|
|
1007
1076
|
const i = listeners.indexOf(record);
|
|
@@ -1018,15 +1087,15 @@
|
|
|
1018
1087
|
}
|
|
1019
1088
|
listeners.push(record);
|
|
1020
1089
|
// TODO: is it possible to separate out the EventTarget delegation logic
|
|
1021
|
-
for (const value of getChildValues(
|
|
1090
|
+
for (const value of getChildValues(ctx.ret)) {
|
|
1022
1091
|
if (isEventTarget(value)) {
|
|
1023
1092
|
value.addEventListener(record.type, record.callback, record.options);
|
|
1024
1093
|
}
|
|
1025
1094
|
}
|
|
1026
1095
|
}
|
|
1027
1096
|
removeEventListener(type, listener, options) {
|
|
1028
|
-
const
|
|
1029
|
-
const listeners = listenersMap.get(
|
|
1097
|
+
const ctx = this[_ContextImpl];
|
|
1098
|
+
const listeners = listenersMap.get(ctx);
|
|
1030
1099
|
if (listeners == null || !isListenerOrListenerObject(listener)) {
|
|
1031
1100
|
return;
|
|
1032
1101
|
}
|
|
@@ -1040,16 +1109,16 @@
|
|
|
1040
1109
|
const record = listeners[i];
|
|
1041
1110
|
listeners.splice(i, 1);
|
|
1042
1111
|
// TODO: is it possible to separate out the EventTarget delegation logic
|
|
1043
|
-
for (const value of getChildValues(
|
|
1112
|
+
for (const value of getChildValues(ctx.ret)) {
|
|
1044
1113
|
if (isEventTarget(value)) {
|
|
1045
1114
|
value.removeEventListener(record.type, record.callback, record.options);
|
|
1046
1115
|
}
|
|
1047
1116
|
}
|
|
1048
1117
|
}
|
|
1049
1118
|
dispatchEvent(ev) {
|
|
1050
|
-
const
|
|
1119
|
+
const ctx = this[_ContextImpl];
|
|
1051
1120
|
const path = [];
|
|
1052
|
-
for (let parent =
|
|
1121
|
+
for (let parent = ctx.parent; parent !== undefined; parent = parent.parent) {
|
|
1053
1122
|
path.push(parent);
|
|
1054
1123
|
}
|
|
1055
1124
|
// We patch the stopImmediatePropagation method because ev.cancelBubble
|
|
@@ -1061,7 +1130,7 @@
|
|
|
1061
1130
|
immediateCancelBubble = true;
|
|
1062
1131
|
return stopImmediatePropagation.call(ev);
|
|
1063
1132
|
});
|
|
1064
|
-
setEventProperty(ev, "target",
|
|
1133
|
+
setEventProperty(ev, "target", ctx.owner);
|
|
1065
1134
|
// The only possible errors in this block are errors thrown by callbacks,
|
|
1066
1135
|
// and dispatchEvent will only log these errors rather than throwing
|
|
1067
1136
|
// them. Therefore, we place all code in a try block, log errors in the
|
|
@@ -1070,18 +1139,21 @@
|
|
|
1070
1139
|
// Each early return within the try block returns true because while the
|
|
1071
1140
|
// return value is overridden in the finally block, TypeScript
|
|
1072
1141
|
// (justifiably) does not recognize the unsafe return statement.
|
|
1073
|
-
//
|
|
1074
|
-
// TODO: Run all callbacks even if one of them errors
|
|
1075
1142
|
try {
|
|
1076
1143
|
setEventProperty(ev, "eventPhase", CAPTURING_PHASE);
|
|
1077
1144
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
1078
1145
|
const target = path[i];
|
|
1079
1146
|
const listeners = listenersMap.get(target);
|
|
1080
1147
|
if (listeners) {
|
|
1081
|
-
setEventProperty(ev, "currentTarget", target.
|
|
1148
|
+
setEventProperty(ev, "currentTarget", target.owner);
|
|
1082
1149
|
for (const record of listeners) {
|
|
1083
1150
|
if (record.type === ev.type && record.options.capture) {
|
|
1084
|
-
|
|
1151
|
+
try {
|
|
1152
|
+
record.callback.call(target.owner, ev);
|
|
1153
|
+
}
|
|
1154
|
+
catch (err) {
|
|
1155
|
+
console.error(err);
|
|
1156
|
+
}
|
|
1085
1157
|
if (immediateCancelBubble) {
|
|
1086
1158
|
return true;
|
|
1087
1159
|
}
|
|
@@ -1093,13 +1165,25 @@
|
|
|
1093
1165
|
}
|
|
1094
1166
|
}
|
|
1095
1167
|
{
|
|
1096
|
-
|
|
1168
|
+
setEventProperty(ev, "eventPhase", AT_TARGET);
|
|
1169
|
+
setEventProperty(ev, "currentTarget", ctx.owner);
|
|
1170
|
+
const propCallback = ctx.ret.el.props["on" + ev.type];
|
|
1171
|
+
if (propCallback != null) {
|
|
1172
|
+
propCallback(ev);
|
|
1173
|
+
if (immediateCancelBubble || ev.cancelBubble) {
|
|
1174
|
+
return true;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
const listeners = listenersMap.get(ctx);
|
|
1097
1178
|
if (listeners) {
|
|
1098
|
-
setEventProperty(ev, "eventPhase", AT_TARGET);
|
|
1099
|
-
setEventProperty(ev, "currentTarget", impl.ctx);
|
|
1100
1179
|
for (const record of listeners) {
|
|
1101
1180
|
if (record.type === ev.type) {
|
|
1102
|
-
|
|
1181
|
+
try {
|
|
1182
|
+
record.callback.call(ctx.owner, ev);
|
|
1183
|
+
}
|
|
1184
|
+
catch (err) {
|
|
1185
|
+
console.error(err);
|
|
1186
|
+
}
|
|
1103
1187
|
if (immediateCancelBubble) {
|
|
1104
1188
|
return true;
|
|
1105
1189
|
}
|
|
@@ -1116,10 +1200,15 @@
|
|
|
1116
1200
|
const target = path[i];
|
|
1117
1201
|
const listeners = listenersMap.get(target);
|
|
1118
1202
|
if (listeners) {
|
|
1119
|
-
setEventProperty(ev, "currentTarget", target.
|
|
1203
|
+
setEventProperty(ev, "currentTarget", target.owner);
|
|
1120
1204
|
for (const record of listeners) {
|
|
1121
1205
|
if (record.type === ev.type && !record.options.capture) {
|
|
1122
|
-
|
|
1206
|
+
try {
|
|
1207
|
+
record.callback.call(target.owner, ev);
|
|
1208
|
+
}
|
|
1209
|
+
catch (err) {
|
|
1210
|
+
console.error(err);
|
|
1211
|
+
}
|
|
1123
1212
|
if (immediateCancelBubble) {
|
|
1124
1213
|
return true;
|
|
1125
1214
|
}
|
|
@@ -1132,10 +1221,6 @@
|
|
|
1132
1221
|
}
|
|
1133
1222
|
}
|
|
1134
1223
|
}
|
|
1135
|
-
catch (err) {
|
|
1136
|
-
// TODO: Use setTimeout to rethrow the error.
|
|
1137
|
-
console.error(err);
|
|
1138
|
-
}
|
|
1139
1224
|
finally {
|
|
1140
1225
|
setEventProperty(ev, "eventPhase", NONE);
|
|
1141
1226
|
setEventProperty(ev, "currentTarget", null);
|
|
@@ -1153,42 +1238,47 @@
|
|
|
1153
1238
|
}
|
|
1154
1239
|
return false;
|
|
1155
1240
|
}
|
|
1156
|
-
function updateComponent(renderer, root, host, parent, scope, ret, oldProps) {
|
|
1241
|
+
function updateComponent(renderer, root, host, parent, scope, ret, oldProps, hydrationData) {
|
|
1157
1242
|
let ctx;
|
|
1158
1243
|
if (oldProps) {
|
|
1159
1244
|
ctx = ret.ctx;
|
|
1160
|
-
if (ctx.f &
|
|
1245
|
+
if (ctx.f & IsSyncExecuting) {
|
|
1161
1246
|
console.error("Component is already executing");
|
|
1162
|
-
return ret.
|
|
1247
|
+
return ret.cachedChildValues;
|
|
1163
1248
|
}
|
|
1164
1249
|
}
|
|
1165
1250
|
else {
|
|
1166
1251
|
ctx = ret.ctx = new ContextImpl(renderer, root, host, parent, scope, ret);
|
|
1167
1252
|
}
|
|
1168
1253
|
ctx.f |= IsUpdating;
|
|
1169
|
-
|
|
1170
|
-
return runComponent(ctx);
|
|
1254
|
+
return enqueueComponentRun(ctx, hydrationData);
|
|
1171
1255
|
}
|
|
1172
|
-
function updateComponentChildren(ctx, children) {
|
|
1173
|
-
if (ctx.f & IsUnmounted
|
|
1256
|
+
function updateComponentChildren(ctx, children, hydrationData) {
|
|
1257
|
+
if (ctx.f & IsUnmounted) {
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
else if (ctx.f & IsErrored) {
|
|
1261
|
+
// This branch is necessary for some race conditions where this function is
|
|
1262
|
+
// called after iterator.throw() in async generator components.
|
|
1174
1263
|
return;
|
|
1175
1264
|
}
|
|
1176
1265
|
else if (children === undefined) {
|
|
1177
1266
|
console.error("A component has returned or yielded undefined. If this was intentional, return or yield null instead.");
|
|
1178
1267
|
}
|
|
1179
1268
|
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
1269
|
try {
|
|
1184
|
-
|
|
1270
|
+
// TODO: WAT
|
|
1271
|
+
// We set the isExecuting flag in case a child component dispatches an event
|
|
1272
|
+
// which bubbles to this component and causes a synchronous refresh().
|
|
1273
|
+
ctx.f |= IsSyncExecuting;
|
|
1274
|
+
childValues = diffChildren(ctx.renderer, ctx.root, ctx.host, ctx, ctx.scope, ctx.ret, narrow(children), hydrationData);
|
|
1185
1275
|
}
|
|
1186
1276
|
finally {
|
|
1187
|
-
ctx.f &= ~
|
|
1277
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1188
1278
|
}
|
|
1189
1279
|
if (isPromiseLike(childValues)) {
|
|
1190
|
-
ctx.ret.
|
|
1191
|
-
return ctx.ret.
|
|
1280
|
+
ctx.ret.inflightValue = childValues.then((childValues) => commitComponent(ctx, childValues));
|
|
1281
|
+
return ctx.ret.inflightValue;
|
|
1192
1282
|
}
|
|
1193
1283
|
return commitComponent(ctx, childValues);
|
|
1194
1284
|
}
|
|
@@ -1208,8 +1298,8 @@
|
|
|
1208
1298
|
}
|
|
1209
1299
|
}
|
|
1210
1300
|
}
|
|
1211
|
-
const oldValues = wrap(ctx.ret.
|
|
1212
|
-
let value = (ctx.ret.
|
|
1301
|
+
const oldValues = wrap(ctx.ret.cachedChildValues);
|
|
1302
|
+
let value = (ctx.ret.cachedChildValues = unwrap(values));
|
|
1213
1303
|
if (ctx.f & IsScheduling) {
|
|
1214
1304
|
ctx.f |= IsSchedulingRefresh;
|
|
1215
1305
|
}
|
|
@@ -1217,7 +1307,7 @@
|
|
|
1217
1307
|
// If we’re not updating the component, which happens when components are
|
|
1218
1308
|
// refreshed, or when async generator components iterate, we have to do a
|
|
1219
1309
|
// little bit housekeeping when a component’s child values have changed.
|
|
1220
|
-
if (!
|
|
1310
|
+
if (!arrayEqual(oldValues, values)) {
|
|
1221
1311
|
const records = getListenerRecords(ctx.parent, ctx.host);
|
|
1222
1312
|
if (records.length) {
|
|
1223
1313
|
for (let i = 0; i < values.length; i++) {
|
|
@@ -1232,7 +1322,7 @@
|
|
|
1232
1322
|
}
|
|
1233
1323
|
// rearranging the nearest ancestor host element
|
|
1234
1324
|
const host = ctx.host;
|
|
1235
|
-
const oldHostValues = wrap(host.
|
|
1325
|
+
const oldHostValues = wrap(host.cachedChildValues);
|
|
1236
1326
|
invalidate(ctx, host);
|
|
1237
1327
|
const hostValues = getChildValues(host);
|
|
1238
1328
|
ctx.renderer.arrange(host.el.tag, host.value, host.el.props, hostValues,
|
|
@@ -1261,50 +1351,79 @@
|
|
|
1261
1351
|
}
|
|
1262
1352
|
function invalidate(ctx, host) {
|
|
1263
1353
|
for (let parent = ctx.parent; parent !== undefined && parent.host === host; parent = parent.parent) {
|
|
1264
|
-
parent.ret.
|
|
1354
|
+
parent.ret.cachedChildValues = undefined;
|
|
1265
1355
|
}
|
|
1266
|
-
host.
|
|
1356
|
+
host.cachedChildValues = undefined;
|
|
1267
1357
|
}
|
|
1268
|
-
function
|
|
1269
|
-
if (
|
|
1358
|
+
function arrayEqual(arr1, arr2) {
|
|
1359
|
+
if (arr1.length !== arr2.length) {
|
|
1270
1360
|
return false;
|
|
1271
1361
|
}
|
|
1272
|
-
for (let i = 0; i <
|
|
1273
|
-
const value1 =
|
|
1274
|
-
const value2 =
|
|
1362
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
1363
|
+
const value1 = arr1[i];
|
|
1364
|
+
const value2 = arr2[i];
|
|
1275
1365
|
if (value1 !== value2) {
|
|
1276
1366
|
return false;
|
|
1277
1367
|
}
|
|
1278
1368
|
}
|
|
1279
1369
|
return true;
|
|
1280
1370
|
}
|
|
1281
|
-
/**
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1371
|
+
/** Enqueues and executes the component associated with the context. */
|
|
1372
|
+
function enqueueComponentRun(ctx, hydrationData) {
|
|
1373
|
+
if (ctx.f & IsAsyncGen && !(ctx.f & IsInForOfLoop)) {
|
|
1374
|
+
if (hydrationData !== undefined) {
|
|
1375
|
+
throw new Error("Hydration error");
|
|
1376
|
+
}
|
|
1377
|
+
// This branch will run for non-initial renders of async generator
|
|
1378
|
+
// components when they are not in for...of loops. When in a for...of loop,
|
|
1379
|
+
// async generator components will behave normally.
|
|
1380
|
+
//
|
|
1381
|
+
// Async gen componennts can be in one of three states:
|
|
1382
|
+
//
|
|
1383
|
+
// 1. propsAvailable flag is true: "available"
|
|
1384
|
+
//
|
|
1385
|
+
// The component is suspended somewhere in the loop. When the component
|
|
1386
|
+
// reaches the bottom of the loop, it will run again with the next props.
|
|
1387
|
+
//
|
|
1388
|
+
// 2. onAvailable callback is defined: "suspended"
|
|
1389
|
+
//
|
|
1390
|
+
// The component has suspended at the bottom of the loop and is waiting
|
|
1391
|
+
// for new props.
|
|
1392
|
+
//
|
|
1393
|
+
// 3. neither 1 or 2: "Running"
|
|
1394
|
+
//
|
|
1395
|
+
// The component is suspended somewhere in the loop. When the component
|
|
1396
|
+
// reaches the bottom of the loop, it will suspend.
|
|
1397
|
+
//
|
|
1398
|
+
// Components will never be both available and suspended at
|
|
1399
|
+
// the same time.
|
|
1400
|
+
//
|
|
1401
|
+
// If the component is at the loop bottom, this means that the next value
|
|
1402
|
+
// produced by the component will have the most up to date props, so we can
|
|
1403
|
+
// simply return the current inflight value. Otherwise, we have to wait for
|
|
1404
|
+
// the bottom of the loop to be reached before returning the inflight
|
|
1405
|
+
// value.
|
|
1406
|
+
const isAtLoopbottom = ctx.f & IsInForAwaitOfLoop && !ctx.onProps;
|
|
1407
|
+
resumePropsIterator(ctx);
|
|
1408
|
+
if (isAtLoopbottom) {
|
|
1409
|
+
if (ctx.inflightBlock == null) {
|
|
1410
|
+
ctx.inflightBlock = new Promise((resolve) => (ctx.onPropsRequested = resolve));
|
|
1411
|
+
}
|
|
1412
|
+
return ctx.inflightBlock.then(() => {
|
|
1413
|
+
ctx.inflightBlock = undefined;
|
|
1414
|
+
return ctx.inflightValue;
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
return ctx.inflightValue;
|
|
1418
|
+
}
|
|
1419
|
+
else if (!ctx.inflightBlock) {
|
|
1299
1420
|
try {
|
|
1300
|
-
const [block, value] =
|
|
1421
|
+
const [block, value] = runComponent(ctx, hydrationData);
|
|
1301
1422
|
if (block) {
|
|
1302
1423
|
ctx.inflightBlock = block
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
}
|
|
1307
|
-
})
|
|
1424
|
+
// TODO: there is some fuckery going on here related to async
|
|
1425
|
+
// generator components resuming when they’re meant to be returned.
|
|
1426
|
+
.then((v) => v)
|
|
1308
1427
|
.finally(() => advanceComponent(ctx));
|
|
1309
1428
|
// stepComponent will only return a block if the value is asynchronous
|
|
1310
1429
|
ctx.inflightValue = value;
|
|
@@ -1318,38 +1437,46 @@
|
|
|
1318
1437
|
throw err;
|
|
1319
1438
|
}
|
|
1320
1439
|
}
|
|
1321
|
-
else if (ctx.f & IsAsyncGen) {
|
|
1322
|
-
return ctx.inflightValue;
|
|
1323
|
-
}
|
|
1324
1440
|
else if (!ctx.enqueuedBlock) {
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1441
|
+
if (hydrationData !== undefined) {
|
|
1442
|
+
throw new Error("Hydration error");
|
|
1443
|
+
}
|
|
1444
|
+
// We need to assign enqueuedBlock and enqueuedValue synchronously, hence
|
|
1445
|
+
// the Promise constructor call here.
|
|
1446
|
+
let resolveEnqueuedBlock;
|
|
1447
|
+
ctx.enqueuedBlock = new Promise((resolve) => (resolveEnqueuedBlock = resolve));
|
|
1448
|
+
ctx.enqueuedValue = ctx.inflightBlock.then(() => {
|
|
1328
1449
|
try {
|
|
1329
|
-
const [block, value] =
|
|
1330
|
-
resolve(value);
|
|
1450
|
+
const [block, value] = runComponent(ctx);
|
|
1331
1451
|
if (block) {
|
|
1332
|
-
|
|
1333
|
-
if (!(ctx.f & IsUpdating)) {
|
|
1334
|
-
return propagateError(ctx.parent, err);
|
|
1335
|
-
}
|
|
1336
|
-
});
|
|
1452
|
+
resolveEnqueuedBlock(block.finally(() => advanceComponent(ctx)));
|
|
1337
1453
|
}
|
|
1454
|
+
return value;
|
|
1338
1455
|
}
|
|
1339
1456
|
catch (err) {
|
|
1340
1457
|
if (!(ctx.f & IsUpdating)) {
|
|
1341
1458
|
return propagateError(ctx.parent, err);
|
|
1342
1459
|
}
|
|
1460
|
+
throw err;
|
|
1343
1461
|
}
|
|
1344
|
-
})
|
|
1345
|
-
.finally(() => advanceComponent(ctx));
|
|
1346
|
-
ctx.enqueuedValue = new Promise((resolve1) => (resolve = resolve1));
|
|
1462
|
+
});
|
|
1347
1463
|
}
|
|
1348
1464
|
return ctx.enqueuedValue;
|
|
1349
1465
|
}
|
|
1466
|
+
/** Called when the inflight block promise settles. */
|
|
1467
|
+
function advanceComponent(ctx) {
|
|
1468
|
+
if (ctx.f & IsAsyncGen && !(ctx.f & IsInForOfLoop)) {
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
ctx.inflightBlock = ctx.enqueuedBlock;
|
|
1472
|
+
ctx.inflightValue = ctx.enqueuedValue;
|
|
1473
|
+
ctx.enqueuedBlock = undefined;
|
|
1474
|
+
ctx.enqueuedValue = undefined;
|
|
1475
|
+
}
|
|
1350
1476
|
/**
|
|
1351
1477
|
* This function is responsible for executing the component and handling all
|
|
1352
|
-
* the different component types.
|
|
1478
|
+
* the different component types. We cannot identify whether a component is a
|
|
1479
|
+
* generator or async without calling it and inspecting the return value.
|
|
1353
1480
|
*
|
|
1354
1481
|
* @returns {[block, value]} A tuple where
|
|
1355
1482
|
* block - A possible promise which represents the duration during which the
|
|
@@ -1364,25 +1491,23 @@
|
|
|
1364
1491
|
* - Sync generator components block while any children are executing, because
|
|
1365
1492
|
* they are expected to only resume when they’ve actually rendered.
|
|
1366
1493
|
*/
|
|
1367
|
-
function
|
|
1494
|
+
function runComponent(ctx, hydrationData) {
|
|
1368
1495
|
const ret = ctx.ret;
|
|
1369
|
-
if (ctx.f & IsDone) {
|
|
1370
|
-
return [undefined, getValue(ret)];
|
|
1371
|
-
}
|
|
1372
1496
|
const initial = !ctx.iterator;
|
|
1373
1497
|
if (initial) {
|
|
1374
|
-
ctx
|
|
1498
|
+
resumePropsIterator(ctx);
|
|
1499
|
+
ctx.f |= IsSyncExecuting;
|
|
1375
1500
|
clearEventListeners(ctx);
|
|
1376
1501
|
let result;
|
|
1377
1502
|
try {
|
|
1378
|
-
result = ret.el.tag.call(ctx.
|
|
1503
|
+
result = ret.el.tag.call(ctx.owner, ret.el.props);
|
|
1379
1504
|
}
|
|
1380
1505
|
catch (err) {
|
|
1381
1506
|
ctx.f |= IsErrored;
|
|
1382
1507
|
throw err;
|
|
1383
1508
|
}
|
|
1384
1509
|
finally {
|
|
1385
|
-
ctx.f &= ~
|
|
1510
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1386
1511
|
}
|
|
1387
1512
|
if (isIteratorLike(result)) {
|
|
1388
1513
|
ctx.iterator = result;
|
|
@@ -1390,124 +1515,241 @@
|
|
|
1390
1515
|
else if (isPromiseLike(result)) {
|
|
1391
1516
|
// async function component
|
|
1392
1517
|
const result1 = result instanceof Promise ? result : Promise.resolve(result);
|
|
1393
|
-
const value = result1.then((result) => updateComponentChildren(ctx, result), (err) => {
|
|
1518
|
+
const value = result1.then((result) => updateComponentChildren(ctx, result, hydrationData), (err) => {
|
|
1394
1519
|
ctx.f |= IsErrored;
|
|
1395
1520
|
throw err;
|
|
1396
1521
|
});
|
|
1397
|
-
return [result1, value];
|
|
1522
|
+
return [result1.catch(NOOP), value];
|
|
1398
1523
|
}
|
|
1399
1524
|
else {
|
|
1400
1525
|
// sync function component
|
|
1401
|
-
return [
|
|
1526
|
+
return [
|
|
1527
|
+
undefined,
|
|
1528
|
+
updateComponentChildren(ctx, result, hydrationData),
|
|
1529
|
+
];
|
|
1402
1530
|
}
|
|
1403
1531
|
}
|
|
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));
|
|
1532
|
+
else if (hydrationData !== undefined) {
|
|
1533
|
+
throw new Error("Hydration error");
|
|
1418
1534
|
}
|
|
1419
1535
|
let iteration;
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1536
|
+
if (initial) {
|
|
1537
|
+
try {
|
|
1538
|
+
ctx.f |= IsSyncExecuting;
|
|
1539
|
+
iteration = ctx.iterator.next();
|
|
1540
|
+
}
|
|
1541
|
+
catch (err) {
|
|
1542
|
+
ctx.f |= IsErrored;
|
|
1543
|
+
throw err;
|
|
1544
|
+
}
|
|
1545
|
+
finally {
|
|
1546
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1547
|
+
}
|
|
1548
|
+
if (isPromiseLike(iteration)) {
|
|
1549
|
+
ctx.f |= IsAsyncGen;
|
|
1550
|
+
}
|
|
1551
|
+
else {
|
|
1552
|
+
ctx.f |= IsSyncGen;
|
|
1553
|
+
}
|
|
1427
1554
|
}
|
|
1428
|
-
|
|
1429
|
-
ctx.f &= ~
|
|
1555
|
+
if (ctx.f & IsSyncGen) {
|
|
1556
|
+
ctx.f &= ~NeedsToYield;
|
|
1557
|
+
// sync generator component
|
|
1558
|
+
if (!initial) {
|
|
1559
|
+
try {
|
|
1560
|
+
ctx.f |= IsSyncExecuting;
|
|
1561
|
+
iteration = ctx.iterator.next(ctx.renderer.read(getValue(ret)));
|
|
1562
|
+
}
|
|
1563
|
+
catch (err) {
|
|
1564
|
+
ctx.f |= IsErrored;
|
|
1565
|
+
throw err;
|
|
1566
|
+
}
|
|
1567
|
+
finally {
|
|
1568
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
if (isPromiseLike(iteration)) {
|
|
1572
|
+
throw new Error("Mixed generator component");
|
|
1573
|
+
}
|
|
1574
|
+
if (iteration.done) {
|
|
1575
|
+
ctx.f &= ~IsSyncGen;
|
|
1576
|
+
ctx.iterator = undefined;
|
|
1577
|
+
}
|
|
1578
|
+
let value;
|
|
1579
|
+
try {
|
|
1580
|
+
value = updateComponentChildren(ctx,
|
|
1581
|
+
// Children can be void so we eliminate that here
|
|
1582
|
+
iteration.value, hydrationData);
|
|
1583
|
+
if (isPromiseLike(value)) {
|
|
1584
|
+
value = value.catch((err) => handleChildError(ctx, err));
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
catch (err) {
|
|
1588
|
+
value = handleChildError(ctx, err);
|
|
1589
|
+
}
|
|
1590
|
+
const block = isPromiseLike(value) ? value.catch(NOOP) : undefined;
|
|
1591
|
+
return [block, value];
|
|
1430
1592
|
}
|
|
1431
|
-
if (
|
|
1432
|
-
//
|
|
1433
|
-
|
|
1434
|
-
|
|
1593
|
+
else if (ctx.f & IsInForOfLoop) {
|
|
1594
|
+
// TODO: does this need to be done async?
|
|
1595
|
+
ctx.f &= ~NeedsToYield;
|
|
1596
|
+
// we are in a for...of loop for async generator
|
|
1597
|
+
if (!initial) {
|
|
1598
|
+
try {
|
|
1599
|
+
ctx.f |= IsSyncExecuting;
|
|
1600
|
+
iteration = ctx.iterator.next(ctx.renderer.read(getValue(ret)));
|
|
1601
|
+
}
|
|
1602
|
+
catch (err) {
|
|
1603
|
+
ctx.f |= IsErrored;
|
|
1604
|
+
throw err;
|
|
1605
|
+
}
|
|
1606
|
+
finally {
|
|
1607
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1608
|
+
}
|
|
1435
1609
|
}
|
|
1610
|
+
if (!isPromiseLike(iteration)) {
|
|
1611
|
+
throw new Error("Mixed generator component");
|
|
1612
|
+
}
|
|
1613
|
+
const block = iteration.catch(NOOP);
|
|
1436
1614
|
const value = iteration.then((iteration) => {
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
ctx.f &= ~IsIterating;
|
|
1441
|
-
if (iteration.done) {
|
|
1442
|
-
ctx.f |= IsDone;
|
|
1615
|
+
let value;
|
|
1616
|
+
if (!(ctx.f & IsInForOfLoop)) {
|
|
1617
|
+
runAsyncGenComponent(ctx, Promise.resolve(iteration), hydrationData);
|
|
1443
1618
|
}
|
|
1444
1619
|
try {
|
|
1445
|
-
|
|
1620
|
+
value = updateComponentChildren(ctx,
|
|
1621
|
+
// Children can be void so we eliminate that here
|
|
1622
|
+
iteration.value, hydrationData);
|
|
1446
1623
|
if (isPromiseLike(value)) {
|
|
1447
|
-
|
|
1624
|
+
value = value.catch((err) => handleChildError(ctx, err));
|
|
1448
1625
|
}
|
|
1449
|
-
return value;
|
|
1450
1626
|
}
|
|
1451
1627
|
catch (err) {
|
|
1452
|
-
|
|
1628
|
+
value = handleChildError(ctx, err);
|
|
1453
1629
|
}
|
|
1630
|
+
return value;
|
|
1454
1631
|
}, (err) => {
|
|
1455
|
-
ctx.f |=
|
|
1632
|
+
ctx.f |= IsErrored;
|
|
1456
1633
|
throw err;
|
|
1457
1634
|
});
|
|
1458
|
-
return [
|
|
1635
|
+
return [block, value];
|
|
1459
1636
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
ctx.f &= ~IsIterating;
|
|
1465
|
-
if (iteration.done) {
|
|
1466
|
-
ctx.f |= IsDone;
|
|
1637
|
+
else {
|
|
1638
|
+
runAsyncGenComponent(ctx, iteration, hydrationData);
|
|
1639
|
+
// async generator component
|
|
1640
|
+
return [ctx.inflightBlock, ctx.inflightValue];
|
|
1467
1641
|
}
|
|
1468
|
-
|
|
1642
|
+
}
|
|
1643
|
+
async function runAsyncGenComponent(ctx, iterationP, hydrationData) {
|
|
1644
|
+
let done = false;
|
|
1469
1645
|
try {
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1646
|
+
while (!done) {
|
|
1647
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1648
|
+
break;
|
|
1649
|
+
}
|
|
1650
|
+
// inflightValue must be set synchronously.
|
|
1651
|
+
let onValue;
|
|
1652
|
+
ctx.inflightValue = new Promise((resolve) => (onValue = resolve));
|
|
1653
|
+
if (ctx.f & IsUpdating) {
|
|
1654
|
+
// We should not swallow unhandled promise rejections if the component is
|
|
1655
|
+
// updating independently.
|
|
1656
|
+
// TODO: Does this handle this.refresh() calls?
|
|
1657
|
+
ctx.inflightValue.catch(NOOP);
|
|
1658
|
+
}
|
|
1659
|
+
let iteration;
|
|
1660
|
+
try {
|
|
1661
|
+
iteration = await iterationP;
|
|
1662
|
+
}
|
|
1663
|
+
catch (err) {
|
|
1664
|
+
done = true;
|
|
1665
|
+
ctx.f |= IsErrored;
|
|
1666
|
+
onValue(Promise.reject(err));
|
|
1667
|
+
break;
|
|
1668
|
+
}
|
|
1669
|
+
finally {
|
|
1670
|
+
ctx.f &= ~NeedsToYield;
|
|
1671
|
+
if (!(ctx.f & IsInForAwaitOfLoop)) {
|
|
1672
|
+
ctx.f &= ~PropsAvailable;
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
done = !!iteration.done;
|
|
1676
|
+
let value;
|
|
1677
|
+
try {
|
|
1678
|
+
value = updateComponentChildren(ctx, iteration.value, hydrationData);
|
|
1679
|
+
hydrationData = undefined;
|
|
1680
|
+
if (isPromiseLike(value)) {
|
|
1681
|
+
value = value.catch((err) => handleChildError(ctx, err));
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
catch (err) {
|
|
1685
|
+
// Do we need to catch potential errors here in the case of unhandled
|
|
1686
|
+
// promise rejections?
|
|
1687
|
+
value = handleChildError(ctx, err);
|
|
1688
|
+
}
|
|
1689
|
+
finally {
|
|
1690
|
+
onValue(value);
|
|
1691
|
+
}
|
|
1692
|
+
// TODO: this can be done more elegantly
|
|
1693
|
+
let oldValue;
|
|
1694
|
+
if (ctx.ret.inflightValue) {
|
|
1695
|
+
// The value passed back into the generator as the argument to the next
|
|
1696
|
+
// method is a promise if an async generator component has async
|
|
1697
|
+
// children. Sync generator components only resume when their children
|
|
1698
|
+
// have fulfilled so the element’s inflight child values will never be
|
|
1699
|
+
// defined.
|
|
1700
|
+
oldValue = ctx.ret.inflightValue.then((value) => ctx.renderer.read(value), () => ctx.renderer.read(undefined));
|
|
1701
|
+
}
|
|
1702
|
+
else {
|
|
1703
|
+
oldValue = ctx.renderer.read(getValue(ctx.ret));
|
|
1704
|
+
}
|
|
1705
|
+
if (ctx.f & IsUnmounted) {
|
|
1706
|
+
if (ctx.f & IsInForAwaitOfLoop) {
|
|
1707
|
+
try {
|
|
1708
|
+
ctx.f |= IsSyncExecuting;
|
|
1709
|
+
iterationP = ctx.iterator.next(oldValue);
|
|
1710
|
+
}
|
|
1711
|
+
finally {
|
|
1712
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
else {
|
|
1716
|
+
returnComponent(ctx);
|
|
1717
|
+
break;
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
else if (!done && !(ctx.f & IsInForOfLoop)) {
|
|
1721
|
+
try {
|
|
1722
|
+
ctx.f |= IsSyncExecuting;
|
|
1723
|
+
iterationP = ctx.iterator.next(oldValue);
|
|
1724
|
+
}
|
|
1725
|
+
finally {
|
|
1726
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1473
1729
|
}
|
|
1474
1730
|
}
|
|
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);
|
|
1731
|
+
finally {
|
|
1732
|
+
if (done) {
|
|
1733
|
+
ctx.f &= ~IsAsyncGen;
|
|
1734
|
+
ctx.iterator = undefined;
|
|
1735
|
+
}
|
|
1493
1736
|
}
|
|
1494
1737
|
}
|
|
1495
1738
|
/**
|
|
1496
|
-
* Called to
|
|
1497
|
-
* generator components.
|
|
1739
|
+
* Called to resume the props async iterator for async generator components.
|
|
1498
1740
|
*/
|
|
1499
|
-
function
|
|
1500
|
-
if (ctx.
|
|
1501
|
-
ctx.
|
|
1502
|
-
ctx.
|
|
1741
|
+
function resumePropsIterator(ctx) {
|
|
1742
|
+
if (ctx.onProps) {
|
|
1743
|
+
ctx.onProps(ctx.ret.el.props);
|
|
1744
|
+
ctx.onProps = undefined;
|
|
1745
|
+
ctx.f &= ~PropsAvailable;
|
|
1503
1746
|
}
|
|
1504
1747
|
else {
|
|
1505
|
-
ctx.f |=
|
|
1748
|
+
ctx.f |= PropsAvailable;
|
|
1506
1749
|
}
|
|
1507
1750
|
}
|
|
1508
1751
|
// TODO: async unmounting
|
|
1509
1752
|
function unmountComponent(ctx) {
|
|
1510
|
-
ctx.f |= IsUnmounted;
|
|
1511
1753
|
clearEventListeners(ctx);
|
|
1512
1754
|
const callbacks = cleanupMap.get(ctx);
|
|
1513
1755
|
if (callbacks) {
|
|
@@ -1517,23 +1759,71 @@
|
|
|
1517
1759
|
callback(value);
|
|
1518
1760
|
}
|
|
1519
1761
|
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
ctx.f
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1762
|
+
ctx.f |= IsUnmounted;
|
|
1763
|
+
if (ctx.iterator) {
|
|
1764
|
+
if (ctx.f & IsSyncGen) {
|
|
1765
|
+
let value;
|
|
1766
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1767
|
+
value = enqueueComponentRun(ctx);
|
|
1768
|
+
}
|
|
1769
|
+
if (isPromiseLike(value)) {
|
|
1770
|
+
value.then(() => {
|
|
1771
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1772
|
+
unmountComponent(ctx);
|
|
1773
|
+
}
|
|
1774
|
+
else {
|
|
1775
|
+
returnComponent(ctx);
|
|
1776
|
+
}
|
|
1777
|
+
}, (err) => {
|
|
1778
|
+
propagateError(ctx.parent, err);
|
|
1779
|
+
});
|
|
1780
|
+
}
|
|
1781
|
+
else {
|
|
1782
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1783
|
+
unmountComponent(ctx);
|
|
1784
|
+
}
|
|
1785
|
+
else {
|
|
1786
|
+
returnComponent(ctx);
|
|
1529
1787
|
}
|
|
1530
1788
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1789
|
+
}
|
|
1790
|
+
else if (ctx.f & IsAsyncGen) {
|
|
1791
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1792
|
+
const value = enqueueComponentRun(ctx);
|
|
1793
|
+
value.then(() => {
|
|
1794
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1795
|
+
unmountComponent(ctx);
|
|
1796
|
+
}
|
|
1797
|
+
else {
|
|
1798
|
+
returnComponent(ctx);
|
|
1799
|
+
}
|
|
1800
|
+
}, (err) => {
|
|
1801
|
+
propagateError(ctx.parent, err);
|
|
1802
|
+
});
|
|
1803
|
+
}
|
|
1804
|
+
else {
|
|
1805
|
+
// The logic for unmounting async generator components is in the
|
|
1806
|
+
// runAsyncGenComponent function.
|
|
1807
|
+
resumePropsIterator(ctx);
|
|
1533
1808
|
}
|
|
1534
1809
|
}
|
|
1535
1810
|
}
|
|
1536
1811
|
}
|
|
1812
|
+
function returnComponent(ctx) {
|
|
1813
|
+
resumePropsIterator(ctx);
|
|
1814
|
+
if (ctx.iterator && typeof ctx.iterator.return === "function") {
|
|
1815
|
+
try {
|
|
1816
|
+
ctx.f |= IsSyncExecuting;
|
|
1817
|
+
const iteration = ctx.iterator.return();
|
|
1818
|
+
if (isPromiseLike(iteration)) {
|
|
1819
|
+
iteration.catch((err) => propagateError(ctx.parent, err));
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
finally {
|
|
1823
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1537
1827
|
/*** EVENT TARGET UTILITIES ***/
|
|
1538
1828
|
// EVENT PHASE CONSTANTS
|
|
1539
1829
|
// https://developer.mozilla.org/en-US/docs/Web/API/Event/eventPhase
|
|
@@ -1602,39 +1892,39 @@
|
|
|
1602
1892
|
}
|
|
1603
1893
|
}
|
|
1604
1894
|
/*** ERROR HANDLING UTILITIES ***/
|
|
1605
|
-
// TODO: generator components which throw errors should be recoverable
|
|
1606
1895
|
function handleChildError(ctx, err) {
|
|
1607
|
-
if (ctx.
|
|
1608
|
-
!ctx.iterator ||
|
|
1609
|
-
typeof ctx.iterator.throw !== "function") {
|
|
1896
|
+
if (!ctx.iterator || typeof ctx.iterator.throw !== "function") {
|
|
1610
1897
|
throw err;
|
|
1611
1898
|
}
|
|
1612
|
-
|
|
1899
|
+
resumePropsIterator(ctx);
|
|
1613
1900
|
let iteration;
|
|
1614
1901
|
try {
|
|
1615
|
-
ctx.f |=
|
|
1902
|
+
ctx.f |= IsSyncExecuting;
|
|
1616
1903
|
iteration = ctx.iterator.throw(err);
|
|
1617
1904
|
}
|
|
1618
1905
|
catch (err) {
|
|
1619
|
-
ctx.f |=
|
|
1906
|
+
ctx.f |= IsErrored;
|
|
1620
1907
|
throw err;
|
|
1621
1908
|
}
|
|
1622
1909
|
finally {
|
|
1623
|
-
ctx.f &= ~
|
|
1910
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1624
1911
|
}
|
|
1625
1912
|
if (isPromiseLike(iteration)) {
|
|
1626
1913
|
return iteration.then((iteration) => {
|
|
1627
1914
|
if (iteration.done) {
|
|
1628
|
-
ctx.f
|
|
1915
|
+
ctx.f &= ~IsAsyncGen;
|
|
1916
|
+
ctx.iterator = undefined;
|
|
1629
1917
|
}
|
|
1630
1918
|
return updateComponentChildren(ctx, iteration.value);
|
|
1631
1919
|
}, (err) => {
|
|
1632
|
-
ctx.f |=
|
|
1920
|
+
ctx.f |= IsErrored;
|
|
1633
1921
|
throw err;
|
|
1634
1922
|
});
|
|
1635
1923
|
}
|
|
1636
1924
|
if (iteration.done) {
|
|
1637
|
-
ctx.f
|
|
1925
|
+
ctx.f &= ~IsSyncGen;
|
|
1926
|
+
ctx.f &= ~IsAsyncGen;
|
|
1927
|
+
ctx.iterator = undefined;
|
|
1638
1928
|
}
|
|
1639
1929
|
return updateComponentChildren(ctx, iteration.value);
|
|
1640
1930
|
}
|
|
@@ -1657,44 +1947,58 @@
|
|
|
1657
1947
|
|
|
1658
1948
|
const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
1659
1949
|
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) {
|
|
1950
|
+
scope(xmlns, tag) {
|
|
1672
1951
|
// TODO: Should we handle xmlns???
|
|
1673
1952
|
switch (tag) {
|
|
1674
1953
|
case Portal:
|
|
1675
1954
|
case "foreignObject":
|
|
1676
|
-
|
|
1955
|
+
xmlns = undefined;
|
|
1956
|
+
break;
|
|
1677
1957
|
case "svg":
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
return scope;
|
|
1958
|
+
xmlns = SVG_NAMESPACE;
|
|
1959
|
+
break;
|
|
1681
1960
|
}
|
|
1961
|
+
return xmlns;
|
|
1682
1962
|
},
|
|
1683
|
-
create(tag, _props,
|
|
1963
|
+
create(tag, _props, xmlns) {
|
|
1684
1964
|
if (typeof tag !== "string") {
|
|
1685
1965
|
throw new Error(`Unknown tag: ${tag.toString()}`);
|
|
1686
1966
|
}
|
|
1687
1967
|
else if (tag.toLowerCase() === "svg") {
|
|
1688
|
-
|
|
1968
|
+
xmlns = SVG_NAMESPACE;
|
|
1969
|
+
}
|
|
1970
|
+
return xmlns
|
|
1971
|
+
? document.createElementNS(xmlns, tag)
|
|
1972
|
+
: document.createElement(tag);
|
|
1973
|
+
},
|
|
1974
|
+
hydrate(tag, node, props) {
|
|
1975
|
+
if (typeof tag !== "string" && tag !== Portal) {
|
|
1976
|
+
throw new Error(`Unknown tag: ${tag.toString()}`);
|
|
1689
1977
|
}
|
|
1690
|
-
|
|
1978
|
+
if (typeof tag === "string" &&
|
|
1979
|
+
tag.toUpperCase() !== node.tagName) {
|
|
1980
|
+
console.error(`Expected <${tag}> while hydrating but found:`, node);
|
|
1981
|
+
return undefined;
|
|
1982
|
+
}
|
|
1983
|
+
const children = [];
|
|
1984
|
+
for (let i = 0; i < node.childNodes.length; i++) {
|
|
1985
|
+
const child = node.childNodes[i];
|
|
1986
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
1987
|
+
children.push(child.data);
|
|
1988
|
+
}
|
|
1989
|
+
else if (child.nodeType === Node.ELEMENT_NODE) {
|
|
1990
|
+
children.push(child);
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
// TODO: extract props from nodes
|
|
1994
|
+
return { props, children };
|
|
1691
1995
|
},
|
|
1692
1996
|
patch(_tag,
|
|
1693
1997
|
// TODO: Why does this assignment work?
|
|
1694
1998
|
node, name,
|
|
1695
1999
|
// TODO: Stricter typings?
|
|
1696
|
-
value, oldValue,
|
|
1697
|
-
const isSVG =
|
|
2000
|
+
value, oldValue, xmlns) {
|
|
2001
|
+
const isSVG = xmlns === SVG_NAMESPACE;
|
|
1698
2002
|
switch (name) {
|
|
1699
2003
|
case "style": {
|
|
1700
2004
|
const style = node.style;
|
|
@@ -1770,7 +2074,9 @@
|
|
|
1770
2074
|
const descriptor = Object.getOwnPropertyDescriptor(obj, name);
|
|
1771
2075
|
if (descriptor != null &&
|
|
1772
2076
|
(descriptor.writable === true || descriptor.set !== undefined)) {
|
|
1773
|
-
node[name]
|
|
2077
|
+
if (node[name] !== value) {
|
|
2078
|
+
node[name] = value;
|
|
2079
|
+
}
|
|
1774
2080
|
return;
|
|
1775
2081
|
}
|
|
1776
2082
|
// if the property wasn't writable, fall through to the code below
|
|
@@ -1857,24 +2163,86 @@
|
|
|
1857
2163
|
}
|
|
1858
2164
|
}
|
|
1859
2165
|
},
|
|
2166
|
+
text(text, _scope, hydrationData) {
|
|
2167
|
+
if (hydrationData != null) {
|
|
2168
|
+
let value = hydrationData.children.shift();
|
|
2169
|
+
if (typeof value !== "string" || !value.startsWith(text)) {
|
|
2170
|
+
console.error(`Expected "${text}" while hydrating but found:`, value);
|
|
2171
|
+
}
|
|
2172
|
+
else if (text.length < value.length) {
|
|
2173
|
+
value = value.slice(text.length);
|
|
2174
|
+
hydrationData.children.unshift(value);
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
return text;
|
|
2178
|
+
},
|
|
2179
|
+
raw(value, xmlns, hydrationData) {
|
|
2180
|
+
let result;
|
|
2181
|
+
if (typeof value === "string") {
|
|
2182
|
+
const el = xmlns == null
|
|
2183
|
+
? document.createElement("div")
|
|
2184
|
+
: document.createElementNS(xmlns, "svg");
|
|
2185
|
+
el.innerHTML = value;
|
|
2186
|
+
if (el.childNodes.length === 0) {
|
|
2187
|
+
result = undefined;
|
|
2188
|
+
}
|
|
2189
|
+
else if (el.childNodes.length === 1) {
|
|
2190
|
+
result = el.childNodes[0];
|
|
2191
|
+
}
|
|
2192
|
+
else {
|
|
2193
|
+
result = Array.from(el.childNodes);
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
else {
|
|
2197
|
+
result = value;
|
|
2198
|
+
}
|
|
2199
|
+
if (hydrationData != null) {
|
|
2200
|
+
// TODO: maybe we should warn on incorrect values
|
|
2201
|
+
if (Array.isArray(result)) {
|
|
2202
|
+
for (let i = 0; i < result.length; i++) {
|
|
2203
|
+
const node = result[i];
|
|
2204
|
+
if (typeof node !== "string" &&
|
|
2205
|
+
(node.nodeType === Node.ELEMENT_NODE ||
|
|
2206
|
+
node.nodeType === Node.TEXT_NODE)) {
|
|
2207
|
+
hydrationData.children.shift();
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
else if (result != null && typeof result !== "string") {
|
|
2212
|
+
if (result.nodeType === Node.ELEMENT_NODE ||
|
|
2213
|
+
result.nodeType === Node.TEXT_NODE) {
|
|
2214
|
+
hydrationData.children.shift();
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
return result;
|
|
2219
|
+
},
|
|
1860
2220
|
};
|
|
1861
2221
|
class DOMRenderer extends Renderer {
|
|
1862
2222
|
constructor() {
|
|
1863
2223
|
super(impl$1);
|
|
1864
2224
|
}
|
|
1865
2225
|
render(children, root, ctx) {
|
|
1866
|
-
|
|
1867
|
-
throw new TypeError(`Render root is not a node. Received: ${JSON.stringify(root && root.toString())}`);
|
|
1868
|
-
}
|
|
2226
|
+
validateRoot(root);
|
|
1869
2227
|
return super.render(children, root, ctx);
|
|
1870
2228
|
}
|
|
2229
|
+
hydrate(children, root, ctx) {
|
|
2230
|
+
validateRoot(root);
|
|
2231
|
+
return super.hydrate(children, root, ctx);
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
function validateRoot(root) {
|
|
2235
|
+
if (root === null ||
|
|
2236
|
+
(typeof root === "object" && typeof root.nodeType !== "number")) {
|
|
2237
|
+
throw new TypeError(`Render root is not a node. Received: ${JSON.stringify(root && root.toString())}`);
|
|
2238
|
+
}
|
|
1871
2239
|
}
|
|
1872
2240
|
const renderer$1 = new DOMRenderer();
|
|
1873
2241
|
|
|
1874
2242
|
var dom = /*#__PURE__*/Object.freeze({
|
|
1875
2243
|
__proto__: null,
|
|
1876
|
-
impl: impl$1,
|
|
1877
2244
|
DOMRenderer: DOMRenderer,
|
|
2245
|
+
impl: impl$1,
|
|
1878
2246
|
renderer: renderer$1
|
|
1879
2247
|
});
|
|
1880
2248
|
|
|
@@ -1971,7 +2339,7 @@
|
|
|
1971
2339
|
create() {
|
|
1972
2340
|
return { value: "" };
|
|
1973
2341
|
},
|
|
1974
|
-
|
|
2342
|
+
text(text) {
|
|
1975
2343
|
return escape(text);
|
|
1976
2344
|
},
|
|
1977
2345
|
read(value) {
|
|
@@ -2018,8 +2386,8 @@
|
|
|
2018
2386
|
|
|
2019
2387
|
var html = /*#__PURE__*/Object.freeze({
|
|
2020
2388
|
__proto__: null,
|
|
2021
|
-
impl: impl,
|
|
2022
2389
|
HTMLRenderer: HTMLRenderer,
|
|
2390
|
+
impl: impl,
|
|
2023
2391
|
renderer: renderer
|
|
2024
2392
|
});
|
|
2025
2393
|
|
|
@@ -2036,7 +2404,5 @@
|
|
|
2036
2404
|
exports.html = html;
|
|
2037
2405
|
exports.isElement = isElement;
|
|
2038
2406
|
|
|
2039
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2040
|
-
|
|
2041
2407
|
}));
|
|
2042
2408
|
//# sourceMappingURL=umd.js.map
|