main
js 83 lines 2.28 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 */
8
9 import {Writable} from 'stream';
10 import * as React from 'react';
11 import {renderToPipeableStream} from 'react-dom/server';
12 import App from '../src/App';
13 import {ABORT_DELAY} from './delays';
14
15 // In a real setup, you'd read it from webpack build stats.
16 let assets = {
17 'main.js': '/main.js',
18 'main.css': '/main.css',
19 };
20
21 function HtmlWritable(options) {
22 Writable.call(this, options);
23 this.chunks = [];
24 this.html = '';
25 }
26
27 HtmlWritable.prototype = Object.create(Writable.prototype);
28 HtmlWritable.prototype.getHtml = function getHtml() {
29 return this.html;
30 };
31 HtmlWritable.prototype._write = function _write(chunk, encoding, callback) {
32 this.chunks.push(chunk);
33 callback();
34 };
35 HtmlWritable.prototype._final = function _final(callback) {
36 this.html = Buffer.concat(this.chunks).toString();
37 callback();
38 };
39
40 module.exports = function render(url, res) {
41 let writable = new HtmlWritable();
42 res.socket.on('error', error => {
43 console.error('Fatal', error);
44 });
45 let didError = false;
46 let didFinish = false;
47
48 writable.on('finish', () => {
49 // If something errored before we started streaming, we set the error code appropriately.
50 res.statusCode = didError ? 500 : 200;
51 res.setHeader('Content-type', 'text/html');
52 res.send(writable.getHtml());
53 });
54
55 const {pipe, abort} = renderToPipeableStream(<App assets={assets} />, {
56 bootstrapScripts: [assets['main.js']],
57 onAllReady() {
58 // Full completion.
59 // You can use this for SSG or crawlers.
60 didFinish = true;
61 },
62 onShellReady() {
63 // If something errored before we started streaming, we set the error code appropriately.
64 pipe(writable);
65 },
66 onShellError(x) {
67 // Something errored before we could complete the shell so we emit an alternative shell.
68 res.statusCode = 500;
69 res.send('<!doctype><p>Error</p>');
70 },
71 onError(x) {
72 didError = true;
73 console.error(x);
74 },
75 });
76 // Abandon and switch to client rendering if enough time passes.
77 // Try lowering this to see the client recover.
78 setTimeout(() => {
79 if (!didFinish) {
80 abort();
81 }
82 }, ABORT_DELAY);
83 };