@b9g/crank 0.5.0-beta.1 → 0.5.0-beta.3
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 +440 -297
- package/crank.cjs.map +1 -1
- package/crank.d.ts +13 -24
- package/crank.js +440 -297
- package/crank.js.map +1 -1
- package/{index.cjs → mod.cjs} +3 -3
- package/mod.cjs.map +1 -0
- package/mod.d.ts +2 -0
- package/{index.js → mod.js} +3 -3
- package/mod.js.map +1 -0
- package/package.json +18 -18
- package/{xm.cjs → tags.cjs} +8 -8
- package/tags.cjs.map +1 -0
- package/tags.d.ts +2 -0
- package/{xm.js → tags.js} +9 -9
- package/tags.js.map +1 -0
- package/umd.js +440 -297
- package/umd.js.map +1 -1
- package/index.cjs.map +0 -1
- package/index.d.ts +0 -2
- package/index.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
|
@@ -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
|
}
|
|
@@ -322,7 +306,7 @@
|
|
|
322
306
|
dispose: NOOP,
|
|
323
307
|
flush: NOOP,
|
|
324
308
|
};
|
|
325
|
-
const
|
|
309
|
+
const _RendererImpl = Symbol.for("crank.RendererImpl");
|
|
326
310
|
/**
|
|
327
311
|
* An abstract class which is subclassed to render to different target
|
|
328
312
|
* environments. This class is responsible for kicking off the rendering
|
|
@@ -336,7 +320,7 @@
|
|
|
336
320
|
class Renderer {
|
|
337
321
|
constructor(impl) {
|
|
338
322
|
this.cache = new WeakMap();
|
|
339
|
-
this[
|
|
323
|
+
this[_RendererImpl] = {
|
|
340
324
|
...defaultRendererImpl,
|
|
341
325
|
...impl,
|
|
342
326
|
};
|
|
@@ -348,7 +332,7 @@
|
|
|
348
332
|
* used root to delete the previously rendered element tree from the cache.
|
|
349
333
|
* @param root - The node to be rendered into. The renderer will cache
|
|
350
334
|
* element trees per root.
|
|
351
|
-
* @param
|
|
335
|
+
* @param bridge - An optional context that will be the ancestor context of all
|
|
352
336
|
* elements in the tree. Useful for connecting different renderers so that
|
|
353
337
|
* events/provisions properly propagate. The context for a given root must be
|
|
354
338
|
* the same or an error will be thrown.
|
|
@@ -358,7 +342,7 @@
|
|
|
358
342
|
*/
|
|
359
343
|
render(children, root, bridge) {
|
|
360
344
|
let ret;
|
|
361
|
-
const ctx = bridge && bridge[
|
|
345
|
+
const ctx = bridge && bridge[_ContextImpl];
|
|
362
346
|
if (typeof root === "object" && root !== null) {
|
|
363
347
|
ret = this.cache.get(root);
|
|
364
348
|
}
|
|
@@ -381,7 +365,7 @@
|
|
|
381
365
|
this.cache.delete(root);
|
|
382
366
|
}
|
|
383
367
|
}
|
|
384
|
-
const impl = this[
|
|
368
|
+
const impl = this[_RendererImpl];
|
|
385
369
|
const childValues = diffChildren(impl, root, ret, ctx, impl.scope(undefined, Portal, ret.el.props), ret, children);
|
|
386
370
|
// We return the child values of the portal because portal elements
|
|
387
371
|
// themselves have no readable value.
|
|
@@ -394,15 +378,15 @@
|
|
|
394
378
|
/*** PRIVATE RENDERER FUNCTIONS ***/
|
|
395
379
|
function commitRootRender(renderer, root, ctx, ret, childValues, oldProps) {
|
|
396
380
|
// element is a host or portal element
|
|
397
|
-
if (root
|
|
398
|
-
renderer.arrange(Portal, root, ret.el.props, childValues, oldProps, wrap(ret.
|
|
381
|
+
if (root != null) {
|
|
382
|
+
renderer.arrange(Portal, root, ret.el.props, childValues, oldProps, wrap(ret.cachedChildValues));
|
|
399
383
|
flush(renderer, root);
|
|
400
384
|
}
|
|
401
|
-
ret.
|
|
385
|
+
ret.cachedChildValues = unwrap(childValues);
|
|
402
386
|
if (root == null) {
|
|
403
387
|
unmount(renderer, ret, ctx, ret);
|
|
404
388
|
}
|
|
405
|
-
return renderer.read(ret.
|
|
389
|
+
return renderer.read(ret.cachedChildValues);
|
|
406
390
|
}
|
|
407
391
|
function diffChildren(renderer, root, host, ctx, scope, parent, children) {
|
|
408
392
|
const oldRetained = wrap(parent.children);
|
|
@@ -474,7 +458,7 @@
|
|
|
474
458
|
}
|
|
475
459
|
const fallback = ret;
|
|
476
460
|
ret = new Retainer(child);
|
|
477
|
-
ret.
|
|
461
|
+
ret.fallbackValue = fallback;
|
|
478
462
|
}
|
|
479
463
|
if (child.tag === Raw) {
|
|
480
464
|
value = updateRaw(renderer, ret, scope, oldProps);
|
|
@@ -542,27 +526,29 @@
|
|
|
542
526
|
childValues1,
|
|
543
527
|
new Promise((resolve) => (onChildValues = resolve)),
|
|
544
528
|
]);
|
|
545
|
-
if (parent.
|
|
546
|
-
parent.
|
|
529
|
+
if (parent.onNextValues) {
|
|
530
|
+
parent.onNextValues(childValues1);
|
|
547
531
|
}
|
|
548
|
-
parent.
|
|
532
|
+
parent.onNextValues = onChildValues;
|
|
549
533
|
return childValues1.then((childValues) => {
|
|
550
|
-
parent.
|
|
534
|
+
parent.inflightValue = parent.fallbackValue = undefined;
|
|
551
535
|
return normalize(childValues);
|
|
552
536
|
});
|
|
553
537
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
538
|
+
else {
|
|
539
|
+
if (graveyard) {
|
|
540
|
+
for (let i = 0; i < graveyard.length; i++) {
|
|
541
|
+
unmount(renderer, host, ctx, graveyard[i]);
|
|
542
|
+
}
|
|
557
543
|
}
|
|
544
|
+
if (parent.onNextValues) {
|
|
545
|
+
parent.onNextValues(values);
|
|
546
|
+
parent.onNextValues = undefined;
|
|
547
|
+
}
|
|
548
|
+
parent.inflightValue = parent.fallbackValue = undefined;
|
|
549
|
+
// We can assert there are no promises in the array because isAsync is false
|
|
550
|
+
return normalize(values);
|
|
558
551
|
}
|
|
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
552
|
}
|
|
567
553
|
function createChildrenByKey(children, offset) {
|
|
568
554
|
const childrenByKey = new Map();
|
|
@@ -582,8 +568,8 @@
|
|
|
582
568
|
if (ctx && ctx.f & IsUpdating && ctx.inflightValue) {
|
|
583
569
|
return ctx.inflightValue;
|
|
584
570
|
}
|
|
585
|
-
else if (child.
|
|
586
|
-
return child.
|
|
571
|
+
else if (child.inflightValue) {
|
|
572
|
+
return child.inflightValue;
|
|
587
573
|
}
|
|
588
574
|
return getValue(child);
|
|
589
575
|
}
|
|
@@ -602,8 +588,8 @@
|
|
|
602
588
|
function updateFragment(renderer, root, host, ctx, scope, ret) {
|
|
603
589
|
const childValues = diffChildren(renderer, root, host, ctx, scope, ret, ret.el.props.children);
|
|
604
590
|
if (isPromiseLike(childValues)) {
|
|
605
|
-
ret.
|
|
606
|
-
return ret.
|
|
591
|
+
ret.inflightValue = childValues.then((childValues) => unwrap(childValues));
|
|
592
|
+
return ret.inflightValue;
|
|
607
593
|
}
|
|
608
594
|
return unwrap(childValues);
|
|
609
595
|
}
|
|
@@ -620,8 +606,8 @@
|
|
|
620
606
|
scope = renderer.scope(scope, tag, el.props);
|
|
621
607
|
const childValues = diffChildren(renderer, root, ret, ctx, scope, ret, ret.el.props.children);
|
|
622
608
|
if (isPromiseLike(childValues)) {
|
|
623
|
-
ret.
|
|
624
|
-
return ret.
|
|
609
|
+
ret.inflightValue = childValues.then((childValues) => commitHost(renderer, scope, ret, childValues, oldProps));
|
|
610
|
+
return ret.inflightValue;
|
|
625
611
|
}
|
|
626
612
|
return commitHost(renderer, scope, ret, childValues, oldProps);
|
|
627
613
|
}
|
|
@@ -648,8 +634,8 @@
|
|
|
648
634
|
}
|
|
649
635
|
ret.el = new Element(tag, props, ret.el.key, ret.el.ref);
|
|
650
636
|
}
|
|
651
|
-
renderer.arrange(tag, value, props, childValues, oldProps, wrap(ret.
|
|
652
|
-
ret.
|
|
637
|
+
renderer.arrange(tag, value, props, childValues, oldProps, wrap(ret.cachedChildValues));
|
|
638
|
+
ret.cachedChildValues = unwrap(childValues);
|
|
653
639
|
if (tag === Portal) {
|
|
654
640
|
flush(renderer, ret.value);
|
|
655
641
|
return;
|
|
@@ -696,7 +682,7 @@
|
|
|
696
682
|
}
|
|
697
683
|
else if (ret.el.tag === Portal) {
|
|
698
684
|
host = ret;
|
|
699
|
-
renderer.arrange(Portal, host.value, host.el.props, [], host.el.props, wrap(host.
|
|
685
|
+
renderer.arrange(Portal, host.value, host.el.props, [], host.el.props, wrap(host.cachedChildValues));
|
|
700
686
|
flush(renderer, host.value);
|
|
701
687
|
}
|
|
702
688
|
else if (ret.el.tag !== Fragment) {
|
|
@@ -720,38 +706,45 @@
|
|
|
720
706
|
}
|
|
721
707
|
/*** CONTEXT FLAGS ***/
|
|
722
708
|
/**
|
|
723
|
-
* A flag which is
|
|
724
|
-
*
|
|
725
|
-
*
|
|
709
|
+
* A flag which is true when the component is initialized or updated by an
|
|
710
|
+
* ancestor component or the root render call.
|
|
711
|
+
*
|
|
712
|
+
* Used to determine things like whether the nearest host ancestor needs to be
|
|
713
|
+
* rearranged.
|
|
726
714
|
*/
|
|
727
715
|
const IsUpdating = 1 << 0;
|
|
728
716
|
/**
|
|
729
|
-
* A flag which is
|
|
730
|
-
*
|
|
731
|
-
*
|
|
732
|
-
|
|
717
|
+
* A flag which is true when the component is synchronously executing.
|
|
718
|
+
*
|
|
719
|
+
* Used to guard against components triggering stack overflow or generator error.
|
|
720
|
+
*/
|
|
721
|
+
const IsSyncExecuting = 1 << 1;
|
|
722
|
+
/**
|
|
723
|
+
* A flag which is true when the component is in the render loop.
|
|
733
724
|
*/
|
|
734
|
-
const
|
|
725
|
+
const IsInRenderLoop = 1 << 2;
|
|
735
726
|
/**
|
|
736
|
-
* A flag
|
|
737
|
-
*
|
|
727
|
+
* A flag which is true when the component starts the render loop but has not
|
|
728
|
+
* yielded yet.
|
|
729
|
+
*
|
|
730
|
+
* Used to make sure that components yield at least once per loop.
|
|
738
731
|
*/
|
|
739
|
-
const
|
|
732
|
+
const NeedsToYield = 1 << 3;
|
|
740
733
|
/**
|
|
741
734
|
* A flag used by async generator components in conjunction with the
|
|
742
|
-
*
|
|
743
|
-
*
|
|
735
|
+
* onAvailable callback to mark whether new props can be pulled via the context
|
|
736
|
+
* async iterator. See the Symbol.asyncIterator method and the
|
|
744
737
|
* resumeCtxIterator function.
|
|
745
738
|
*/
|
|
746
|
-
const
|
|
739
|
+
const PropsAvailable = 1 << 4;
|
|
747
740
|
/**
|
|
748
741
|
* A flag which is set when a generator components returns, i.e. the done
|
|
749
|
-
* property on the iteration is
|
|
750
|
-
*
|
|
742
|
+
* property on the iteration is true. Generator components will stick to their
|
|
743
|
+
* last rendered value and ignore further updates.
|
|
751
744
|
*/
|
|
752
|
-
const IsDone = 1 <<
|
|
745
|
+
const IsDone = 1 << 5;
|
|
753
746
|
/**
|
|
754
|
-
* A flag which is set when a
|
|
747
|
+
* A flag which is set when a component errors.
|
|
755
748
|
*
|
|
756
749
|
* NOTE: This is mainly used to prevent some false positives in component
|
|
757
750
|
* yields or returns undefined warnings. The reason we’re using this versus
|
|
@@ -759,28 +752,28 @@
|
|
|
759
752
|
* sync generator child) where synchronous code causes a stack overflow error
|
|
760
753
|
* in a non-deterministic way. Deeply disturbing stuff.
|
|
761
754
|
*/
|
|
762
|
-
const IsErrored = 1 <<
|
|
755
|
+
const IsErrored = 1 << 6;
|
|
763
756
|
/**
|
|
764
757
|
* A flag which is set when the component is unmounted. Unmounted components
|
|
765
758
|
* are no longer in the element tree and cannot refresh or rerender.
|
|
766
759
|
*/
|
|
767
|
-
const IsUnmounted = 1 <<
|
|
760
|
+
const IsUnmounted = 1 << 7;
|
|
768
761
|
/**
|
|
769
762
|
* A flag which indicates that the component is a sync generator component.
|
|
770
763
|
*/
|
|
771
|
-
const IsSyncGen = 1 <<
|
|
764
|
+
const IsSyncGen = 1 << 8;
|
|
772
765
|
/**
|
|
773
766
|
* A flag which indicates that the component is an async generator component.
|
|
774
767
|
*/
|
|
775
|
-
const IsAsyncGen = 1 <<
|
|
768
|
+
const IsAsyncGen = 1 << 9;
|
|
776
769
|
/**
|
|
777
770
|
* A flag which is set while schedule callbacks are called.
|
|
778
771
|
*/
|
|
779
|
-
const IsScheduling = 1 <<
|
|
772
|
+
const IsScheduling = 1 << 10;
|
|
780
773
|
/**
|
|
781
774
|
* A flag which is set when a schedule callback calls refresh.
|
|
782
775
|
*/
|
|
783
|
-
const IsSchedulingRefresh = 1 <<
|
|
776
|
+
const IsSchedulingRefresh = 1 << 11;
|
|
784
777
|
const provisionMaps = new WeakMap();
|
|
785
778
|
const scheduleMap = new WeakMap();
|
|
786
779
|
const cleanupMap = new WeakMap();
|
|
@@ -788,7 +781,7 @@
|
|
|
788
781
|
const flushMaps = new WeakMap();
|
|
789
782
|
/**
|
|
790
783
|
* @internal
|
|
791
|
-
* The internal class which holds
|
|
784
|
+
* The internal class which holds context data.
|
|
792
785
|
*/
|
|
793
786
|
class ContextImpl {
|
|
794
787
|
constructor(renderer, root, host, parent, scope, ret) {
|
|
@@ -805,10 +798,11 @@
|
|
|
805
798
|
this.inflightValue = undefined;
|
|
806
799
|
this.enqueuedBlock = undefined;
|
|
807
800
|
this.enqueuedValue = undefined;
|
|
808
|
-
this.
|
|
801
|
+
this.onProps = undefined;
|
|
802
|
+
this.onPropsRequested = undefined;
|
|
809
803
|
}
|
|
810
804
|
}
|
|
811
|
-
const
|
|
805
|
+
const _ContextImpl = Symbol.for("crank.ContextImpl");
|
|
812
806
|
/**
|
|
813
807
|
* A class which is instantiated and passed to every component as its this
|
|
814
808
|
* value. Contexts form a tree just like elements and all components in the
|
|
@@ -822,8 +816,10 @@
|
|
|
822
816
|
* schedule and cleanup callbacks.
|
|
823
817
|
*/
|
|
824
818
|
class Context {
|
|
819
|
+
// TODO: If we could make the constructor function take a nicer value, it
|
|
820
|
+
// would be useful for testing purposes.
|
|
825
821
|
constructor(impl) {
|
|
826
|
-
this[
|
|
822
|
+
this[_ContextImpl] = impl;
|
|
827
823
|
}
|
|
828
824
|
/**
|
|
829
825
|
* The current props of the associated element.
|
|
@@ -833,7 +829,7 @@
|
|
|
833
829
|
* plugins or utilities which wrap contexts.
|
|
834
830
|
*/
|
|
835
831
|
get props() {
|
|
836
|
-
return this[
|
|
832
|
+
return this[_ContextImpl].ret.el.props;
|
|
837
833
|
}
|
|
838
834
|
// TODO: Should we rename this???
|
|
839
835
|
/**
|
|
@@ -844,45 +840,70 @@
|
|
|
844
840
|
* mainly for plugins or utilities which wrap contexts.
|
|
845
841
|
*/
|
|
846
842
|
get value() {
|
|
847
|
-
return this[
|
|
843
|
+
return this[_ContextImpl].renderer.read(getValue(this[_ContextImpl].ret));
|
|
848
844
|
}
|
|
849
845
|
*[Symbol.iterator]() {
|
|
850
|
-
const impl = this[
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
846
|
+
const impl = this[_ContextImpl];
|
|
847
|
+
if (impl.f & IsAsyncGen) {
|
|
848
|
+
throw new Error("Use for await…of in async generator components");
|
|
849
|
+
}
|
|
850
|
+
try {
|
|
851
|
+
impl.f |= IsInRenderLoop;
|
|
852
|
+
while (!(impl.f & IsUnmounted)) {
|
|
853
|
+
if (impl.f & NeedsToYield) {
|
|
854
|
+
throw new Error("Context iterated twice without a yield");
|
|
855
|
+
}
|
|
856
|
+
else {
|
|
857
|
+
impl.f |= NeedsToYield;
|
|
858
|
+
}
|
|
859
|
+
yield impl.ret.el.props;
|
|
857
860
|
}
|
|
858
|
-
|
|
859
|
-
|
|
861
|
+
}
|
|
862
|
+
finally {
|
|
863
|
+
impl.f &= ~IsInRenderLoop;
|
|
860
864
|
}
|
|
861
865
|
}
|
|
862
866
|
async *[Symbol.asyncIterator]() {
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
867
|
+
const impl = this[_ContextImpl];
|
|
868
|
+
if (impl.f & IsSyncGen) {
|
|
869
|
+
throw new Error("Use for…of in sync generator components");
|
|
870
|
+
}
|
|
871
|
+
try {
|
|
872
|
+
// await an empty promise to prevent the IsInRenderLoop flag from
|
|
873
|
+
// returning false positives in the case of async generator components
|
|
874
|
+
// which immediately enter the loop
|
|
875
|
+
impl.f |= IsInRenderLoop;
|
|
876
|
+
while (!(impl.f & IsUnmounted)) {
|
|
877
|
+
if (impl.f & NeedsToYield) {
|
|
878
|
+
throw new Error("Context iterated twice without a yield");
|
|
879
|
+
}
|
|
880
|
+
else {
|
|
881
|
+
impl.f |= NeedsToYield;
|
|
882
|
+
}
|
|
883
|
+
if (impl.f & PropsAvailable) {
|
|
884
|
+
impl.f &= ~PropsAvailable;
|
|
885
|
+
yield impl.ret.el.props;
|
|
886
|
+
}
|
|
887
|
+
else {
|
|
888
|
+
const props = await new Promise((resolve) => (impl.onProps = resolve));
|
|
889
|
+
if (impl.f & IsUnmounted) {
|
|
890
|
+
break;
|
|
891
|
+
}
|
|
892
|
+
yield props;
|
|
893
|
+
}
|
|
894
|
+
if (impl.onPropsRequested) {
|
|
895
|
+
impl.onPropsRequested();
|
|
896
|
+
impl.onPropsRequested = undefined;
|
|
882
897
|
}
|
|
883
898
|
}
|
|
884
|
-
|
|
885
|
-
|
|
899
|
+
}
|
|
900
|
+
finally {
|
|
901
|
+
impl.f &= ~IsInRenderLoop;
|
|
902
|
+
if (impl.onPropsRequested) {
|
|
903
|
+
impl.onPropsRequested();
|
|
904
|
+
impl.onPropsRequested = undefined;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
886
907
|
}
|
|
887
908
|
/**
|
|
888
909
|
* Re-executes a component.
|
|
@@ -897,17 +918,16 @@
|
|
|
897
918
|
* async iterator to suspend.
|
|
898
919
|
*/
|
|
899
920
|
refresh() {
|
|
900
|
-
const impl = this[
|
|
921
|
+
const impl = this[_ContextImpl];
|
|
901
922
|
if (impl.f & IsUnmounted) {
|
|
902
923
|
console.error("Component is unmounted");
|
|
903
924
|
return impl.renderer.read(undefined);
|
|
904
925
|
}
|
|
905
|
-
else if (impl.f &
|
|
926
|
+
else if (impl.f & IsSyncExecuting) {
|
|
906
927
|
console.error("Component is already executing");
|
|
907
928
|
return this.value;
|
|
908
929
|
}
|
|
909
|
-
|
|
910
|
-
const value = runComponent(impl);
|
|
930
|
+
const value = enqueueComponentRun(impl);
|
|
911
931
|
if (isPromiseLike(value)) {
|
|
912
932
|
return value.then((value) => impl.renderer.read(value));
|
|
913
933
|
}
|
|
@@ -918,7 +938,7 @@
|
|
|
918
938
|
* fire once per callback and update.
|
|
919
939
|
*/
|
|
920
940
|
schedule(callback) {
|
|
921
|
-
const impl = this[
|
|
941
|
+
const impl = this[_ContextImpl];
|
|
922
942
|
let callbacks = scheduleMap.get(impl);
|
|
923
943
|
if (!callbacks) {
|
|
924
944
|
callbacks = new Set();
|
|
@@ -931,7 +951,7 @@
|
|
|
931
951
|
* rendered into the root. Will only fire once per callback and render.
|
|
932
952
|
*/
|
|
933
953
|
flush(callback) {
|
|
934
|
-
const impl = this[
|
|
954
|
+
const impl = this[_ContextImpl];
|
|
935
955
|
if (typeof impl.root !== "object" || impl.root === null) {
|
|
936
956
|
return;
|
|
937
957
|
}
|
|
@@ -952,7 +972,7 @@
|
|
|
952
972
|
* fire once per callback.
|
|
953
973
|
*/
|
|
954
974
|
cleanup(callback) {
|
|
955
|
-
const impl = this[
|
|
975
|
+
const impl = this[_ContextImpl];
|
|
956
976
|
let callbacks = cleanupMap.get(impl);
|
|
957
977
|
if (!callbacks) {
|
|
958
978
|
callbacks = new Set();
|
|
@@ -961,7 +981,7 @@
|
|
|
961
981
|
callbacks.add(callback);
|
|
962
982
|
}
|
|
963
983
|
consume(key) {
|
|
964
|
-
for (let parent = this[
|
|
984
|
+
for (let parent = this[_ContextImpl].parent; parent !== undefined; parent = parent.parent) {
|
|
965
985
|
const provisions = provisionMaps.get(parent);
|
|
966
986
|
if (provisions && provisions.has(key)) {
|
|
967
987
|
return provisions.get(key);
|
|
@@ -969,7 +989,7 @@
|
|
|
969
989
|
}
|
|
970
990
|
}
|
|
971
991
|
provide(key, value) {
|
|
972
|
-
const impl = this[
|
|
992
|
+
const impl = this[_ContextImpl];
|
|
973
993
|
let provisions = provisionMaps.get(impl);
|
|
974
994
|
if (!provisions) {
|
|
975
995
|
provisions = new Map();
|
|
@@ -978,7 +998,7 @@
|
|
|
978
998
|
provisions.set(key, value);
|
|
979
999
|
}
|
|
980
1000
|
addEventListener(type, listener, options) {
|
|
981
|
-
const impl = this[
|
|
1001
|
+
const impl = this[_ContextImpl];
|
|
982
1002
|
let listeners;
|
|
983
1003
|
if (!isListenerOrListenerObject(listener)) {
|
|
984
1004
|
return;
|
|
@@ -1025,7 +1045,7 @@
|
|
|
1025
1045
|
}
|
|
1026
1046
|
}
|
|
1027
1047
|
removeEventListener(type, listener, options) {
|
|
1028
|
-
const impl = this[
|
|
1048
|
+
const impl = this[_ContextImpl];
|
|
1029
1049
|
const listeners = listenersMap.get(impl);
|
|
1030
1050
|
if (listeners == null || !isListenerOrListenerObject(listener)) {
|
|
1031
1051
|
return;
|
|
@@ -1047,7 +1067,7 @@
|
|
|
1047
1067
|
}
|
|
1048
1068
|
}
|
|
1049
1069
|
dispatchEvent(ev) {
|
|
1050
|
-
const impl = this[
|
|
1070
|
+
const impl = this[_ContextImpl];
|
|
1051
1071
|
const path = [];
|
|
1052
1072
|
for (let parent = impl.parent; parent !== undefined; parent = parent.parent) {
|
|
1053
1073
|
path.push(parent);
|
|
@@ -1157,17 +1177,16 @@
|
|
|
1157
1177
|
let ctx;
|
|
1158
1178
|
if (oldProps) {
|
|
1159
1179
|
ctx = ret.ctx;
|
|
1160
|
-
if (ctx.f &
|
|
1180
|
+
if (ctx.f & IsSyncExecuting) {
|
|
1161
1181
|
console.error("Component is already executing");
|
|
1162
|
-
return ret.
|
|
1182
|
+
return ret.cachedChildValues;
|
|
1163
1183
|
}
|
|
1164
1184
|
}
|
|
1165
1185
|
else {
|
|
1166
1186
|
ctx = ret.ctx = new ContextImpl(renderer, root, host, parent, scope, ret);
|
|
1167
1187
|
}
|
|
1168
1188
|
ctx.f |= IsUpdating;
|
|
1169
|
-
|
|
1170
|
-
return runComponent(ctx);
|
|
1189
|
+
return enqueueComponentRun(ctx);
|
|
1171
1190
|
}
|
|
1172
1191
|
function updateComponentChildren(ctx, children) {
|
|
1173
1192
|
if (ctx.f & IsUnmounted || ctx.f & IsErrored) {
|
|
@@ -1177,18 +1196,19 @@
|
|
|
1177
1196
|
console.error("A component has returned or yielded undefined. If this was intentional, return or yield null instead.");
|
|
1178
1197
|
}
|
|
1179
1198
|
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
1199
|
try {
|
|
1200
|
+
// TODO: WAT
|
|
1201
|
+
// We set the isExecuting flag in case a child component dispatches an event
|
|
1202
|
+
// which bubbles to this component and causes a synchronous refresh().
|
|
1203
|
+
ctx.f |= IsSyncExecuting;
|
|
1184
1204
|
childValues = diffChildren(ctx.renderer, ctx.root, ctx.host, ctx, ctx.scope, ctx.ret, narrow(children));
|
|
1185
1205
|
}
|
|
1186
1206
|
finally {
|
|
1187
|
-
ctx.f &= ~
|
|
1207
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1188
1208
|
}
|
|
1189
1209
|
if (isPromiseLike(childValues)) {
|
|
1190
|
-
ctx.ret.
|
|
1191
|
-
return ctx.ret.
|
|
1210
|
+
ctx.ret.inflightValue = childValues.then((childValues) => commitComponent(ctx, childValues));
|
|
1211
|
+
return ctx.ret.inflightValue;
|
|
1192
1212
|
}
|
|
1193
1213
|
return commitComponent(ctx, childValues);
|
|
1194
1214
|
}
|
|
@@ -1208,8 +1228,8 @@
|
|
|
1208
1228
|
}
|
|
1209
1229
|
}
|
|
1210
1230
|
}
|
|
1211
|
-
const oldValues = wrap(ctx.ret.
|
|
1212
|
-
let value = (ctx.ret.
|
|
1231
|
+
const oldValues = wrap(ctx.ret.cachedChildValues);
|
|
1232
|
+
let value = (ctx.ret.cachedChildValues = unwrap(values));
|
|
1213
1233
|
if (ctx.f & IsScheduling) {
|
|
1214
1234
|
ctx.f |= IsSchedulingRefresh;
|
|
1215
1235
|
}
|
|
@@ -1217,7 +1237,7 @@
|
|
|
1217
1237
|
// If we’re not updating the component, which happens when components are
|
|
1218
1238
|
// refreshed, or when async generator components iterate, we have to do a
|
|
1219
1239
|
// little bit housekeeping when a component’s child values have changed.
|
|
1220
|
-
if (!
|
|
1240
|
+
if (!arrayEqual(oldValues, values)) {
|
|
1221
1241
|
const records = getListenerRecords(ctx.parent, ctx.host);
|
|
1222
1242
|
if (records.length) {
|
|
1223
1243
|
for (let i = 0; i < values.length; i++) {
|
|
@@ -1232,7 +1252,7 @@
|
|
|
1232
1252
|
}
|
|
1233
1253
|
// rearranging the nearest ancestor host element
|
|
1234
1254
|
const host = ctx.host;
|
|
1235
|
-
const oldHostValues = wrap(host.
|
|
1255
|
+
const oldHostValues = wrap(host.cachedChildValues);
|
|
1236
1256
|
invalidate(ctx, host);
|
|
1237
1257
|
const hostValues = getChildValues(host);
|
|
1238
1258
|
ctx.renderer.arrange(host.el.tag, host.value, host.el.props, hostValues,
|
|
@@ -1261,50 +1281,75 @@
|
|
|
1261
1281
|
}
|
|
1262
1282
|
function invalidate(ctx, host) {
|
|
1263
1283
|
for (let parent = ctx.parent; parent !== undefined && parent.host === host; parent = parent.parent) {
|
|
1264
|
-
parent.ret.
|
|
1284
|
+
parent.ret.cachedChildValues = undefined;
|
|
1265
1285
|
}
|
|
1266
|
-
host.
|
|
1286
|
+
host.cachedChildValues = undefined;
|
|
1267
1287
|
}
|
|
1268
|
-
function
|
|
1269
|
-
if (
|
|
1288
|
+
function arrayEqual(arr1, arr2) {
|
|
1289
|
+
if (arr1.length !== arr2.length) {
|
|
1270
1290
|
return false;
|
|
1271
1291
|
}
|
|
1272
|
-
for (let i = 0; i <
|
|
1273
|
-
const value1 =
|
|
1274
|
-
const value2 =
|
|
1292
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
1293
|
+
const value1 = arr1[i];
|
|
1294
|
+
const value2 = arr2[i];
|
|
1275
1295
|
if (value1 !== value2) {
|
|
1276
1296
|
return false;
|
|
1277
1297
|
}
|
|
1278
1298
|
}
|
|
1279
1299
|
return true;
|
|
1280
1300
|
}
|
|
1281
|
-
/**
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1301
|
+
/** Enqueues and executes the component associated with the context. */
|
|
1302
|
+
function enqueueComponentRun(ctx) {
|
|
1303
|
+
if (ctx.f & IsAsyncGen) {
|
|
1304
|
+
// This branch will only run for async generator components after the
|
|
1305
|
+
// initial render.
|
|
1306
|
+
//
|
|
1307
|
+
// Async generator components which are in the props loop can be in one of
|
|
1308
|
+
// three states:
|
|
1309
|
+
//
|
|
1310
|
+
// 1. propsAvailable flag is true: "available"
|
|
1311
|
+
//
|
|
1312
|
+
// The component is paused somewhere in the loop. When the component
|
|
1313
|
+
// reaches the bottom of the loop, it will run again with the next props.
|
|
1314
|
+
//
|
|
1315
|
+
// 2. onAvailable callback is defined: "suspended"
|
|
1316
|
+
//
|
|
1317
|
+
// The component has reached the bottom of the loop and is waiting for
|
|
1318
|
+
// new props.
|
|
1319
|
+
//
|
|
1320
|
+
// 3. neither 1 or 2: "Running"
|
|
1321
|
+
//
|
|
1322
|
+
// The component is paused somewhere in the loop. When the component
|
|
1323
|
+
// reaches the bottom of the loop, it will suspend.
|
|
1324
|
+
//
|
|
1325
|
+
// By definition, components will never be both available and suspended at
|
|
1326
|
+
// the same time.
|
|
1327
|
+
//
|
|
1328
|
+
// If the component is at the loop bottom, this means that the next value
|
|
1329
|
+
// produced by the component will have the most up to date props, so we can
|
|
1330
|
+
// simply return the current inflight value. Otherwise, we have to wait for
|
|
1331
|
+
// the bottom of the loop before returning the inflight value.
|
|
1332
|
+
const isAtLoopbottom = ctx.f & IsInRenderLoop && !ctx.onProps;
|
|
1333
|
+
resumePropsIterator(ctx);
|
|
1334
|
+
if (isAtLoopbottom) {
|
|
1335
|
+
if (ctx.inflightBlock == null) {
|
|
1336
|
+
ctx.inflightBlock = new Promise((resolve) => (ctx.onPropsRequested = resolve));
|
|
1337
|
+
}
|
|
1338
|
+
return ctx.inflightBlock.then(() => {
|
|
1339
|
+
ctx.inflightBlock = undefined;
|
|
1340
|
+
return ctx.inflightValue;
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
return ctx.inflightValue;
|
|
1344
|
+
}
|
|
1345
|
+
else if (!ctx.inflightBlock) {
|
|
1299
1346
|
try {
|
|
1300
|
-
const [block, value] =
|
|
1347
|
+
const [block, value] = runComponent(ctx);
|
|
1301
1348
|
if (block) {
|
|
1302
1349
|
ctx.inflightBlock = block
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
}
|
|
1307
|
-
})
|
|
1350
|
+
// TODO: there is some fuckery going on here related to async
|
|
1351
|
+
// generator components resuming when they’re meant to be returned.
|
|
1352
|
+
.then((v) => v)
|
|
1308
1353
|
.finally(() => advanceComponent(ctx));
|
|
1309
1354
|
// stepComponent will only return a block if the value is asynchronous
|
|
1310
1355
|
ctx.inflightValue = value;
|
|
@@ -1318,38 +1363,43 @@
|
|
|
1318
1363
|
throw err;
|
|
1319
1364
|
}
|
|
1320
1365
|
}
|
|
1321
|
-
else if (ctx.f & IsAsyncGen) {
|
|
1322
|
-
return ctx.inflightValue;
|
|
1323
|
-
}
|
|
1324
1366
|
else if (!ctx.enqueuedBlock) {
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1367
|
+
// We need to assign enqueuedBlock and enqueuedValue synchronously, hence
|
|
1368
|
+
// the Promise constructor call.
|
|
1369
|
+
let resolveEnqueuedBlock;
|
|
1370
|
+
ctx.enqueuedBlock = new Promise((resolve) => (resolveEnqueuedBlock = resolve));
|
|
1371
|
+
ctx.enqueuedValue = ctx.inflightBlock.then(() => {
|
|
1328
1372
|
try {
|
|
1329
|
-
const [block, value] =
|
|
1330
|
-
resolve(value);
|
|
1373
|
+
const [block, value] = runComponent(ctx);
|
|
1331
1374
|
if (block) {
|
|
1332
|
-
|
|
1333
|
-
if (!(ctx.f & IsUpdating)) {
|
|
1334
|
-
return propagateError(ctx.parent, err);
|
|
1335
|
-
}
|
|
1336
|
-
});
|
|
1375
|
+
resolveEnqueuedBlock(block.finally(() => advanceComponent(ctx)));
|
|
1337
1376
|
}
|
|
1377
|
+
return value;
|
|
1338
1378
|
}
|
|
1339
1379
|
catch (err) {
|
|
1340
1380
|
if (!(ctx.f & IsUpdating)) {
|
|
1341
1381
|
return propagateError(ctx.parent, err);
|
|
1342
1382
|
}
|
|
1383
|
+
throw err;
|
|
1343
1384
|
}
|
|
1344
|
-
})
|
|
1345
|
-
.finally(() => advanceComponent(ctx));
|
|
1346
|
-
ctx.enqueuedValue = new Promise((resolve1) => (resolve = resolve1));
|
|
1385
|
+
});
|
|
1347
1386
|
}
|
|
1348
1387
|
return ctx.enqueuedValue;
|
|
1349
1388
|
}
|
|
1389
|
+
/** Called when the inflight block promise settles. */
|
|
1390
|
+
function advanceComponent(ctx) {
|
|
1391
|
+
if (ctx.f & IsAsyncGen) {
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
ctx.inflightBlock = ctx.enqueuedBlock;
|
|
1395
|
+
ctx.inflightValue = ctx.enqueuedValue;
|
|
1396
|
+
ctx.enqueuedBlock = undefined;
|
|
1397
|
+
ctx.enqueuedValue = undefined;
|
|
1398
|
+
}
|
|
1350
1399
|
/**
|
|
1351
1400
|
* This function is responsible for executing the component and handling all
|
|
1352
|
-
* the different component types.
|
|
1401
|
+
* the different component types. We cannot identify whether a component is a
|
|
1402
|
+
* generator or async without calling it and inspecting the return value.
|
|
1353
1403
|
*
|
|
1354
1404
|
* @returns {[block, value]} A tuple where
|
|
1355
1405
|
* block - A possible promise which represents the duration during which the
|
|
@@ -1364,14 +1414,15 @@
|
|
|
1364
1414
|
* - Sync generator components block while any children are executing, because
|
|
1365
1415
|
* they are expected to only resume when they’ve actually rendered.
|
|
1366
1416
|
*/
|
|
1367
|
-
function
|
|
1417
|
+
function runComponent(ctx) {
|
|
1368
1418
|
const ret = ctx.ret;
|
|
1369
1419
|
if (ctx.f & IsDone) {
|
|
1370
1420
|
return [undefined, getValue(ret)];
|
|
1371
1421
|
}
|
|
1372
1422
|
const initial = !ctx.iterator;
|
|
1373
1423
|
if (initial) {
|
|
1374
|
-
ctx
|
|
1424
|
+
resumePropsIterator(ctx);
|
|
1425
|
+
ctx.f |= IsSyncExecuting;
|
|
1375
1426
|
clearEventListeners(ctx);
|
|
1376
1427
|
let result;
|
|
1377
1428
|
try {
|
|
@@ -1382,7 +1433,7 @@
|
|
|
1382
1433
|
throw err;
|
|
1383
1434
|
}
|
|
1384
1435
|
finally {
|
|
1385
|
-
ctx.f &= ~
|
|
1436
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1386
1437
|
}
|
|
1387
1438
|
if (isIteratorLike(result)) {
|
|
1388
1439
|
ctx.iterator = result;
|
|
@@ -1394,120 +1445,179 @@
|
|
|
1394
1445
|
ctx.f |= IsErrored;
|
|
1395
1446
|
throw err;
|
|
1396
1447
|
});
|
|
1397
|
-
return [result1, value];
|
|
1448
|
+
return [result1.catch(NOOP), value];
|
|
1398
1449
|
}
|
|
1399
1450
|
else {
|
|
1400
1451
|
// sync function component
|
|
1401
1452
|
return [undefined, updateComponentChildren(ctx, result)];
|
|
1402
1453
|
}
|
|
1403
1454
|
}
|
|
1404
|
-
let oldValue;
|
|
1405
|
-
if (initial) {
|
|
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));
|
|
1418
|
-
}
|
|
1419
1455
|
let iteration;
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
if (initial) {
|
|
1456
|
+
if (initial) {
|
|
1457
|
+
try {
|
|
1458
|
+
ctx.f |= IsSyncExecuting;
|
|
1459
|
+
iteration = ctx.iterator.next();
|
|
1460
|
+
}
|
|
1461
|
+
catch (err) {
|
|
1462
|
+
ctx.f |= IsDone | IsErrored;
|
|
1463
|
+
throw err;
|
|
1464
|
+
}
|
|
1465
|
+
finally {
|
|
1466
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1467
|
+
}
|
|
1468
|
+
if (isPromiseLike(iteration)) {
|
|
1434
1469
|
ctx.f |= IsAsyncGen;
|
|
1470
|
+
runAsyncGenComponent(ctx, iteration);
|
|
1435
1471
|
}
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1472
|
+
else {
|
|
1473
|
+
ctx.f |= IsSyncGen;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
if (ctx.f & IsSyncGen) {
|
|
1477
|
+
// sync generator component
|
|
1478
|
+
ctx.f &= ~NeedsToYield;
|
|
1479
|
+
if (!initial) {
|
|
1444
1480
|
try {
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
}
|
|
1449
|
-
return value;
|
|
1481
|
+
ctx.f |= IsSyncExecuting;
|
|
1482
|
+
const oldValue = ctx.renderer.read(getValue(ret));
|
|
1483
|
+
iteration = ctx.iterator.next(oldValue);
|
|
1450
1484
|
}
|
|
1451
1485
|
catch (err) {
|
|
1452
|
-
|
|
1486
|
+
ctx.f |= IsDone | IsErrored;
|
|
1487
|
+
throw err;
|
|
1453
1488
|
}
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
}
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1489
|
+
finally {
|
|
1490
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
if (isPromiseLike(iteration)) {
|
|
1494
|
+
throw new Error("Sync generator component returned an async iteration");
|
|
1495
|
+
}
|
|
1496
|
+
if (iteration.done) {
|
|
1497
|
+
ctx.f |= IsDone;
|
|
1498
|
+
}
|
|
1499
|
+
let value;
|
|
1500
|
+
try {
|
|
1501
|
+
value = updateComponentChildren(ctx,
|
|
1502
|
+
// Children can be void so we eliminate that here
|
|
1503
|
+
iteration.value);
|
|
1504
|
+
if (isPromiseLike(value)) {
|
|
1505
|
+
value = value.catch((err) => handleChildError(ctx, err));
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
catch (err) {
|
|
1509
|
+
value = handleChildError(ctx, err);
|
|
1510
|
+
}
|
|
1471
1511
|
if (isPromiseLike(value)) {
|
|
1472
|
-
|
|
1512
|
+
return [value.catch(NOOP), value];
|
|
1473
1513
|
}
|
|
1514
|
+
return [undefined, value];
|
|
1474
1515
|
}
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
if (isPromiseLike(value)) {
|
|
1479
|
-
return [value.catch(NOOP), value];
|
|
1516
|
+
else {
|
|
1517
|
+
// async generator component
|
|
1518
|
+
return [undefined, ctx.inflightValue];
|
|
1480
1519
|
}
|
|
1481
|
-
return [undefined, value];
|
|
1482
1520
|
}
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1521
|
+
async function runAsyncGenComponent(ctx, iterationP) {
|
|
1522
|
+
do {
|
|
1523
|
+
// block and value must be assigned at the same time.
|
|
1524
|
+
let onValue;
|
|
1525
|
+
ctx.inflightValue = new Promise((resolve) => (onValue = resolve));
|
|
1526
|
+
if (ctx.f & IsUpdating) {
|
|
1527
|
+
// We should not swallow unhandled promise rejections if the component is
|
|
1528
|
+
// updating independently.
|
|
1529
|
+
// TODO: Does this handle this.refresh() calls?
|
|
1530
|
+
ctx.inflightValue.catch(NOOP);
|
|
1531
|
+
}
|
|
1532
|
+
let iteration;
|
|
1533
|
+
try {
|
|
1534
|
+
iteration = await iterationP;
|
|
1535
|
+
}
|
|
1536
|
+
catch (err) {
|
|
1537
|
+
ctx.f |= IsDone;
|
|
1538
|
+
ctx.f |= IsErrored;
|
|
1539
|
+
onValue(Promise.reject(err));
|
|
1540
|
+
break;
|
|
1541
|
+
}
|
|
1542
|
+
finally {
|
|
1543
|
+
if (!(ctx.f & IsInRenderLoop)) {
|
|
1544
|
+
ctx.f &= ~PropsAvailable;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
if (ctx.f & NeedsToYield) {
|
|
1548
|
+
ctx.f &= ~NeedsToYield;
|
|
1549
|
+
}
|
|
1550
|
+
if (iteration.done) {
|
|
1551
|
+
ctx.f |= IsDone;
|
|
1552
|
+
}
|
|
1553
|
+
let value;
|
|
1554
|
+
try {
|
|
1555
|
+
value = updateComponentChildren(ctx, iteration.value);
|
|
1556
|
+
if (isPromiseLike(value)) {
|
|
1557
|
+
value = value.catch((err) => handleChildError(ctx, err));
|
|
1558
|
+
}
|
|
1559
|
+
onValue(value);
|
|
1560
|
+
}
|
|
1561
|
+
catch (err) {
|
|
1562
|
+
// Do we need to catch potential errors here in the case of unhandled
|
|
1563
|
+
// promise rejections?
|
|
1564
|
+
value = handleChildError(ctx, err);
|
|
1565
|
+
}
|
|
1566
|
+
// TODO: this can be done more elegantly
|
|
1567
|
+
let oldValue;
|
|
1568
|
+
if (ctx.ret.inflightValue) {
|
|
1569
|
+
// The value passed back into the generator as the argument to the next
|
|
1570
|
+
// method is a promise if an async generator component has async
|
|
1571
|
+
// children. Sync generator components only resume when their children
|
|
1572
|
+
// have fulfilled so the element’s inflight child values will never be
|
|
1573
|
+
// defined.
|
|
1574
|
+
oldValue = ctx.ret.inflightValue.then((value) => ctx.renderer.read(value), () => ctx.renderer.read(undefined));
|
|
1575
|
+
}
|
|
1576
|
+
else {
|
|
1577
|
+
oldValue = ctx.renderer.read(getValue(ctx.ret));
|
|
1578
|
+
}
|
|
1579
|
+
if (ctx.f & IsUnmounted) {
|
|
1580
|
+
if (ctx.f & IsInRenderLoop) {
|
|
1581
|
+
try {
|
|
1582
|
+
ctx.f |= IsSyncExecuting;
|
|
1583
|
+
iterationP = ctx.iterator.next(oldValue);
|
|
1584
|
+
}
|
|
1585
|
+
finally {
|
|
1586
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
else {
|
|
1590
|
+
returnComponent(ctx);
|
|
1591
|
+
break;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
else if (!(ctx.f & IsDone)) {
|
|
1595
|
+
try {
|
|
1596
|
+
ctx.f |= IsSyncExecuting;
|
|
1597
|
+
iterationP = ctx.iterator.next(oldValue);
|
|
1598
|
+
}
|
|
1599
|
+
finally {
|
|
1600
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
} while (!(ctx.f & IsDone));
|
|
1494
1604
|
}
|
|
1495
1605
|
/**
|
|
1496
1606
|
* Called to make props available to the props async iterator for async
|
|
1497
1607
|
* generator components.
|
|
1498
1608
|
*/
|
|
1499
|
-
function
|
|
1500
|
-
if (ctx.
|
|
1501
|
-
ctx.
|
|
1502
|
-
ctx.
|
|
1609
|
+
function resumePropsIterator(ctx) {
|
|
1610
|
+
if (ctx.onProps) {
|
|
1611
|
+
ctx.onProps(ctx.ret.el.props);
|
|
1612
|
+
ctx.onProps = undefined;
|
|
1613
|
+
ctx.f &= ~PropsAvailable;
|
|
1503
1614
|
}
|
|
1504
1615
|
else {
|
|
1505
|
-
ctx.f |=
|
|
1616
|
+
ctx.f |= PropsAvailable;
|
|
1506
1617
|
}
|
|
1507
1618
|
}
|
|
1508
1619
|
// TODO: async unmounting
|
|
1509
1620
|
function unmountComponent(ctx) {
|
|
1510
|
-
ctx.f |= IsUnmounted;
|
|
1511
1621
|
clearEventListeners(ctx);
|
|
1512
1622
|
const callbacks = cleanupMap.get(ctx);
|
|
1513
1623
|
if (callbacks) {
|
|
@@ -1517,22 +1627,55 @@
|
|
|
1517
1627
|
callback(value);
|
|
1518
1628
|
}
|
|
1519
1629
|
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
ctx.f
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1630
|
+
ctx.f |= IsUnmounted;
|
|
1631
|
+
if (ctx.iterator && !(ctx.f & IsDone)) {
|
|
1632
|
+
if (ctx.f & IsSyncGen) {
|
|
1633
|
+
let value;
|
|
1634
|
+
if (ctx.f & IsInRenderLoop) {
|
|
1635
|
+
value = enqueueComponentRun(ctx);
|
|
1636
|
+
}
|
|
1637
|
+
if (isPromiseLike(value)) {
|
|
1638
|
+
value.then(() => {
|
|
1639
|
+
if (ctx.f & IsInRenderLoop) {
|
|
1640
|
+
unmountComponent(ctx);
|
|
1641
|
+
}
|
|
1642
|
+
else {
|
|
1643
|
+
returnComponent(ctx);
|
|
1644
|
+
}
|
|
1645
|
+
}, (err) => {
|
|
1646
|
+
propagateError(ctx.parent, err);
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
else {
|
|
1650
|
+
if (ctx.f & IsInRenderLoop) {
|
|
1651
|
+
unmountComponent(ctx);
|
|
1652
|
+
}
|
|
1653
|
+
else {
|
|
1654
|
+
returnComponent(ctx);
|
|
1529
1655
|
}
|
|
1530
1656
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1657
|
+
}
|
|
1658
|
+
else {
|
|
1659
|
+
// async generator component
|
|
1660
|
+
resumePropsIterator(ctx);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
function returnComponent(ctx) {
|
|
1665
|
+
resumePropsIterator(ctx);
|
|
1666
|
+
if (!(ctx.f & IsDone) && typeof ctx.iterator.return === "function") {
|
|
1667
|
+
ctx.f |= IsSyncExecuting;
|
|
1668
|
+
try {
|
|
1669
|
+
const iteration = ctx.iterator.return();
|
|
1670
|
+
if (isPromiseLike(iteration)) {
|
|
1671
|
+
iteration.catch((err) => propagateError(ctx.parent, err));
|
|
1533
1672
|
}
|
|
1534
1673
|
}
|
|
1674
|
+
finally {
|
|
1675
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1676
|
+
}
|
|
1535
1677
|
}
|
|
1678
|
+
ctx.f |= IsDone;
|
|
1536
1679
|
}
|
|
1537
1680
|
/*** EVENT TARGET UTILITIES ***/
|
|
1538
1681
|
// EVENT PHASE CONSTANTS
|
|
@@ -1609,10 +1752,10 @@
|
|
|
1609
1752
|
typeof ctx.iterator.throw !== "function") {
|
|
1610
1753
|
throw err;
|
|
1611
1754
|
}
|
|
1612
|
-
|
|
1755
|
+
resumePropsIterator(ctx);
|
|
1613
1756
|
let iteration;
|
|
1614
1757
|
try {
|
|
1615
|
-
ctx.f |=
|
|
1758
|
+
ctx.f |= IsSyncExecuting;
|
|
1616
1759
|
iteration = ctx.iterator.throw(err);
|
|
1617
1760
|
}
|
|
1618
1761
|
catch (err) {
|
|
@@ -1620,7 +1763,7 @@
|
|
|
1620
1763
|
throw err;
|
|
1621
1764
|
}
|
|
1622
1765
|
finally {
|
|
1623
|
-
ctx.f &= ~
|
|
1766
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1624
1767
|
}
|
|
1625
1768
|
if (isPromiseLike(iteration)) {
|
|
1626
1769
|
return iteration.then((iteration) => {
|