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.  */
    
  8. 
    
  9. import * as React from 'react';
    
  10. // import {renderToString} from 'react-dom/server';
    
  11. import {renderToPipeableStream} from 'react-dom/server';
    
  12. import App from '../src/App';
    
  13. import {DataProvider} from '../src/data';
    
  14. import {API_DELAY, ABORT_DELAY} from './delays';
    
  15. 
    
  16. // In a real setup, you'd read it from webpack build stats.
    
  17. let assets = {
    
  18.   'main.js': '/main.js',
    
  19.   'main.css': '/main.css',
    
  20. };
    
  21. 
    
  22. module.exports = function render(url, res) {
    
  23.   const data = createServerData();
    
  24.   // This is how you would wire it up previously:
    
  25.   //
    
  26.   // res.send(
    
  27.   //   '<!DOCTYPE html>' +
    
  28.   //   renderToString(
    
  29.   //     <DataProvider data={data}>
    
  30.   //       <App assets={assets} />
    
  31.   //     </DataProvider>,
    
  32.   //   )
    
  33.   // );
    
  34. 
    
  35.   // The new wiring is a bit more involved.
    
  36.   res.socket.on('error', error => {
    
  37.     console.error('Fatal', error);
    
  38.   });
    
  39.   let didError = false;
    
  40.   const {pipe, abort} = renderToPipeableStream(
    
  41.     <DataProvider data={data}>
    
  42.       <App assets={assets} />
    
  43.     </DataProvider>,
    
  44.     {
    
  45.       bootstrapScripts: [assets['main.js']],
    
  46.       onAllReady() {
    
  47.         // Full completion.
    
  48.         // You can use this for SSG or crawlers.
    
  49.       },
    
  50.       onShellReady() {
    
  51.         // If something errored before we started streaming, we set the error code appropriately.
    
  52.         res.statusCode = didError ? 500 : 200;
    
  53.         res.setHeader('Content-type', 'text/html');
    
  54.         pipe(res);
    
  55.       },
    
  56.       onShellError(x) {
    
  57.         // Something errored before we could complete the shell so we emit an alternative shell.
    
  58.         res.statusCode = 500;
    
  59.         res.send('<!doctype><p>Error</p>');
    
  60.       },
    
  61.       onError(x) {
    
  62.         didError = true;
    
  63.         console.error(x);
    
  64.       },
    
  65.     }
    
  66.   );
    
  67.   // Abandon and switch to client rendering if enough time passes.
    
  68.   // Try lowering this to see the client recover.
    
  69.   setTimeout(abort, ABORT_DELAY);
    
  70. };
    
  71. 
    
  72. // Simulate a delay caused by data fetching.
    
  73. // We fake this because the streaming HTML renderer
    
  74. // is not yet integrated with real data fetching strategies.
    
  75. function createServerData() {
    
  76.   let done = false;
    
  77.   let promise = null;
    
  78.   return {
    
  79.     read() {
    
  80.       if (done) {
    
  81.         return;
    
  82.       }
    
  83.       if (promise) {
    
  84.         throw promise;
    
  85.       }
    
  86.       promise = new Promise(resolve => {
    
  87.         setTimeout(() => {
    
  88.           done = true;
    
  89.           promise = null;
    
  90.           resolve();
    
  91.         }, API_DELAY);
    
  92.       });
    
  93.       throw promise;
    
  94.     },
    
  95.   };
    
  96. }