'use strict';
// This is a server to host CDN distributed resources like module source files and SSR
const path = require('path');
const url = require('url');
const fs = require('fs').promises;
const compress = require('compression');
const chalk = require('chalk');
const express = require('express');
const http = require('http');
const React = require('react');
const {renderToPipeableStream} = require('react-dom/server');
const {createFromNodeStream} = require('react-server-dom-esm/client');
const moduleBasePath = new URL('../src', url.pathToFileURL(__filename)).href;
const app = express();
app.use(compress());
function request(options, body) {
return new Promise((resolve, reject) => {
const req = http.request(options, res => {
resolve(res);
});
req.on('error', e => {
reject(e);
});
body.pipe(req);
});
}
app.all('/', async function (req, res, next) {
// Proxy the request to the regional server.
const proxiedHeaders = {
'X-Forwarded-Host': req.hostname,
'X-Forwarded-For': req.ips,
'X-Forwarded-Port': 3000,
'X-Forwarded-Proto': req.protocol,
};
// Proxy other headers as desired.
if (req.get('rsc-action')) {
proxiedHeaders['Content-type'] = req.get('Content-type');
proxiedHeaders['rsc-action'] = req.get('rsc-action');
} else if (req.get('Content-type')) {
proxiedHeaders['Content-type'] = req.get('Content-type');
}
const promiseForData = request(
{
host: '127.0.0.1',
port: 3001,
method: req.method,
path: '/',
headers: proxiedHeaders,
},
req
);
if (req.accepts('text/html')) {
try {
const rscResponse = await promiseForData;
const moduleBaseURL = '/src';
// For HTML, we're a "client" emulator that runs the client code,
// so we start by consuming the RSC payload. This needs the local file path
// to load the source files from as well as the URL path for preloads.
let root;
let Root = () => {
if (root) {
return React.use(root);
}
return React.use(
(root = createFromNodeStream(
rscResponse,
moduleBasePath,
moduleBaseURL
))
);
};
// Render it into HTML by resolving the client components
res.set('Content-type', 'text/html');
const {pipe} = renderToPipeableStream(React.createElement(Root), {
importMap: {
imports: {
react: 'https://esm.sh/react@experimental?pin=v124&dev',
'react-dom': 'https://esm.sh/react-dom@experimental?pin=v124&dev',
'react-dom/': 'https://esm.sh/react-dom@experimental&pin=v124&dev/',
'react-server-dom-esm/client':
'/node_modules/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js',
},
},
bootstrapModules: ['/src/index.js'],
});
pipe(res);
} catch (e) {
console.error(`Failed to SSR: ${e.stack}`);
res.statusCode = 500;
res.end();
}
} else {
try {
const rscResponse = await promiseForData;
// For other request, we pass-through the RSC payload.
res.set('Content-type', 'text/x-component');
rscResponse.on('data', data => {
res.write(data);
res.flush();
});
rscResponse.on('end', data => {
res.end();
});
} catch (e) {
console.error(`Failed to proxy request: ${e.stack}`);
res.statusCode = 500;
res.end();
}
}
});
app.use(express.static('public'));
app.use('/src', express.static('src'));
app.use(
'/node_modules/react-server-dom-esm/esm',
express.static('node_modules/react-server-dom-esm/esm')
);
app.listen(3000, () => {
console.log('Global Fizz/Webpack Server listening on port 3000...');
});
app.on('error', function (error) {
if (error.syscall !== 'listen') {
throw error;
}
switch (error.code) {
case 'EACCES':
console.error('port 3000 requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error('Port 3000 is already in use');
process.exit(1);
break;
default:
throw error;
}
});