@b9g/crank 0.5.0-beta.3 → 0.5.0-beta.4
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 +197 -183
- package/crank.cjs.map +1 -1
- package/crank.d.ts +17 -31
- package/crank.js +197 -183
- package/crank.js.map +1 -1
- package/package.json +8 -8
- package/umd.js +197 -183
- package/umd.js.map +1 -1
package/crank.cjs
CHANGED
|
@@ -735,12 +735,6 @@ const NeedsToYield = 1 << 3;
|
|
|
735
735
|
* resumeCtxIterator function.
|
|
736
736
|
*/
|
|
737
737
|
const PropsAvailable = 1 << 4;
|
|
738
|
-
/**
|
|
739
|
-
* A flag which is set when a generator components returns, i.e. the done
|
|
740
|
-
* property on the iteration is true. Generator components will stick to their
|
|
741
|
-
* last rendered value and ignore further updates.
|
|
742
|
-
*/
|
|
743
|
-
const IsDone = 1 << 5;
|
|
744
738
|
/**
|
|
745
739
|
* A flag which is set when a component errors.
|
|
746
740
|
*
|
|
@@ -784,7 +778,7 @@ const flushMaps = new WeakMap();
|
|
|
784
778
|
class ContextImpl {
|
|
785
779
|
constructor(renderer, root, host, parent, scope, ret) {
|
|
786
780
|
this.f = 0;
|
|
787
|
-
this.
|
|
781
|
+
this.owner = new Context(this);
|
|
788
782
|
this.renderer = renderer;
|
|
789
783
|
this.root = root;
|
|
790
784
|
this.host = host;
|
|
@@ -841,65 +835,65 @@ class Context {
|
|
|
841
835
|
return this[_ContextImpl].renderer.read(getValue(this[_ContextImpl].ret));
|
|
842
836
|
}
|
|
843
837
|
*[Symbol.iterator]() {
|
|
844
|
-
const
|
|
845
|
-
if (
|
|
838
|
+
const ctx = this[_ContextImpl];
|
|
839
|
+
if (ctx.f & IsAsyncGen) {
|
|
846
840
|
throw new Error("Use for await…of in async generator components");
|
|
847
841
|
}
|
|
848
842
|
try {
|
|
849
|
-
|
|
850
|
-
while (!(
|
|
851
|
-
if (
|
|
843
|
+
ctx.f |= IsInRenderLoop;
|
|
844
|
+
while (!(ctx.f & IsUnmounted)) {
|
|
845
|
+
if (ctx.f & NeedsToYield) {
|
|
852
846
|
throw new Error("Context iterated twice without a yield");
|
|
853
847
|
}
|
|
854
848
|
else {
|
|
855
|
-
|
|
849
|
+
ctx.f |= NeedsToYield;
|
|
856
850
|
}
|
|
857
|
-
yield
|
|
851
|
+
yield ctx.ret.el.props;
|
|
858
852
|
}
|
|
859
853
|
}
|
|
860
854
|
finally {
|
|
861
|
-
|
|
855
|
+
ctx.f &= ~IsInRenderLoop;
|
|
862
856
|
}
|
|
863
857
|
}
|
|
864
858
|
async *[Symbol.asyncIterator]() {
|
|
865
|
-
const
|
|
866
|
-
if (
|
|
859
|
+
const ctx = this[_ContextImpl];
|
|
860
|
+
if (ctx.f & IsSyncGen) {
|
|
867
861
|
throw new Error("Use for…of in sync generator components");
|
|
868
862
|
}
|
|
869
863
|
try {
|
|
870
864
|
// await an empty promise to prevent the IsInRenderLoop flag from
|
|
871
865
|
// returning false positives in the case of async generator components
|
|
872
866
|
// which immediately enter the loop
|
|
873
|
-
|
|
874
|
-
while (!(
|
|
875
|
-
if (
|
|
867
|
+
ctx.f |= IsInRenderLoop;
|
|
868
|
+
while (!(ctx.f & IsUnmounted)) {
|
|
869
|
+
if (ctx.f & NeedsToYield) {
|
|
876
870
|
throw new Error("Context iterated twice without a yield");
|
|
877
871
|
}
|
|
878
872
|
else {
|
|
879
|
-
|
|
873
|
+
ctx.f |= NeedsToYield;
|
|
880
874
|
}
|
|
881
|
-
if (
|
|
882
|
-
|
|
883
|
-
yield
|
|
875
|
+
if (ctx.f & PropsAvailable) {
|
|
876
|
+
ctx.f &= ~PropsAvailable;
|
|
877
|
+
yield ctx.ret.el.props;
|
|
884
878
|
}
|
|
885
879
|
else {
|
|
886
|
-
const props = await new Promise((resolve) => (
|
|
887
|
-
if (
|
|
880
|
+
const props = await new Promise((resolve) => (ctx.onProps = resolve));
|
|
881
|
+
if (ctx.f & IsUnmounted) {
|
|
888
882
|
break;
|
|
889
883
|
}
|
|
890
884
|
yield props;
|
|
891
885
|
}
|
|
892
|
-
if (
|
|
893
|
-
|
|
894
|
-
|
|
886
|
+
if (ctx.onPropsRequested) {
|
|
887
|
+
ctx.onPropsRequested();
|
|
888
|
+
ctx.onPropsRequested = undefined;
|
|
895
889
|
}
|
|
896
890
|
}
|
|
897
891
|
}
|
|
898
892
|
finally {
|
|
899
|
-
|
|
900
|
-
if (
|
|
901
|
-
|
|
902
|
-
|
|
893
|
+
ctx.f &= ~IsInRenderLoop;
|
|
894
|
+
if (ctx.onPropsRequested) {
|
|
895
|
+
ctx.onPropsRequested();
|
|
896
|
+
ctx.onPropsRequested = undefined;
|
|
903
897
|
}
|
|
904
898
|
}
|
|
905
899
|
}
|
|
@@ -916,31 +910,31 @@ class Context {
|
|
|
916
910
|
* async iterator to suspend.
|
|
917
911
|
*/
|
|
918
912
|
refresh() {
|
|
919
|
-
const
|
|
920
|
-
if (
|
|
913
|
+
const ctx = this[_ContextImpl];
|
|
914
|
+
if (ctx.f & IsUnmounted) {
|
|
921
915
|
console.error("Component is unmounted");
|
|
922
|
-
return
|
|
916
|
+
return ctx.renderer.read(undefined);
|
|
923
917
|
}
|
|
924
|
-
else if (
|
|
918
|
+
else if (ctx.f & IsSyncExecuting) {
|
|
925
919
|
console.error("Component is already executing");
|
|
926
920
|
return this.value;
|
|
927
921
|
}
|
|
928
|
-
const value = enqueueComponentRun(
|
|
922
|
+
const value = enqueueComponentRun(ctx);
|
|
929
923
|
if (isPromiseLike(value)) {
|
|
930
|
-
return value.then((value) =>
|
|
924
|
+
return value.then((value) => ctx.renderer.read(value));
|
|
931
925
|
}
|
|
932
|
-
return
|
|
926
|
+
return ctx.renderer.read(value);
|
|
933
927
|
}
|
|
934
928
|
/**
|
|
935
929
|
* Registers a callback which fires when the component commits. Will only
|
|
936
930
|
* fire once per callback and update.
|
|
937
931
|
*/
|
|
938
932
|
schedule(callback) {
|
|
939
|
-
const
|
|
940
|
-
let callbacks = scheduleMap.get(
|
|
933
|
+
const ctx = this[_ContextImpl];
|
|
934
|
+
let callbacks = scheduleMap.get(ctx);
|
|
941
935
|
if (!callbacks) {
|
|
942
936
|
callbacks = new Set();
|
|
943
|
-
scheduleMap.set(
|
|
937
|
+
scheduleMap.set(ctx, callbacks);
|
|
944
938
|
}
|
|
945
939
|
callbacks.add(callback);
|
|
946
940
|
}
|
|
@@ -949,19 +943,19 @@ class Context {
|
|
|
949
943
|
* rendered into the root. Will only fire once per callback and render.
|
|
950
944
|
*/
|
|
951
945
|
flush(callback) {
|
|
952
|
-
const
|
|
953
|
-
if (typeof
|
|
946
|
+
const ctx = this[_ContextImpl];
|
|
947
|
+
if (typeof ctx.root !== "object" || ctx.root === null) {
|
|
954
948
|
return;
|
|
955
949
|
}
|
|
956
|
-
let flushMap = flushMaps.get(
|
|
950
|
+
let flushMap = flushMaps.get(ctx.root);
|
|
957
951
|
if (!flushMap) {
|
|
958
952
|
flushMap = new Map();
|
|
959
|
-
flushMaps.set(
|
|
953
|
+
flushMaps.set(ctx.root, flushMap);
|
|
960
954
|
}
|
|
961
|
-
let callbacks = flushMap.get(
|
|
955
|
+
let callbacks = flushMap.get(ctx);
|
|
962
956
|
if (!callbacks) {
|
|
963
957
|
callbacks = new Set();
|
|
964
|
-
flushMap.set(
|
|
958
|
+
flushMap.set(ctx, callbacks);
|
|
965
959
|
}
|
|
966
960
|
callbacks.add(callback);
|
|
967
961
|
}
|
|
@@ -970,45 +964,45 @@ class Context {
|
|
|
970
964
|
* fire once per callback.
|
|
971
965
|
*/
|
|
972
966
|
cleanup(callback) {
|
|
973
|
-
const
|
|
974
|
-
let callbacks = cleanupMap.get(
|
|
967
|
+
const ctx = this[_ContextImpl];
|
|
968
|
+
let callbacks = cleanupMap.get(ctx);
|
|
975
969
|
if (!callbacks) {
|
|
976
970
|
callbacks = new Set();
|
|
977
|
-
cleanupMap.set(
|
|
971
|
+
cleanupMap.set(ctx, callbacks);
|
|
978
972
|
}
|
|
979
973
|
callbacks.add(callback);
|
|
980
974
|
}
|
|
981
975
|
consume(key) {
|
|
982
|
-
for (let
|
|
983
|
-
const provisions = provisionMaps.get(
|
|
976
|
+
for (let ctx = this[_ContextImpl].parent; ctx !== undefined; ctx = ctx.parent) {
|
|
977
|
+
const provisions = provisionMaps.get(ctx);
|
|
984
978
|
if (provisions && provisions.has(key)) {
|
|
985
979
|
return provisions.get(key);
|
|
986
980
|
}
|
|
987
981
|
}
|
|
988
982
|
}
|
|
989
983
|
provide(key, value) {
|
|
990
|
-
const
|
|
991
|
-
let provisions = provisionMaps.get(
|
|
984
|
+
const ctx = this[_ContextImpl];
|
|
985
|
+
let provisions = provisionMaps.get(ctx);
|
|
992
986
|
if (!provisions) {
|
|
993
987
|
provisions = new Map();
|
|
994
|
-
provisionMaps.set(
|
|
988
|
+
provisionMaps.set(ctx, provisions);
|
|
995
989
|
}
|
|
996
990
|
provisions.set(key, value);
|
|
997
991
|
}
|
|
998
992
|
addEventListener(type, listener, options) {
|
|
999
|
-
const
|
|
993
|
+
const ctx = this[_ContextImpl];
|
|
1000
994
|
let listeners;
|
|
1001
995
|
if (!isListenerOrListenerObject(listener)) {
|
|
1002
996
|
return;
|
|
1003
997
|
}
|
|
1004
998
|
else {
|
|
1005
|
-
const listeners1 = listenersMap.get(
|
|
999
|
+
const listeners1 = listenersMap.get(ctx);
|
|
1006
1000
|
if (listeners1) {
|
|
1007
1001
|
listeners = listeners1;
|
|
1008
1002
|
}
|
|
1009
1003
|
else {
|
|
1010
1004
|
listeners = [];
|
|
1011
|
-
listenersMap.set(
|
|
1005
|
+
listenersMap.set(ctx, listeners);
|
|
1012
1006
|
}
|
|
1013
1007
|
}
|
|
1014
1008
|
options = normalizeListenerOptions(options);
|
|
@@ -1019,7 +1013,7 @@ class Context {
|
|
|
1019
1013
|
else {
|
|
1020
1014
|
callback = listener;
|
|
1021
1015
|
}
|
|
1022
|
-
const record = { type,
|
|
1016
|
+
const record = { type, listener, callback, options };
|
|
1023
1017
|
if (options.once) {
|
|
1024
1018
|
record.callback = function () {
|
|
1025
1019
|
const i = listeners.indexOf(record);
|
|
@@ -1036,15 +1030,15 @@ class Context {
|
|
|
1036
1030
|
}
|
|
1037
1031
|
listeners.push(record);
|
|
1038
1032
|
// TODO: is it possible to separate out the EventTarget delegation logic
|
|
1039
|
-
for (const value of getChildValues(
|
|
1033
|
+
for (const value of getChildValues(ctx.ret)) {
|
|
1040
1034
|
if (isEventTarget(value)) {
|
|
1041
1035
|
value.addEventListener(record.type, record.callback, record.options);
|
|
1042
1036
|
}
|
|
1043
1037
|
}
|
|
1044
1038
|
}
|
|
1045
1039
|
removeEventListener(type, listener, options) {
|
|
1046
|
-
const
|
|
1047
|
-
const listeners = listenersMap.get(
|
|
1040
|
+
const ctx = this[_ContextImpl];
|
|
1041
|
+
const listeners = listenersMap.get(ctx);
|
|
1048
1042
|
if (listeners == null || !isListenerOrListenerObject(listener)) {
|
|
1049
1043
|
return;
|
|
1050
1044
|
}
|
|
@@ -1058,16 +1052,16 @@ class Context {
|
|
|
1058
1052
|
const record = listeners[i];
|
|
1059
1053
|
listeners.splice(i, 1);
|
|
1060
1054
|
// TODO: is it possible to separate out the EventTarget delegation logic
|
|
1061
|
-
for (const value of getChildValues(
|
|
1055
|
+
for (const value of getChildValues(ctx.ret)) {
|
|
1062
1056
|
if (isEventTarget(value)) {
|
|
1063
1057
|
value.removeEventListener(record.type, record.callback, record.options);
|
|
1064
1058
|
}
|
|
1065
1059
|
}
|
|
1066
1060
|
}
|
|
1067
1061
|
dispatchEvent(ev) {
|
|
1068
|
-
const
|
|
1062
|
+
const ctx = this[_ContextImpl];
|
|
1069
1063
|
const path = [];
|
|
1070
|
-
for (let parent =
|
|
1064
|
+
for (let parent = ctx.parent; parent !== undefined; parent = parent.parent) {
|
|
1071
1065
|
path.push(parent);
|
|
1072
1066
|
}
|
|
1073
1067
|
// We patch the stopImmediatePropagation method because ev.cancelBubble
|
|
@@ -1079,7 +1073,7 @@ class Context {
|
|
|
1079
1073
|
immediateCancelBubble = true;
|
|
1080
1074
|
return stopImmediatePropagation.call(ev);
|
|
1081
1075
|
});
|
|
1082
|
-
setEventProperty(ev, "target",
|
|
1076
|
+
setEventProperty(ev, "target", ctx.owner);
|
|
1083
1077
|
// The only possible errors in this block are errors thrown by callbacks,
|
|
1084
1078
|
// and dispatchEvent will only log these errors rather than throwing
|
|
1085
1079
|
// them. Therefore, we place all code in a try block, log errors in the
|
|
@@ -1088,18 +1082,21 @@ class Context {
|
|
|
1088
1082
|
// Each early return within the try block returns true because while the
|
|
1089
1083
|
// return value is overridden in the finally block, TypeScript
|
|
1090
1084
|
// (justifiably) does not recognize the unsafe return statement.
|
|
1091
|
-
//
|
|
1092
|
-
// TODO: Run all callbacks even if one of them errors
|
|
1093
1085
|
try {
|
|
1094
1086
|
setEventProperty(ev, "eventPhase", CAPTURING_PHASE);
|
|
1095
1087
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
1096
1088
|
const target = path[i];
|
|
1097
1089
|
const listeners = listenersMap.get(target);
|
|
1098
1090
|
if (listeners) {
|
|
1099
|
-
setEventProperty(ev, "currentTarget", target.
|
|
1091
|
+
setEventProperty(ev, "currentTarget", target.owner);
|
|
1100
1092
|
for (const record of listeners) {
|
|
1101
1093
|
if (record.type === ev.type && record.options.capture) {
|
|
1102
|
-
|
|
1094
|
+
try {
|
|
1095
|
+
record.callback.call(target.owner, ev);
|
|
1096
|
+
}
|
|
1097
|
+
catch (err) {
|
|
1098
|
+
console.error(err);
|
|
1099
|
+
}
|
|
1103
1100
|
if (immediateCancelBubble) {
|
|
1104
1101
|
return true;
|
|
1105
1102
|
}
|
|
@@ -1111,13 +1108,25 @@ class Context {
|
|
|
1111
1108
|
}
|
|
1112
1109
|
}
|
|
1113
1110
|
{
|
|
1114
|
-
|
|
1111
|
+
setEventProperty(ev, "eventPhase", AT_TARGET);
|
|
1112
|
+
setEventProperty(ev, "currentTarget", ctx.owner);
|
|
1113
|
+
const propCallback = ctx.ret.el.props["on" + ev.type];
|
|
1114
|
+
if (propCallback != null) {
|
|
1115
|
+
propCallback(ev);
|
|
1116
|
+
if (immediateCancelBubble || ev.cancelBubble) {
|
|
1117
|
+
return true;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
const listeners = listenersMap.get(ctx);
|
|
1115
1121
|
if (listeners) {
|
|
1116
|
-
setEventProperty(ev, "eventPhase", AT_TARGET);
|
|
1117
|
-
setEventProperty(ev, "currentTarget", impl.ctx);
|
|
1118
1122
|
for (const record of listeners) {
|
|
1119
1123
|
if (record.type === ev.type) {
|
|
1120
|
-
|
|
1124
|
+
try {
|
|
1125
|
+
record.callback.call(ctx.owner, ev);
|
|
1126
|
+
}
|
|
1127
|
+
catch (err) {
|
|
1128
|
+
console.error(err);
|
|
1129
|
+
}
|
|
1121
1130
|
if (immediateCancelBubble) {
|
|
1122
1131
|
return true;
|
|
1123
1132
|
}
|
|
@@ -1134,10 +1143,15 @@ class Context {
|
|
|
1134
1143
|
const target = path[i];
|
|
1135
1144
|
const listeners = listenersMap.get(target);
|
|
1136
1145
|
if (listeners) {
|
|
1137
|
-
setEventProperty(ev, "currentTarget", target.
|
|
1146
|
+
setEventProperty(ev, "currentTarget", target.owner);
|
|
1138
1147
|
for (const record of listeners) {
|
|
1139
1148
|
if (record.type === ev.type && !record.options.capture) {
|
|
1140
|
-
|
|
1149
|
+
try {
|
|
1150
|
+
record.callback.call(target.owner, ev);
|
|
1151
|
+
}
|
|
1152
|
+
catch (err) {
|
|
1153
|
+
console.error(err);
|
|
1154
|
+
}
|
|
1141
1155
|
if (immediateCancelBubble) {
|
|
1142
1156
|
return true;
|
|
1143
1157
|
}
|
|
@@ -1150,10 +1164,6 @@ class Context {
|
|
|
1150
1164
|
}
|
|
1151
1165
|
}
|
|
1152
1166
|
}
|
|
1153
|
-
catch (err) {
|
|
1154
|
-
// TODO: Use setTimeout to rethrow the error.
|
|
1155
|
-
console.error(err);
|
|
1156
|
-
}
|
|
1157
1167
|
finally {
|
|
1158
1168
|
setEventProperty(ev, "eventPhase", NONE);
|
|
1159
1169
|
setEventProperty(ev, "currentTarget", null);
|
|
@@ -1187,7 +1197,12 @@ function updateComponent(renderer, root, host, parent, scope, ret, oldProps) {
|
|
|
1187
1197
|
return enqueueComponentRun(ctx);
|
|
1188
1198
|
}
|
|
1189
1199
|
function updateComponentChildren(ctx, children) {
|
|
1190
|
-
if (ctx.f & IsUnmounted
|
|
1200
|
+
if (ctx.f & IsUnmounted) {
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
else if (ctx.f & IsErrored) {
|
|
1204
|
+
// This branch is necessary for some race conditions where this function is
|
|
1205
|
+
// called after iterator.throw() in async generator components.
|
|
1191
1206
|
return;
|
|
1192
1207
|
}
|
|
1193
1208
|
else if (children === undefined) {
|
|
@@ -1414,9 +1429,6 @@ function advanceComponent(ctx) {
|
|
|
1414
1429
|
*/
|
|
1415
1430
|
function runComponent(ctx) {
|
|
1416
1431
|
const ret = ctx.ret;
|
|
1417
|
-
if (ctx.f & IsDone) {
|
|
1418
|
-
return [undefined, getValue(ret)];
|
|
1419
|
-
}
|
|
1420
1432
|
const initial = !ctx.iterator;
|
|
1421
1433
|
if (initial) {
|
|
1422
1434
|
resumePropsIterator(ctx);
|
|
@@ -1424,7 +1436,7 @@ function runComponent(ctx) {
|
|
|
1424
1436
|
clearEventListeners(ctx);
|
|
1425
1437
|
let result;
|
|
1426
1438
|
try {
|
|
1427
|
-
result = ret.el.tag.call(ctx.
|
|
1439
|
+
result = ret.el.tag.call(ctx.owner, ret.el.props);
|
|
1428
1440
|
}
|
|
1429
1441
|
catch (err) {
|
|
1430
1442
|
ctx.f |= IsErrored;
|
|
@@ -1457,7 +1469,7 @@ function runComponent(ctx) {
|
|
|
1457
1469
|
iteration = ctx.iterator.next();
|
|
1458
1470
|
}
|
|
1459
1471
|
catch (err) {
|
|
1460
|
-
ctx.f |=
|
|
1472
|
+
ctx.f |= IsErrored;
|
|
1461
1473
|
throw err;
|
|
1462
1474
|
}
|
|
1463
1475
|
finally {
|
|
@@ -1477,11 +1489,10 @@ function runComponent(ctx) {
|
|
|
1477
1489
|
if (!initial) {
|
|
1478
1490
|
try {
|
|
1479
1491
|
ctx.f |= IsSyncExecuting;
|
|
1480
|
-
|
|
1481
|
-
iteration = ctx.iterator.next(oldValue);
|
|
1492
|
+
iteration = ctx.iterator.next(ctx.renderer.read(getValue(ret)));
|
|
1482
1493
|
}
|
|
1483
1494
|
catch (err) {
|
|
1484
|
-
ctx.f |=
|
|
1495
|
+
ctx.f |= IsErrored;
|
|
1485
1496
|
throw err;
|
|
1486
1497
|
}
|
|
1487
1498
|
finally {
|
|
@@ -1492,7 +1503,8 @@ function runComponent(ctx) {
|
|
|
1492
1503
|
throw new Error("Sync generator component returned an async iteration");
|
|
1493
1504
|
}
|
|
1494
1505
|
if (iteration.done) {
|
|
1495
|
-
ctx.f
|
|
1506
|
+
ctx.f &= ~IsSyncGen;
|
|
1507
|
+
ctx.iterator = undefined;
|
|
1496
1508
|
}
|
|
1497
1509
|
let value;
|
|
1498
1510
|
try {
|
|
@@ -1506,10 +1518,8 @@ function runComponent(ctx) {
|
|
|
1506
1518
|
catch (err) {
|
|
1507
1519
|
value = handleChildError(ctx, err);
|
|
1508
1520
|
}
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
}
|
|
1512
|
-
return [undefined, value];
|
|
1521
|
+
const block = isPromiseLike(value) ? value.catch(NOOP) : undefined;
|
|
1522
|
+
return [block, value];
|
|
1513
1523
|
}
|
|
1514
1524
|
else {
|
|
1515
1525
|
// async generator component
|
|
@@ -1517,65 +1527,80 @@ function runComponent(ctx) {
|
|
|
1517
1527
|
}
|
|
1518
1528
|
}
|
|
1519
1529
|
async function runAsyncGenComponent(ctx, iterationP) {
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
try {
|
|
1532
|
-
iteration = await iterationP;
|
|
1533
|
-
}
|
|
1534
|
-
catch (err) {
|
|
1535
|
-
ctx.f |= IsDone;
|
|
1536
|
-
ctx.f |= IsErrored;
|
|
1537
|
-
onValue(Promise.reject(err));
|
|
1538
|
-
break;
|
|
1539
|
-
}
|
|
1540
|
-
finally {
|
|
1541
|
-
if (!(ctx.f & IsInRenderLoop)) {
|
|
1542
|
-
ctx.f &= ~PropsAvailable;
|
|
1530
|
+
let done = false;
|
|
1531
|
+
try {
|
|
1532
|
+
while (!done) {
|
|
1533
|
+
// inflightValue must be set synchronously.
|
|
1534
|
+
let onValue;
|
|
1535
|
+
ctx.inflightValue = new Promise((resolve) => (onValue = resolve));
|
|
1536
|
+
if (ctx.f & IsUpdating) {
|
|
1537
|
+
// We should not swallow unhandled promise rejections if the component is
|
|
1538
|
+
// updating independently.
|
|
1539
|
+
// TODO: Does this handle this.refresh() calls?
|
|
1540
|
+
ctx.inflightValue.catch(NOOP);
|
|
1543
1541
|
}
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
}
|
|
1548
|
-
if (iteration.done) {
|
|
1549
|
-
ctx.f |= IsDone;
|
|
1550
|
-
}
|
|
1551
|
-
let value;
|
|
1552
|
-
try {
|
|
1553
|
-
value = updateComponentChildren(ctx, iteration.value);
|
|
1554
|
-
if (isPromiseLike(value)) {
|
|
1555
|
-
value = value.catch((err) => handleChildError(ctx, err));
|
|
1542
|
+
let iteration;
|
|
1543
|
+
try {
|
|
1544
|
+
iteration = await iterationP;
|
|
1556
1545
|
}
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1546
|
+
catch (err) {
|
|
1547
|
+
done = true;
|
|
1548
|
+
ctx.f |= IsErrored;
|
|
1549
|
+
onValue(Promise.reject(err));
|
|
1550
|
+
break;
|
|
1551
|
+
}
|
|
1552
|
+
finally {
|
|
1553
|
+
ctx.f &= ~NeedsToYield;
|
|
1554
|
+
if (!(ctx.f & IsInRenderLoop)) {
|
|
1555
|
+
ctx.f &= ~PropsAvailable;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
done = !!iteration.done;
|
|
1559
|
+
let value;
|
|
1560
|
+
try {
|
|
1561
|
+
value = updateComponentChildren(ctx, iteration.value);
|
|
1562
|
+
if (isPromiseLike(value)) {
|
|
1563
|
+
value = value.catch((err) => handleChildError(ctx, err));
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
catch (err) {
|
|
1567
|
+
done = true;
|
|
1568
|
+
// Do we need to catch potential errors here in the case of unhandled
|
|
1569
|
+
// promise rejections?
|
|
1570
|
+
value = handleChildError(ctx, err);
|
|
1571
|
+
}
|
|
1572
|
+
finally {
|
|
1573
|
+
onValue(value);
|
|
1574
|
+
}
|
|
1575
|
+
// TODO: this can be done more elegantly
|
|
1576
|
+
let oldValue;
|
|
1577
|
+
if (ctx.ret.inflightValue) {
|
|
1578
|
+
// The value passed back into the generator as the argument to the next
|
|
1579
|
+
// method is a promise if an async generator component has async
|
|
1580
|
+
// children. Sync generator components only resume when their children
|
|
1581
|
+
// have fulfilled so the element’s inflight child values will never be
|
|
1582
|
+
// defined.
|
|
1583
|
+
oldValue = ctx.ret.inflightValue.then((value) => ctx.renderer.read(value), () => ctx.renderer.read(undefined));
|
|
1584
|
+
}
|
|
1585
|
+
else {
|
|
1586
|
+
oldValue = ctx.renderer.read(getValue(ctx.ret));
|
|
1587
|
+
}
|
|
1588
|
+
if (ctx.f & IsUnmounted) {
|
|
1589
|
+
if (ctx.f & IsInRenderLoop) {
|
|
1590
|
+
try {
|
|
1591
|
+
ctx.f |= IsSyncExecuting;
|
|
1592
|
+
iterationP = ctx.iterator.next(oldValue);
|
|
1593
|
+
}
|
|
1594
|
+
finally {
|
|
1595
|
+
ctx.f &= ~IsSyncExecuting;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
else {
|
|
1599
|
+
returnComponent(ctx);
|
|
1600
|
+
break;
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
else if (!done) {
|
|
1579
1604
|
try {
|
|
1580
1605
|
ctx.f |= IsSyncExecuting;
|
|
1581
1606
|
iterationP = ctx.iterator.next(oldValue);
|
|
@@ -1584,25 +1609,15 @@ async function runAsyncGenComponent(ctx, iterationP) {
|
|
|
1584
1609
|
ctx.f &= ~IsSyncExecuting;
|
|
1585
1610
|
}
|
|
1586
1611
|
}
|
|
1587
|
-
else {
|
|
1588
|
-
returnComponent(ctx);
|
|
1589
|
-
break;
|
|
1590
|
-
}
|
|
1591
1612
|
}
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
finally {
|
|
1598
|
-
ctx.f &= ~IsSyncExecuting;
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
} while (!(ctx.f & IsDone));
|
|
1613
|
+
}
|
|
1614
|
+
finally {
|
|
1615
|
+
ctx.f &= ~IsAsyncGen;
|
|
1616
|
+
ctx.iterator = undefined;
|
|
1617
|
+
}
|
|
1602
1618
|
}
|
|
1603
1619
|
/**
|
|
1604
|
-
* Called to
|
|
1605
|
-
* generator components.
|
|
1620
|
+
* Called to resume the props async iterator for async generator components.
|
|
1606
1621
|
*/
|
|
1607
1622
|
function resumePropsIterator(ctx) {
|
|
1608
1623
|
if (ctx.onProps) {
|
|
@@ -1626,7 +1641,7 @@ function unmountComponent(ctx) {
|
|
|
1626
1641
|
}
|
|
1627
1642
|
}
|
|
1628
1643
|
ctx.f |= IsUnmounted;
|
|
1629
|
-
if (ctx.iterator
|
|
1644
|
+
if (ctx.iterator) {
|
|
1630
1645
|
if (ctx.f & IsSyncGen) {
|
|
1631
1646
|
let value;
|
|
1632
1647
|
if (ctx.f & IsInRenderLoop) {
|
|
@@ -1653,17 +1668,18 @@ function unmountComponent(ctx) {
|
|
|
1653
1668
|
}
|
|
1654
1669
|
}
|
|
1655
1670
|
}
|
|
1656
|
-
else {
|
|
1657
|
-
// async generator
|
|
1671
|
+
else if (ctx.f & IsAsyncGen) {
|
|
1672
|
+
// The logic for unmounting async generator components is in the
|
|
1673
|
+
// runAsyncGenComponent function.
|
|
1658
1674
|
resumePropsIterator(ctx);
|
|
1659
1675
|
}
|
|
1660
1676
|
}
|
|
1661
1677
|
}
|
|
1662
1678
|
function returnComponent(ctx) {
|
|
1663
1679
|
resumePropsIterator(ctx);
|
|
1664
|
-
if (
|
|
1665
|
-
ctx.f |= IsSyncExecuting;
|
|
1680
|
+
if (ctx.iterator && typeof ctx.iterator.return === "function") {
|
|
1666
1681
|
try {
|
|
1682
|
+
ctx.f |= IsSyncExecuting;
|
|
1667
1683
|
const iteration = ctx.iterator.return();
|
|
1668
1684
|
if (isPromiseLike(iteration)) {
|
|
1669
1685
|
iteration.catch((err) => propagateError(ctx.parent, err));
|
|
@@ -1673,7 +1689,6 @@ function returnComponent(ctx) {
|
|
|
1673
1689
|
ctx.f &= ~IsSyncExecuting;
|
|
1674
1690
|
}
|
|
1675
1691
|
}
|
|
1676
|
-
ctx.f |= IsDone;
|
|
1677
1692
|
}
|
|
1678
1693
|
/*** EVENT TARGET UTILITIES ***/
|
|
1679
1694
|
// EVENT PHASE CONSTANTS
|
|
@@ -1743,11 +1758,8 @@ function clearEventListeners(ctx) {
|
|
|
1743
1758
|
}
|
|
1744
1759
|
}
|
|
1745
1760
|
/*** ERROR HANDLING UTILITIES ***/
|
|
1746
|
-
// TODO: generator components which throw errors should be recoverable
|
|
1747
1761
|
function handleChildError(ctx, err) {
|
|
1748
|
-
if (ctx.
|
|
1749
|
-
!ctx.iterator ||
|
|
1750
|
-
typeof ctx.iterator.throw !== "function") {
|
|
1762
|
+
if (!ctx.iterator || typeof ctx.iterator.throw !== "function") {
|
|
1751
1763
|
throw err;
|
|
1752
1764
|
}
|
|
1753
1765
|
resumePropsIterator(ctx);
|
|
@@ -1757,7 +1769,7 @@ function handleChildError(ctx, err) {
|
|
|
1757
1769
|
iteration = ctx.iterator.throw(err);
|
|
1758
1770
|
}
|
|
1759
1771
|
catch (err) {
|
|
1760
|
-
ctx.f |=
|
|
1772
|
+
ctx.f |= IsErrored;
|
|
1761
1773
|
throw err;
|
|
1762
1774
|
}
|
|
1763
1775
|
finally {
|
|
@@ -1766,16 +1778,18 @@ function handleChildError(ctx, err) {
|
|
|
1766
1778
|
if (isPromiseLike(iteration)) {
|
|
1767
1779
|
return iteration.then((iteration) => {
|
|
1768
1780
|
if (iteration.done) {
|
|
1769
|
-
ctx.f
|
|
1781
|
+
ctx.f &= ~IsAsyncGen;
|
|
1782
|
+
ctx.iterator = undefined;
|
|
1770
1783
|
}
|
|
1771
1784
|
return updateComponentChildren(ctx, iteration.value);
|
|
1772
1785
|
}, (err) => {
|
|
1773
|
-
ctx.f |=
|
|
1786
|
+
ctx.f |= IsErrored;
|
|
1774
1787
|
throw err;
|
|
1775
1788
|
});
|
|
1776
1789
|
}
|
|
1777
1790
|
if (iteration.done) {
|
|
1778
|
-
ctx.f
|
|
1791
|
+
ctx.f &= ~IsSyncGen;
|
|
1792
|
+
ctx.iterator = undefined;
|
|
1779
1793
|
}
|
|
1780
1794
|
return updateComponentChildren(ctx, iteration.value);
|
|
1781
1795
|
}
|