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.     onShellReady,
    
  76.     undefined,
    
  77.     undefined,
    
  78.     undefined,
    
  79.   );
    
  80.   startWork(request);
    
  81.   // If anything suspended and is still pending, we'll abort it before writing.
    
  82.   // That way we write only client-rendered boundaries from the start.
    
  83.   abort(request, abortReason);
    
  84.   startFlowing(request, destination);
    
  85.   if (didFatal && fatalError !== abortReason) {
    
  86.     throw fatalError;
    
  87.   }
    
  88. 
    
  89.   if (!readyToStream) {
    
  90.     // Note: This error message is the one we use on the client. It doesn't
    
  91.     // really make sense here. But this is the legacy server renderer, anyway.
    
  92.     // We're going to delete it soon.
    
  93.     throw new Error(
    
  94.       'A component suspended while responding to synchronous input. This ' +
    
  95.         'will cause the UI to be replaced with a loading indicator. To fix, ' +
    
  96.         'updates that suspend should be wrapped with startTransition.',
    
  97.     );
    
  98.   }
    
  99. 
    
  100.   return result;
    
  101. }
    
  102. 
    
  103. export {renderToStringImpl, ReactVersion as version};