diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 5293bc794af26..38d0c7b1c5b68 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -201,14 +201,6 @@ function createErrorChunk( return new Chunk(ERRORED, null, error, response); } -function createInitializedChunk( - response: Response, - value: T, -): InitializedChunk { - // $FlowFixMe Flow doesn't support functions as constructors - return new Chunk(INITIALIZED, value, null, response); -} - function wakeChunk(listeners: Array<(T) => mixed>, value: T): void { for (let i = 0; i < listeners.length; i++) { const listener = listeners[i]; @@ -483,14 +475,32 @@ export function parseModelString( key: string, value: string, ): any { - switch (value[0]) { - case '$': { - if (value === '$') { - return REACT_ELEMENT_TYPE; - } else if (value[1] === '$' || value[1] === '@') { + if (value[0] === '$') { + if (value === '$') { + // A very common symbol. + return REACT_ELEMENT_TYPE; + } + switch (value[1]) { + case '$': { // This was an escaped string value. return value.substring(1); - } else { + } + case 'L': { + // Lazy node + const id = parseInt(value.substring(2), 16); + const chunk = getChunk(response, id); + // We create a React.lazy wrapper around any lazy values. + // When passed into React, we'll know how to suspend on this. + return createLazyChunkWrapper(chunk); + } + case 'S': { + return Symbol.for(value.substring(2)); + } + case 'P': { + return getOrCreateServerContext(value.substring(2)).Provider; + } + default: { + // We assume that anything else is a reference ID. const id = parseInt(value.substring(1), 16); const chunk = getChunk(response, id); switch (chunk.status) { @@ -518,13 +528,6 @@ export function parseModelString( } } } - case '@': { - const id = parseInt(value.substring(1), 16); - const chunk = getChunk(response, id); - // We create a React.lazy wrapper around any lazy values. - // When passed into React, we'll know how to suspend on this. - return createLazyChunkWrapper(chunk); - } } return value; } @@ -566,21 +569,6 @@ export function resolveModel( } } -export function resolveProvider( - response: Response, - id: number, - contextName: string, -): void { - const chunks = response._chunks; - chunks.set( - id, - createInitializedChunk( - response, - getOrCreateServerContext(contextName).Provider, - ), - ); -} - export function resolveModule( response: Response, id: number, @@ -626,17 +614,6 @@ export function resolveModule( } } -export function resolveSymbol( - response: Response, - id: number, - name: string, -): void { - const chunks = response._chunks; - // We assume that we'll always emit the symbol before anything references it - // to save a few bytes. - chunks.set(id, createInitializedChunk(response, Symbol.for(name))); -} - type ErrorWithDigest = Error & {digest?: string}; export function resolveErrorProd( response: Response, diff --git a/packages/react-client/src/ReactFlightClientStream.js b/packages/react-client/src/ReactFlightClientStream.js index 4709ca8085ddc..79ed3b1c9cf17 100644 --- a/packages/react-client/src/ReactFlightClientStream.js +++ b/packages/react-client/src/ReactFlightClientStream.js @@ -14,8 +14,6 @@ import type {BundlerConfig} from './ReactFlightClientHostConfig'; import { resolveModule, resolveModel, - resolveProvider, - resolveSymbol, resolveErrorProd, resolveErrorDev, createResponse as createResponseBase, @@ -36,33 +34,20 @@ function processFullRow(response: Response, row: string): void { if (row === '') { return; } - const tag = row[0]; + const colon = row.indexOf(':', 0); + const id = parseInt(row.substring(0, colon), 16); + const tag = row[colon + 1]; // When tags that are not text are added, check them here before // parsing the row as text. // switch (tag) { // } - const colon = row.indexOf(':', 1); - const id = parseInt(row.substring(1, colon), 16); - const text = row.substring(colon + 1); switch (tag) { - case 'J': { - resolveModel(response, id, text); - return; - } - case 'M': { - resolveModule(response, id, text); - return; - } - case 'P': { - resolveProvider(response, id, text); - return; - } - case 'S': { - resolveSymbol(response, id, JSON.parse(text)); + case 'I': { + resolveModule(response, id, row.substring(colon + 2)); return; } case 'E': { - const errorInfo = JSON.parse(text); + const errorInfo = JSON.parse(row.substring(colon + 2)); if (__DEV__) { resolveErrorDev( response, @@ -77,9 +62,9 @@ function processFullRow(response: Response, row: string): void { return; } default: { - throw new Error( - "Error parsing the data. It's probably an error code or network corruption.", - ); + // We assume anything else is JSON. + resolveModel(response, id, row.substring(colon + 1)); + return; } } } diff --git a/packages/react-server-dom-relay/src/ReactFlightDOMRelayClient.js b/packages/react-server-dom-relay/src/ReactFlightDOMRelayClient.js index 5e344c1398c53..5bf775bfb5f26 100644 --- a/packages/react-server-dom-relay/src/ReactFlightDOMRelayClient.js +++ b/packages/react-server-dom-relay/src/ReactFlightDOMRelayClient.js @@ -15,7 +15,6 @@ import { createResponse, resolveModel, resolveModule, - resolveSymbol, resolveErrorDev, resolveErrorProd, close, @@ -25,15 +24,12 @@ import { export {createResponse, close, getRoot}; export function resolveRow(response: Response, chunk: RowEncoding): void { - if (chunk[0] === 'J') { + if (chunk[0] === 'O') { // $FlowFixMe unable to refine on array indices resolveModel(response, chunk[1], chunk[2]); - } else if (chunk[0] === 'M') { + } else if (chunk[0] === 'I') { // $FlowFixMe unable to refine on array indices resolveModule(response, chunk[1], chunk[2]); - } else if (chunk[0] === 'S') { - // $FlowFixMe: Flow doesn't support disjoint unions on tuples. - resolveSymbol(response, chunk[1], chunk[2]); } else { if (__DEV__) { resolveErrorDev( diff --git a/packages/react-server-dom-relay/src/ReactFlightDOMRelayProtocol.js b/packages/react-server-dom-relay/src/ReactFlightDOMRelayProtocol.js index 3b3425abeca06..f770432dce1e2 100644 --- a/packages/react-server-dom-relay/src/ReactFlightDOMRelayProtocol.js +++ b/packages/react-server-dom-relay/src/ReactFlightDOMRelayProtocol.js @@ -18,8 +18,8 @@ export type JSONValue = | $ReadOnlyArray; export type RowEncoding = - | ['J', number, JSONValue] - | ['M', number, ModuleMetaData] + | ['O', number, JSONValue] + | ['I', number, ModuleMetaData] | ['P', number, string] | ['S', number, string] | [ diff --git a/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js b/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js index 27fbaf25f64ad..bc5a321622ede 100644 --- a/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js +++ b/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js @@ -151,7 +151,7 @@ export function processModelChunk( ): Chunk { // $FlowFixMe no good way to define an empty exact object const json = convertModelToJSON(request, {}, '', model); - return ['J', id, json]; + return ['O', id, json]; } export function processReferenceChunk( @@ -159,7 +159,7 @@ export function processReferenceChunk( id: number, reference: string, ): Chunk { - return ['J', id, reference]; + return ['O', id, reference]; } export function processModuleChunk( @@ -168,23 +168,7 @@ export function processModuleChunk( moduleMetaData: ModuleMetaData, ): Chunk { // The moduleMetaData is already a JSON serializable value. - return ['M', id, moduleMetaData]; -} - -export function processProviderChunk( - request: Request, - id: number, - contextName: string, -): Chunk { - return ['P', id, contextName]; -} - -export function processSymbolChunk( - request: Request, - id: number, - name: string, -): Chunk { - return ['S', id, name]; + return ['I', id, moduleMetaData]; } export function scheduleWork(callback: () => void) { diff --git a/packages/react-server-native-relay/src/ReactFlightNativeRelayClient.js b/packages/react-server-native-relay/src/ReactFlightNativeRelayClient.js index 94f287971441f..b9a398cc9328b 100644 --- a/packages/react-server-native-relay/src/ReactFlightNativeRelayClient.js +++ b/packages/react-server-native-relay/src/ReactFlightNativeRelayClient.js @@ -15,7 +15,6 @@ import { createResponse, resolveModel, resolveModule, - resolveSymbol, resolveErrorDev, resolveErrorProd, close, @@ -25,15 +24,12 @@ import { export {createResponse, close, getRoot}; export function resolveRow(response: Response, chunk: RowEncoding): void { - if (chunk[0] === 'J') { + if (chunk[0] === 'O') { // $FlowFixMe `Chunk` doesn't flow into `JSONValue` because of the `E` row type. resolveModel(response, chunk[1], chunk[2]); - } else if (chunk[0] === 'M') { + } else if (chunk[0] === 'I') { // $FlowFixMe `Chunk` doesn't flow into `JSONValue` because of the `E` row type. resolveModule(response, chunk[1], chunk[2]); - } else if (chunk[0] === 'S') { - // $FlowFixMe: Flow doesn't support disjoint unions on tuples. - resolveSymbol(response, chunk[1], chunk[2]); } else { if (__DEV__) { resolveErrorDev( diff --git a/packages/react-server-native-relay/src/ReactFlightNativeRelayProtocol.js b/packages/react-server-native-relay/src/ReactFlightNativeRelayProtocol.js index a69b14407aec1..788fd14293ca3 100644 --- a/packages/react-server-native-relay/src/ReactFlightNativeRelayProtocol.js +++ b/packages/react-server-native-relay/src/ReactFlightNativeRelayProtocol.js @@ -18,8 +18,8 @@ export type JSONValue = | Array; export type RowEncoding = - | ['J', number, JSONValue] - | ['M', number, ModuleMetaData] + | ['O', number, JSONValue] + | ['I', number, ModuleMetaData] | ['P', number, string] | ['S', number, string] | [ diff --git a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js index 552b37913819b..d005ccbdf1e6d 100644 --- a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js +++ b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js @@ -146,7 +146,7 @@ export function processModelChunk( ): Chunk { // $FlowFixMe no good way to define an empty exact object const json = convertModelToJSON(request, {}, '', model); - return ['J', id, json]; + return ['O', id, json]; } export function processReferenceChunk( @@ -154,7 +154,7 @@ export function processReferenceChunk( id: number, reference: string, ): Chunk { - return ['J', id, reference]; + return ['O', id, reference]; } export function processModuleChunk( @@ -163,23 +163,7 @@ export function processModuleChunk( moduleMetaData: ModuleMetaData, ): Chunk { // The moduleMetaData is already a JSON serializable value. - return ['M', id, moduleMetaData]; -} - -export function processProviderChunk( - request: Request, - id: number, - contextName: string, -): Chunk { - return ['P', id, contextName]; -} - -export function processSymbolChunk( - request: Request, - id: number, - name: string, -): Chunk { - return ['S', id, name]; + return ['I', id, moduleMetaData]; } export function scheduleWork(callback: () => void) { diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 1d1b32f8ca49f..af90377e53227 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -38,8 +38,6 @@ import { closeWithError, processModelChunk, processModuleChunk, - processProviderChunk, - processSymbolChunk, processErrorChunkProd, processErrorChunkDev, processReferenceChunk, @@ -417,7 +415,15 @@ function serializeByValueID(id: number): string { } function serializeByRefID(id: number): string { - return '@' + id.toString(16); + return '$L' + id.toString(16); +} + +function serializeSymbolReference(name: string): string { + return '$S' + name; +} + +function serializeProviderReference(name: string): string { + return '$P' + name; } function serializeClientReference( @@ -473,7 +479,7 @@ function serializeClientReference( } function escapeStringValue(value: string): string { - if (value[0] === '$' || value[0] === '@') { + if (value[0] === '$') { // We need to escape $ or @ prefixed strings since we use those to encode // references to IDs and as special symbol values. return '$' + value; @@ -1109,7 +1115,8 @@ function emitModuleChunk( } function emitSymbolChunk(request: Request, id: number, name: string): void { - const processedChunk = processSymbolChunk(request, id, name); + const symbolReference = serializeSymbolReference(name); + const processedChunk = processReferenceChunk(request, id, symbolReference); request.completedModuleChunks.push(processedChunk); } @@ -1118,7 +1125,8 @@ function emitProviderChunk( id: number, contextName: string, ): void { - const processedChunk = processProviderChunk(request, id, contextName); + const contextReference = serializeProviderReference(contextName); + const processedChunk = processReferenceChunk(request, id, contextReference); request.completedJSONChunks.push(processedChunk); } diff --git a/packages/react-server/src/ReactFlightServerConfigStream.js b/packages/react-server/src/ReactFlightServerConfigStream.js index d5e4ec6f6371a..7364ab86dbfb7 100644 --- a/packages/react-server/src/ReactFlightServerConfigStream.js +++ b/packages/react-server/src/ReactFlightServerConfigStream.js @@ -80,7 +80,7 @@ export { const stringify = JSON.stringify; function serializeRowHeader(tag: string, id: number) { - return tag + id.toString(16) + ':'; + return id.toString(16) + ':' + tag; } export function processErrorChunkProd( @@ -127,7 +127,7 @@ export function processModelChunk( model: ReactModel, ): Chunk { const json: string = stringify(model, request.toJSON); - const row = serializeRowHeader('J', id) + json + '\n'; + const row = id.toString(16) + ':' + json + '\n'; return stringToChunk(row); } @@ -137,7 +137,7 @@ export function processReferenceChunk( reference: string, ): Chunk { const json = stringify(reference); - const row = serializeRowHeader('J', id) + json + '\n'; + const row = id.toString(16) + ':' + json + '\n'; return stringToChunk(row); } @@ -147,26 +147,7 @@ export function processModuleChunk( moduleMetaData: ReactModel, ): Chunk { const json: string = stringify(moduleMetaData); - const row = serializeRowHeader('M', id) + json + '\n'; - return stringToChunk(row); -} - -export function processProviderChunk( - request: Request, - id: number, - contextName: string, -): Chunk { - const row = serializeRowHeader('P', id) + contextName + '\n'; - return stringToChunk(row); -} - -export function processSymbolChunk( - request: Request, - id: number, - name: string, -): Chunk { - const json = stringify(name); - const row = serializeRowHeader('S', id) + json + '\n'; + const row = serializeRowHeader('I', id) + json + '\n'; return stringToChunk(row); }