7
7
* @flow
8
8
*/
9
9
10
- import { REACT_ELEMENT_TYPE } from 'shared/ReactSymbols' ;
10
+ import type { BlockComponent , BlockRenderFunction } from 'react/src/ReactBlock' ;
11
+ import type { LazyComponent } from 'react/src/ReactLazy' ;
11
12
12
- // import type {
13
- // ModuleReference,
14
- // ModuleMetaData,
15
- // } from './ReactFlightClientHostConfig';
13
+ import type {
14
+ ModuleReference ,
15
+ ModuleMetaData ,
16
+ } from './ReactFlightClientHostConfig' ;
16
17
17
- // import {
18
- // resolveModuleReference,
19
- // preloadModule,
20
- // requireModule,
21
- // } from './ReactFlightClientHostConfig';
18
+ import {
19
+ resolveModuleReference ,
20
+ preloadModule ,
21
+ requireModule ,
22
+ } from './ReactFlightClientHostConfig' ;
23
+
24
+ import {
25
+ REACT_LAZY_TYPE ,
26
+ REACT_BLOCK_TYPE ,
27
+ REACT_ELEMENT_TYPE ,
28
+ } from 'shared/ReactSymbols' ;
22
29
23
30
export type ReactModelRoot < T > = { |
24
31
model : T ,
@@ -32,40 +39,43 @@ export type JSONValue =
32
39
| { [ key : string ] : JSONValue }
33
40
| Array < JSONValue > ;
34
41
35
- const isArray = Array . isArray ;
36
-
37
42
const PENDING = 0 ;
38
43
const RESOLVED = 1 ;
39
44
const ERRORED = 2 ;
40
45
46
+ const CHUNK_TYPE = Symbol ( 'flight.chunk' ) ;
47
+
41
48
type PendingChunk = { |
49
+ $$typeof : Symbol ,
42
50
status : 0 ,
43
51
value : Promise < void > ,
44
52
resolve : ( ) => void ,
45
53
| } ;
46
- type ResolvedChunk = { |
54
+ type ResolvedChunk < T > = { |
55
+ $$typeof : Symbol ,
47
56
status : 1 ,
48
- value : mixed ,
57
+ value : T ,
49
58
resolve : null ,
50
59
| } ;
51
60
type ErroredChunk = { |
61
+ $$typeof : Symbol ,
52
62
status : 2 ,
53
63
value : Error ,
54
64
resolve : null ,
55
65
| } ;
56
- type Chunk = PendingChunk | ResolvedChunk | ErroredChunk ;
66
+ type Chunk < T > = PendingChunk | ResolvedChunk < T > | ErroredChunk ;
57
67
58
68
export type Response = {
59
69
partialRow : string ,
60
70
modelRoot : ReactModelRoot < any > ,
61
- chunks : Map < number , Chunk> ,
71
+ chunks : Map < number , Chunk< any > >,
62
72
} ;
63
73
64
74
export function createResponse ( ) : Response {
65
75
let modelRoot : ReactModelRoot < any > = ( { } : any ) ;
66
- let rootChunk : Chunk = createPendingChunk ( ) ;
76
+ let rootChunk : Chunk < any > = createPendingChunk ( ) ;
67
77
definePendingProperty ( modelRoot , 'model' , rootChunk ) ;
68
- let chunks : Map < number , Chunk > = new Map ( ) ;
78
+ let chunks : Map < number , Chunk < any > > = new Map ( ) ;
69
79
chunks . set ( 0 , rootChunk ) ;
70
80
let response = {
71
81
partialRow : '' ,
@@ -79,6 +89,7 @@ function createPendingChunk(): PendingChunk {
79
89
let resolve : ( ) => void = ( null : any ) ;
80
90
let promise = new Promise ( r => ( resolve = r ) ) ;
81
91
return {
92
+ $$typeof : CHUNK_TYPE ,
82
93
status : PENDING ,
83
94
value : promise ,
84
95
resolve : resolve ,
@@ -87,13 +98,14 @@ function createPendingChunk(): PendingChunk {
87
98
88
99
function createErrorChunk ( error : Error ) : ErroredChunk {
89
100
return {
101
+ $$typeof : CHUNK_TYPE ,
90
102
status : ERRORED ,
91
103
value : error ,
92
104
resolve : null ,
93
105
} ;
94
106
}
95
107
96
- function triggerErrorOnChunk ( chunk : Chunk , error : Error ) : void {
108
+ function triggerErrorOnChunk < T > (chunk: Chunk< T > , error: Error): void {
97
109
if ( chunk . status !== PENDING ) {
98
110
// We already resolved. We didn't expect to see this.
99
111
return ;
@@ -106,21 +118,22 @@ function triggerErrorOnChunk(chunk: Chunk, error: Error): void {
106
118
resolve();
107
119
}
108
120
109
- function createResolvedChunk ( value : mixed ) : ResolvedChunk {
121
+ function createResolvedChunk < T > (value: T ): ResolvedChunk< T > {
110
122
return {
123
+ $$typeof : CHUNK_TYPE ,
111
124
status : RESOLVED ,
112
125
value : value ,
113
126
resolve : null ,
114
127
} ;
115
128
}
116
129
117
- function resolveChunk ( chunk : Chunk , value : mixed ) : void {
130
+ function resolveChunk< T > (chunk: Chunk< T > , value: T ): void {
118
131
if ( chunk . status !== PENDING ) {
119
132
// We already resolved. We didn't expect to see this.
120
133
return ;
121
134
}
122
135
let resolve = chunk.resolve;
123
- let resolvedChunk : ResolvedChunk = ( chunk : any ) ;
136
+ let resolvedChunk: ResolvedChunk< T > = (chunk: any);
124
137
resolvedChunk.status = RESOLVED;
125
138
resolvedChunk.value = value;
126
139
resolvedChunk.resolve = null;
@@ -138,10 +151,23 @@ export function reportGlobalError(response: Response, error: Error): void {
138
151
} ) ;
139
152
}
140
153
141
- function definePendingProperty (
154
+ function readMaybeChunk< T > (maybeChunk: Chunk< T > | T): T {
155
+ if ( ( maybeChunk : any ) . $$typeof !== CHUNK_TYPE ) {
156
+ // $FlowFixMe
157
+ return maybeChunk ;
158
+ }
159
+ let chunk: Chunk< T > = (maybeChunk: any);
160
+ if (chunk.status === RESOLVED) {
161
+ return chunk . value ;
162
+ } else {
163
+ throw chunk . value ;
164
+ }
165
+ }
166
+
167
+ function definePendingProperty < T > (
142
168
object: Object,
143
169
key: string,
144
- chunk : Chunk ,
170
+ chunk: Chunk< T > ,
145
171
): void {
146
172
Object . defineProperty ( object , key , {
147
173
configurable : false ,
@@ -197,6 +223,55 @@ function createElement(type, key, props): React$Element<any> {
197
223
return element;
198
224
}
199
225
226
+ type UninitializedBlockPayload < Data > = [
227
+ mixed,
228
+ ModuleMetaData | Chunk< ModuleMetaData > ,
229
+ Data | Chunk< Data > ,
230
+ ];
231
+
232
+ type Thenable< T > = {
233
+ then ( resolve : ( T ) = > mixed , reject ? : ( mixed ) => mixed ) : Thenable < any > ,
234
+ } ;
235
+
236
+ function initializeBlock< Props , Data > (
237
+ tuple: UninitializedBlockPayload< Data > ,
238
+ ): BlockComponent< Props , Data > {
239
+ // Require module first and then data. The ordering matters.
240
+ let moduleMetaData : ModuleMetaData = readMaybeChunk ( tuple [ 1 ] ) ;
241
+ let moduleReference : ModuleReference <
242
+ BlockRenderFunction < Props , Data > ,
243
+ > = resolveModuleReference ( moduleMetaData ) ;
244
+ // TODO: Do this earlier, as the chunk is resolved.
245
+ preloadModule ( moduleReference ) ;
246
+
247
+ let moduleExport = requireModule ( moduleReference ) ;
248
+
249
+ // The ordering here is important because this call might suspend.
250
+ // We don't want that to prevent the module graph for being initialized.
251
+ let data : Data = readMaybeChunk ( tuple [ 2 ] ) ;
252
+
253
+ return {
254
+ $$typeof : REACT_BLOCK_TYPE ,
255
+ _status : - 1 ,
256
+ _data : data ,
257
+ _render : moduleExport ,
258
+ } ;
259
+ }
260
+
261
+ function createLazyBlock< Props , Data > (
262
+ tuple: UninitializedBlockPayload< Data > ,
263
+ ): LazyComponent< BlockComponent < Props , Data > , UninitializedBlockPayload< Data > > {
264
+ let lazyType : LazyComponent <
265
+ BlockComponent < Props , Data > ,
266
+ UninitializedBlockPayload < Data > ,
267
+ > = {
268
+ $$typeof : REACT_LAZY_TYPE ,
269
+ _payload : tuple ,
270
+ _init : initializeBlock ,
271
+ } ;
272
+ return lazyType ;
273
+ }
274
+
200
275
export function parseModelFromJSON(
201
276
response: Response,
202
277
targetObj: Object,
@@ -217,20 +292,26 @@ export function parseModelFromJSON(
217
292
if ( ! chunk ) {
218
293
chunk = createPendingChunk ( ) ;
219
294
chunks . set ( id , chunk ) ;
220
- } else if ( chunk . status === RESOLVED ) {
221
- return chunk . value ;
222
295
}
223
- definePendingProperty ( targetObj , key , chunk ) ;
224
- return undefined ;
296
+ return chunk ;
225
297
}
226
298
}
299
+ if ( value === '@') {
300
+ return REACT_BLOCK_TYPE ;
301
+ }
227
302
}
228
- if ( isArray ( value ) ) {
303
+ if ( typeof value === ' object ' && value !== null ) {
229
304
let tuple : [ mixed , mixed , mixed , mixed ] = ( value : any ) ;
230
- if ( tuple [ 0 ] === REACT_ELEMENT_TYPE ) {
231
- // TODO: Consider having React just directly accept these arrays as elements.
232
- // Or even change the ReactElement type to be an array.
233
- return createElement ( tuple [ 1 ] , tuple [ 2 ] , tuple [ 3 ] ) ;
305
+ switch ( tuple [ 0 ] ) {
306
+ case REACT_ELEMENT_TYPE : {
307
+ // TODO: Consider having React just directly accept these arrays as elements.
308
+ // Or even change the ReactElement type to be an array.
309
+ return createElement ( tuple [ 1 ] , tuple [ 2 ] , tuple [ 3 ] ) ;
310
+ }
311
+ case REACT_BLOCK_TYPE: {
312
+ // TODO: Consider having React just directly accept these arrays as blocks.
313
+ return createLazyBlock ( ( tuple : any ) ) ;
314
+ }
234
315
}
235
316
}
236
317
return value ;
0 commit comments