@b9g/crank 0.5.0-beta.5 → 0.5.0-beta.7
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/crank.cjs +1955 -15
- package/crank.cjs.map +1 -1
- package/crank.d.ts +486 -2
- package/crank.js +1946 -2
- package/crank.js.map +1 -1
- package/dom.cjs +107 -31
- package/dom.cjs.map +1 -1
- package/dom.d.ts +2 -1
- package/dom.js +104 -26
- package/dom.js.map +1 -1
- package/html.cjs +4 -6
- package/html.cjs.map +1 -1
- package/html.d.ts +2 -2
- package/html.js +2 -2
- 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/{tags.cjs → jsx-tag.cjs} +3 -5
- package/jsx-tag.cjs.map +1 -0
- package/{tags.d.ts → jsx-tag.d.ts} +1 -1
- package/{tags.js → jsx-tag.js} +3 -3
- package/jsx-tag.js.map +1 -0
- package/package.json +36 -28
- package/standalone.cjs +19 -0
- package/standalone.cjs.map +1 -0
- package/standalone.d.ts +2 -0
- package/standalone.js +4 -0
- package/standalone.js.map +1 -0
- package/umd.js +340 -554
- package/umd.js.map +1 -1
- package/core.cjs +0 -1827
- package/core.cjs.map +0 -1
- package/core.d.ts +0 -481
- package/core.js +0 -1814
- package/core.js.map +0 -1
- package/tags.cjs.map +0 -1
- package/tags.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");
|
|
@@ -297,10 +297,13 @@
|
|
|
297
297
|
create() {
|
|
298
298
|
throw new Error("Not implemented");
|
|
299
299
|
},
|
|
300
|
+
hydrate() {
|
|
301
|
+
throw new Error("Not implemented");
|
|
302
|
+
},
|
|
300
303
|
scope: IDENTITY,
|
|
301
304
|
read: IDENTITY,
|
|
302
|
-
|
|
303
|
-
|
|
305
|
+
text: IDENTITY,
|
|
306
|
+
raw: IDENTITY,
|
|
304
307
|
patch: NOOP,
|
|
305
308
|
arrange: NOOP,
|
|
306
309
|
dispose: NOOP,
|
|
@@ -366,7 +369,31 @@
|
|
|
366
369
|
}
|
|
367
370
|
}
|
|
368
371
|
const impl = this[_RendererImpl];
|
|
369
|
-
const childValues = diffChildren(impl, root, ret, ctx, impl.scope(undefined, Portal, ret.el.props), ret, children);
|
|
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);
|
|
370
397
|
// We return the child values of the portal because portal elements
|
|
371
398
|
// themselves have no readable value.
|
|
372
399
|
if (isPromiseLike(childValues)) {
|
|
@@ -388,7 +415,7 @@
|
|
|
388
415
|
}
|
|
389
416
|
return renderer.read(ret.cachedChildValues);
|
|
390
417
|
}
|
|
391
|
-
function diffChildren(renderer, root, host, ctx, scope, parent, children) {
|
|
418
|
+
function diffChildren(renderer, root, host, ctx, scope, parent, children, hydrationData) {
|
|
392
419
|
const oldRetained = wrap(parent.children);
|
|
393
420
|
const newRetained = [];
|
|
394
421
|
const newChildren = arrayify(children);
|
|
@@ -397,14 +424,15 @@
|
|
|
397
424
|
let childrenByKey;
|
|
398
425
|
let seenKeys;
|
|
399
426
|
let isAsync = false;
|
|
400
|
-
let
|
|
427
|
+
let hydrationBlock;
|
|
428
|
+
let oi = 0;
|
|
429
|
+
let oldLength = oldRetained.length;
|
|
401
430
|
for (let ni = 0, newLength = newChildren.length; ni < newLength; ni++) {
|
|
402
|
-
//
|
|
403
|
-
// deoptimizations.
|
|
431
|
+
// length checks to prevent index out of bounds deoptimizations.
|
|
404
432
|
let ret = oi >= oldLength ? undefined : oldRetained[oi];
|
|
405
433
|
let child = narrow(newChildren[ni]);
|
|
406
434
|
{
|
|
407
|
-
//
|
|
435
|
+
// aligning new children with old retainers
|
|
408
436
|
let oldKey = typeof ret === "object" ? ret.el.key : undefined;
|
|
409
437
|
let newKey = typeof child === "object" ? child.key : undefined;
|
|
410
438
|
if (newKey !== undefined && seenKeys && seenKeys.has(newKey)) {
|
|
@@ -439,18 +467,19 @@
|
|
|
439
467
|
// Updating
|
|
440
468
|
let value;
|
|
441
469
|
if (typeof child === "object") {
|
|
442
|
-
if (
|
|
443
|
-
ret.el = child;
|
|
444
|
-
value = getInflightValue(ret);
|
|
445
|
-
}
|
|
446
|
-
else if (child.tag === Copy) {
|
|
470
|
+
if (child.tag === Copy) {
|
|
447
471
|
value = getInflightValue(ret);
|
|
448
472
|
}
|
|
449
473
|
else {
|
|
450
474
|
let oldProps;
|
|
475
|
+
let static_ = false;
|
|
451
476
|
if (typeof ret === "object" && ret.el.tag === child.tag) {
|
|
452
477
|
oldProps = ret.el.props;
|
|
453
478
|
ret.el = child;
|
|
479
|
+
if (child.static_) {
|
|
480
|
+
value = getInflightValue(ret);
|
|
481
|
+
static_ = true;
|
|
482
|
+
}
|
|
454
483
|
}
|
|
455
484
|
else {
|
|
456
485
|
if (typeof ret === "object") {
|
|
@@ -460,17 +489,26 @@
|
|
|
460
489
|
ret = new Retainer(child);
|
|
461
490
|
ret.fallbackValue = fallback;
|
|
462
491
|
}
|
|
463
|
-
if (
|
|
464
|
-
|
|
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);
|
|
465
497
|
}
|
|
466
498
|
else if (child.tag === Fragment) {
|
|
467
|
-
value =
|
|
499
|
+
value = hydrationBlock
|
|
500
|
+
? hydrationBlock.then(() => updateFragment(renderer, root, host, ctx, scope, ret, hydrationData))
|
|
501
|
+
: updateFragment(renderer, root, host, ctx, scope, ret, hydrationData);
|
|
468
502
|
}
|
|
469
503
|
else if (typeof child.tag === "function") {
|
|
470
|
-
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);
|
|
471
507
|
}
|
|
472
508
|
else {
|
|
473
|
-
value =
|
|
509
|
+
value = hydrationBlock
|
|
510
|
+
? hydrationBlock.then(() => updateHost(renderer, root, ctx, scope, ret, oldProps, hydrationData))
|
|
511
|
+
: updateHost(renderer, root, ctx, scope, ret, oldProps, hydrationData);
|
|
474
512
|
}
|
|
475
513
|
}
|
|
476
514
|
const ref = child.ref;
|
|
@@ -482,9 +520,14 @@
|
|
|
482
520
|
return value;
|
|
483
521
|
});
|
|
484
522
|
}
|
|
523
|
+
if (hydrationData !== undefined) {
|
|
524
|
+
hydrationBlock = value;
|
|
525
|
+
}
|
|
485
526
|
}
|
|
486
|
-
else
|
|
487
|
-
ref
|
|
527
|
+
else {
|
|
528
|
+
if (typeof ref === "function") {
|
|
529
|
+
ref(renderer.read(value));
|
|
530
|
+
}
|
|
488
531
|
}
|
|
489
532
|
}
|
|
490
533
|
else {
|
|
@@ -493,7 +536,7 @@
|
|
|
493
536
|
(graveyard = graveyard || []).push(ret);
|
|
494
537
|
}
|
|
495
538
|
if (typeof child === "string") {
|
|
496
|
-
value = ret = renderer.
|
|
539
|
+
value = ret = renderer.text(child, scope, hydrationData);
|
|
497
540
|
}
|
|
498
541
|
else {
|
|
499
542
|
ret = undefined;
|
|
@@ -573,53 +616,64 @@
|
|
|
573
616
|
}
|
|
574
617
|
return getValue(child);
|
|
575
618
|
}
|
|
576
|
-
function updateRaw(renderer, ret, scope, oldProps) {
|
|
619
|
+
function updateRaw(renderer, ret, scope, oldProps, hydrationData) {
|
|
577
620
|
const props = ret.el.props;
|
|
578
|
-
if (
|
|
579
|
-
|
|
580
|
-
ret.value = renderer.parse(props.value, scope);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
else {
|
|
584
|
-
ret.value = props.value;
|
|
621
|
+
if (!oldProps || oldProps.value !== props.value) {
|
|
622
|
+
ret.value = renderer.raw(props.value, scope, hydrationData);
|
|
585
623
|
}
|
|
586
624
|
return ret.value;
|
|
587
625
|
}
|
|
588
|
-
function updateFragment(renderer, root, host, ctx, scope, ret) {
|
|
589
|
-
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);
|
|
590
628
|
if (isPromiseLike(childValues)) {
|
|
591
629
|
ret.inflightValue = childValues.then((childValues) => unwrap(childValues));
|
|
592
630
|
return ret.inflightValue;
|
|
593
631
|
}
|
|
594
632
|
return unwrap(childValues);
|
|
595
633
|
}
|
|
596
|
-
function updateHost(renderer, root, ctx, scope, ret, oldProps) {
|
|
634
|
+
function updateHost(renderer, root, ctx, scope, ret, oldProps, hydrationData) {
|
|
597
635
|
const el = ret.el;
|
|
598
636
|
const tag = el.tag;
|
|
637
|
+
let hydrationValue;
|
|
599
638
|
if (el.tag === Portal) {
|
|
600
639
|
root = ret.value = el.props.root;
|
|
601
640
|
}
|
|
602
|
-
else
|
|
603
|
-
|
|
604
|
-
|
|
641
|
+
else {
|
|
642
|
+
if (hydrationData !== undefined) {
|
|
643
|
+
const value = hydrationData.children.shift();
|
|
644
|
+
hydrationValue = value;
|
|
645
|
+
}
|
|
605
646
|
}
|
|
606
647
|
scope = renderer.scope(scope, tag, el.props);
|
|
607
|
-
|
|
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);
|
|
608
656
|
if (isPromiseLike(childValues)) {
|
|
609
|
-
ret.inflightValue = childValues.then((childValues) => commitHost(renderer, scope, ret, childValues, oldProps));
|
|
657
|
+
ret.inflightValue = childValues.then((childValues) => commitHost(renderer, scope, ret, childValues, oldProps, hydrationValue));
|
|
610
658
|
return ret.inflightValue;
|
|
611
659
|
}
|
|
612
|
-
return commitHost(renderer, scope, ret, childValues, oldProps);
|
|
660
|
+
return commitHost(renderer, scope, ret, childValues, oldProps, hydrationValue);
|
|
613
661
|
}
|
|
614
|
-
function commitHost(renderer, scope, ret, childValues, oldProps) {
|
|
662
|
+
function commitHost(renderer, scope, ret, childValues, oldProps, hydrationValue) {
|
|
615
663
|
const tag = ret.el.tag;
|
|
616
|
-
|
|
664
|
+
let value = hydrationValue || ret.value;
|
|
617
665
|
let props = ret.el.props;
|
|
618
666
|
let copied;
|
|
619
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
|
+
}
|
|
620
672
|
for (const propName in { ...oldProps, ...props }) {
|
|
621
673
|
const propValue = props[propName];
|
|
622
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.
|
|
623
677
|
(copied = copied || new Set()).add(propName);
|
|
624
678
|
}
|
|
625
679
|
else if (propName !== "children") {
|
|
@@ -720,23 +774,27 @@
|
|
|
720
774
|
*/
|
|
721
775
|
const IsSyncExecuting = 1 << 1;
|
|
722
776
|
/**
|
|
723
|
-
* A flag which is true when the component is in
|
|
777
|
+
* A flag which is true when the component is in a for...of loop.
|
|
724
778
|
*/
|
|
725
|
-
const
|
|
779
|
+
const IsInForOfLoop = 1 << 2;
|
|
780
|
+
/**
|
|
781
|
+
* A flag which is true when the component is in a for await...of loop.
|
|
782
|
+
*/
|
|
783
|
+
const IsInForAwaitOfLoop = 1 << 3;
|
|
726
784
|
/**
|
|
727
785
|
* A flag which is true when the component starts the render loop but has not
|
|
728
786
|
* yielded yet.
|
|
729
787
|
*
|
|
730
788
|
* Used to make sure that components yield at least once per loop.
|
|
731
789
|
*/
|
|
732
|
-
const NeedsToYield = 1 <<
|
|
790
|
+
const NeedsToYield = 1 << 4;
|
|
733
791
|
/**
|
|
734
792
|
* A flag used by async generator components in conjunction with the
|
|
735
793
|
* onAvailable callback to mark whether new props can be pulled via the context
|
|
736
794
|
* async iterator. See the Symbol.asyncIterator method and the
|
|
737
795
|
* resumeCtxIterator function.
|
|
738
796
|
*/
|
|
739
|
-
const PropsAvailable = 1 <<
|
|
797
|
+
const PropsAvailable = 1 << 5;
|
|
740
798
|
/**
|
|
741
799
|
* A flag which is set when a component errors.
|
|
742
800
|
*
|
|
@@ -838,11 +896,8 @@
|
|
|
838
896
|
}
|
|
839
897
|
*[Symbol.iterator]() {
|
|
840
898
|
const ctx = this[_ContextImpl];
|
|
841
|
-
if (ctx.f & IsAsyncGen) {
|
|
842
|
-
throw new Error("Use for await…of in async generator components");
|
|
843
|
-
}
|
|
844
899
|
try {
|
|
845
|
-
ctx.f |=
|
|
900
|
+
ctx.f |= IsInForOfLoop;
|
|
846
901
|
while (!(ctx.f & IsUnmounted)) {
|
|
847
902
|
if (ctx.f & NeedsToYield) {
|
|
848
903
|
throw new Error("Context iterated twice without a yield");
|
|
@@ -854,19 +909,16 @@
|
|
|
854
909
|
}
|
|
855
910
|
}
|
|
856
911
|
finally {
|
|
857
|
-
ctx.f &= ~
|
|
912
|
+
ctx.f &= ~IsInForOfLoop;
|
|
858
913
|
}
|
|
859
914
|
}
|
|
860
915
|
async *[Symbol.asyncIterator]() {
|
|
861
916
|
const ctx = this[_ContextImpl];
|
|
862
917
|
if (ctx.f & IsSyncGen) {
|
|
863
|
-
throw new Error("Use for
|
|
918
|
+
throw new Error("Use for...of in sync generator components");
|
|
864
919
|
}
|
|
865
920
|
try {
|
|
866
|
-
|
|
867
|
-
// returning false positives in the case of async generator components
|
|
868
|
-
// which immediately enter the loop
|
|
869
|
-
ctx.f |= IsInRenderLoop;
|
|
921
|
+
ctx.f |= IsInForAwaitOfLoop;
|
|
870
922
|
while (!(ctx.f & IsUnmounted)) {
|
|
871
923
|
if (ctx.f & NeedsToYield) {
|
|
872
924
|
throw new Error("Context iterated twice without a yield");
|
|
@@ -892,7 +944,7 @@
|
|
|
892
944
|
}
|
|
893
945
|
}
|
|
894
946
|
finally {
|
|
895
|
-
ctx.f &= ~
|
|
947
|
+
ctx.f &= ~IsInForAwaitOfLoop;
|
|
896
948
|
if (ctx.onPropsRequested) {
|
|
897
949
|
ctx.onPropsRequested();
|
|
898
950
|
ctx.onPropsRequested = undefined;
|
|
@@ -1183,9 +1235,13 @@
|
|
|
1183
1235
|
}
|
|
1184
1236
|
return false;
|
|
1185
1237
|
}
|
|
1186
|
-
function updateComponent(renderer, root, host, parent, scope, ret, oldProps) {
|
|
1238
|
+
function updateComponent(renderer, root, host, parent, scope, ret, oldProps, hydrationData) {
|
|
1187
1239
|
let ctx;
|
|
1188
1240
|
if (oldProps) {
|
|
1241
|
+
// TODO: we should probably use the existence of ret.ctx
|
|
1242
|
+
if (ret.ctx == null) {
|
|
1243
|
+
throw new Error("Hmmm");
|
|
1244
|
+
}
|
|
1189
1245
|
ctx = ret.ctx;
|
|
1190
1246
|
if (ctx.f & IsSyncExecuting) {
|
|
1191
1247
|
console.error("Component is already executing");
|
|
@@ -1196,9 +1252,9 @@
|
|
|
1196
1252
|
ctx = ret.ctx = new ContextImpl(renderer, root, host, parent, scope, ret);
|
|
1197
1253
|
}
|
|
1198
1254
|
ctx.f |= IsUpdating;
|
|
1199
|
-
return enqueueComponentRun(ctx);
|
|
1255
|
+
return enqueueComponentRun(ctx, hydrationData);
|
|
1200
1256
|
}
|
|
1201
|
-
function updateComponentChildren(ctx, children) {
|
|
1257
|
+
function updateComponentChildren(ctx, children, hydrationData) {
|
|
1202
1258
|
if (ctx.f & IsUnmounted) {
|
|
1203
1259
|
return;
|
|
1204
1260
|
}
|
|
@@ -1216,7 +1272,7 @@
|
|
|
1216
1272
|
// We set the isExecuting flag in case a child component dispatches an event
|
|
1217
1273
|
// which bubbles to this component and causes a synchronous refresh().
|
|
1218
1274
|
ctx.f |= IsSyncExecuting;
|
|
1219
|
-
childValues = diffChildren(ctx.renderer, ctx.root, ctx.host, ctx, ctx.scope, ctx.ret, narrow(children));
|
|
1275
|
+
childValues = diffChildren(ctx.renderer, ctx.root, ctx.host, ctx, ctx.scope, ctx.ret, narrow(children), hydrationData);
|
|
1220
1276
|
}
|
|
1221
1277
|
finally {
|
|
1222
1278
|
ctx.f &= ~IsSyncExecuting;
|
|
@@ -1314,37 +1370,41 @@
|
|
|
1314
1370
|
return true;
|
|
1315
1371
|
}
|
|
1316
1372
|
/** Enqueues and executes the component associated with the context. */
|
|
1317
|
-
function enqueueComponentRun(ctx) {
|
|
1318
|
-
if (ctx.f & IsAsyncGen) {
|
|
1319
|
-
|
|
1320
|
-
|
|
1373
|
+
function enqueueComponentRun(ctx, hydrationData) {
|
|
1374
|
+
if (ctx.f & IsAsyncGen && !(ctx.f & IsInForOfLoop)) {
|
|
1375
|
+
if (hydrationData !== undefined) {
|
|
1376
|
+
throw new Error("Hydration error");
|
|
1377
|
+
}
|
|
1378
|
+
// This branch will run for non-initial renders of async generator
|
|
1379
|
+
// components when they are not in for...of loops. When in a for...of loop,
|
|
1380
|
+
// async generator components will behave normally.
|
|
1321
1381
|
//
|
|
1322
|
-
// Async
|
|
1323
|
-
// three states:
|
|
1382
|
+
// Async gen componennts can be in one of three states:
|
|
1324
1383
|
//
|
|
1325
1384
|
// 1. propsAvailable flag is true: "available"
|
|
1326
1385
|
//
|
|
1327
|
-
// The component is
|
|
1386
|
+
// The component is suspended somewhere in the loop. When the component
|
|
1328
1387
|
// reaches the bottom of the loop, it will run again with the next props.
|
|
1329
1388
|
//
|
|
1330
1389
|
// 2. onAvailable callback is defined: "suspended"
|
|
1331
1390
|
//
|
|
1332
|
-
// The component has
|
|
1333
|
-
// new props.
|
|
1391
|
+
// The component has suspended at the bottom of the loop and is waiting
|
|
1392
|
+
// for new props.
|
|
1334
1393
|
//
|
|
1335
1394
|
// 3. neither 1 or 2: "Running"
|
|
1336
1395
|
//
|
|
1337
|
-
// The component is
|
|
1396
|
+
// The component is suspended somewhere in the loop. When the component
|
|
1338
1397
|
// reaches the bottom of the loop, it will suspend.
|
|
1339
1398
|
//
|
|
1340
|
-
//
|
|
1399
|
+
// Components will never be both available and suspended at
|
|
1341
1400
|
// the same time.
|
|
1342
1401
|
//
|
|
1343
1402
|
// If the component is at the loop bottom, this means that the next value
|
|
1344
1403
|
// produced by the component will have the most up to date props, so we can
|
|
1345
1404
|
// simply return the current inflight value. Otherwise, we have to wait for
|
|
1346
|
-
// the bottom of the loop before returning the inflight
|
|
1347
|
-
|
|
1405
|
+
// the bottom of the loop to be reached before returning the inflight
|
|
1406
|
+
// value.
|
|
1407
|
+
const isAtLoopbottom = ctx.f & IsInForAwaitOfLoop && !ctx.onProps;
|
|
1348
1408
|
resumePropsIterator(ctx);
|
|
1349
1409
|
if (isAtLoopbottom) {
|
|
1350
1410
|
if (ctx.inflightBlock == null) {
|
|
@@ -1359,7 +1419,7 @@
|
|
|
1359
1419
|
}
|
|
1360
1420
|
else if (!ctx.inflightBlock) {
|
|
1361
1421
|
try {
|
|
1362
|
-
const [block, value] = runComponent(ctx);
|
|
1422
|
+
const [block, value] = runComponent(ctx, hydrationData);
|
|
1363
1423
|
if (block) {
|
|
1364
1424
|
ctx.inflightBlock = block
|
|
1365
1425
|
// TODO: there is some fuckery going on here related to async
|
|
@@ -1379,8 +1439,11 @@
|
|
|
1379
1439
|
}
|
|
1380
1440
|
}
|
|
1381
1441
|
else if (!ctx.enqueuedBlock) {
|
|
1442
|
+
if (hydrationData !== undefined) {
|
|
1443
|
+
throw new Error("Hydration error");
|
|
1444
|
+
}
|
|
1382
1445
|
// We need to assign enqueuedBlock and enqueuedValue synchronously, hence
|
|
1383
|
-
// the Promise constructor call.
|
|
1446
|
+
// the Promise constructor call here.
|
|
1384
1447
|
let resolveEnqueuedBlock;
|
|
1385
1448
|
ctx.enqueuedBlock = new Promise((resolve) => (resolveEnqueuedBlock = resolve));
|
|
1386
1449
|
ctx.enqueuedValue = ctx.inflightBlock.then(() => {
|
|
@@ -1403,7 +1466,7 @@
|
|
|
1403
1466
|
}
|
|
1404
1467
|
/** Called when the inflight block promise settles. */
|
|
1405
1468
|
function advanceComponent(ctx) {
|
|
1406
|
-
if (ctx.f & IsAsyncGen) {
|
|
1469
|
+
if (ctx.f & IsAsyncGen && !(ctx.f & IsInForOfLoop)) {
|
|
1407
1470
|
return;
|
|
1408
1471
|
}
|
|
1409
1472
|
ctx.inflightBlock = ctx.enqueuedBlock;
|
|
@@ -1429,7 +1492,7 @@
|
|
|
1429
1492
|
* - Sync generator components block while any children are executing, because
|
|
1430
1493
|
* they are expected to only resume when they’ve actually rendered.
|
|
1431
1494
|
*/
|
|
1432
|
-
function runComponent(ctx) {
|
|
1495
|
+
function runComponent(ctx, hydrationData) {
|
|
1433
1496
|
const ret = ctx.ret;
|
|
1434
1497
|
const initial = !ctx.iterator;
|
|
1435
1498
|
if (initial) {
|
|
@@ -1453,7 +1516,7 @@
|
|
|
1453
1516
|
else if (isPromiseLike(result)) {
|
|
1454
1517
|
// async function component
|
|
1455
1518
|
const result1 = result instanceof Promise ? result : Promise.resolve(result);
|
|
1456
|
-
const value = result1.then((result) => updateComponentChildren(ctx, result), (err) => {
|
|
1519
|
+
const value = result1.then((result) => updateComponentChildren(ctx, result, hydrationData), (err) => {
|
|
1457
1520
|
ctx.f |= IsErrored;
|
|
1458
1521
|
throw err;
|
|
1459
1522
|
});
|
|
@@ -1461,9 +1524,15 @@
|
|
|
1461
1524
|
}
|
|
1462
1525
|
else {
|
|
1463
1526
|
// sync function component
|
|
1464
|
-
return [
|
|
1527
|
+
return [
|
|
1528
|
+
undefined,
|
|
1529
|
+
updateComponentChildren(ctx, result, hydrationData),
|
|
1530
|
+
];
|
|
1465
1531
|
}
|
|
1466
1532
|
}
|
|
1533
|
+
else if (hydrationData !== undefined) {
|
|
1534
|
+
throw new Error("Hydration error");
|
|
1535
|
+
}
|
|
1467
1536
|
let iteration;
|
|
1468
1537
|
if (initial) {
|
|
1469
1538
|
try {
|
|
@@ -1479,15 +1548,14 @@
|
|
|
1479
1548
|
}
|
|
1480
1549
|
if (isPromiseLike(iteration)) {
|
|
1481
1550
|
ctx.f |= IsAsyncGen;
|
|
1482
|
-
runAsyncGenComponent(ctx, iteration);
|
|
1483
1551
|
}
|
|
1484
1552
|
else {
|
|
1485
1553
|
ctx.f |= IsSyncGen;
|
|
1486
1554
|
}
|
|
1487
1555
|
}
|
|
1488
1556
|
if (ctx.f & IsSyncGen) {
|
|
1489
|
-
// sync generator component
|
|
1490
1557
|
ctx.f &= ~NeedsToYield;
|
|
1558
|
+
// sync generator component
|
|
1491
1559
|
if (!initial) {
|
|
1492
1560
|
try {
|
|
1493
1561
|
ctx.f |= IsSyncExecuting;
|
|
@@ -1502,7 +1570,7 @@
|
|
|
1502
1570
|
}
|
|
1503
1571
|
}
|
|
1504
1572
|
if (isPromiseLike(iteration)) {
|
|
1505
|
-
throw new Error("
|
|
1573
|
+
throw new Error("Mixed generator component");
|
|
1506
1574
|
}
|
|
1507
1575
|
if (iteration.done) {
|
|
1508
1576
|
ctx.f &= ~IsSyncGen;
|
|
@@ -1512,7 +1580,7 @@
|
|
|
1512
1580
|
try {
|
|
1513
1581
|
value = updateComponentChildren(ctx,
|
|
1514
1582
|
// Children can be void so we eliminate that here
|
|
1515
|
-
iteration.value);
|
|
1583
|
+
iteration.value, hydrationData);
|
|
1516
1584
|
if (isPromiseLike(value)) {
|
|
1517
1585
|
value = value.catch((err) => handleChildError(ctx, err));
|
|
1518
1586
|
}
|
|
@@ -1523,15 +1591,63 @@
|
|
|
1523
1591
|
const block = isPromiseLike(value) ? value.catch(NOOP) : undefined;
|
|
1524
1592
|
return [block, value];
|
|
1525
1593
|
}
|
|
1594
|
+
else if (ctx.f & IsInForOfLoop) {
|
|
1595
|
+
// TODO: does this need to be done async?
|
|
1596
|
+
ctx.f &= ~NeedsToYield;
|
|
1597
|
+
// we are in a for...of loop for async generator
|
|
1598
|
+
if (!initial) {
|
|
1599
|
+
try {
|
|
1600
|
+
ctx.f |= IsSyncExecuting;
|
|
1601
|
+
iteration = ctx.iterator.next(ctx.renderer.read(getValue(ret)));
|
|
1602
|
+
}
|
|
1603
|
+
catch (err) {
|
|
1604
|
+
ctx.f |= IsErrored;
|
|
1605
|
+
throw err;
|
|
1606
|
+
}
|
|
1607
|
+
finally {
|
|
1608
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
if (!isPromiseLike(iteration)) {
|
|
1612
|
+
throw new Error("Mixed generator component");
|
|
1613
|
+
}
|
|
1614
|
+
const block = iteration.catch(NOOP);
|
|
1615
|
+
const value = iteration.then((iteration) => {
|
|
1616
|
+
let value;
|
|
1617
|
+
if (!(ctx.f & IsInForOfLoop)) {
|
|
1618
|
+
runAsyncGenComponent(ctx, Promise.resolve(iteration), hydrationData);
|
|
1619
|
+
}
|
|
1620
|
+
try {
|
|
1621
|
+
value = updateComponentChildren(ctx,
|
|
1622
|
+
// Children can be void so we eliminate that here
|
|
1623
|
+
iteration.value, hydrationData);
|
|
1624
|
+
if (isPromiseLike(value)) {
|
|
1625
|
+
value = value.catch((err) => handleChildError(ctx, err));
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
catch (err) {
|
|
1629
|
+
value = handleChildError(ctx, err);
|
|
1630
|
+
}
|
|
1631
|
+
return value;
|
|
1632
|
+
}, (err) => {
|
|
1633
|
+
ctx.f |= IsErrored;
|
|
1634
|
+
throw err;
|
|
1635
|
+
});
|
|
1636
|
+
return [block, value];
|
|
1637
|
+
}
|
|
1526
1638
|
else {
|
|
1639
|
+
runAsyncGenComponent(ctx, iteration, hydrationData);
|
|
1527
1640
|
// async generator component
|
|
1528
|
-
return [
|
|
1641
|
+
return [ctx.inflightBlock, ctx.inflightValue];
|
|
1529
1642
|
}
|
|
1530
1643
|
}
|
|
1531
|
-
async function runAsyncGenComponent(ctx, iterationP) {
|
|
1644
|
+
async function runAsyncGenComponent(ctx, iterationP, hydrationData) {
|
|
1532
1645
|
let done = false;
|
|
1533
1646
|
try {
|
|
1534
1647
|
while (!done) {
|
|
1648
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1649
|
+
break;
|
|
1650
|
+
}
|
|
1535
1651
|
// inflightValue must be set synchronously.
|
|
1536
1652
|
let onValue;
|
|
1537
1653
|
ctx.inflightValue = new Promise((resolve) => (onValue = resolve));
|
|
@@ -1553,20 +1669,20 @@
|
|
|
1553
1669
|
}
|
|
1554
1670
|
finally {
|
|
1555
1671
|
ctx.f &= ~NeedsToYield;
|
|
1556
|
-
if (!(ctx.f &
|
|
1672
|
+
if (!(ctx.f & IsInForAwaitOfLoop)) {
|
|
1557
1673
|
ctx.f &= ~PropsAvailable;
|
|
1558
1674
|
}
|
|
1559
1675
|
}
|
|
1560
1676
|
done = !!iteration.done;
|
|
1561
1677
|
let value;
|
|
1562
1678
|
try {
|
|
1563
|
-
value = updateComponentChildren(ctx, iteration.value);
|
|
1679
|
+
value = updateComponentChildren(ctx, iteration.value, hydrationData);
|
|
1680
|
+
hydrationData = undefined;
|
|
1564
1681
|
if (isPromiseLike(value)) {
|
|
1565
1682
|
value = value.catch((err) => handleChildError(ctx, err));
|
|
1566
1683
|
}
|
|
1567
1684
|
}
|
|
1568
1685
|
catch (err) {
|
|
1569
|
-
done = true;
|
|
1570
1686
|
// Do we need to catch potential errors here in the case of unhandled
|
|
1571
1687
|
// promise rejections?
|
|
1572
1688
|
value = handleChildError(ctx, err);
|
|
@@ -1588,7 +1704,7 @@
|
|
|
1588
1704
|
oldValue = ctx.renderer.read(getValue(ctx.ret));
|
|
1589
1705
|
}
|
|
1590
1706
|
if (ctx.f & IsUnmounted) {
|
|
1591
|
-
if (ctx.f &
|
|
1707
|
+
if (ctx.f & IsInForAwaitOfLoop) {
|
|
1592
1708
|
try {
|
|
1593
1709
|
ctx.f |= IsSyncExecuting;
|
|
1594
1710
|
iterationP = ctx.iterator.next(oldValue);
|
|
@@ -1602,7 +1718,7 @@
|
|
|
1602
1718
|
break;
|
|
1603
1719
|
}
|
|
1604
1720
|
}
|
|
1605
|
-
else if (!done) {
|
|
1721
|
+
else if (!done && !(ctx.f & IsInForOfLoop)) {
|
|
1606
1722
|
try {
|
|
1607
1723
|
ctx.f |= IsSyncExecuting;
|
|
1608
1724
|
iterationP = ctx.iterator.next(oldValue);
|
|
@@ -1614,8 +1730,10 @@
|
|
|
1614
1730
|
}
|
|
1615
1731
|
}
|
|
1616
1732
|
finally {
|
|
1617
|
-
|
|
1618
|
-
|
|
1733
|
+
if (done) {
|
|
1734
|
+
ctx.f &= ~IsAsyncGen;
|
|
1735
|
+
ctx.iterator = undefined;
|
|
1736
|
+
}
|
|
1619
1737
|
}
|
|
1620
1738
|
}
|
|
1621
1739
|
/**
|
|
@@ -1646,12 +1764,12 @@
|
|
|
1646
1764
|
if (ctx.iterator) {
|
|
1647
1765
|
if (ctx.f & IsSyncGen) {
|
|
1648
1766
|
let value;
|
|
1649
|
-
if (ctx.f &
|
|
1767
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1650
1768
|
value = enqueueComponentRun(ctx);
|
|
1651
1769
|
}
|
|
1652
1770
|
if (isPromiseLike(value)) {
|
|
1653
1771
|
value.then(() => {
|
|
1654
|
-
if (ctx.f &
|
|
1772
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1655
1773
|
unmountComponent(ctx);
|
|
1656
1774
|
}
|
|
1657
1775
|
else {
|
|
@@ -1662,7 +1780,7 @@
|
|
|
1662
1780
|
});
|
|
1663
1781
|
}
|
|
1664
1782
|
else {
|
|
1665
|
-
if (ctx.f &
|
|
1783
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1666
1784
|
unmountComponent(ctx);
|
|
1667
1785
|
}
|
|
1668
1786
|
else {
|
|
@@ -1671,9 +1789,24 @@
|
|
|
1671
1789
|
}
|
|
1672
1790
|
}
|
|
1673
1791
|
else if (ctx.f & IsAsyncGen) {
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1792
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1793
|
+
const value = enqueueComponentRun(ctx);
|
|
1794
|
+
value.then(() => {
|
|
1795
|
+
if (ctx.f & IsInForOfLoop) {
|
|
1796
|
+
unmountComponent(ctx);
|
|
1797
|
+
}
|
|
1798
|
+
else {
|
|
1799
|
+
returnComponent(ctx);
|
|
1800
|
+
}
|
|
1801
|
+
}, (err) => {
|
|
1802
|
+
propagateError(ctx.parent, err);
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1805
|
+
else {
|
|
1806
|
+
// The logic for unmounting async generator components is in the
|
|
1807
|
+
// runAsyncGenComponent function.
|
|
1808
|
+
resumePropsIterator(ctx);
|
|
1809
|
+
}
|
|
1677
1810
|
}
|
|
1678
1811
|
}
|
|
1679
1812
|
}
|
|
@@ -1791,6 +1924,7 @@
|
|
|
1791
1924
|
}
|
|
1792
1925
|
if (iteration.done) {
|
|
1793
1926
|
ctx.f &= ~IsSyncGen;
|
|
1927
|
+
ctx.f &= ~IsAsyncGen;
|
|
1794
1928
|
ctx.iterator = undefined;
|
|
1795
1929
|
}
|
|
1796
1930
|
return updateComponentChildren(ctx, iteration.value);
|
|
@@ -1812,469 +1946,60 @@
|
|
|
1812
1946
|
return result;
|
|
1813
1947
|
}
|
|
1814
1948
|
|
|
1815
|
-
const cache = new Map();
|
|
1816
|
-
function jsx(spans, ...expressions) {
|
|
1817
|
-
const key = JSON.stringify(spans.raw);
|
|
1818
|
-
let parseResult = cache.get(key);
|
|
1819
|
-
if (parseResult == null) {
|
|
1820
|
-
parseResult = parse(spans.raw);
|
|
1821
|
-
cache.set(key, parseResult);
|
|
1822
|
-
}
|
|
1823
|
-
const { element, targets } = parseResult;
|
|
1824
|
-
for (let i = 0; i < expressions.length; i++) {
|
|
1825
|
-
const exp = expressions[i];
|
|
1826
|
-
const target = targets[i];
|
|
1827
|
-
if (target) {
|
|
1828
|
-
if (target.type === "error") {
|
|
1829
|
-
throw new SyntaxError(target.message.replace("${}", formatTagForError(exp)));
|
|
1830
|
-
}
|
|
1831
|
-
target.value = exp;
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
return build(element);
|
|
1835
|
-
}
|
|
1836
|
-
/**
|
|
1837
|
-
* Matches first significant character in children mode.
|
|
1838
|
-
*
|
|
1839
|
-
* Group 1: newline
|
|
1840
|
-
* Group 2: comment
|
|
1841
|
-
* Group 3: tag
|
|
1842
|
-
* Group 4: closing slash
|
|
1843
|
-
* Group 5: tag name
|
|
1844
|
-
*
|
|
1845
|
-
* The comment group must appear first because the tag group can potentially
|
|
1846
|
-
* match a comment, so that we can handle tag expressions where we’ve reached
|
|
1847
|
-
* the end of a span.
|
|
1848
|
-
*/
|
|
1849
|
-
const CHILDREN_RE = /((?:\r|\n|\r\n)\s*)|(<!--[\S\s]*?(?:-->|$))|(<\s*(\/{0,2})\s*([-_$\w]*))/g;
|
|
1850
|
-
/**
|
|
1851
|
-
* Matches props after element tags.
|
|
1852
|
-
*
|
|
1853
|
-
* Group 1: tag end
|
|
1854
|
-
* Group 2: spread props
|
|
1855
|
-
* Group 3: prop name
|
|
1856
|
-
* Group 4: equals
|
|
1857
|
-
* Group 5: prop value string
|
|
1858
|
-
*/
|
|
1859
|
-
const PROPS_RE = /\s*(?:(\/?\s*>)|(\.\.\.\s*)|(?:([-_$\w]+)\s*(=)?\s*(?:("(\\"|[\S\s])*?(?:"|$)|'(?:\\'|[\S\s])*?(?:'|$)))?))/g;
|
|
1860
|
-
const CLOSING_BRACKET_RE = />/g;
|
|
1861
|
-
const CLOSING_SINGLE_QUOTE_RE = /[^\\]?'/g;
|
|
1862
|
-
const CLOSING_DOUBLE_QUOTE_RE = /[^\\]?"/g;
|
|
1863
|
-
const CLOSING_COMMENT_RE = /-->/g;
|
|
1864
|
-
function parse(spans) {
|
|
1865
|
-
let matcher = CHILDREN_RE;
|
|
1866
|
-
const stack = [];
|
|
1867
|
-
let element = {
|
|
1868
|
-
type: "element",
|
|
1869
|
-
open: { type: "tag", slash: "", value: "" },
|
|
1870
|
-
close: null,
|
|
1871
|
-
props: [],
|
|
1872
|
-
children: [],
|
|
1873
|
-
};
|
|
1874
|
-
const targets = [];
|
|
1875
|
-
let lineStart = true;
|
|
1876
|
-
for (let s = 0; s < spans.length; s++) {
|
|
1877
|
-
const span = spans[s];
|
|
1878
|
-
// Whether or not an expression is upcoming. Used to provide better errors.
|
|
1879
|
-
const expressing = s < spans.length - 1;
|
|
1880
|
-
let expressionTarget = null;
|
|
1881
|
-
for (let i = 0, end = i; i < span.length; i = end) {
|
|
1882
|
-
matcher.lastIndex = i;
|
|
1883
|
-
const match = matcher.exec(span);
|
|
1884
|
-
end = match ? match.index + match[0].length : span.length;
|
|
1885
|
-
switch (matcher) {
|
|
1886
|
-
case CHILDREN_RE: {
|
|
1887
|
-
if (match) {
|
|
1888
|
-
const [, newline, comment, tag, closingSlash, tagName] = match;
|
|
1889
|
-
if (i < match.index) {
|
|
1890
|
-
let before = span.slice(i, match.index);
|
|
1891
|
-
if (lineStart) {
|
|
1892
|
-
before = before.replace(/^\s*/, "");
|
|
1893
|
-
}
|
|
1894
|
-
if (newline) {
|
|
1895
|
-
if (span[Math.max(0, match.index - 1)] === "\\") {
|
|
1896
|
-
// We preserve whitespace before escaped newlines and have to
|
|
1897
|
-
// remove the backslash.
|
|
1898
|
-
// jsx` \
|
|
1899
|
-
// `
|
|
1900
|
-
before = before.slice(0, -1);
|
|
1901
|
-
}
|
|
1902
|
-
else {
|
|
1903
|
-
before = before.replace(/\s*$/, "");
|
|
1904
|
-
}
|
|
1905
|
-
}
|
|
1906
|
-
if (before) {
|
|
1907
|
-
element.children.push({ type: "value", value: before });
|
|
1908
|
-
}
|
|
1909
|
-
}
|
|
1910
|
-
lineStart = !!newline;
|
|
1911
|
-
if (comment) {
|
|
1912
|
-
if (end === span.length) {
|
|
1913
|
-
// Expression in a comment:
|
|
1914
|
-
// jsx`<!-- ${exp} -->`
|
|
1915
|
-
matcher = CLOSING_COMMENT_RE;
|
|
1916
|
-
}
|
|
1917
|
-
}
|
|
1918
|
-
else if (tag) {
|
|
1919
|
-
if (closingSlash) {
|
|
1920
|
-
element.close = {
|
|
1921
|
-
type: "tag",
|
|
1922
|
-
slash: closingSlash,
|
|
1923
|
-
value: tagName,
|
|
1924
|
-
};
|
|
1925
|
-
if (!stack.length) {
|
|
1926
|
-
if (end !== span.length) {
|
|
1927
|
-
throw new SyntaxError(`Unmatched closing tag "${tagName}"`);
|
|
1928
|
-
}
|
|
1929
|
-
// ERROR EXPRESSION
|
|
1930
|
-
expressionTarget = {
|
|
1931
|
-
type: "error",
|
|
1932
|
-
message: "Unmatched closing tag ${}",
|
|
1933
|
-
value: null,
|
|
1934
|
-
};
|
|
1935
|
-
}
|
|
1936
|
-
else {
|
|
1937
|
-
if (end === span.length) {
|
|
1938
|
-
// TAG EXPRESSION
|
|
1939
|
-
expressionTarget = element.close;
|
|
1940
|
-
}
|
|
1941
|
-
element = stack.pop();
|
|
1942
|
-
matcher = CLOSING_BRACKET_RE;
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1945
|
-
else {
|
|
1946
|
-
const next = {
|
|
1947
|
-
type: "element",
|
|
1948
|
-
open: {
|
|
1949
|
-
type: "tag",
|
|
1950
|
-
slash: "",
|
|
1951
|
-
value: tagName,
|
|
1952
|
-
},
|
|
1953
|
-
close: null,
|
|
1954
|
-
props: [],
|
|
1955
|
-
children: [],
|
|
1956
|
-
};
|
|
1957
|
-
element.children.push(next);
|
|
1958
|
-
stack.push(element);
|
|
1959
|
-
element = next;
|
|
1960
|
-
matcher = PROPS_RE;
|
|
1961
|
-
if (end === span.length) {
|
|
1962
|
-
// TAG EXPRESSION
|
|
1963
|
-
expressionTarget = element.open;
|
|
1964
|
-
}
|
|
1965
|
-
}
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
|
-
else {
|
|
1969
|
-
if (i < span.length) {
|
|
1970
|
-
let after = span.slice(i);
|
|
1971
|
-
if (!expressing) {
|
|
1972
|
-
// trim trailing whitespace
|
|
1973
|
-
after = after.replace(/\s*$/, "");
|
|
1974
|
-
}
|
|
1975
|
-
if (after) {
|
|
1976
|
-
element.children.push({ type: "value", value: after });
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
1979
|
-
}
|
|
1980
|
-
break;
|
|
1981
|
-
}
|
|
1982
|
-
case PROPS_RE: {
|
|
1983
|
-
if (match) {
|
|
1984
|
-
const [, tagEnd, spread, name, equals, string] = match;
|
|
1985
|
-
if (i < match.index) {
|
|
1986
|
-
throw new SyntaxError(`Unexpected text \`${span.slice(i, match.index).trim()}\``);
|
|
1987
|
-
}
|
|
1988
|
-
if (tagEnd) {
|
|
1989
|
-
if (tagEnd[0] === "/") {
|
|
1990
|
-
// This is a self-closing element, so there will always be a
|
|
1991
|
-
// result on the stack.
|
|
1992
|
-
element = stack.pop();
|
|
1993
|
-
}
|
|
1994
|
-
matcher = CHILDREN_RE;
|
|
1995
|
-
}
|
|
1996
|
-
else if (spread) {
|
|
1997
|
-
const value = {
|
|
1998
|
-
type: "value",
|
|
1999
|
-
value: null,
|
|
2000
|
-
};
|
|
2001
|
-
element.props.push(value);
|
|
2002
|
-
// SPREAD PROP EXPRESSION
|
|
2003
|
-
expressionTarget = value;
|
|
2004
|
-
if (!(expressing && end === span.length)) {
|
|
2005
|
-
throw new SyntaxError('Expression expected after "..."');
|
|
2006
|
-
}
|
|
2007
|
-
}
|
|
2008
|
-
else if (name) {
|
|
2009
|
-
let value;
|
|
2010
|
-
if (string == null) {
|
|
2011
|
-
if (!equals) {
|
|
2012
|
-
value = { type: "value", value: true };
|
|
2013
|
-
}
|
|
2014
|
-
else if (end < span.length) {
|
|
2015
|
-
throw new SyntaxError(`Unexpected text \`${span.slice(end, end + 20)}\``);
|
|
2016
|
-
}
|
|
2017
|
-
else {
|
|
2018
|
-
value = { type: "value", value: null };
|
|
2019
|
-
// PROP EXPRESSION
|
|
2020
|
-
expressionTarget = value;
|
|
2021
|
-
if (!(expressing && end === span.length)) {
|
|
2022
|
-
throw new SyntaxError(`Expression expected for prop "${name}"`);
|
|
2023
|
-
}
|
|
2024
|
-
}
|
|
2025
|
-
}
|
|
2026
|
-
else {
|
|
2027
|
-
const quote = string[0];
|
|
2028
|
-
value = { type: "propString", parts: [] };
|
|
2029
|
-
value.parts.push(string);
|
|
2030
|
-
if (end === span.length) {
|
|
2031
|
-
matcher =
|
|
2032
|
-
quote === "'"
|
|
2033
|
-
? CLOSING_SINGLE_QUOTE_RE
|
|
2034
|
-
: CLOSING_DOUBLE_QUOTE_RE;
|
|
2035
|
-
}
|
|
2036
|
-
}
|
|
2037
|
-
const prop = {
|
|
2038
|
-
type: "prop",
|
|
2039
|
-
name,
|
|
2040
|
-
value,
|
|
2041
|
-
};
|
|
2042
|
-
element.props.push(prop);
|
|
2043
|
-
}
|
|
2044
|
-
}
|
|
2045
|
-
else {
|
|
2046
|
-
if (!expressing) {
|
|
2047
|
-
if (i === span.length) {
|
|
2048
|
-
throw new SyntaxError(`Expected props but reached end of document`);
|
|
2049
|
-
}
|
|
2050
|
-
else {
|
|
2051
|
-
throw new SyntaxError(`Unexpected text \`${span.slice(i, i + 20).trim()}\``);
|
|
2052
|
-
}
|
|
2053
|
-
}
|
|
2054
|
-
// Unexpected expression errors are handled in the outer loop.
|
|
2055
|
-
//
|
|
2056
|
-
// This would most likely be the starting point for the logic of
|
|
2057
|
-
// prop name expressions.
|
|
2058
|
-
// jsx`<p ${name}=${value}>`
|
|
2059
|
-
}
|
|
2060
|
-
break;
|
|
2061
|
-
}
|
|
2062
|
-
case CLOSING_BRACKET_RE: {
|
|
2063
|
-
// We’re in a closing tag and looking for the >.
|
|
2064
|
-
if (match) {
|
|
2065
|
-
if (i < match.index) {
|
|
2066
|
-
throw new SyntaxError(`Unexpected text \`${span.slice(i, match.index).trim()}\``);
|
|
2067
|
-
}
|
|
2068
|
-
matcher = CHILDREN_RE;
|
|
2069
|
-
}
|
|
2070
|
-
else {
|
|
2071
|
-
if (!expressing) {
|
|
2072
|
-
throw new SyntaxError(`Unexpected text \`${span.slice(i, i + 20).trim()}\``);
|
|
2073
|
-
}
|
|
2074
|
-
}
|
|
2075
|
-
break;
|
|
2076
|
-
}
|
|
2077
|
-
case CLOSING_SINGLE_QUOTE_RE:
|
|
2078
|
-
case CLOSING_DOUBLE_QUOTE_RE: {
|
|
2079
|
-
const string = span.slice(i, end);
|
|
2080
|
-
const prop = element.props[element.props.length - 1];
|
|
2081
|
-
const propString = prop.value;
|
|
2082
|
-
propString.parts.push(string);
|
|
2083
|
-
if (match) {
|
|
2084
|
-
matcher = PROPS_RE;
|
|
2085
|
-
}
|
|
2086
|
-
else {
|
|
2087
|
-
if (!expressing) {
|
|
2088
|
-
throw new SyntaxError(`Missing \`${matcher === CLOSING_SINGLE_QUOTE_RE ? "'" : '"'}\``);
|
|
2089
|
-
}
|
|
2090
|
-
}
|
|
2091
|
-
break;
|
|
2092
|
-
}
|
|
2093
|
-
case CLOSING_COMMENT_RE: {
|
|
2094
|
-
if (match) {
|
|
2095
|
-
matcher = CHILDREN_RE;
|
|
2096
|
-
}
|
|
2097
|
-
else {
|
|
2098
|
-
if (!expressing) {
|
|
2099
|
-
throw new SyntaxError("Expected `-->` but reached end of template");
|
|
2100
|
-
}
|
|
2101
|
-
}
|
|
2102
|
-
break;
|
|
2103
|
-
}
|
|
2104
|
-
}
|
|
2105
|
-
}
|
|
2106
|
-
if (expressing) {
|
|
2107
|
-
if (expressionTarget) {
|
|
2108
|
-
targets.push(expressionTarget);
|
|
2109
|
-
if (expressionTarget.type === "error") {
|
|
2110
|
-
break;
|
|
2111
|
-
}
|
|
2112
|
-
continue;
|
|
2113
|
-
}
|
|
2114
|
-
switch (matcher) {
|
|
2115
|
-
case CHILDREN_RE: {
|
|
2116
|
-
const target = { type: "value", value: null };
|
|
2117
|
-
element.children.push(target);
|
|
2118
|
-
targets.push(target);
|
|
2119
|
-
break;
|
|
2120
|
-
}
|
|
2121
|
-
case CLOSING_SINGLE_QUOTE_RE:
|
|
2122
|
-
case CLOSING_DOUBLE_QUOTE_RE: {
|
|
2123
|
-
const prop = element.props[element.props.length - 1];
|
|
2124
|
-
const target = { type: "value", value: null };
|
|
2125
|
-
prop.value.parts.push(target);
|
|
2126
|
-
targets.push(target);
|
|
2127
|
-
break;
|
|
2128
|
-
}
|
|
2129
|
-
case CLOSING_COMMENT_RE:
|
|
2130
|
-
targets.push(null);
|
|
2131
|
-
break;
|
|
2132
|
-
default:
|
|
2133
|
-
throw new SyntaxError("Unexpected expression");
|
|
2134
|
-
}
|
|
2135
|
-
}
|
|
2136
|
-
else if (expressionTarget) {
|
|
2137
|
-
throw new SyntaxError("Expression expected");
|
|
2138
|
-
}
|
|
2139
|
-
lineStart = false;
|
|
2140
|
-
}
|
|
2141
|
-
if (stack.length) {
|
|
2142
|
-
const ti = targets.indexOf(element.open);
|
|
2143
|
-
if (ti === -1) {
|
|
2144
|
-
throw new SyntaxError(`Unmatched opening tag "${element.open.value}"`);
|
|
2145
|
-
}
|
|
2146
|
-
targets[ti] = {
|
|
2147
|
-
type: "error",
|
|
2148
|
-
message: "Unmatched opening tag ${}",
|
|
2149
|
-
value: null,
|
|
2150
|
-
};
|
|
2151
|
-
}
|
|
2152
|
-
if (element.children.length === 1 && element.children[0].type === "element") {
|
|
2153
|
-
element = element.children[0];
|
|
2154
|
-
}
|
|
2155
|
-
return { element, targets };
|
|
2156
|
-
}
|
|
2157
|
-
function build(parsed) {
|
|
2158
|
-
if (parsed.close !== null &&
|
|
2159
|
-
parsed.close.slash !== "//" &&
|
|
2160
|
-
parsed.open.value !== parsed.close.value) {
|
|
2161
|
-
throw new SyntaxError(`Unmatched closing tag ${formatTagForError(parsed.close.value)}, expected ${formatTagForError(parsed.open.value)}`);
|
|
2162
|
-
}
|
|
2163
|
-
const children = [];
|
|
2164
|
-
for (let i = 0; i < parsed.children.length; i++) {
|
|
2165
|
-
const child = parsed.children[i];
|
|
2166
|
-
children.push(child.type === "element" ? build(child) : child.value);
|
|
2167
|
-
}
|
|
2168
|
-
let props = parsed.props.length ? {} : null;
|
|
2169
|
-
for (let i = 0; i < parsed.props.length; i++) {
|
|
2170
|
-
const prop = parsed.props[i];
|
|
2171
|
-
if (prop.type === "prop") {
|
|
2172
|
-
let value;
|
|
2173
|
-
if (prop.value.type === "value") {
|
|
2174
|
-
value = prop.value.value;
|
|
2175
|
-
}
|
|
2176
|
-
else {
|
|
2177
|
-
let string = "";
|
|
2178
|
-
for (let i = 0; i < prop.value.parts.length; i++) {
|
|
2179
|
-
const part = prop.value.parts[i];
|
|
2180
|
-
if (typeof part === "string") {
|
|
2181
|
-
string += part;
|
|
2182
|
-
}
|
|
2183
|
-
else if (typeof part.value !== "boolean" && part.value != null) {
|
|
2184
|
-
string +=
|
|
2185
|
-
typeof part.value === "string" ? part.value : String(part.value);
|
|
2186
|
-
}
|
|
2187
|
-
}
|
|
2188
|
-
value = string
|
|
2189
|
-
// remove quotes
|
|
2190
|
-
.slice(1, -1)
|
|
2191
|
-
// unescape things
|
|
2192
|
-
// adapted from https://stackoverflow.com/a/57330383/1825413
|
|
2193
|
-
.replace(/\\x[0-9a-f]{2}|\\u[0-9a-f]{4}|\\u\{[0-9a-f]+\}|\\./gi, (match) => {
|
|
2194
|
-
switch (match[1]) {
|
|
2195
|
-
case "b":
|
|
2196
|
-
return "\b";
|
|
2197
|
-
case "f":
|
|
2198
|
-
return "\f";
|
|
2199
|
-
case "n":
|
|
2200
|
-
return "\n";
|
|
2201
|
-
case "r":
|
|
2202
|
-
return "\r";
|
|
2203
|
-
case "t":
|
|
2204
|
-
return "\t";
|
|
2205
|
-
case "v":
|
|
2206
|
-
return "\v";
|
|
2207
|
-
case "x":
|
|
2208
|
-
return String.fromCharCode(parseInt(match.slice(2), 16));
|
|
2209
|
-
case "u":
|
|
2210
|
-
if (match[2] === "{") {
|
|
2211
|
-
return String.fromCodePoint(parseInt(match.slice(3, -1), 16));
|
|
2212
|
-
}
|
|
2213
|
-
return String.fromCharCode(parseInt(match.slice(2), 16));
|
|
2214
|
-
case "0":
|
|
2215
|
-
return "\0";
|
|
2216
|
-
default:
|
|
2217
|
-
return match.slice(1);
|
|
2218
|
-
}
|
|
2219
|
-
});
|
|
2220
|
-
}
|
|
2221
|
-
props[prop.name] = value;
|
|
2222
|
-
}
|
|
2223
|
-
else {
|
|
2224
|
-
// spread prop
|
|
2225
|
-
props = { ...props, ...prop.value };
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
return createElement(parsed.open.value, props, ...children);
|
|
2229
|
-
}
|
|
2230
|
-
function formatTagForError(tag) {
|
|
2231
|
-
return typeof tag === "function"
|
|
2232
|
-
? tag.name + "()"
|
|
2233
|
-
: typeof tag === "string"
|
|
2234
|
-
? `"${tag}"`
|
|
2235
|
-
: JSON.stringify(tag);
|
|
2236
|
-
}
|
|
2237
|
-
|
|
2238
1949
|
const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
2239
1950
|
const impl$1 = {
|
|
2240
|
-
|
|
2241
|
-
if (typeof document.createRange === "function") {
|
|
2242
|
-
const fragment = document.createRange().createContextualFragment(text);
|
|
2243
|
-
return Array.from(fragment.childNodes);
|
|
2244
|
-
}
|
|
2245
|
-
else {
|
|
2246
|
-
const childNodes = new DOMParser().parseFromString(text, "text/html").body
|
|
2247
|
-
.childNodes;
|
|
2248
|
-
return Array.from(childNodes);
|
|
2249
|
-
}
|
|
2250
|
-
},
|
|
2251
|
-
scope(scope, tag) {
|
|
1951
|
+
scope(xmlns, tag) {
|
|
2252
1952
|
// TODO: Should we handle xmlns???
|
|
2253
1953
|
switch (tag) {
|
|
2254
1954
|
case Portal:
|
|
2255
1955
|
case "foreignObject":
|
|
2256
|
-
|
|
1956
|
+
xmlns = undefined;
|
|
1957
|
+
break;
|
|
2257
1958
|
case "svg":
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
return scope;
|
|
1959
|
+
xmlns = SVG_NAMESPACE;
|
|
1960
|
+
break;
|
|
2261
1961
|
}
|
|
1962
|
+
return xmlns;
|
|
2262
1963
|
},
|
|
2263
|
-
create(tag, _props,
|
|
1964
|
+
create(tag, _props, xmlns) {
|
|
2264
1965
|
if (typeof tag !== "string") {
|
|
2265
1966
|
throw new Error(`Unknown tag: ${tag.toString()}`);
|
|
2266
1967
|
}
|
|
2267
1968
|
else if (tag.toLowerCase() === "svg") {
|
|
2268
|
-
|
|
1969
|
+
xmlns = SVG_NAMESPACE;
|
|
2269
1970
|
}
|
|
2270
|
-
return
|
|
1971
|
+
return xmlns
|
|
1972
|
+
? document.createElementNS(xmlns, tag)
|
|
1973
|
+
: document.createElement(tag);
|
|
1974
|
+
},
|
|
1975
|
+
hydrate(tag, node, props) {
|
|
1976
|
+
if (typeof tag !== "string" && tag !== Portal) {
|
|
1977
|
+
throw new Error(`Unknown tag: ${tag.toString()}`);
|
|
1978
|
+
}
|
|
1979
|
+
if (typeof tag === "string" &&
|
|
1980
|
+
tag.toUpperCase() !== node.tagName) {
|
|
1981
|
+
console.error(`Expected <${tag}> while hydrating but found:`, node);
|
|
1982
|
+
return undefined;
|
|
1983
|
+
}
|
|
1984
|
+
const children = [];
|
|
1985
|
+
for (let i = 0; i < node.childNodes.length; i++) {
|
|
1986
|
+
const child = node.childNodes[i];
|
|
1987
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
1988
|
+
children.push(child.data);
|
|
1989
|
+
}
|
|
1990
|
+
else if (child.nodeType === Node.ELEMENT_NODE) {
|
|
1991
|
+
children.push(child);
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
// TODO: extract props from nodes
|
|
1995
|
+
return { props, children };
|
|
2271
1996
|
},
|
|
2272
1997
|
patch(_tag,
|
|
2273
1998
|
// TODO: Why does this assignment work?
|
|
2274
1999
|
node, name,
|
|
2275
2000
|
// TODO: Stricter typings?
|
|
2276
|
-
value, oldValue,
|
|
2277
|
-
const isSVG =
|
|
2001
|
+
value, oldValue, xmlns) {
|
|
2002
|
+
const isSVG = xmlns === SVG_NAMESPACE;
|
|
2278
2003
|
switch (name) {
|
|
2279
2004
|
case "style": {
|
|
2280
2005
|
const style = node.style;
|
|
@@ -2350,7 +2075,9 @@
|
|
|
2350
2075
|
const descriptor = Object.getOwnPropertyDescriptor(obj, name);
|
|
2351
2076
|
if (descriptor != null &&
|
|
2352
2077
|
(descriptor.writable === true || descriptor.set !== undefined)) {
|
|
2353
|
-
node[name]
|
|
2078
|
+
if (node[name] !== value) {
|
|
2079
|
+
node[name] = value;
|
|
2080
|
+
}
|
|
2354
2081
|
return;
|
|
2355
2082
|
}
|
|
2356
2083
|
// if the property wasn't writable, fall through to the code below
|
|
@@ -2437,24 +2164,86 @@
|
|
|
2437
2164
|
}
|
|
2438
2165
|
}
|
|
2439
2166
|
},
|
|
2167
|
+
text(text, _scope, hydrationData) {
|
|
2168
|
+
if (hydrationData != null) {
|
|
2169
|
+
let value = hydrationData.children.shift();
|
|
2170
|
+
if (typeof value !== "string" || !value.startsWith(text)) {
|
|
2171
|
+
console.error(`Expected "${text}" while hydrating but found:`, value);
|
|
2172
|
+
}
|
|
2173
|
+
else if (text.length < value.length) {
|
|
2174
|
+
value = value.slice(text.length);
|
|
2175
|
+
hydrationData.children.unshift(value);
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
return text;
|
|
2179
|
+
},
|
|
2180
|
+
raw(value, xmlns, hydrationData) {
|
|
2181
|
+
let result;
|
|
2182
|
+
if (typeof value === "string") {
|
|
2183
|
+
const el = xmlns == null
|
|
2184
|
+
? document.createElement("div")
|
|
2185
|
+
: document.createElementNS(xmlns, "svg");
|
|
2186
|
+
el.innerHTML = value;
|
|
2187
|
+
if (el.childNodes.length === 0) {
|
|
2188
|
+
result = undefined;
|
|
2189
|
+
}
|
|
2190
|
+
else if (el.childNodes.length === 1) {
|
|
2191
|
+
result = el.childNodes[0];
|
|
2192
|
+
}
|
|
2193
|
+
else {
|
|
2194
|
+
result = Array.from(el.childNodes);
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
else {
|
|
2198
|
+
result = value;
|
|
2199
|
+
}
|
|
2200
|
+
if (hydrationData != null) {
|
|
2201
|
+
// TODO: maybe we should warn on incorrect values
|
|
2202
|
+
if (Array.isArray(result)) {
|
|
2203
|
+
for (let i = 0; i < result.length; i++) {
|
|
2204
|
+
const node = result[i];
|
|
2205
|
+
if (typeof node !== "string" &&
|
|
2206
|
+
(node.nodeType === Node.ELEMENT_NODE ||
|
|
2207
|
+
node.nodeType === Node.TEXT_NODE)) {
|
|
2208
|
+
hydrationData.children.shift();
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
else if (result != null && typeof result !== "string") {
|
|
2213
|
+
if (result.nodeType === Node.ELEMENT_NODE ||
|
|
2214
|
+
result.nodeType === Node.TEXT_NODE) {
|
|
2215
|
+
hydrationData.children.shift();
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
return result;
|
|
2220
|
+
},
|
|
2440
2221
|
};
|
|
2441
2222
|
class DOMRenderer extends Renderer {
|
|
2442
2223
|
constructor() {
|
|
2443
2224
|
super(impl$1);
|
|
2444
2225
|
}
|
|
2445
2226
|
render(children, root, ctx) {
|
|
2446
|
-
|
|
2447
|
-
throw new TypeError(`Render root is not a node. Received: ${JSON.stringify(root && root.toString())}`);
|
|
2448
|
-
}
|
|
2227
|
+
validateRoot(root);
|
|
2449
2228
|
return super.render(children, root, ctx);
|
|
2450
2229
|
}
|
|
2230
|
+
hydrate(children, root, ctx) {
|
|
2231
|
+
validateRoot(root);
|
|
2232
|
+
return super.hydrate(children, root, ctx);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
function validateRoot(root) {
|
|
2236
|
+
if (root === null ||
|
|
2237
|
+
(typeof root === "object" && typeof root.nodeType !== "number")) {
|
|
2238
|
+
throw new TypeError(`Render root is not a node. Received: ${JSON.stringify(root && root.toString())}`);
|
|
2239
|
+
}
|
|
2451
2240
|
}
|
|
2452
2241
|
const renderer$1 = new DOMRenderer();
|
|
2453
2242
|
|
|
2454
2243
|
var dom = /*#__PURE__*/Object.freeze({
|
|
2455
2244
|
__proto__: null,
|
|
2456
|
-
impl: impl$1,
|
|
2457
2245
|
DOMRenderer: DOMRenderer,
|
|
2246
|
+
impl: impl$1,
|
|
2458
2247
|
renderer: renderer$1
|
|
2459
2248
|
});
|
|
2460
2249
|
|
|
@@ -2551,7 +2340,7 @@
|
|
|
2551
2340
|
create() {
|
|
2552
2341
|
return { value: "" };
|
|
2553
2342
|
},
|
|
2554
|
-
|
|
2343
|
+
text(text) {
|
|
2555
2344
|
return escape(text);
|
|
2556
2345
|
},
|
|
2557
2346
|
read(value) {
|
|
@@ -2598,8 +2387,8 @@
|
|
|
2598
2387
|
|
|
2599
2388
|
var html = /*#__PURE__*/Object.freeze({
|
|
2600
2389
|
__proto__: null,
|
|
2601
|
-
impl: impl,
|
|
2602
2390
|
HTMLRenderer: HTMLRenderer,
|
|
2391
|
+
impl: impl,
|
|
2603
2392
|
renderer: renderer
|
|
2604
2393
|
});
|
|
2605
2394
|
|
|
@@ -2615,9 +2404,6 @@
|
|
|
2615
2404
|
exports.dom = dom;
|
|
2616
2405
|
exports.html = html;
|
|
2617
2406
|
exports.isElement = isElement;
|
|
2618
|
-
exports.jsx = jsx;
|
|
2619
|
-
|
|
2620
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2621
2407
|
|
|
2622
2408
|
}));
|
|
2623
2409
|
//# sourceMappingURL=umd.js.map
|