@samitouri / QOS-React-2 / commits / 2b003a5cc6

Split out ServerReferenceMetadata into Id and Bound Arguments (#26351)

This is just moving some stuff around and renaming things. This tuple is opaque to the Flight implementation and we should probably encode it separately as a single string instead of a model object. The term "Metadata" isn't the same as when used for ClientReferences so it's not really the right term anyway. I also made it optional since a bound function with no arguments bound is technically different than a raw instance of that function (it's a clone). I also renamed the type ReactModel to ReactClientValue. This is the generic serializable type for something that can pass through the serializable boundary from server to client. There will be another one for client to server. I also filled in missing classes and ensure the serializable sub-types are explicit. E.g. Array and Thenable.

Sebastian Markbåge committed Mar 8, 2023 at 23:45 UTC 2b003a5cc6df77f745e1639404f912bd0077a39b
24 files changed +179 -122
packages/react-client/src/ReactFlightClient.js
+7 -4
@@ -15,7 +15,7 @@ import type {
15 ClientReferenceMetadata,
16 UninitializedModel,
17 Response,
18 - BundlerConfig,
18 + SSRManifest,
19 } from './ReactFlightClientHostConfig';
20
21 import {
@@ -149,7 +149,7 @@ Chunk.prototype.then = function <T>(
149 };
150
151 export type ResponseBase = {
152 - _bundlerConfig: BundlerConfig,
152 + _bundlerConfig: SSRManifest,
153 _callServer: CallServerCallback,
154 _chunks: Map<number, SomeChunk<any>>,
155 ...
@@ -473,13 +473,16 @@ function createModelReject<T>(chunk: SomeChunk<T>): (error: mixed) => void {
473
474 function createServerReferenceProxy<A: Iterable<any>, T>(
475 response: Response,
476 - metaData: {id: any, bound: Thenable<Array<any>>},
476 + metaData: {id: any, bound: null | Thenable<Array<any>>},
477 ): (...A) => Promise<T> {
478 const callServer = response._callServer;
479 const proxy = function (): Promise<T> {
480 // $FlowFixMe[method-unbinding]
481 const args = Array.prototype.slice.call(arguments);
482 const p = metaData.bound;
483 + if (!p) {
484 + return callServer(metaData.id, args);
485 + }
486 if (p.status === INITIALIZED) {
487 const bound = p.value;
488 return callServer(metaData.id, bound.concat(args));
@@ -608,7 +611,7 @@ function missingCall() {
611 }
612
613 export function createResponse(
611 - bundlerConfig: BundlerConfig,
614 + bundlerConfig: SSRManifest,
615 callServer: void | CallServerCallback,
616 ): ResponseBase {
617 const chunks: Map<number, SomeChunk<any>> = new Map();
packages/react-client/src/ReactFlightClientStream.js
+2 -2
@@ -9,7 +9,7 @@
9
10 import type {CallServerCallback} from './ReactFlightClient';
11 import type {Response} from './ReactFlightClientHostConfigStream';
12 -import type {BundlerConfig} from './ReactFlightClientHostConfig';
12 +import type {SSRManifest} from './ReactFlightClientHostConfig';
13
14 import {
15 resolveModule,
@@ -121,7 +121,7 @@ function createFromJSONCallback(response: Response) {
121 }
122
123 export function createResponse(
124 - bundlerConfig: BundlerConfig,
124 + bundlerConfig: SSRManifest,
125 callServer: void | CallServerCallback,
126 ): Response {
127 // NOTE: CHECK THE COMPILER OUTPUT EACH TIME YOU CHANGE THIS.
packages/react-client/src/forks/ReactFlightClientHostConfig.custom.js
+1 -1
@@ -26,7 +26,7 @@
26 declare var $$$hostConfig: any;
27
28 export type Response = any;
29 -export opaque type BundlerConfig = mixed;
29 +export opaque type SSRManifest = mixed;
30 export opaque type ClientReferenceMetadata = mixed;
31 export opaque type ClientReference<T> = mixed; // eslint-disable-line no-unused-vars
32 export const resolveClientReference = $$$hostConfig.resolveClientReference;
packages/react-client/src/forks/ReactFlightClientHostConfig.dom-bun.js
+1 -1
@@ -11,7 +11,7 @@ export * from 'react-client/src/ReactFlightClientHostConfigBrowser';
11 export * from 'react-client/src/ReactFlightClientHostConfigStream';
12
13 export type Response = any;
14 -export opaque type BundlerConfig = mixed;
14 +export opaque type SSRManifest = mixed;
15 export opaque type ClientReferenceMetadata = mixed;
16 export opaque type ClientReference<T> = mixed; // eslint-disable-line no-unused-vars
17 export const resolveClientReference: any = null;
packages/react-noop-renderer/src/ReactNoopFlightServer.js
+2 -2
@@ -14,7 +14,7 @@
14 * environment.
15 */
16
17 -import type {ReactModel} from 'react-server/src/ReactFlightServer';
17 +import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
18 import type {ServerContextJSONValue} from 'shared/ReactTypes';
19
20 import {saveModule} from 'react-noop-renderer/flight-modules';
@@ -71,7 +71,7 @@ type Options = {
71 identifierPrefix?: string,
72 };
73
74 -function render(model: ReactModel, options?: Options): Destination {
74 +function render(model: ReactClientValue, options?: Options): Destination {
75 const destination: Destination = [];
76 const bundlerConfig = undefined;
77 const request = ReactNoopFlightServer.createRequest(
packages/react-server-dom-relay/src/ReactFlightDOMRelayClientHostConfig.js
+2 -2
@@ -31,14 +31,14 @@ import isArray from 'shared/isArray';
31
32 export type {ClientReferenceMetadata} from 'ReactFlightDOMRelayClientIntegration';
33
34 -export type BundlerConfig = null;
34 +export type SSRManifest = null;
35
36 export type UninitializedModel = JSONValue;
37
38 export type Response = ResponseBase;
39
40 export function resolveClientReference<T>(
41 - bundlerConfig: BundlerConfig,
41 + bundlerConfig: SSRManifest,
42 metadata: ClientReferenceMetadata,
43 ): ClientReference<T> {
44 return resolveClientReferenceImpl(metadata);
packages/react-server-dom-relay/src/ReactFlightDOMRelayServer.js
+4 -4
@@ -7,9 +7,9 @@
7 * @flow
8 */
9
10 -import type {ReactModel} from 'react-server/src/ReactFlightServer';
10 +import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
11 import type {
12 - BundlerConfig,
12 + ClientManifest,
13 Destination,
14 } from './ReactFlightDOMRelayServerHostConfig';
15
@@ -25,9 +25,9 @@ type Options = {
25 };
26
27 function render(
28 - model: ReactModel,
28 + model: ReactClientValue,
29 destination: Destination,
30 - config: BundlerConfig,
30 + config: ClientManifest,
31 options?: Options,
32 ): void {
33 const request = createRequest(
packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js
+20 -10
@@ -9,7 +9,10 @@
9
10 import type {RowEncoding, JSONValue} from './ReactFlightDOMRelayProtocol';
11
12 -import type {Request, ReactModel} from 'react-server/src/ReactFlightServer';
12 +import type {
13 + Request,
14 + ReactClientValue,
15 +} from 'react-server/src/ReactFlightServer';
16
17 import type {JSResourceReference} from 'JSResourceReference';
18 import JSResourceReferenceImpl from 'JSResourceReferenceImpl';
@@ -23,7 +26,7 @@ export type ServerReferenceId = {};
26
27 import type {
28 Destination,
26 - BundlerConfig,
29 + BundlerConfig as ClientManifest,
30 ClientReferenceMetadata,
31 } from 'ReactFlightDOMRelayServerIntegration';
32
@@ -37,7 +40,7 @@ import {
40
41 export type {
42 Destination,
40 - BundlerConfig,
43 + BundlerConfig as ClientManifest,
44 ClientReferenceMetadata,
45 } from 'ReactFlightDOMRelayServerIntegration';
46
@@ -60,16 +63,23 @@ export function getClientReferenceKey(
63 }
64
65 export function resolveClientReferenceMetadata<T>(
63 - config: BundlerConfig,
66 + config: ClientManifest,
67 resource: ClientReference<T>,
68 ): ClientReferenceMetadata {
69 return resolveClientReferenceMetadataImpl(config, resource);
70 }
71
69 -export function resolveServerReferenceMetadata<T>(
70 - config: BundlerConfig,
72 +export function getServerReferenceId<T>(
73 + config: ClientManifest,
74 + resource: ServerReference<T>,
75 +): ServerReferenceId {
76 + throw new Error('Not implemented.');
77 +}
78 +
79 +export function getServerReferenceBoundArguments<T>(
80 + config: ClientManifest,
81 resource: ServerReference<T>,
72 -): {id: ServerReferenceId, bound: Promise<Array<any>>} {
82 +): Array<ReactClientValue> {
83 throw new Error('Not implemented.');
84 }
85
@@ -125,9 +135,9 @@ export function processErrorChunkDev(
135
136 function convertModelToJSON(
137 request: Request,
128 - parent: {+[key: string]: ReactModel} | $ReadOnlyArray<ReactModel>,
138 + parent: {+[key: string]: ReactClientValue} | $ReadOnlyArray<ReactClientValue>,
139 key: string,
130 - model: ReactModel,
140 + model: ReactClientValue,
141 ): JSONValue {
142 const json = resolveModelToJSON(request, parent, key, model);
143 if (typeof json === 'object' && json !== null) {
@@ -160,7 +170,7 @@ function convertModelToJSON(
170 export function processModelChunk(
171 request: Request,
172 id: number,
163 - model: ReactModel,
173 + model: ReactClientValue,
174 ): Chunk {
175 // $FlowFixMe no good way to define an empty exact object
176 const json = convertModelToJSON(request, {}, '', model);
packages/react-server-dom-webpack/src/ReactFlightClientNodeBundlerConfig.js
+2 -4
@@ -13,14 +13,12 @@ import type {
13 RejectedThenable,
14 } from 'shared/ReactTypes';
15
16 -export type WebpackSSRMap = {
16 +export type SSRManifest = {
17 [clientId: string]: {
18 [clientExportName: string]: ClientReference<any>,
19 },
20 };
21
22 -export type BundlerConfig = WebpackSSRMap;
23 -
22 export opaque type ClientReferenceMetadata = {
23 id: string,
24 chunks: Array<string>,
@@ -34,7 +32,7 @@ export opaque type ClientReference<T> = {
32 };
33
34 export function resolveClientReference<T>(
37 - bundlerConfig: BundlerConfig,
35 + bundlerConfig: SSRManifest,
36 metadata: ClientReferenceMetadata,
37 ): ClientReference<T> {
38 const resolvedModuleData = bundlerConfig[metadata.id][metadata.name];
packages/react-server-dom-webpack/src/ReactFlightClientWebpackBundlerConfig.js
+2 -4
@@ -13,14 +13,12 @@ import type {
13 RejectedThenable,
14 } from 'shared/ReactTypes';
15
16 -export type WebpackSSRMap = {
16 +export type SSRManifest = null | {
17 [clientId: string]: {
18 [clientExportName: string]: ClientReferenceMetadata,
19 },
20 };
21
22 -export type BundlerConfig = null | WebpackSSRMap;
23 -
22 export opaque type ClientReferenceMetadata = {
23 id: string,
24 chunks: Array<string>,
@@ -32,7 +30,7 @@ export opaque type ClientReferenceMetadata = {
30 export opaque type ClientReference<T> = ClientReferenceMetadata;
31
32 export function resolveClientReference<T>(
35 - bundlerConfig: BundlerConfig,
33 + bundlerConfig: SSRManifest,
34 metadata: ClientReferenceMetadata,
35 ): ClientReference<T> {
36 if (bundlerConfig) {
packages/react-server-dom-webpack/src/ReactFlightDOMClientEdge.js
+2 -2
@@ -11,7 +11,7 @@ import type {Thenable} from 'shared/ReactTypes.js';
11
12 import type {Response as FlightResponse} from 'react-client/src/ReactFlightClientStream';
13
14 -import type {BundlerConfig} from './ReactFlightClientWebpackBundlerConfig';
14 +import type {SSRManifest} from './ReactFlightClientWebpackBundlerConfig';
15
16 import {
17 createResponse,
@@ -30,7 +30,7 @@ function noServerCall() {
30 }
31
32 export type Options = {
33 - moduleMap?: BundlerConfig,
33 + moduleMap?: SSRManifest,
34 };
35
36 function createResponseFromOptions(options: void | Options) {
packages/react-server-dom-webpack/src/ReactFlightDOMClientNode.js
+2 -2
@@ -11,7 +11,7 @@ import type {Thenable} from 'shared/ReactTypes.js';
11
12 import type {Response} from 'react-client/src/ReactFlightClientStream';
13
14 -import type {BundlerConfig} from 'react-client/src/ReactFlightClientHostConfig';
14 +import type {SSRManifest} from 'react-client/src/ReactFlightClientHostConfig';
15
16 import type {Readable} from 'stream';
17
@@ -34,7 +34,7 @@ function noServerCall() {
34
35 function createFromNodeStream<T>(
36 stream: Readable,
37 - moduleMap: $NonMaybeType<BundlerConfig>,
37 + moduleMap: $NonMaybeType<SSRManifest>,
38 ): Thenable<T> {
39 const response: Response = createResponse(moduleMap, noServerCall);
40 stream.on('data', chunk => {
packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js
+4 -4
@@ -7,9 +7,9 @@
7 * @flow
8 */
9
10 -import type {ReactModel} from 'react-server/src/ReactFlightServer';
10 +import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
11 import type {ServerContextJSONValue} from 'shared/ReactTypes';
12 -import type {BundlerConfig} from './ReactFlightServerWebpackBundlerConfig';
12 +import type {ClientManifest} from './ReactFlightServerWebpackBundlerConfig';
13
14 import {
15 createRequest,
@@ -26,8 +26,8 @@ type Options = {
26 };
27
28 function renderToReadableStream(
29 - model: ReactModel,
30 - webpackMap: BundlerConfig,
29 + model: ReactClientValue,
30 + webpackMap: ClientManifest,
31 options?: Options,
32 ): ReadableStream {
33 const request = createRequest(
packages/react-server-dom-webpack/src/ReactFlightDOMServerEdge.js
+4 -4
@@ -7,9 +7,9 @@
7 * @flow
8 */
9
10 -import type {ReactModel} from 'react-server/src/ReactFlightServer';
10 +import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
11 import type {ServerContextJSONValue} from 'shared/ReactTypes';
12 -import type {BundlerConfig} from './ReactFlightServerWebpackBundlerConfig';
12 +import type {ClientManifest} from './ReactFlightServerWebpackBundlerConfig';
13
14 import {
15 createRequest,
@@ -26,8 +26,8 @@ type Options = {
26 };
27
28 function renderToReadableStream(
29 - model: ReactModel,
30 - webpackMap: BundlerConfig,
29 + model: ReactClientValue,
30 + webpackMap: ClientManifest,
31 options?: Options,
32 ): ReadableStream {
33 const request = createRequest(
packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js
+7 -4
@@ -7,9 +7,12 @@
7 * @flow
8 */
9
10 -import type {Request, ReactModel} from 'react-server/src/ReactFlightServer';
10 +import type {
11 + Request,
12 + ReactClientValue,
13 +} from 'react-server/src/ReactFlightServer';
14 import type {Destination} from 'react-server/src/ReactServerStreamConfigNode';
12 -import type {BundlerConfig} from './ReactFlightServerWebpackBundlerConfig';
15 +import type {ClientManifest} from './ReactFlightServerWebpackBundlerConfig';
16 import type {Writable} from 'stream';
17 import type {ServerContextJSONValue} from 'shared/ReactTypes';
18
@@ -36,8 +39,8 @@ type PipeableStream = {
39 };
40
41 function renderToPipeableStream(
39 - model: ReactModel,
40 - webpackMap: BundlerConfig,
42 + model: ReactClientValue,
43 + webpackMap: ClientManifest,
44 options?: Options,
45 ): PipeableStream {
46 const request = createRequest(
packages/react-server-dom-webpack/src/ReactFlightServerWebpackBundlerConfig.js
+15 -13
@@ -7,18 +7,16 @@
7 * @flow
8 */
9
10 -import type {ReactModel} from 'react-server/src/ReactFlightServer';
10 +import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
11
12 -type WebpackMap = {
12 +export type ClientManifest = {
13 [id: string]: ClientReferenceMetadata,
14 };
15
16 -export type BundlerConfig = WebpackMap;
17 -
16 export type ServerReference<T: Function> = T & {
17 $$typeof: symbol,
18 $$id: string,
21 - $$bound: Array<ReactModel>,
19 + $$bound: null | Array<ReactClientValue>,
20 };
21
22 export type ServerReferenceId = string;
@@ -57,7 +55,7 @@ export function isServerReference(reference: Object): boolean {
55 }
56
57 export function resolveClientReferenceMetadata<T>(
60 - config: BundlerConfig,
58 + config: ClientManifest,
59 clientReference: ClientReference<T>,
60 ): ClientReferenceMetadata {
61 const resolvedModuleData = config[clientReference.$$id];
@@ -73,12 +71,16 @@ export function resolveClientReferenceMetadata<T>(
71 }
72 }
73
76 -export function resolveServerReferenceMetadata<T>(
77 - config: BundlerConfig,
74 +export function getServerReferenceId<T>(
75 + config: ClientManifest,
76 + serverReference: ServerReference<T>,
77 +): ServerReferenceId {
78 + return serverReference.$$id;
79 +}
80 +
81 +export function getServerReferenceBoundArguments<T>(
82 + config: ClientManifest,
83 serverReference: ServerReference<T>,
79 -): {id: ServerReferenceId, bound: Promise<Array<any>>} {
80 - return {
81 - id: serverReference.$$id,
82 - bound: Promise.resolve(serverReference.$$bound),
83 - };
84 +): null | Array<ReactClientValue> {
85 + return serverReference.$$bound;
86 }
packages/react-server-dom-webpack/src/ReactFlightWebpackNodeLoader.js
+1 -1
@@ -188,7 +188,7 @@ function transformServerModule(
188 newSrc += 'Object.defineProperties(' + local + ',{';
189 newSrc += '$$typeof: {value: Symbol.for("react.server.reference")},';
190 newSrc += '$$id: {value: ' + JSON.stringify(url + '#' + exported) + '},';
191 - newSrc += '$$bound: { value: [] }';
191 + newSrc += '$$bound: { value: null }';
192 newSrc += '});\n';
193 });
194 return newSrc;
packages/react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js
+3 -3
@@ -30,7 +30,7 @@ module.exports = function register() {
30 const args = Array.prototype.slice.call(arguments, 1);
31 newFn.$$typeof = SERVER_REFERENCE;
32 newFn.$$id = this.$$id;
33 - newFn.$$bound = this.$$bound.concat(args);
33 + newFn.$$bound = this.$$bound ? this.$$bound.concat(args) : args;
34 }
35 return newFn;
36 }: any);
@@ -289,7 +289,7 @@ module.exports = function register() {
289 $$typeof: {value: SERVER_REFERENCE},
290 // Represents the whole Module object instead of a particular import.
291 $$id: {value: moduleId},
292 - $$bound: {value: []},
292 + $$bound: {value: null},
293 });
294 } else {
295 const keys = Object.keys(exports);
@@ -300,7 +300,7 @@ module.exports = function register() {
300 Object.defineProperties((value: any), {
301 $$typeof: {value: SERVER_REFERENCE},
302 $$id: {value: moduleId + '#' + key},
303 - $$bound: {value: []},
303 + $$bound: {value: null},
304 });
305 }
306 }
packages/react-server-native-relay/src/ReactFlightNativeRelayClientHostConfig.js
+2 -2
@@ -31,14 +31,14 @@ import isArray from 'shared/isArray';
31
32 export type {ClientReferenceMetadata} from 'ReactFlightNativeRelayClientIntegration';
33
34 -export type BundlerConfig = null;
34 +export type SSRManifest = null;
35
36 export type UninitializedModel = JSONValue;
37
38 export type Response = ResponseBase;
39
40 export function resolveClientReference<T>(
41 - bundlerConfig: BundlerConfig,
41 + bundlerConfig: SSRManifest,
42 metadata: ClientReferenceMetadata,
43 ): ClientReference<T> {
44 return resolveClientReferenceImpl(metadata);
packages/react-server-native-relay/src/ReactFlightNativeRelayServer.js
+4 -4
@@ -7,9 +7,9 @@
7 * @flow
8 */
9
10 -import type {ReactModel} from 'react-server/src/ReactFlightServer';
10 +import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
11 import type {
12 - BundlerConfig,
12 + ClientManifest,
13 Destination,
14 } from './ReactFlightNativeRelayServerHostConfig';
15
@@ -20,9 +20,9 @@ import {
20 } from 'react-server/src/ReactFlightServer';
21
22 function render(
23 - model: ReactModel,
23 + model: ReactClientValue,
24 destination: Destination,
25 - config: BundlerConfig,
25 + config: ClientManifest,
26 ): void {
27 const request = createRequest(model, config);
28 startWork(request);
packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js
+20 -10
@@ -8,7 +8,10 @@
8 */
9
10 import type {RowEncoding, JSONValue} from './ReactFlightNativeRelayProtocol';
11 -import type {Request, ReactModel} from 'react-server/src/ReactFlightServer';
11 +import type {
12 + Request,
13 + ReactClientValue,
14 +} from 'react-server/src/ReactFlightServer';
15 import hasOwnProperty from 'shared/hasOwnProperty';
16 import isArray from 'shared/isArray';
17 import type {JSResourceReference} from 'JSResourceReference';
@@ -20,7 +23,7 @@ export type ServerReferenceId = {};
23
24 import type {
25 Destination,
23 - BundlerConfig,
26 + BundlerConfig as ClientManifest,
27 ClientReferenceMetadata,
28 } from 'ReactFlightNativeRelayServerIntegration';
29
@@ -34,7 +37,7 @@ import {
37
38 export type {
39 Destination,
37 - BundlerConfig,
40 + BundlerConfig as ClientManifest,
41 ClientReferenceMetadata,
42 } from 'ReactFlightNativeRelayServerIntegration';
43
@@ -57,16 +60,23 @@ export function getClientReferenceKey(
60 }
61
62 export function resolveClientReferenceMetadata<T>(
60 - config: BundlerConfig,
63 + config: ClientManifest,
64 resource: ClientReference<T>,
65 ): ClientReferenceMetadata {
66 return resolveClientReferenceMetadataImpl(config, resource);
67 }
68
66 -export function resolveServerReferenceMetadata<T>(
67 - config: BundlerConfig,
69 +export function getServerReferenceId<T>(
70 + config: ClientManifest,
71 + resource: ServerReference<T>,
72 +): ServerReferenceId {
73 + throw new Error('Not implemented.');
74 +}
75 +
76 +export function getServerReferenceBoundArguments<T>(
77 + config: ClientManifest,
78 resource: ServerReference<T>,
69 -): {id: ServerReferenceId, bound: Promise<Array<any>>} {
79 +): Array<ReactClientValue> {
80 throw new Error('Not implemented.');
81 }
82
@@ -121,9 +131,9 @@ export function processErrorChunkDev(
131
132 function convertModelToJSON(
133 request: Request,
124 - parent: {+[key: string]: ReactModel} | $ReadOnlyArray<ReactModel>,
134 + parent: {+[key: string]: ReactClientValue} | $ReadOnlyArray<ReactClientValue>,
135 key: string,
126 - model: ReactModel,
136 + model: ReactClientValue,
137 ): JSONValue {
138 const json = resolveModelToJSON(request, parent, key, model);
139 if (typeof json === 'object' && json !== null) {
@@ -155,7 +165,7 @@ function convertModelToJSON(
165 export function processModelChunk(
166 request: Request,
167 id: number,
158 - model: ReactModel,
168 + model: ReactClientValue,
169 ): Chunk {
170 // $FlowFixMe no good way to define an empty exact object
171 const json = convertModelToJSON(request, {}, '', model);
packages/react-server/src/ReactFlightServer.js
+62 -33
@@ -10,7 +10,7 @@
10 import type {
11 Destination,
12 Chunk,
13 - BundlerConfig,
13 + ClientManifest,
14 ClientReferenceMetadata,
15 ClientReference,
16 ClientReferenceKey,
@@ -27,6 +27,7 @@ import type {
27 PendingThenable,
28 FulfilledThenable,
29 RejectedThenable,
30 + ReactServerContext,
31 } from 'shared/ReactTypes';
32 import type {LazyComponent} from 'react/src/ReactLazy';
33
@@ -44,7 +45,8 @@ import {
45 processErrorChunkDev,
46 processReferenceChunk,
47 resolveClientReferenceMetadata,
47 - resolveServerReferenceMetadata,
48 + getServerReferenceId,
49 + getServerReferenceBoundArguments,
50 getClientReferenceKey,
51 isClientReference,
52 isServerReference,
@@ -95,21 +97,32 @@ type ReactJSONValue =
97 | number
98 | null
99 | $ReadOnlyArray<ReactJSONValue>
98 - | ReactModelObject;
99 -
100 -export type ReactModel =
101 - | React$Element<any>
102 - | LazyComponent<any, any>
100 + | ReactClientObject;
101 +
102 +// Serializable values
103 +export type ReactClientValue =
104 + // Server Elements and Lazy Components are unwrapped on the Server
105 + | React$Element<React$AbstractComponent<any, any>>
106 + | LazyComponent<ReactClientValue, any>
107 + // References are passed by their value
108 + | ClientReference<any>
109 + | ServerReference<any>
110 + // The rest are passed as is. Sub-types can be passed in but lose their
111 + // subtype, so the receiver can only accept once of these.
112 + | React$Element<string>
113 + | React$Element<ClientReference<any> & any>
114 + | ReactServerContext<any>
115 | string
116 | boolean
117 | number
118 | symbol
119 | null
108 - | Iterable<ReactModel>
109 - | ReactModelObject
110 - | Promise<ReactModel>;
120 + | Iterable<ReactClientValue>
121 + | Array<ReactClientValue>
122 + | ReactClientObject
123 + | Promise<ReactClientValue>; // Thenable<ReactClientValue>
124
112 -type ReactModelObject = {+[key: string]: ReactModel};
125 +type ReactClientObject = {+[key: string]: ReactClientValue};
126
127 const PENDING = 0;
128 const COMPLETED = 1;
@@ -119,7 +132,7 @@ const ERRORED = 4;
132 type Task = {
133 id: number,
134 status: 0 | 1 | 3 | 4,
122 - model: ReactModel,
135 + model: ReactClientValue,
136 ping: () => void,
137 context: ContextSnapshot,
138 thenableState: ThenableState | null,
@@ -129,7 +142,7 @@ export type Request = {
142 status: 0 | 1 | 2,
143 fatalError: mixed,
144 destination: null | Destination,
132 - bundlerConfig: BundlerConfig,
145 + bundlerConfig: ClientManifest,
146 cache: Map<Function, mixed>,
147 nextChunkId: number,
148 pendingChunks: number,
@@ -145,7 +158,7 @@ export type Request = {
158 identifierPrefix: string,
159 identifierCount: number,
160 onError: (error: mixed) => ?string,
148 - toJSON: (key: string, value: ReactModel) => ReactJSONValue,
161 + toJSON: (key: string, value: ReactClientValue) => ReactJSONValue,
162 };
163
164 const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
@@ -161,8 +174,8 @@ const CLOSING = 1;
174 const CLOSED = 2;
175
176 export function createRequest(
164 - model: ReactModel,
165 - bundlerConfig: BundlerConfig,
177 + model: ReactClientValue,
178 + bundlerConfig: ClientManifest,
179 onError: void | ((error: mixed) => ?string),
180 context?: Array<[string, ServerContextJSONValue]>,
181 identifierPrefix?: string,
@@ -200,7 +213,7 @@ export function createRequest(
213 identifierCount: 1,
214 onError: onError === undefined ? defaultErrorHandler : onError,
215 // $FlowFixMe[missing-this-annot]
203 - toJSON: function (key: string, value: ReactModel): ReactJSONValue {
216 + toJSON: function (key: string, value: ReactClientValue): ReactJSONValue {
217 return resolveModelToJSON(request, this, key, value);
218 },
219 };
@@ -364,7 +377,7 @@ function attemptResolveElement(
377 ref: mixed,
378 props: any,
379 prevThenableState: ThenableState | null,
367 -): ReactModel {
380 +): ReactClientValue {
381 if (ref !== null && ref !== undefined) {
382 // When the ref moves to the regular props object this will implicitly
383 // throw for functions. We could probably relax it to a DEV warning for other
@@ -492,7 +505,7 @@ function pingTask(request: Request, task: Task): void {
505
506 function createTask(
507 request: Request,
495 - model: ReactModel,
508 + model: ReactClientValue,
509 context: ContextSnapshot,
510 abortSet: Set<Task>,
511 ): Task {
@@ -535,7 +548,9 @@ function serializeProviderReference(name: string): string {
548
549 function serializeClientReference(
550 request: Request,
538 - parent: {+[key: string | number]: ReactModel} | $ReadOnlyArray<ReactModel>,
551 + parent:
552 + | {+[key: string | number]: ReactClientValue}
553 + | $ReadOnlyArray<ReactClientValue>,
554 key: string,
555 clientReference: ClientReference<any>,
556 ): string {
@@ -586,7 +601,9 @@ function serializeClientReference(
601
602 function serializeServerReference(
603 request: Request,
589 - parent: {+[key: string | number]: ReactModel} | $ReadOnlyArray<ReactModel>,
604 + parent:
605 + | {+[key: string | number]: ReactClientValue}
606 + | $ReadOnlyArray<ReactClientValue>,
607 key: string,
608 serverReference: ServerReference<any>,
609 ): string {
@@ -595,10 +612,18 @@ function serializeServerReference(
612 if (existingId !== undefined) {
613 return serializeServerReferenceID(existingId);
614 }
615 +
616 + const bound: null | Array<any> = getServerReferenceBoundArguments(
617 + request.bundlerConfig,
618 + serverReference,
619 + );
620 const serverReferenceMetadata: {
621 id: ServerReferenceId,
600 - bound: Promise<Array<any>>,
601 - } = resolveServerReferenceMetadata(request.bundlerConfig, serverReference);
622 + bound: null | Promise<Array<any>>,
623 + } = {
624 + id: getServerReferenceId(request.bundlerConfig, serverReference),
625 + bound: bound ? Promise.resolve(bound) : null,
626 + };
627 request.pendingChunks++;
628 const metadataId = request.nextChunkId++;
629 // We assume that this object doesn't suspend.
@@ -683,7 +708,7 @@ function describeKeyForErrorMessage(key: string): string {
708 return '"' + key + '"' === encodedKey ? key : encodedKey;
709 }
710
686 -function describeValueForErrorMessage(value: ReactModel): string {
711 +function describeValueForErrorMessage(value: ReactClientValue): string {
712 switch (typeof value) {
713 case 'string': {
714 return JSON.stringify(
@@ -740,8 +765,8 @@ function describeElementType(type: any): string {
765
766 function describeObjectForErrorMessage(
767 objectOrArray:
743 - | {+[key: string | number]: ReactModel, ...}
744 - | $ReadOnlyArray<ReactModel>,
768 + | {+[key: string | number]: ReactClientValue, ...}
769 + | $ReadOnlyArray<ReactClientValue>,
770 expandedName?: string,
771 ): string {
772 const objKind = objectName(objectOrArray);
@@ -756,7 +781,7 @@ function describeObjectForErrorMessage(
781 // Print JSX Children
782 const type = jsxChildrenParents.get(objectOrArray);
783 str = '<' + describeElementType(type) + '>';
759 - const array: $ReadOnlyArray<ReactModel> = objectOrArray;
784 + const array: $ReadOnlyArray<ReactClientValue> = objectOrArray;
785 for (let i = 0; i < array.length; i++) {
786 const value = array[i];
787 let substr;
@@ -782,7 +807,7 @@ function describeObjectForErrorMessage(
807 } else {
808 // Print Array
809 str = '[';
785 - const array: $ReadOnlyArray<ReactModel> = objectOrArray;
810 + const array: $ReadOnlyArray<ReactClientValue> = objectOrArray;
811 for (let i = 0; i < array.length; i++) {
812 if (i > 0) {
813 str += ', ';
@@ -814,7 +839,8 @@ function describeObjectForErrorMessage(
839 // Print JSX
840 const type = jsxPropsParents.get(objectOrArray);
841 str = '<' + (describeElementType(type) || '...');
817 - const object: {+[key: string | number]: ReactModel, ...} = objectOrArray;
842 + const object: {+[key: string | number]: ReactClientValue, ...} =
843 + objectOrArray;
844 const names = Object.keys(object);
845 for (let i = 0; i < names.length; i++) {
846 str += ' ';
@@ -849,7 +875,8 @@ function describeObjectForErrorMessage(
875 } else {
876 // Print Object
877 str = '{';
852 - const object: {+[key: string | number]: ReactModel, ...} = objectOrArray;
878 + const object: {+[key: string | number]: ReactClientValue, ...} =
879 + objectOrArray;
880 const names = Object.keys(object);
881 for (let i = 0; i < names.length; i++) {
882 if (i > 0) {
@@ -893,9 +920,11 @@ let isInsideContextValue = false;
920
921 export function resolveModelToJSON(
922 request: Request,
896 - parent: {+[key: string | number]: ReactModel} | $ReadOnlyArray<ReactModel>,
923 + parent:
924 + | {+[key: string | number]: ReactClientValue}
925 + | $ReadOnlyArray<ReactClientValue>,
926 key: string,
898 - value: ReactModel,
927 + value: ReactClientValue,
928 ): ReactJSONValue {
929 if (__DEV__) {
930 // $FlowFixMe
@@ -938,7 +967,7 @@ export function resolveModelToJSON(
967 if (
968 parent[0] === REACT_ELEMENT_TYPE &&
969 parent[1] &&
941 - parent[1].$$typeof === REACT_PROVIDER_TYPE &&
970 + (parent[1]: any).$$typeof === REACT_PROVIDER_TYPE &&
971 key === '3'
972 ) {
973 insideContextProps = value;
packages/react-server/src/ReactFlightServerBundlerConfigCustom.js
+4 -3
@@ -9,7 +9,7 @@
9
10 declare var $$$hostConfig: any;
11
12 -export opaque type BundlerConfig = mixed;
12 +export opaque type ClientManifest = mixed;
13 export opaque type ClientReference<T> = mixed; // eslint-disable-line no-unused-vars
14 export opaque type ServerReference<T> = mixed; // eslint-disable-line no-unused-vars
15 export opaque type ClientReferenceMetadata: any = mixed;
@@ -20,5 +20,6 @@ export const isServerReference = $$$hostConfig.isServerReference;
20 export const getClientReferenceKey = $$$hostConfig.getClientReferenceKey;
21 export const resolveClientReferenceMetadata =
22 $$$hostConfig.resolveClientReferenceMetadata;
23 -export const resolveServerReferenceMetadata =
24 - $$$hostConfig.resolveServerReferenceMetadata;
23 +export const getServerReferenceId = $$$hostConfig.getServerReferenceId;
24 +export const getServerReferenceBoundArguments =
25 + $$$hostConfig.getServerReferenceBoundArguments;
packages/react-server/src/ReactFlightServerConfigStream.js
+6 -3
@@ -64,7 +64,10 @@ ByteSize
64
65 // TODO: Implement HTMLData, BlobData and URLData.
66
67 -import type {Request, ReactModel} from 'react-server/src/ReactFlightServer';
67 +import type {
68 + Request,
69 + ReactClientValue,
70 +} from 'react-server/src/ReactFlightServer';
71
72 import {stringToChunk} from './ReactServerStreamConfig';
73
@@ -124,7 +127,7 @@ export function processErrorChunkDev(
127 export function processModelChunk(
128 request: Request,
129 id: number,
127 - model: ReactModel,
130 + model: ReactClientValue,
131 ): Chunk {
132 // $FlowFixMe[incompatible-type] stringify can return null
133 const json: string = stringify(model, request.toJSON);
@@ -145,7 +148,7 @@ export function processReferenceChunk(
148 export function processImportChunk(
149 request: Request,
150 id: number,
148 - clientReferenceMetadata: ReactModel,
151 + clientReferenceMetadata: ReactClientValue,
152 ): Chunk {
153 // $FlowFixMe[incompatible-type] stringify can return null
154 const json: string = stringify(clientReferenceMetadata);