Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
[Fizz] Add another fixture
  • Loading branch information
gaearon committed Jun 4, 2021
commit 426af886e2074bcc536fedfa223233f3000e872c
12,684 changes: 12,684 additions & 0 deletions fixtures/ssr2/package-lock.json

Large diffs are not rendered by default.

53 changes: 53 additions & 0 deletions fixtures/ssr2/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "react-ssr",
"version": "0.1.0",
"private": true,
"engines": {
"node": ">=14.9.0"
},
"license": "MIT",
"dependencies": {
"@babel/core": "7.14.3",
"@babel/register": "7.13.16",
"babel-loader": "8.1.0",
"babel-preset-react-app": "10.0.0",
"compression": "^1.7.4",
"concurrently": "^5.3.0",
"express": "^4.17.1",
"nodemon": "^2.0.6",
"react": "0.0.0-cc4d24ab0",
"react-dom": "0.0.0-cc4d24ab0",
"react-error-boundary": "^3.1.3",
"resolve": "1.12.0",
"rimraf": "^3.0.2",
"webpack": "4.44.2",
"webpack-cli": "^4.2.0"
},
"devDependencies": {
"cross-env": "^7.0.3",
"prettier": "1.19.1"
},
"scripts": {
"start": "concurrently \"npm run server:dev\" \"npm run bundler:dev\"",
"start:prod": "concurrently \"npm run server:prod\" \"npm run bundler:prod\"",
"server:dev": "cross-env NODE_ENV=development nodemon -- server/server.js",
"server:prod": "cross-env NODE_ENV=production nodemon -- server/server.js",
"bundler:dev": "cross-env NODE_ENV=development nodemon -- scripts/build.js",
"bundler:prod": "cross-env NODE_ENV=production nodemon -- scripts/build.js"
},
"babel": {
"presets": [
[
"react-app",
{
"runtime": "automatic"
}
]
]
},
"nodemonConfig": {
"ignore": [
"build/*"
]
}
}
70 changes: 70 additions & 0 deletions fixtures/ssr2/public/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
body {
font-family: system-ui, sans-serif;
}

nav {
padding: 20px;
}

.sidebar {
padding: 10px;
height: 500px;
float: left;
width: 30%;
}

.post {
padding: 20px;
float: left;
width: 60%;
}

h1, h2 {
padding: 0;
}

ul, li {
margin: 0;
}

.post p {
font-size: larger;
font-family: Georgia, serif;
}

.comments {
margin-top: 40px;
}

.comment {
border: 2px solid #aaa;
border-radius: 4px;
padding: 20px;
}

/* https://codepen.io/mandelid/pen/vwKoe */
.spinner {
display: inline-block;
transition: opacity linear 0.1s;
width: 20px;
height: 20px;
border: 3px solid rgba(80, 80, 80, 0.5);
border-radius: 50%;
border-top-color: #fff;
animation: spin 1s ease-in-out infinite;
opacity: 0;
}
.spinner--active {
opacity: 1;
}

@keyframes spin {
to {
transform: rotate(360deg);
}
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
54 changes: 54 additions & 0 deletions fixtures/ssr2/scripts/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

'use strict';

const path = require('path');
const rimraf = require('rimraf');
const webpack = require('webpack');

const isProduction = process.env.NODE_ENV === 'production';
rimraf.sync(path.resolve(__dirname, '../build'));
webpack(
{
mode: isProduction ? 'production' : 'development',
devtool: isProduction ? 'source-map' : 'cheap-module-source-map',
entry: [path.resolve(__dirname, '../src/index.js')],
output: {
path: path.resolve(__dirname, '../build'),
filename: 'main.js',
},
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/,
},
],
},
},
(err, stats) => {
if (err) {
console.error(err.stack || err);
if (err.details) {
console.error(err.details);
}
process.exit(1);
return;
}
const info = stats.toJson();
if (stats.hasErrors()) {
console.log('Finished running webpack with errors.');
info.errors.forEach(e => console.error(e));
process.exit(1);
} else {
console.log('Finished running webpack.');
}
}
);
18 changes: 18 additions & 0 deletions fixtures/ssr2/server/delays.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

