11
Destination,
12
Chunk,
13
BundlerConfig,
14
- ModuleMetaData,
14
+ ClientReferenceMetadata,
15
ClientReference,
16
ClientReferenceKey,
17
+ ServerReference,
18
+ ServerReferenceMetadata,
19
} from './ReactFlightServerConfig';
20
import type {ContextSnapshot} from './ReactFlightNewContext';
21
import type {ThenableState} from './ReactFlightThenable';
39
close,
40
closeWithError,
41
processModelChunk,
40
- processModuleChunk,
42
+ processImportChunk,
43
processErrorChunkProd,
44
processErrorChunkDev,
45
processReferenceChunk,
44
- resolveModuleMetaData,
46
+ resolveClientReferenceMetadata,
47
+ resolveServerReferenceMetadata,
48
getClientReferenceKey,
49
isClientReference,
50
+ isServerReference,
51
supportsRequestStorage,
52
requestStorage,
53
} from './ReactFlightServerConfig';
105
| symbol
106
| null
107
| Iterable<ReactModel>
104
- | ReactModelObject;
108
+ | ReactModelObject
109
+ | Promise<ReactModel>;
110
111
type ReactModelObject = {+[key: string]: ReactModel};
112
134
pendingChunks: number,
135
abortableTasks: Set<Task>,
136
pingedTasks: Array<Task>,
132
- completedModuleChunks: Array<Chunk>,
137
+ completedImportChunks: Array<Chunk>,
138
completedJSONChunks: Array<Chunk>,
139
completedErrorChunks: Array<Chunk>,
140
writtenSymbols: Map<symbol, number>,
136
- writtenModules: Map<ClientReferenceKey, number>,
141
+ writtenClientReferences: Map<ClientReferenceKey, number>,
142
+ writtenServerReferences: Map<ServerReference<any>, number>,
143
writtenProviders: Map<string, number>,
144
identifierPrefix: string,
145
identifierCount: number,
188
pendingChunks: 0,
189
abortableTasks: abortSet,
190
pingedTasks: pingedTasks,
185
- completedModuleChunks: [],
186
- completedJSONChunks: [],
187
- completedErrorChunks: [],
191
+ completedImportChunks: ([]: Array<Chunk>),
192
+ completedJSONChunks: ([]: Array<Chunk>),
193
+ completedErrorChunks: ([]: Array<Chunk>),
194
writtenSymbols: new Map(),
189
- writtenModules: new Map(),
195
+ writtenClientReferences: new Map(),
196
+ writtenServerReferences: new Map(),
197
writtenProviders: new Map(),
198
identifierPrefix: identifierPrefix || '',
199
identifierCount: 1,
516
return '$@' + id.toString(16);
517
}
518
519
+function serializeServerReferenceID(id: number): string {
520
+ return '$F' + id.toString(16);
521
+}
522
+
523
function serializeSymbolReference(name: string): string {
524
return '$S' + name;
525
}
532
request: Request,
533
parent: {+[key: string | number]: ReactModel} | $ReadOnlyArray<ReactModel>,
534
key: string,
524
- moduleReference: ClientReference<any>,
535
+ clientReference: ClientReference<any>,
536
): string {
526
- const moduleKey: ClientReferenceKey = getClientReferenceKey(moduleReference);
527
- const writtenModules = request.writtenModules;
528
- const existingId = writtenModules.get(moduleKey);
537
+ const clientReferenceKey: ClientReferenceKey =
538
+ getClientReferenceKey(clientReference);
539
+ const writtenClientReferences = request.writtenClientReferences;
540
+ const existingId = writtenClientReferences.get(clientReferenceKey);
541
if (existingId !== undefined) {
542
if (parent[0] === REACT_ELEMENT_TYPE && key === '1') {
543
// If we're encoding the "type" of an element, we can refer
550
return serializeByValueID(existingId);
551
}
552
try {
541
- const moduleMetaData: ModuleMetaData = resolveModuleMetaData(
542
- request.bundlerConfig,
543
- moduleReference,
544
- );
553
+ const clientReferenceMetadata: ClientReferenceMetadata =
554
+ resolveClientReferenceMetadata(request.bundlerConfig, clientReference);
555
request.pendingChunks++;
546
- const moduleId = request.nextChunkId++;
547
- emitModuleChunk(request, moduleId, moduleMetaData);
548
- writtenModules.set(moduleKey, moduleId);
556
+ const importId = request.nextChunkId++;
557
+ emitImportChunk(request, importId, clientReferenceMetadata);
558
+ writtenClientReferences.set(clientReferenceKey, importId);
559
if (parent[0] === REACT_ELEMENT_TYPE && key === '1') {
560
// If we're encoding the "type" of an element, we can refer
561
// to that by a lazy reference instead of directly since React
562
// knows how to deal with lazy values. This lets us suspend
563
// on this component rather than its parent until the code has
564
// loaded.
555
- return serializeLazyID(moduleId);
565
+ return serializeLazyID(importId);
566
}
557
- return serializeByValueID(moduleId);
567
+ return serializeByValueID(importId);
568
} catch (x) {
569
request.pendingChunks++;
570
const errorId = request.nextChunkId++;
579
}
580
}
581
582
+function serializeServerReference(
583
+ request: Request,
584
+ parent: {+[key: string | number]: ReactModel} | $ReadOnlyArray<ReactModel>,
585
+ key: string,
586
+ serverReference: ServerReference<any>,
587
+): string {
588
+ const writtenServerReferences = request.writtenServerReferences;
589
+ const existingId = writtenServerReferences.get(serverReference);
590
+ if (existingId !== undefined) {
591
+ return serializeServerReferenceID(existingId);
592
+ }
593
+ const serverReferenceMetadata: ServerReferenceMetadata =
594
+ resolveServerReferenceMetadata(request.bundlerConfig, serverReference);
595
+ request.pendingChunks++;
596
+ const metadataId = request.nextChunkId++;
597
+ // We assume that this object doesn't suspend.
598
+ const processedChunk = processModelChunk(
599
+ request,
600
+ metadataId,
601
+ serverReferenceMetadata,
602
+ );
603
+ request.completedJSONChunks.push(processedChunk);
604
+ writtenServerReferences.set(serverReference, metadataId);
605
+ return serializeServerReferenceID(metadataId);
606
+}
607
+
608
function escapeStringValue(value: string): string {
609
if (value[0] === '$') {
610
// We need to escape $ or @ prefixed strings since we use those to encode
1027
if (typeof value === 'object') {
1028
if (isClientReference(value)) {
1029
return serializeClientReference(request, parent, key, (value: any));
1030
+ // $FlowFixMe[method-unbinding]
1031
} else if (typeof value.then === 'function') {
1032
// We assume that any object with a .then property is a "Thenable" type,
1033
// or a Promise type. Either of which can be represented by a Promise.
1104
if (isClientReference(value)) {
1105
return serializeClientReference(request, parent, key, (value: any));
1106
}
1107
+ if (isServerReference(value)) {
1108
+ return serializeServerReference(request, parent, key, (value: any));
1109
+ }
1110
if (/^on[A-Z]/.test(key)) {
1111
throw new Error(
1112
'Event handlers cannot be passed to Client Component props.' +
1116
} else {
1117
throw new Error(
1118
'Functions cannot be passed directly to Client Components ' +
1079
- "because they're not serializable." +
1119
+ 'unless you explicitly expose it by marking it with "use server".' +
1120
describeObjectForErrorMessage(parent, key),
1121
);
1122
}
1243
request.completedErrorChunks.push(processedChunk);
1244
}
1245
1206
-function emitModuleChunk(
1246
+function emitImportChunk(
1247
request: Request,
1248
id: number,
1209
- moduleMetaData: ModuleMetaData,
1249
+ clientReferenceMetadata: ClientReferenceMetadata,
1250
): void {
1211
- const processedChunk = processModuleChunk(request, id, moduleMetaData);
1212
- request.completedModuleChunks.push(processedChunk);
1251
+ const processedChunk = processImportChunk(
1252
+ request,
1253
+ id,
1254
+ clientReferenceMetadata,
1255
+ );
1256
+ request.completedImportChunks.push(processedChunk);
1257
}
1258
1259
function emitSymbolChunk(request: Request, id: number, name: string): void {
1260
const symbolReference = serializeSymbolReference(name);
1261
const processedChunk = processReferenceChunk(request, id, symbolReference);
1218
- request.completedModuleChunks.push(processedChunk);
1262
+ request.completedImportChunks.push(processedChunk);
1263
}
1264
1265
function emitProviderChunk(
1411
try {
1412
// We emit module chunks first in the stream so that
1413
// they can be preloaded as early as possible.
1370
- const moduleChunks = request.completedModuleChunks;
1414
+ const importsChunks = request.completedImportChunks;
1415
let i = 0;
1372
- for (; i < moduleChunks.length; i++) {
1416
+ for (; i < importsChunks.length; i++) {
1417
request.pendingChunks--;
1374
- const chunk = moduleChunks[i];
1418
+ const chunk = importsChunks[i];
1419
const keepWriting: boolean = writeChunkAndReturn(destination, chunk);
1420
if (!keepWriting) {
1421
request.destination = null;
1423
break;
1424
}
1425
}
1382
- moduleChunks.splice(0, i);
1426
+ importsChunks.splice(0, i);
1427
// Next comes model data.
1428
const jsonChunks = request.completedJSONChunks;
1429
i = 0;