[Flight] Refactor emit and process functions (#27234)
Since we no longer have externally configured "process" methods, I just inlined all of those. The main thing in this refactor is that I just inlined all the error branches into just `emitErrorChunk`. I'm not sure why it was split up an repeated before but this seems simpler. I need it since I'm going to be doing similar copies of this.
Sebastian Markbåge committed
Aug 16, 2023 at 17:04 UTC
ade82b8dd956bdaa5b7c47400fba9152c2435756
1 file changed
+74
-218
packages/react-server/src/ReactFlightServer.js
+74
-218
@@ -298,12 +298,7 @@ function serializeThenable(request: Request, thenable: Thenable<any>): number {
298
case 'rejected': {
299
const x = thenable.reason;
300
const digest = logRecoverableError(request, x);
301
- if (__DEV__) {
302
- const {message, stack} = getErrorMessageAndStackDev(x);
303
- emitErrorChunkDev(request, newTask.id, digest, message, stack);
304
- } else {
305
- emitErrorChunkProd(request, newTask.id, digest);
306
- }
301
+ emitErrorChunk(request, newTask.id, digest, x);
302
return newTask.id;
303
}
304
default: {
@@ -344,12 +339,7 @@ function serializeThenable(request: Request, thenable: Thenable<any>): number {
339
newTask.status = ERRORED;
340
// TODO: We should ideally do this inside performWork so it's scheduled
341
const digest = logRecoverableError(request, reason);
347
- if (__DEV__) {
348
- const {message, stack} = getErrorMessageAndStackDev(reason);
349
- emitErrorChunkDev(request, newTask.id, digest, message, stack);
350
- } else {
351
- emitErrorChunkProd(request, newTask.id, digest);
352
- }
342
+ emitErrorChunk(request, newTask.id, digest, reason);
343
if (request.destination !== null) {
344
flushCompletedChunks(request, request.destination);
345
}
@@ -638,6 +628,20 @@ function serializeBigInt(n: bigint): string {
628
return '$n' + n.toString(10);
629
}
630
631
+function serializeRowHeader(tag: string, id: number) {
632
+ return id.toString(16) + ':' + tag;
633
+}
634
+
635
+function encodeReferenceChunk(
636
+ request: Request,
637
+ id: number,
638
+ reference: string,
639
+): Chunk {
640
+ const json = stringify(reference);
641
+ const row = id.toString(16) + ':' + json + '\n';
642
+ return stringToChunk(row);
643
+}
644
+
645
function serializeClientReference(
646
request: Request,
647
parent:
@@ -681,12 +685,7 @@ function serializeClientReference(
685
request.pendingChunks++;
686
const errorId = request.nextChunkId++;
687
const digest = logRecoverableError(request, x);
684
- if (__DEV__) {
685
- const {message, stack} = getErrorMessageAndStackDev(x);
686
- emitErrorChunkDev(request, errorId, digest, message, stack);
687
- } else {
688
- emitErrorChunkProd(request, errorId, digest);
689
- }
688
+ emitErrorChunk(request, errorId, digest, x);
689
return serializeByValueID(errorId);
690
}
691
}
@@ -695,8 +694,7 @@ function outlineModel(request: Request, value: any): number {
694
request.pendingChunks++;
695
const outlinedId = request.nextChunkId++;
696
// We assume that this object doesn't suspend, but a child might.
698
- const processedChunk = processModelChunk(request, outlinedId, value);
699
- request.completedRegularChunks.push(processedChunk);
697
+ emitModelChunk(request, outlinedId, value);
698
return outlinedId;
699
}
700
@@ -734,11 +732,9 @@ function serializeLargeTextString(request: Request, text: string): string {
732
request.pendingChunks += 2;
733
const textId = request.nextChunkId++;
734
const textChunk = stringToChunk(text);
737
- const headerChunk = processTextHeader(
738
- request,
739
- textId,
740
- byteLengthOfChunk(textChunk),
741
- );
735
+ const binaryLength = byteLengthOfChunk(textChunk);
736
+ const row = textId.toString(16) + ':T' + binaryLength.toString(16) + ',';
737
+ const headerChunk = stringToChunk(row);
738
request.completedRegularChunks.push(headerChunk, textChunk);
739
return serializeByValueID(textId);
740
}
@@ -765,12 +761,10 @@ function serializeTypedArray(
761
const bufferId = request.nextChunkId++;
762
// TODO: Convert to little endian if that's not the server default.
763
const binaryChunk = typedArrayToBinaryChunk(typedArray);
768
- const headerChunk = processBufferHeader(
769
- request,
770
- tag,
771
- bufferId,
772
- byteLengthOfBinaryChunk(binaryChunk),
773
- );
764
+ const binaryLength = byteLengthOfBinaryChunk(binaryChunk);
765
+ const row =
766
+ bufferId.toString(16) + ':' + tag + binaryLength.toString(16) + ',';
767
+ const headerChunk = stringToChunk(row);
768
request.completedRegularChunks.push(headerChunk, binaryChunk);
769
return serializeByValueID(bufferId);
770
}
@@ -920,12 +914,7 @@ function resolveModelToJSON(
914
request.pendingChunks++;
915
const errorId = request.nextChunkId++;
916
const digest = logRecoverableError(request, x);
923
- if (__DEV__) {
924
- const {message, stack} = getErrorMessageAndStackDev(x);
925
- emitErrorChunkDev(request, errorId, digest, message, stack);
926
- } else {
927
- emitErrorChunkProd(request, errorId, digest);
928
- }
917
+ emitErrorChunk(request, errorId, digest, x);
918
return serializeLazyID(errorId);
919
}
920
}
@@ -1169,10 +1158,24 @@ function logRecoverableError(request: Request, error: mixed): string {
1158
return errorDigest || '';
1159
}
1160
1172
-function getErrorMessageAndStackDev(error: mixed): {
1173
- message: string,
1174
- stack: string,
1175
-} {
1161
+function fatalError(request: Request, error: mixed): void {
1162
+ // This is called outside error handling code such as if an error happens in React internals.
1163
+ if (request.destination !== null) {
1164
+ request.status = CLOSED;
1165
+ closeWithError(request.destination, error);
1166
+ } else {
1167
+ request.status = CLOSING;
1168
+ request.fatalError = error;
1169
+ }
1170
+}
1171
+
1172
+function emitErrorChunk(
1173
+ request: Request,
1174
+ id: number,
1175
+ digest: string,
1176
+ error: mixed,
1177
+): void {
1178
+ let errorInfo: any;
1179
if (__DEV__) {
1180
let message;
1181
let stack = '';
@@ -1188,53 +1191,12 @@ function getErrorMessageAndStackDev(error: mixed): {
1191
} catch (x) {
1192
message = 'An error occurred but serializing the error message failed.';
1193
}
1191
- return {
1192
- message,
1193
- stack,
1194
- };
1194
+ errorInfo = {digest, message, stack};
1195
} else {
1196
- // These errors should never make it into a build so we don't need to encode them in codes.json
1197
- // eslint-disable-next-line react-internal/prod-error-codes
1198
- throw new Error(
1199
- 'getErrorMessageAndStackDev should never be called from production mode. This is a bug in React.',
1200
- );
1201
- }
1202
-}
1203
-
1204
-function fatalError(request: Request, error: mixed): void {
1205
- // This is called outside error handling code such as if an error happens in React internals.
1206
- if (request.destination !== null) {
1207
- request.status = CLOSED;
1208
- closeWithError(request.destination, error);
1209
- } else {
1210
- request.status = CLOSING;
1211
- request.fatalError = error;
1196
+ errorInfo = {digest};
1197
}
1213
-}
1214
-
1215
-function emitErrorChunkProd(
1216
- request: Request,
1217
- id: number,
1218
- digest: string,
1219
-): void {
1220
- const processedChunk = processErrorChunkProd(request, id, digest);
1221
- request.completedErrorChunks.push(processedChunk);
1222
-}
1223
-
1224
-function emitErrorChunkDev(
1225
- request: Request,
1226
- id: number,
1227
- digest: string,
1228
- message: string,
1229
- stack: string,
1230
-): void {
1231
- const processedChunk = processErrorChunkDev(
1232
- request,
1233
- id,
1234
- digest,
1235
- message,
1236
- stack,
1237
- );
1198
+ const row = serializeRowHeader('E', id) + stringify(errorInfo) + '\n';
1199
+ const processedChunk = stringToChunk(row);
1200
request.completedErrorChunks.push(processedChunk);
1201
}
1202
@@ -1243,27 +1205,24 @@ function emitImportChunk(
1205
id: number,
1206
clientReferenceMetadata: ClientReferenceMetadata,
1207
): void {
1246
- const processedChunk = processImportChunk(
1247
- request,
1248
- id,
1249
- clientReferenceMetadata,
1250
- );
1208
+ // $FlowFixMe[incompatible-type] stringify can return null
1209
+ const json: string = stringify(clientReferenceMetadata);
1210
+ const row = serializeRowHeader('I', id) + json + '\n';
1211
+ const processedChunk = stringToChunk(row);
1212
request.completedImportChunks.push(processedChunk);
1213
}
1214
1215
function emitHintChunk(request: Request, code: string, model: HintModel): void {
1255
- const processedChunk = processHintChunk(
1256
- request,
1257
- request.nextChunkId++,
1258
- code,
1259
- model,
1260
- );
1216
+ const json: string = stringify(model);
1217
+ const id = request.nextChunkId++;
1218
+ const row = serializeRowHeader('H' + code, id) + json + '\n';
1219
+ const processedChunk = stringToChunk(row);
1220
request.completedHintChunks.push(processedChunk);
1221
}
1222
1223
function emitSymbolChunk(request: Request, id: number, name: string): void {
1224
const symbolReference = serializeSymbolReference(name);
1266
- const processedChunk = processReferenceChunk(request, id, symbolReference);
1225
+ const processedChunk = encodeReferenceChunk(request, id, symbolReference);
1226
request.completedImportChunks.push(processedChunk);
1227
}
1228
@@ -1273,7 +1232,19 @@ function emitProviderChunk(
1232
contextName: string,
1233
): void {
1234
const contextReference = serializeProviderReference(contextName);
1276
- const processedChunk = processReferenceChunk(request, id, contextReference);
1235
+ const processedChunk = encodeReferenceChunk(request, id, contextReference);
1236
+ request.completedRegularChunks.push(processedChunk);
1237
+}
1238
+
1239
+function emitModelChunk(
1240
+ request: Request,
1241
+ id: number,
1242
+ model: ReactClientValue,
1243
+): void {
1244
+ // $FlowFixMe[incompatible-type] stringify can return null
1245
+ const json: string = stringify(model, request.toJSON);
1246
+ const row = id.toString(16) + ':' + json + '\n';
1247
+ const processedChunk = stringToChunk(row);
1248
request.completedRegularChunks.push(processedChunk);
1249
}
1250
@@ -1337,8 +1308,7 @@ function retryTask(request: Request, task: Task): void {
1308
}
1309
}
1310
1340
- const processedChunk = processModelChunk(request, task.id, value);
1341
- request.completedRegularChunks.push(processedChunk);
1311
+ emitModelChunk(request, task.id, value);
1312
request.abortableTasks.delete(task);
1313
task.status = COMPLETED;
1314
} catch (thrownValue) {
@@ -1362,12 +1332,7 @@ function retryTask(request: Request, task: Task): void {
1332
request.abortableTasks.delete(task);
1333
task.status = ERRORED;
1334
const digest = logRecoverableError(request, x);
1365
- if (__DEV__) {
1366
- const {message, stack} = getErrorMessageAndStackDev(x);
1367
- emitErrorChunkDev(request, task.id, digest, message, stack);
1368
- } else {
1369
- emitErrorChunkProd(request, task.id, digest);
1370
- }
1335
+ emitErrorChunk(request, task.id, digest, x);
1336
}
1337
}
1338
}
@@ -1404,7 +1369,7 @@ function abortTask(task: Task, request: Request, errorId: number): void {
1369
// Instead of emitting an error per task.id, we emit a model that only
1370
// has a single value referencing the error.
1371
const ref = serializeByValueID(errorId);
1407
- const processedChunk = processReferenceChunk(request, task.id, ref);
1372
+ const processedChunk = encodeReferenceChunk(request, task.id, ref);
1373
request.completedErrorChunks.push(processedChunk);
1374
}
1375
@@ -1547,12 +1512,7 @@ export function abort(request: Request, reason: mixed): void {
1512
const digest = logRecoverableError(request, error);
1513
request.pendingChunks++;
1514
const errorId = request.nextChunkId++;
1550
- if (__DEV__) {
1551
- const {message, stack} = getErrorMessageAndStackDev(error);
1552
- emitErrorChunkDev(request, errorId, digest, message, stack);
1553
- } else {
1554
- emitErrorChunkProd(request, errorId, digest);
1555
- }
1515
+ emitErrorChunk(request, errorId, digest, error);
1516
abortableTasks.forEach(task => abortTask(task, request, errorId));
1517
abortableTasks.clear();
1518
}
@@ -1582,107 +1542,3 @@ function importServerContexts(
1542
}
1543
return rootContextSnapshot;
1544
}
1585
-
1586
-function serializeRowHeader(tag: string, id: number) {
1587
- return id.toString(16) + ':' + tag;
1588
-}
1589
-
1590
-function processErrorChunkProd(
1591
- request: Request,
1592
- id: number,
1593
- digest: string,
1594
-): Chunk {
1595
- if (__DEV__) {
1596
- // These errors should never make it into a build so we don't need to encode them in codes.json
1597
- // eslint-disable-next-line react-internal/prod-error-codes
1598
- throw new Error(
1599
- 'processErrorChunkProd should never be called while in development mode. Use processErrorChunkDev instead. This is a bug in React.',
1600
- );
1601
- }
1602
-
1603
- const errorInfo: any = {digest};
1604
- const row = serializeRowHeader('E', id) + stringify(errorInfo) + '\n';
1605
- return stringToChunk(row);
1606
-}
1607
-
1608
-function processErrorChunkDev(
1609
- request: Request,
1610
- id: number,
1611
- digest: string,
1612
- message: string,
1613
- stack: string,
1614
-): Chunk {
1615
- if (!__DEV__) {
1616
- // These errors should never make it into a build so we don't need to encode them in codes.json
1617
- // eslint-disable-next-line react-internal/prod-error-codes
1618
- throw new Error(
1619
- 'processErrorChunkDev should never be called while in production mode. Use processErrorChunkProd instead. This is a bug in React.',
1620
- );
1621
- }
1622
-
1623
- const errorInfo: any = {digest, message, stack};
1624
- const row = serializeRowHeader('E', id) + stringify(errorInfo) + '\n';
1625
- return stringToChunk(row);
1626
-}
1627
-
1628
-function processModelChunk(
1629
- request: Request,
1630
- id: number,
1631
- model: ReactClientValue,
1632
-): Chunk {
1633
- // $FlowFixMe[incompatible-type] stringify can return null
1634
- const json: string = stringify(model, request.toJSON);
1635
- const row = id.toString(16) + ':' + json + '\n';
1636
- return stringToChunk(row);
1637
-}
1638
-
1639
-function processReferenceChunk(
1640
- request: Request,
1641
- id: number,
1642
- reference: string,
1643
-): Chunk {
1644
- const json = stringify(reference);
1645
- const row = id.toString(16) + ':' + json + '\n';
1646
- return stringToChunk(row);
1647
-}
1648
-
1649
-function processImportChunk(
1650
- request: Request,
1651
- id: number,
1652
- clientReferenceMetadata: ReactClientValue,
1653
-): Chunk {
1654
- // $FlowFixMe[incompatible-type] stringify can return null
1655
- const json: string = stringify(clientReferenceMetadata);
1656
- const row = serializeRowHeader('I', id) + json + '\n';
1657
- return stringToChunk(row);
1658
-}
1659
-
1660
-function processHintChunk(
1661
- request: Request,
1662
- id: number,
1663
- code: string,
1664
- model: JSONValue,
1665
-): Chunk {
1666
- const json: string = stringify(model);
1667
- const row = serializeRowHeader('H' + code, id) + json + '\n';
1668
- return stringToChunk(row);
1669
-}
1670
-
1671
-function processTextHeader(
1672
- request: Request,
1673
- id: number,
1674
- binaryLength: number,
1675
-): Chunk {
1676
- const row = id.toString(16) + ':T' + binaryLength.toString(16) + ',';
1677
- return stringToChunk(row);
1678
-}
1679
-
1680
-function processBufferHeader(
1681
- request: Request,
1682
- tag: string,
1683
- id: number,
1684
- binaryLength: number,
1685
-): Chunk {
1686
- const row = id.toString(16) + ':' + tag + binaryLength.toString(16) + ',';
1687
- return stringToChunk(row);
1688
-}