@samitouri / QOS-React-1 / commits / ade82b8dd9

[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 -}