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