// Tweak these to play with different kinds of latency.

// How long the data fetches on the server.
exports.API_DELAY = 2000;

// How long the server waits for data before giving up.
exports.ABORT_DELAY = 10000;

// How long serving the JS bundles is delayed.
exports.JS_BUNDLE_DELAY = 5000;
88 changes: 88 additions & 0 deletions fixtures/ssr2/server/render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import * as React from 'react';
// import {renderToString} from 'react-dom/server';
import {pipeToNodeWritable} from 'react-dom/unstable-fizz';
import App from '../src/App';
import {DataProvider} from '../src/data';
import {API_DELAY, ABORT_DELAY} from './delays';

// In a real setup, you'd read it from webpack build stats.
let assets = {
'main.js': '/main.js',
'main.css': '/main.css',
};

module.exports = function render(url, res) {
// This is how you would wire it up previously:
//
// res.send(
// '<!DOCTYPE html>' +
// renderToString(
// <DataProvider data={data}>
// <App assets={assets} />
// </DataProvider>,
// )
// );

// The new wiring is a bit more involved.
res.socket.on('error', error => {
console.error('Fatal', error);
});
let didError = false;
const data = createServerData();
const {startWriting, abort} = pipeToNodeWritable(
<DataProvider data={data}>
<App assets={assets} />
</DataProvider>,
res,
{
onReadyToStream() {
// If something errored before we started streaming, we set the error code appropriately.
res.statusCode = didError ? 500 : 200;
res.setHeader('Content-type', 'text/html');
res.write('<!DOCTYPE html>');
startWriting();
},
onError(x) {
didError = true;
console.error(x);
},
}
);
// Abandon and switch to client rendering if enough time passes.
// Try lowering this to see the client recover.
setTimeout(abort, ABORT_DELAY);
};

// Simulate a delay caused by data fetching.
// We fake this because the streaming HTML renderer
// is not yet integrated with real data fetching strategies.
function createServerData() {
let done = false;
let promise = null;
return {
read() {
if (done) {
return;
}
if (promise) {
throw promise;
}
promise = new Promise(resolve => {
setTimeout(() => {
done = true;
promise = null;
resolve();
}, API_DELAY);
});
throw promise;
},
};
}
96 changes: 96 additions & 0 deletions fixtures/ssr2/server/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

'use strict';

const babelRegister = require('@babel/register');
babelRegister({
ignore: [/[\\\/](build|server\/server|node_modules)[\\\/]/],
presets: [['react-app', {runtime: 'automatic'}]],
plugins: ['@babel/transform-modules-commonjs'],
});

const express = require('express');
const compress = require('compression');
const {readFileSync} = require('fs');
const path = require('path');
const React = require('react');
const render = require('./render');
const {JS_BUNDLE_DELAY} = require('./delays');

const PORT = process.env.PORT || 4000;
const app = express();

app.use((req, res, next) => {
if (req.url.endsWith('.js')) {
// Artificially delay serving JS
// to demonstrate streaming HTML.
setTimeout(next, JS_BUNDLE_DELAY);
} else {
next();
}
});

app.use(compress());
app.get(
'/',
handleErrors(async function(req, res) {
await waitForWebpack();
render(req.url, res);
})
);
app.use(express.static('build'));
app.use(express.static('public'));

app
.listen(PORT, () => {
console.log(`Listening at ${PORT}...`);
})
.on('error', function(error) {
if (error.syscall !== 'listen') {
throw error;
}
const isPipe = portOrPipe => Number.isNaN(portOrPipe);
const bind = isPipe(PORT) ? 'Pipe ' + PORT : 'Port ' + PORT;
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
});

function handleErrors(fn) {
return async function(req, res, next) {
try {
return await fn(req, res);
} catch (x) {
next(x);
}
};
}

async function waitForWebpack() {
while (true) {
try {
readFileSync(path.resolve(__dirname, '../build/main.js'));
return;
} catch (err) {
console.log(
'Could not find webpack build output. Will retry in a second...'
);
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
Loading