main
js 104 lines 2.49 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 ReactVersion from 'shared/ReactVersion';
11
12 import type {ReactNodeList} from 'shared/ReactTypes';
13
14 import {
15 createRequest,
16 startWork,
17 startFlowing,
18 abort,
19 } from 'react-server/src/ReactFizzServer';
20
21 import {
22 createResumableState,
23 createRenderState,
24 createRootFormatContext,
25 } from 'react-dom-bindings/src/server/ReactFizzConfigDOMLegacy';
26
27 type ServerOptions = {
28 identifierPrefix?: string,
29 };
30
31 function onError() {
32 // Non-fatal errors are ignored.
33 }
34
35 function renderToStringImpl(
36 children: ReactNodeList,
37 options: void | ServerOptions,
38 generateStaticMarkup: boolean,
39 abortReason: string,
40 ): string {
41 let didFatal = false;
42 let fatalError = null;
43 let result = '';
44 const destination = {
45 // $FlowFixMe[missing-local-annot]
46 push(chunk) {
47 if (chunk !== null) {
48 result += chunk;
49 }
50 return true;
51 },
52 // $FlowFixMe[missing-local-annot]
53 destroy(error) {
54 didFatal = true;
55 fatalError = error;
56 },
57 };
58
59 let readyToStream = false;
60 function onShellReady() {
61 readyToStream = true;
62 }
63 const resumableState = createResumableState(
64 options ? options.identifierPrefix : undefined,
65 undefined,
66 );
67 const request = createRequest(
68 children,
69 resumableState,
70 createRenderState(resumableState, generateStaticMarkup),
71 createRootFormatContext(),
72 Infinity,
73 onError,
74 undefined,
75 undefined,
76 onShellReady,
77 undefined,
78 undefined,
79 undefined,
80 );
81 startWork(request);
82 // If anything suspended and is still pending, we'll abort it before writing.
83 // That way we write only client-rendered boundaries from the start.
84 abort(request, abortReason);
85 startFlowing(request, destination);
86 if (didFatal && fatalError !== abortReason) {
87 throw fatalError;
88 }
89
90 if (!readyToStream) {
91 // Note: This error message is the one we use on the client. It doesn't
92 // really make sense here. But this is the legacy server renderer, anyway.
93 // We're going to delete it soon.
94 throw new Error(
95 'A component suspended while responding to synchronous input. This ' +
96 'will cause the UI to be replaced with a loading indicator. To fix, ' +
97 'updates that suspend should be wrapped with startTransition.',
98 );
99 }
100
101 return result;
102 }
103
104 export {renderToStringImpl, ReactVersion as version};