Skip to content

Commit 977bccd

Browse files
authored
Refactor Flight Encoding (#26082)
This is just shifting around some encoding strategies for Flight in preparation for more types. ``` S1:"react.suspense" J2:["$", "$1", {children: "@3"}] J3:"Hello" ``` ``` 1:"$Sreact.suspense" 2:["$", "$1", {children: "$L3"}] 3:"Hello" ```
1 parent 8b9ac81 commit 977bccd

10 files changed

+65
-154
lines changed

packages/react-client/src/ReactFlightClient.js

Lines changed: 24 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -201,14 +201,6 @@ function createErrorChunk<T>(
201201
return new Chunk(ERRORED, null, error, response);
202202
}
203203

204-
function createInitializedChunk<T>(
205-
response: Response,
206-
value: T,
207-
): InitializedChunk<T> {
208-
// $FlowFixMe Flow doesn't support functions as constructors
209-
return new Chunk(INITIALIZED, value, null, response);
210-
}
211-
212204
function wakeChunk<T>(listeners: Array<(T) => mixed>, value: T): void {
213205
for (let i = 0; i < listeners.length; i++) {
214206
const listener = listeners[i];
@@ -483,14 +475,32 @@ export function parseModelString(
483475
key: string,
484476
value: string,
485477
): any {
486-
switch (value[0]) {
487-
case '$': {
488-
if (value === '$') {
489-
return REACT_ELEMENT_TYPE;
490-
} else if (value[1] === '$' || value[1] === '@') {
478+
if (value[0] === '$') {
479+
if (value === '$') {
480+
// A very common symbol.
481+
return REACT_ELEMENT_TYPE;
482+
}
483+
switch (value[1]) {
484+
case '$': {
491485
// This was an escaped string value.
492486
return value.substring(1);
493-
} else {
487+
}
488+
case 'L': {
489+
// Lazy node
490+
const id = parseInt(value.substring(2), 16);
491+
const chunk = getChunk(response, id);
492+
// We create a React.lazy wrapper around any lazy values.
493+
// When passed into React, we'll know how to suspend on this.
494+
return createLazyChunkWrapper(chunk);
495+
}
496+
case 'S': {
497+
return Symbol.for(value.substring(2));
498+
}
499+
case 'P': {
500+
return getOrCreateServerContext(value.substring(2)).Provider;
501+
}
502+
default: {
503+
// We assume that anything else is a reference ID.
494504
const id = parseInt(value.substring(1), 16);
495505
const chunk = getChunk(response, id);
496506
switch (chunk.status) {
@@ -518,13 +528,6 @@ export function parseModelString(
518528
}
519529
}
520530
}
521-
case '@': {
522-
const id = parseInt(value.substring(1), 16);
523-
const chunk = getChunk(response, id);
524-
// We create a React.lazy wrapper around any lazy values.
525-
// When passed into React, we'll know how to suspend on this.
526-
return createLazyChunkWrapper(chunk);
527-
}
528531
}
529532
return value;
530533
}
@@ -566,21 +569,6 @@ export function resolveModel(
566569
}
567570
}
568571

569-
export function resolveProvider(
570-
response: Response,
571-
id: number,
572-
contextName: string,
573-
): void {
574-
const chunks = response._chunks;
575-
chunks.set(
576-
id,
577-
createInitializedChunk(
578-
response,
579-
getOrCreateServerContext(contextName).Provider,
580-
),
581-
);
582-
}
583-
584572
export function resolveModule(
585573
response: Response,
586574
id: number,
@@ -626,17 +614,6 @@ export function resolveModule(
626614
}
627615
}
628616

629-
export function resolveSymbol(
630-
response: Response,
631-
id: number,
632-
name: string,
633-
): void {
634-
const chunks = response._chunks;
635-
// We assume that we'll always emit the symbol before anything references it
636-
// to save a few bytes.
637-
chunks.set(id, createInitializedChunk(response, Symbol.for(name)));
638-
}
639-
640617
type ErrorWithDigest = Error & {digest?: string};
641618
export function resolveErrorProd(
642619
response: Response,

packages/react-client/src/ReactFlightClientStream.js

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ import type {BundlerConfig} from './ReactFlightClientHostConfig';
1414
import {
1515
resolveModule,
1616
resolveModel,
17-
resolveProvider,
18-
resolveSymbol,
1917
resolveErrorProd,
2018
resolveErrorDev,
2119
createResponse as createResponseBase,
@@ -36,33 +34,20 @@ function processFullRow(response: Response, row: string): void {
3634
if (row === '') {
3735
return;
3836
}
39-
const tag = row[0];
37+
const colon = row.indexOf(':', 0);
38+
const id = parseInt(row.substring(0, colon), 16);
39+
const tag = row[colon + 1];
4040
// When tags that are not text are added, check them here before
4141
// parsing the row as text.
4242
// switch (tag) {
4343
// }
44-
const colon = row.indexOf(':', 1);
45-
const id = parseInt(row.substring(1, colon), 16);
46-
const text = row.substring(colon + 1);
4744
switch (tag) {
48-
case 'J': {
49-
resolveModel(response, id, text);
50-
return;
51-
}
52-
case 'M': {
53-
resolveModule(response, id, text);
54-
return;
55-
}
56-
case 'P': {
57-
resolveProvider(response, id, text);
58-
return;
59-
}
60-
case 'S': {
61-
resolveSymbol(response, id, JSON.parse(text));
45+
case 'I': {
46+
resolveModule(response, id, row.substring(colon + 2));
6247
return;
6348
}
6449
case 'E': {
65-
const errorInfo = JSON.parse(text);
50+
const errorInfo = JSON.parse(row.substring(colon + 2));
6651
if (__DEV__) {
6752
resolveErrorDev(
6853
response,
@@ -77,9 +62,9 @@ function processFullRow(response: Response, row: string): void {
7762
return;
7863
}
7964
default: {
80-
throw new Error(
81-
"Error parsing the data. It's probably an error code or network corruption.",
82-
);
65+
// We assume anything else is JSON.
66+
resolveModel(response, id, row.substring(colon + 1));
67+
return;
8368
}
8469
}
8570
}

packages/react-server-dom-relay/src/ReactFlightDOMRelayClient.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
createResponse,
1616
resolveModel,
1717
resolveModule,
18-
resolveSymbol,
1918
resolveErrorDev,
2019
resolveErrorProd,
2120
close,
@@ -25,15 +24,12 @@ import {
2524
export {createResponse, close, getRoot};
2625

2726
export function resolveRow(response: Response, chunk: RowEncoding): void {
28-
if (chunk[0] === 'J') {
27+
if (chunk[0] === 'O') {
2928
// $FlowFixMe unable to refine on array indices
3029
resolveModel(response, chunk[1], chunk[2]);
31-
} else if (chunk[0] === 'M') {
30+
} else if (chunk[0] === 'I') {
3231
// $FlowFixMe unable to refine on array indices
3332
resolveModule(response, chunk[1], chunk[2]);
34-
} else if (chunk[0] === 'S') {
35-
// $FlowFixMe: Flow doesn't support disjoint unions on tuples.
36-
resolveSymbol(response, chunk[1], chunk[2]);
3733
} else {
3834
if (__DEV__) {
3935
resolveErrorDev(

packages/react-server-dom-relay/src/ReactFlightDOMRelayProtocol.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ export type JSONValue =
1818
| $ReadOnlyArray<JSONValue>;
1919

2020
export type RowEncoding =
21-
| ['J', number, JSONValue]
22-
| ['M', number, ModuleMetaData]
21+
| ['O', number, JSONValue]
22+
| ['I', number, ModuleMetaData]
2323
| ['P', number, string]
2424
| ['S', number, string]
2525
| [

packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -151,15 +151,15 @@ export function processModelChunk(
151151
): Chunk {
152152
// $FlowFixMe no good way to define an empty exact object
153153
const json = convertModelToJSON(request, {}, '', model);
154-
return ['J', id, json];
154+
return ['O', id, json];
155155
}
156156

157157
export function processReferenceChunk(
158158
request: Request,
159159
id: number,
160160
reference: string,
161161
): Chunk {
162-
return ['J', id, reference];
162+
return ['O', id, reference];
163163
}
164164

165165
export function processModuleChunk(
@@ -168,23 +168,7 @@ export function processModuleChunk(
168168
moduleMetaData: ModuleMetaData,
169169
): Chunk {
170170
// The moduleMetaData is already a JSON serializable value.
171-
return ['M', id, moduleMetaData];
172-
}
173-
174-
export function processProviderChunk(
175-
request: Request,
176-
id: number,
177-
contextName: string,
178-
): Chunk {
179-
return ['P', id, contextName];
180-
}
181-
182-
export function processSymbolChunk(
183-
request: Request,
184-
id: number,
185-
name: string,
186-
): Chunk {
187-
return ['S', id, name];
171+
return ['I', id, moduleMetaData];
188172
}
189173

190174
export function scheduleWork(callback: () => void) {

packages/react-server-native-relay/src/ReactFlightNativeRelayClient.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
createResponse,
1616
resolveModel,
1717
resolveModule,
18-
resolveSymbol,
1918
resolveErrorDev,
2019
resolveErrorProd,
2120
close,
@@ -25,15 +24,12 @@ import {
2524
export {createResponse, close, getRoot};
2625

2726
export function resolveRow(response: Response, chunk: RowEncoding): void {
28-
if (chunk[0] === 'J') {
27+
if (chunk[0] === 'O') {
2928
// $FlowFixMe `Chunk` doesn't flow into `JSONValue` because of the `E` row type.
3029
resolveModel(response, chunk[1], chunk[2]);
31-
} else if (chunk[0] === 'M') {
30+
} else if (chunk[0] === 'I') {
3231
// $FlowFixMe `Chunk` doesn't flow into `JSONValue` because of the `E` row type.
3332
resolveModule(response, chunk[1], chunk[2]);
34-
} else if (chunk[0] === 'S') {
35-
// $FlowFixMe: Flow doesn't support disjoint unions on tuples.
36-
resolveSymbol(response, chunk[1], chunk[2]);
3733
} else {
3834
if (__DEV__) {
3935
resolveErrorDev(

packages/react-server-native-relay/src/ReactFlightNativeRelayProtocol.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ export type JSONValue =
1818
| Array<JSONValue>;
1919

2020
export type RowEncoding =
21-
| ['J', number, JSONValue]
22-
| ['M', number, ModuleMetaData]
21+
| ['O', number, JSONValue]
22+
| ['I', number, ModuleMetaData]
2323
| ['P', number, string]
2424
| ['S', number, string]
2525
| [

packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -146,15 +146,15 @@ export function processModelChunk(
146146
): Chunk {
147147
// $FlowFixMe no good way to define an empty exact object
148148
const json = convertModelToJSON(request, {}, '', model);
149-
return ['J', id, json];
149+
return ['O', id, json];
150150
}
151151

152152
export function processReferenceChunk(
153153
request: Request,
154154
id: number,
155155
reference: string,
156156
): Chunk {
157-
return ['J', id, reference];
157+
return ['O', id, reference];
158158
}
159159

160160
export function processModuleChunk(
@@ -163,23 +163,7 @@ export function processModuleChunk(
163163
moduleMetaData: ModuleMetaData,
164164
): Chunk {
165165
// The moduleMetaData is already a JSON serializable value.
166-
return ['M', id, moduleMetaData];
167-
}
168-
169-
export function processProviderChunk(
170-
request: Request,
171-
id: number,
172-
contextName: string,
173-
): Chunk {
174-
return ['P', id, contextName];
175-
}
176-
177-
export function processSymbolChunk(
178-
request: Request,
179-
id: number,
180-
name: string,
181-
): Chunk {
182-
return ['S', id, name];
166+
return ['I', id, moduleMetaData];
183167
}
184168

185169
export function scheduleWork(callback: () => void) {

packages/react-server/src/ReactFlightServer.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ import {
3838
closeWithError,
3939
processModelChunk,
4040
processModuleChunk,
41-
processProviderChunk,
42-
processSymbolChunk,
4341
processErrorChunkProd,
4442
processErrorChunkDev,
4543
processReferenceChunk,
@@ -417,7 +415,15 @@ function serializeByValueID(id: number): string {
417415
}
418416

419417
function serializeByRefID(id: number): string {
420-
return '@' + id.toString(16);
418+
return '$L' + id.toString(16);
419+
}
420+
421+
function serializeSymbolReference(name: string): string {
422+
return '$S' + name;
423+
}
424+
425+
function serializeProviderReference(name: string): string {
426+
return '$P' + name;
421427
}
422428

423429
function serializeClientReference(
@@ -473,7 +479,7 @@ function serializeClientReference(
473479
}
474480

475481
function escapeStringValue(value: string): string {
476-
if (value[0] === '$' || value[0] === '@') {
482+
if (value[0] === '$') {
477483
// We need to escape $ or @ prefixed strings since we use those to encode
478484
// references to IDs and as special symbol values.
479485
return '$' + value;
@@ -1110,7 +1116,8 @@ function emitModuleChunk(
11101116
}
11111117

11121118
function emitSymbolChunk(request: Request, id: number, name: string): void {
1113-
const processedChunk = processSymbolChunk(request, id, name);
1119+
const symbolReference = serializeSymbolReference(name);
1120+
const processedChunk = processReferenceChunk(request, id, symbolReference);
11141121
request.completedModuleChunks.push(processedChunk);
11151122
}
11161123

@@ -1119,7 +1126,8 @@ function emitProviderChunk(
11191126
id: number,
11201127
contextName: string,
11211128
): void {
1122-
const processedChunk = processProviderChunk(request, id, contextName);
1129+
const contextReference = serializeProviderReference(contextName);
1130+
const processedChunk = processReferenceChunk(request, id, contextReference);
11231131
request.completedJSONChunks.push(processedChunk);
11241132
}
11251133

0 commit comments

Comments
 (0)