-
Notifications
You must be signed in to change notification settings - Fork 45.6k
/
ReactNativeServerFormatConfig.js
223 lines (199 loc) · 6.09 KB
/
ReactNativeServerFormatConfig.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type {
Destination,
Chunk,
PrecomputedChunk,
} from 'react-server/src/ReactServerStreamConfig';
import {
writeChunk,
stringToChunk,
stringToPrecomputedChunk,
} from 'react-server/src/ReactServerStreamConfig';
import invariant from 'shared/invariant';
// Every list of children or string is null terminated.
const END_TAG = 0;
// Tree node tags.
const INSTANCE_TAG = 1;
const PLACEHOLDER_TAG = 2;
const SUSPENSE_PENDING_TAG = 3;
const SUSPENSE_COMPLETE_TAG = 4;
const SUSPENSE_CLIENT_RENDER_TAG = 5;
// Command tags.
const SEGMENT_TAG = 1;
const SUSPENSE_UPDATE_TO_COMPLETE_TAG = 2;
const SUSPENSE_UPDATE_TO_CLIENT_RENDER_TAG = 3;
const END = new Uint8Array(1);
END[0] = END_TAG;
const PLACEHOLDER = new Uint8Array(1);
PLACEHOLDER[0] = PLACEHOLDER_TAG;
const INSTANCE = new Uint8Array(1);
INSTANCE[0] = INSTANCE_TAG;
const SUSPENSE_PENDING = new Uint8Array(1);
SUSPENSE_PENDING[0] = SUSPENSE_PENDING_TAG;
const SUSPENSE_COMPLETE = new Uint8Array(1);
SUSPENSE_COMPLETE[0] = SUSPENSE_COMPLETE_TAG;
const SUSPENSE_CLIENT_RENDER = new Uint8Array(1);
SUSPENSE_CLIENT_RENDER[0] = SUSPENSE_CLIENT_RENDER_TAG;
const SEGMENT = new Uint8Array(1);
SEGMENT[0] = SEGMENT_TAG;
const SUSPENSE_UPDATE_TO_COMPLETE = new Uint8Array(1);
SUSPENSE_UPDATE_TO_COMPLETE[0] = SUSPENSE_UPDATE_TO_COMPLETE_TAG;
const SUSPENSE_UPDATE_TO_CLIENT_RENDER = new Uint8Array(1);
SUSPENSE_UPDATE_TO_CLIENT_RENDER[0] = SUSPENSE_UPDATE_TO_CLIENT_RENDER_TAG;
// Per response,
export type ResponseState = {
nextSuspenseID: number,
};
// Allows us to keep track of what we've already written so we can refer back to it.
export function createResponseState(): ResponseState {
return {
nextSuspenseID: 0,
};
}
// This object is used to lazily reuse the ID of the first generated node, or assign one.
// This is very specific to DOM where we can't assign an ID to.
export type SuspenseBoundaryID = number;
export function createSuspenseBoundaryID(
responseState: ResponseState,
): SuspenseBoundaryID {
// TODO: This is not deterministic since it's created during render.
return responseState.nextSuspenseID++;
}
const RAW_TEXT = stringToPrecomputedChunk('RCTRawText');
export function pushEmpty(
target: Array<Chunk | PrecomputedChunk>,
responseState: ResponseState,
assignID: null | SuspenseBoundaryID,
): void {
// This is not used since we don't need to assign any IDs.
}
export function pushTextInstance(
target: Array<Chunk | PrecomputedChunk>,
text: string,
responseState: ResponseState,
assignID: null | SuspenseBoundaryID,
): void {
target.push(
INSTANCE,
RAW_TEXT, // Type
END, // Null terminated type string
// TODO: props { text: text }
END, // End of children
);
}
export function pushStartInstance(
target: Array<Chunk | PrecomputedChunk>,
type: string,
props: Object,
responseState: ResponseState,
assignID: null | SuspenseBoundaryID,
): void {
target.push(
INSTANCE,
stringToChunk(type),
END, // Null terminated type string
// TODO: props
);
}
export function pushEndInstance(
target: Array<Chunk | PrecomputedChunk>,
type: string,
props: Object,
): void {
target.push(END);
}
// IDs are formatted as little endian Uint16
function formatID(id: number): Uint8Array {
if (id > 0xffff) {
invariant(
false,
'More boundaries or placeholders than we expected to ever emit.',
);
}
const buffer = new Uint8Array(2);
buffer[0] = (id >>> 8) & 0xff;
buffer[1] = id & 0xff;
return buffer;
}
// Structural Nodes
// A placeholder is a node inside a hidden partial tree that can be filled in later, but before
// display. It's never visible to users.
export function writePlaceholder(
destination: Destination,
responseState: ResponseState,
id: number,
): boolean {
writeChunk(destination, PLACEHOLDER);
return writeChunk(destination, formatID(id));
}
// Suspense boundaries are encoded as comments.
export function writeStartCompletedSuspenseBoundary(
destination: Destination,
id: SuspenseBoundaryID,
): boolean {
writeChunk(destination, SUSPENSE_COMPLETE);
return writeChunk(destination, formatID(id));
}
export function writeStartPendingSuspenseBoundary(
destination: Destination,
id: SuspenseBoundaryID,
): boolean {
writeChunk(destination, SUSPENSE_PENDING);
return writeChunk(destination, formatID(id));
}
export function writeStartClientRenderedSuspenseBoundary(
destination: Destination,
id: SuspenseBoundaryID,
): boolean {
writeChunk(destination, SUSPENSE_CLIENT_RENDER);
return writeChunk(destination, formatID(id));
}
export function writeEndSuspenseBoundary(destination: Destination): boolean {
return writeChunk(destination, END);
}
export function writeStartSegment(
destination: Destination,
responseState: ResponseState,
id: number,
): boolean {
writeChunk(destination, SEGMENT);
return writeChunk(destination, formatID(id));
}
export function writeEndSegment(destination: Destination): boolean {
return writeChunk(destination, END);
}
// Instruction Set
export function writeCompletedSegmentInstruction(
destination: Destination,
responseState: ResponseState,
contentSegmentID: number,
): boolean {
// We don't need to emit this. Instead the client will keep track of pending placeholders.
// TODO: Returning true here is not correct. Avoid having to call this function at all.
return true;
}
export function writeCompletedBoundaryInstruction(
destination: Destination,
responseState: ResponseState,
boundaryID: SuspenseBoundaryID,
contentSegmentID: number,
): boolean {
writeChunk(destination, SUSPENSE_UPDATE_TO_COMPLETE);
writeChunk(destination, formatID(boundaryID));
return writeChunk(destination, formatID(contentSegmentID));
}
export function writeClientRenderBoundaryInstruction(
destination: Destination,
responseState: ResponseState,
boundaryID: SuspenseBoundaryID,
): boolean {
writeChunk(destination, SUSPENSE_UPDATE_TO_CLIENT_RENDER);
return writeChunk(destination, formatID(boundaryID));
}