main
js 119 lines 3.18 KB
Raw
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 {Request, ErrorInfo} from 'react-server/src/ReactFizzServer';
13
14 import type {Destination} from 'react-server/src/ReactServerStreamConfig';
15 import type {BootstrapScriptDescriptor} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
16
17 import {
18 createRequest,
19 startWork,
20 performWork,
21 startFlowing,
22 abort,
23 } from 'react-server/src/ReactFizzServer';
24
25 import {
26 createResumableState,
27 createRenderState,
28 createRootFormatContext,
29 } from 'react-server/src/ReactFizzConfig';
30
31 type Options = {
32 identifierPrefix?: string,
33 bootstrapScriptContent?: string,
34 bootstrapScripts: Array<string>,
35 bootstrapModules: Array<string>,
36 progressiveChunkSize?: number,
37 onError: (error: mixed) => void,
38 onBrowserBailout?: (error: mixed, errorInfo: ErrorInfo) => void,
39 unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
40 };
41
42 opaque type Stream = {
43 destination: Destination,
44 request: Request,
45 };
46
47 function renderToStream(children: ReactNodeList, options: Options): Stream {
48 const destination: Destination = {
49 buffer: '',
50 done: false,
51 fatal: false,
52 error: null,
53 };
54 const resumableState = createResumableState(
55 options ? options.identifierPrefix : undefined,
56 options ? options.unstable_externalRuntimeSrc : undefined,
57 options ? options.bootstrapScriptContent : undefined,
58 options ? options.bootstrapScripts : undefined,
59 options ? options.bootstrapModules : undefined,
60 );
61 const request = createRequest(
62 children,
63 resumableState,
64 createRenderState(
65 resumableState,
66 undefined,
67 options ? options.unstable_externalRuntimeSrc : undefined,
68 ),
69 createRootFormatContext(undefined),
70 options ? options.progressiveChunkSize : undefined,
71 options.onError,
72 options.onBrowserBailout,
73 undefined,
74 undefined,
75 );
76 startWork(request);
77 if (destination.fatal) {
78 throw destination.error;
79 }
80 return {
81 destination,
82 request,
83 };
84 }
85
86 function abortStream(stream: Stream, reason: mixed): void {
87 abort(stream.request, reason);
88 }
89
90 function renderNextChunk(stream: Stream): string {
91 const {request, destination} = stream;
92 performWork(request);
93 startFlowing(request, destination);
94 if (destination.fatal) {
95 throw destination.error;
96 }
97 const chunk = destination.buffer;
98 destination.buffer = '';
99 return chunk;
100 }
101
102 function hasFinished(stream: Stream): boolean {
103 return stream.destination.done;
104 }
105
106 function debug(stream: Stream): any {
107 // convert to any to silence flow errors from opaque type
108 const request = stream.request as any;
109 return {
110 pendingRootTasks: request.pendingRootTasks,
111 clientRenderedBoundaries: request.clientRenderedBoundaries.length,
112 completedBoundaries: request.completedBoundaries.length,
113 partialBoundaries: request.partialBoundaries.length,
114 allPendingTasks: request.allPendingTasks,
115 pingedTasks: request.pingedTasks.length,
116 };
117 }
118
119 export {renderToStream, renderNextChunk, hasFinished, abortStream, debug};