@@ -21,6 +21,7 @@ import type {
21
21
ClientReference,
22
22
ClientReferenceMetadata,
23
23
ServerConsumerModuleMap,
24
+ ServerManifest,
24
25
StringDecoder,
25
26
ModuleLoading,
26
27
} from './ReactFlightClientConfig';
@@ -51,6 +52,7 @@ import {
51
52
52
53
import {
53
54
resolveClientReference,
55
+ resolveServerReference,
54
56
preloadModule,
55
57
requireModule,
56
58
dispatchHint,
@@ -270,6 +272,7 @@ export type FindSourceMapURLCallback = (
270
272
271
273
export type Response = {
272
274
_bundlerConfig: ServerConsumerModuleMap,
275
+ _serverReferenceConfig: null | ServerManifest,
273
276
_moduleLoading: ModuleLoading,
274
277
_callServer: CallServerCallback,
275
278
_encodeFormAction: void | EncodeFormActionCallback,
@@ -896,7 +899,7 @@ function waitForReference<T>(
896
899
parentObject: Object,
897
900
key: string,
898
901
response: Response,
899
- map : ( response : Response , model : any ) => T ,
902
+ map: (response: Response, model: any, parentObject: Object, key: string ) => T,
900
903
path: Array<string>,
901
904
): T {
902
905
let handler: InitializationHandler;
@@ -938,7 +941,7 @@ function waitForReference<T>(
938
941
}
939
942
value = value[path[i]];
940
943
}
941
- const mappedValue = map ( response , value ) ;
944
+ const mappedValue = map(response, value, parentObject, key );
942
945
parentObject[key] = mappedValue;
943
946
944
947
// If this is the root object for a model reference, where `handler.value`
@@ -1041,7 +1044,7 @@ function waitForReference<T>(
1041
1044
return (null: any);
1042
1045
}
1043
1046
1044
- function createServerReferenceProxy < A : Iterable < any > , T> (
1047
+ function loadServerReference <A: Iterable<any>, T>(
1045
1048
response: Response,
1046
1049
metaData: {
1047
1050
id: any,
@@ -1050,21 +1053,155 @@ function createServerReferenceProxy<A: Iterable<any>, T>(
1050
1053
env?: string, // DEV-only
1051
1054
location?: ReactCallSite, // DEV-only
1052
1055
},
1056
+ parentObject: Object,
1057
+ key: string,
1053
1058
): (...A) => Promise<T> {
1054
- return createBoundServerReference (
1055
- metaData ,
1056
- response . _callServer ,
1057
- response . _encodeFormAction ,
1058
- __DEV__ ? response . _debugFindSourceMapURL : undefined ,
1059
- ) ;
1059
+ if (!response._serverReferenceConfig) {
1060
+ // In the normal case, we can't load this Server Reference in the current environment and
1061
+ // we just return a proxy to it.
1062
+ return createBoundServerReference(
1063
+ metaData,
1064
+ response._callServer,
1065
+ response._encodeFormAction,
1066
+ __DEV__ ? response._debugFindSourceMapURL : undefined,
1067
+ );
1068
+ }
1069
+ // If we have a module mapping we can load the real version of this Server Reference.
1070
+ const serverReference: ClientReference<T> =
1071
+ resolveServerReference<$FlowFixMe>(
1072
+ response._serverReferenceConfig,
1073
+ metaData.id,
1074
+ );
1075
+
1076
+ const promise = preloadModule(serverReference);
1077
+ if (!promise) {
1078
+ return (requireModule(serverReference): any);
1079
+ }
1080
+
1081
+ let handler: InitializationHandler;
1082
+ if (initializingHandler) {
1083
+ handler = initializingHandler;
1084
+ handler.deps++;
1085
+ } else {
1086
+ handler = initializingHandler = {
1087
+ parent: null,
1088
+ chunk: null,
1089
+ value: null,
1090
+ deps: 1,
1091
+ errored: false,
1092
+ };
1093
+ }
1094
+
1095
+ function fulfill(): void {
1096
+ const resolvedValue = (requireModule(serverReference): any);
1097
+ parentObject[key] = resolvedValue;
1098
+
1099
+ // If this is the root object for a model reference, where `handler.value`
1100
+ // is a stale `null`, the resolved value can be used directly.
1101
+ if (key === '' && handler.value === null) {
1102
+ handler.value = resolvedValue;
1103
+ }
1104
+
1105
+ // If the parent object is an unparsed React element tuple, we also need to
1106
+ // update the props and owner of the parsed element object (i.e.
1107
+ // handler.value).
1108
+ if (
1109
+ parentObject[0] === REACT_ELEMENT_TYPE &&
1110
+ typeof handler.value === 'object' &&
1111
+ handler.value !== null &&
1112
+ handler.value.$$typeof === REACT_ELEMENT_TYPE
1113
+ ) {
1114
+ const element: any = handler.value;
1115
+ switch (key) {
1116
+ case '3':
1117
+ element.props = resolvedValue;
1118
+ break;
1119
+ case '4':
1120
+ if (__DEV__) {
1121
+ element._owner = resolvedValue;
1122
+ }
1123
+ break;
1124
+ }
1125
+ }
1126
+
1127
+ handler.deps--;
1128
+
1129
+ if (handler.deps === 0) {
1130
+ const chunk = handler.chunk;
1131
+ if (chunk === null || chunk.status !== BLOCKED) {
1132
+ return;
1133
+ }
1134
+ const resolveListeners = chunk.value;
1135
+ const initializedChunk: InitializedChunk<T> = (chunk: any);
1136
+ initializedChunk.status = INITIALIZED;
1137
+ initializedChunk.value = handler.value;
1138
+ if (resolveListeners !== null) {
1139
+ wakeChunk(resolveListeners, handler.value);
1140
+ }
1141
+ }
1142
+ }
1143
+
1144
+ function reject(error: mixed): void {
1145
+ if (handler.errored) {
1146
+ // We've already errored. We could instead build up an AggregateError
1147
+ // but if there are multiple errors we just take the first one like
1148
+ // Promise.all.
1149
+ return;
1150
+ }
1151
+ const blockedValue = handler.value;
1152
+ handler.errored = true;
1153
+ handler.value = error;
1154
+ const chunk = handler.chunk;
1155
+ if (chunk === null || chunk.status !== BLOCKED) {
1156
+ return;
1157
+ }
1158
+
1159
+ if (__DEV__) {
1160
+ if (
1161
+ typeof blockedValue === 'object' &&
1162
+ blockedValue !== null &&
1163
+ blockedValue.$$typeof === REACT_ELEMENT_TYPE
1164
+ ) {
1165
+ const element = blockedValue;
1166
+ // Conceptually the error happened inside this Element but right before
1167
+ // it was rendered. We don't have a client side component to render but
1168
+ // we can add some DebugInfo to explain that this was conceptually a
1169
+ // Server side error that errored inside this element. That way any stack
1170
+ // traces will point to the nearest JSX that errored - e.g. during
1171
+ // serialization.
1172
+ const erroredComponent: ReactComponentInfo = {
1173
+ name: getComponentNameFromType(element.type) || '',
1174
+ owner: element._owner,
1175
+ };
1176
+ if (enableOwnerStacks) {
1177
+ // $FlowFixMe[cannot-write]
1178
+ erroredComponent.debugStack = element._debugStack;
1179
+ if (supportsCreateTask) {
1180
+ // $FlowFixMe[cannot-write]
1181
+ erroredComponent.debugTask = element._debugTask;
1182
+ }
1183
+ }
1184
+ const chunkDebugInfo: ReactDebugInfo =
1185
+ chunk._debugInfo || (chunk._debugInfo = []);
1186
+ chunkDebugInfo.push(erroredComponent);
1187
+ }
1188
+ }
1189
+
1190
+ triggerErrorOnChunk(chunk, error);
1191
+ }
1192
+
1193
+ promise.then(fulfill, reject);
1194
+
1195
+ // Return a place holder value for now.
1196
+ return (null: any);
1060
1197
}
1061
1198
1062
1199
function getOutlinedModel<T>(
1063
1200
response: Response,
1064
1201
reference: string,
1065
1202
parentObject: Object,
1066
1203
key: string,
1067
- map: (response: Response, model: any) => T ,
1204
+ map: (response: Response, model: any, parentObject: Object, key: string ) => T,
1068
1205
): T {
1069
1206
const path = reference.split(':');
1070
1207
const id = parseInt(path[0], 16);
@@ -1099,7 +1236,7 @@ function getOutlinedModel<T>(
1099
1236
}
1100
1237
value = value[path[i]];
1101
1238
}
1102
- const chunkValue = map ( response , value ) ;
1239
+ const chunkValue = map(response, value, parentObject, key );
1103
1240
if (__DEV__ && chunk._debugInfo) {
1104
1241
// If we have a direct reference to an object that was rendered by a synchronous
1105
1242
// server component, it might have some debug info about how it was rendered.
@@ -1244,7 +1381,7 @@ function parseModelString(
1244
1381
ref,
1245
1382
parentObject,
1246
1383
key,
1247
- createServerReferenceProxy ,
1384
+ loadServerReference ,
1248
1385
);
1249
1386
}
1250
1387
case 'T': {
@@ -1421,6 +1558,7 @@ function missingCall() {
1421
1558
function ResponseInstance(
1422
1559
this: $FlowFixMe,
1423
1560
bundlerConfig: ServerConsumerModuleMap,
1561
+ serverReferenceConfig: null | ServerManifest,
1424
1562
moduleLoading: ModuleLoading,
1425
1563
callServer: void | CallServerCallback,
1426
1564
encodeFormAction: void | EncodeFormActionCallback,
@@ -1432,6 +1570,7 @@ function ResponseInstance(
1432
1570
) {
1433
1571
const chunks: Map<number, SomeChunk<any>> = new Map();
1434
1572
this._bundlerConfig = bundlerConfig;
1573
+ this._serverReferenceConfig = serverReferenceConfig;
1435
1574
this._moduleLoading = moduleLoading;
1436
1575
this._callServer = callServer !== undefined ? callServer : missingCall;
1437
1576
this._encodeFormAction = encodeFormAction;
@@ -1486,6 +1625,7 @@ function ResponseInstance(
1486
1625
1487
1626
export function createResponse(
1488
1627
bundlerConfig: ServerConsumerModuleMap,
1628
+ serverReferenceConfig: null | ServerManifest,
1489
1629
moduleLoading: ModuleLoading,
1490
1630
callServer: void | CallServerCallback,
1491
1631
encodeFormAction: void | EncodeFormActionCallback,
@@ -1498,6 +1638,7 @@ export function createResponse(
1498
1638
// $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn't like constructors
1499
1639
return new ResponseInstance(
1500
1640
bundlerConfig,
1641
+ serverReferenceConfig,
1501
1642
moduleLoading,
1502
1643
callServer,
1503
1644
encodeFormAction,
0 commit comments