1
-/**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @flow
8
- */
9
-
10
-import type {ReactNodeList} from 'shared/ReactTypes';
11
-
12
-import type {
13
- Destination,
14
- Chunk,
15
- PrecomputedChunk,
16
-} from 'react-server/src/ReactServerStreamConfig';
17
-
18
-import {
19
- writeChunk,
20
- writeChunkAndReturn,
21
- stringToChunk,
22
- stringToPrecomputedChunk,
23
-} from 'react-server/src/ReactServerStreamConfig';
24
-
25
-export const isPrimaryRenderer = true;
26
-
27
-// Every list of children or string is null terminated.
28
-const END_TAG = 0;
29
-// Tree node tags.
30
-const INSTANCE_TAG = 1;
31
-const PLACEHOLDER_TAG = 2;
32
-const SUSPENSE_PENDING_TAG = 3;
33
-const SUSPENSE_COMPLETE_TAG = 4;
34
-const SUSPENSE_CLIENT_RENDER_TAG = 5;
35
-// Command tags.
36
-const SEGMENT_TAG = 1;
37
-const SUSPENSE_UPDATE_TO_COMPLETE_TAG = 2;
38
-const SUSPENSE_UPDATE_TO_CLIENT_RENDER_TAG = 3;
39
-
40
-const END = new Uint8Array(1);
41
-END[0] = END_TAG;
42
-const PLACEHOLDER = new Uint8Array(1);
43
-PLACEHOLDER[0] = PLACEHOLDER_TAG;
44
-const INSTANCE = new Uint8Array(1);
45
-INSTANCE[0] = INSTANCE_TAG;
46
-const SUSPENSE_PENDING = new Uint8Array(1);
47
-SUSPENSE_PENDING[0] = SUSPENSE_PENDING_TAG;
48
-const SUSPENSE_COMPLETE = new Uint8Array(1);
49
-SUSPENSE_COMPLETE[0] = SUSPENSE_COMPLETE_TAG;
50
-const SUSPENSE_CLIENT_RENDER = new Uint8Array(1);
51
-SUSPENSE_CLIENT_RENDER[0] = SUSPENSE_CLIENT_RENDER_TAG;
52
-
53
-const SEGMENT = new Uint8Array(1);
54
-SEGMENT[0] = SEGMENT_TAG;
55
-const SUSPENSE_UPDATE_TO_COMPLETE = new Uint8Array(1);
56
-SUSPENSE_UPDATE_TO_COMPLETE[0] = SUSPENSE_UPDATE_TO_COMPLETE_TAG;
57
-const SUSPENSE_UPDATE_TO_CLIENT_RENDER = new Uint8Array(1);
58
-SUSPENSE_UPDATE_TO_CLIENT_RENDER[0] = SUSPENSE_UPDATE_TO_CLIENT_RENDER_TAG;
59
-
60
-export type Resources = void;
61
-export type BoundaryResources = void;
62
-
63
-// Per response,
64
-export type ResponseState = {
65
- nextSuspenseID: number,
66
-};
67
-
68
-// Allows us to keep track of what we've already written so we can refer back to it.
69
-export function createResponseState(): ResponseState {
70
- return {
71
- nextSuspenseID: 0,
72
- };
73
-}
74
-
75
-// isInAParentText
76
-export type FormatContext = boolean;
77
-
78
-export function createRootFormatContext(): FormatContext {
79
- return false;
80
-}
81
-
82
-export function getChildFormatContext(
83
- parentContext: FormatContext,
84
- type: string,
85
- props: Object,
86
-): FormatContext {
87
- const prevIsInAParentText = parentContext;
88
- const isInAParentText =
89
- type === 'AndroidTextInput' || // Android
90
- type === 'RCTMultilineTextInputView' || // iOS
91
- type === 'RCTSinglelineTextInputView' || // iOS
92
- type === 'RCTText' ||
93
- type === 'RCTVirtualText';
94
-
95
- if (prevIsInAParentText !== isInAParentText) {
96
- return isInAParentText;
97
- } else {
98
- return parentContext;
99
- }
100
-}
101
-
102
-// This object is used to lazily reuse the ID of the first generated node, or assign one.
103
-// This is very specific to DOM where we can't assign an ID to.
104
-export type SuspenseBoundaryID = number;
105
-
106
-export const UNINITIALIZED_SUSPENSE_BOUNDARY_ID = -1;
107
-
108
-export function assignSuspenseBoundaryID(
109
- responseState: ResponseState,
110
-): SuspenseBoundaryID {
111
- return responseState.nextSuspenseID++;
112
-}
113
-
114
-export function makeId(
115
- responseState: ResponseState,
116
- treeId: string,
117
- localId: number,
118
-): string {
119
- throw new Error('Not implemented');
120
-}
121
-
122
-const RAW_TEXT = stringToPrecomputedChunk('RCTRawText');
123
-
124
-export function pushTextInstance(
125
- target: Array<Chunk | PrecomputedChunk>,
126
- text: string,
127
- responseState: ResponseState,
128
- // This Renderer does not use this argument
129
- textEmbedded: boolean,
130
-): boolean {
131
- target.push(
132
- INSTANCE,
133
- RAW_TEXT, // Type
134
- END, // Null terminated type string
135
- // TODO: props { text: text }
136
- END, // End of children
137
- );
138
- return false;
139
-}
140
-
141
-export function pushStartInstance(
142
- target: Array<Chunk | PrecomputedChunk>,
143
- type: string,
144
- props: Object,
145
- resources: Resources,
146
- responseState: ResponseState,
147
- formatContext: FormatContext,
148
- textEmbedded: boolean,
149
-): ReactNodeList {
150
- target.push(
151
- INSTANCE,
152
- stringToChunk(type),
153
- END, // Null terminated type string
154
- // TODO: props
155
- );
156
- return props.children;
157
-}
158
-
159
-export function pushEndInstance(
160
- target: Array<Chunk | PrecomputedChunk>,
161
- type: string,
162
- props: Object,
163
- responseState: ResponseState,
164
- formatContext: FormatContext,
165
-): void {
166
- target.push(END);
167
-}
168
-
169
-// In this Renderer this is a noop
170
-export function pushSegmentFinale(
171
- target: Array<Chunk | PrecomputedChunk>,
172
- responseState: ResponseState,
173
- lastPushedText: boolean,
174
- textEmbedded: boolean,
175
-): void {}
176
-
177
-export function writeCompletedRoot(
178
- destination: Destination,
179
- responseState: ResponseState,
180
-): boolean {
181
- return true;
182
-}
183
-
184
-// IDs are formatted as little endian Uint16
185
-function formatID(id: number): Uint8Array {
186
- if (id > 0xffff) {
187
- throw new Error(
188
- 'More boundaries or placeholders than we expected to ever emit.',
189
- );
190
- }
191
- const buffer = new Uint8Array(2);
192
- buffer[0] = (id >>> 8) & 0xff;
193
- buffer[1] = id & 0xff;
194
- return buffer;
195
-}
196
-
197
-// Structural Nodes
198
-
199
-// A placeholder is a node inside a hidden partial tree that can be filled in later, but before
200
-// display. It's never visible to users.
201
-export function writePlaceholder(
202
- destination: Destination,
203
- responseState: ResponseState,
204
- id: number,
205
-): boolean {
206
- writeChunk(destination, PLACEHOLDER);
207
- return writeChunkAndReturn(destination, formatID(id));
208
-}
209
-
210
-// Suspense boundaries are encoded as comments.
211
-export function writeStartCompletedSuspenseBoundary(
212
- destination: Destination,
213
- responseState: ResponseState,
214
-): boolean {
215
- return writeChunkAndReturn(destination, SUSPENSE_COMPLETE);
216
-}
217
-
218
-export function pushStartCompletedSuspenseBoundary(
219
- target: Array<Chunk | PrecomputedChunk>,
220
-): void {
221
- target.push(SUSPENSE_COMPLETE);
222
-}
223
-
224
-export function writeStartPendingSuspenseBoundary(
225
- destination: Destination,
226
- responseState: ResponseState,
227
- id: SuspenseBoundaryID,
228
-): boolean {
229
- writeChunk(destination, SUSPENSE_PENDING);
230
- return writeChunkAndReturn(destination, formatID(id));
231
-}
232
-export function writeStartClientRenderedSuspenseBoundary(
233
- destination: Destination,
234
- responseState: ResponseState,
235
- // TODO: encode error for native
236
- errorDigest: ?string,
237
- errorMessage: ?string,
238
- errorComponentStack: ?string,
239
-): boolean {
240
- return writeChunkAndReturn(destination, SUSPENSE_CLIENT_RENDER);
241
-}
242
-export function writeEndCompletedSuspenseBoundary(
243
- destination: Destination,
244
- responseState: ResponseState,
245
-): boolean {
246
- return writeChunkAndReturn(destination, END);
247
-}
248
-export function pushEndCompletedSuspenseBoundary(
249
- target: Array<Chunk | PrecomputedChunk>,
250
-): void {
251
- target.push(END);
252
-}
253
-export function writeEndPendingSuspenseBoundary(
254
- destination: Destination,
255
- responseState: ResponseState,
256
-): boolean {
257
- return writeChunkAndReturn(destination, END);
258
-}
259
-export function writeEndClientRenderedSuspenseBoundary(
260
- destination: Destination,
261
- responseState: ResponseState,
262
-): boolean {
263
- return writeChunkAndReturn(destination, END);
264
-}
265
-
266
-export function writeStartSegment(
267
- destination: Destination,
268
- responseState: ResponseState,
269
- formatContext: FormatContext,
270
- id: number,
271
-): boolean {
272
- writeChunk(destination, SEGMENT);
273
- return writeChunkAndReturn(destination, formatID(id));
274
-}
275
-export function writeEndSegment(
276
- destination: Destination,
277
- formatContext: FormatContext,
278
-): boolean {
279
- return writeChunkAndReturn(destination, END);
280
-}
281
-
282
-// Instruction Set
283
-
284
-export function writeCompletedSegmentInstruction(
285
- destination: Destination,
286
- responseState: ResponseState,
287
- contentSegmentID: number,
288
-): boolean {
289
- // We don't need to emit this. Instead the client will keep track of pending placeholders.
290
- // TODO: Returning true here is not correct. Avoid having to call this function at all.
291
- return true;
292
-}
293
-
294
-export function writeCompletedBoundaryInstruction(
295
- destination: Destination,
296
- responseState: ResponseState,
297
- boundaryID: SuspenseBoundaryID,
298
- contentSegmentID: number,
299
- resources: BoundaryResources,
300
-): boolean {
301
- writeChunk(destination, SUSPENSE_UPDATE_TO_COMPLETE);
302
- writeChunk(destination, formatID(boundaryID));
303
- return writeChunkAndReturn(destination, formatID(contentSegmentID));
304
-}
305
-
306
-export function writeClientRenderBoundaryInstruction(
307
- destination: Destination,
308
- responseState: ResponseState,
309
- boundaryID: SuspenseBoundaryID,
310
- // TODO: encode error for native
311
- errorDigest: ?string,
312
- errorMessage: ?string,
313
- errorComponentStack: ?string,
314
-): boolean {
315
- writeChunk(destination, SUSPENSE_UPDATE_TO_CLIENT_RENDER);
316
- return writeChunkAndReturn(destination, formatID(boundaryID));
317
-}
318
-
319
-export function writePreamble(
320
- destination: Destination,
321
- resources: Resources,
322
- responseState: ResponseState,
323
- willFlushAllSegments: boolean,
324
-) {}
325
-
326
-export function writeHoistables(
327
- destination: Destination,
328
- resources: Resources,
329
- responseState: ResponseState,
330
-) {}
331
-
332
-export function writePostamble(
333
- destination: Destination,
334
- responseState: ResponseState,
335
-) {}
336
-
337
-export function hoistResources(
338
- resources: Resources,
339
- boundaryResources: BoundaryResources,
340
-) {}
341
-
342
-export function prepareHostDispatcher() {}
343
-export function createResources() {}
344
-export function createBoundaryResources() {}
345
-export function setCurrentlyRenderingBoundaryResourcesTarget(
346
- resources: Resources,
347
- boundaryResources: ?BoundaryResources,
348
-) {}
349
-
350
-export function writeResourcesForBoundary(
351
- destination: Destination,
352
- boundaryResources: BoundaryResources,
353
- responseState: ResponseState,
354
-): boolean {
355
- return true;
356
-}
357
-
358
-export type TransitionStatus = mixed;
359
-export const NotPendingTransition: TransitionStatus = null;