diff --git a/fixtures/ssr/README.md b/fixtures/ssr/README.md new file mode 100644 index 0000000000..b3195de543 --- /dev/null +++ b/fixtures/ssr/README.md @@ -0,0 +1,24 @@ +# SSR Fixtures + +A set of test cases for quickly identifying issues with server-side rendering. + +## Setup + +To reference a local build of React, first run `npm run build` at the root +of the React project. Then: + +``` +cd fixtures/ssr +npm install +npm start +``` + +The `start` command runs a webpack dev server and a server-side rendering server in development mode with hot reloading. + +If you want to try the production mode instead run: + +``` +npm run start:prod +``` + +This will pre-build all static resources and then start a server-side rendering HTTP server that hosts the React app and service the static resources (without hot reloading). diff --git a/fixtures/ssr/package.json b/fixtures/ssr/package.json new file mode 100644 index 0000000000..126cba70ca --- /dev/null +++ b/fixtures/ssr/package.json @@ -0,0 +1,25 @@ +{ + "name": "react-fixtures-ssr", + "version": "0.1.0", + "private": true, + "devDependencies": { + "concurrently": "3.1.0", + "http-proxy-middleware": "0.17.3", + "react-scripts": "0.9.5" + }, + "dependencies": { + "express": "^4.14.0", + "ignore-styles": "^5.0.1", + "import-export": "^1.0.1", + "node-fetch": "^1.6.3" + }, + "scripts": { + "start": "concurrently \"npm run start:server\" \"npm run start:client\"", + "start:client": "NODE_PATH=../../build/packages PORT=3001 react-scripts start", + "start:server": "NODE_PATH=../../build/packages NODE_ENV=development node server", + "start:prod": "NODE_PATH=../../build/packages react-scripts build && NODE_PATH=../../build/packages NODE_ENV=production node server", + "build": "NODE_PATH=../../build/packages react-scripts build", + "test": "NODE_PATH=../../build/packages react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +} diff --git a/fixtures/ssr/public/favicon.ico b/fixtures/ssr/public/favicon.ico new file mode 100644 index 0000000000..5c125de5d8 Binary files /dev/null and b/fixtures/ssr/public/favicon.ico differ diff --git a/fixtures/ssr/public/index.html b/fixtures/ssr/public/index.html new file mode 100644 index 0000000000..a94d9ac64a --- /dev/null +++ b/fixtures/ssr/public/index.html @@ -0,0 +1,13 @@ + + + + + + diff --git a/fixtures/ssr/server/index.js b/fixtures/ssr/server/index.js new file mode 100644 index 0000000000..80ab7df3ad --- /dev/null +++ b/fixtures/ssr/server/index.js @@ -0,0 +1,69 @@ +require('ignore-styles'); +const babelRegister = require('babel-register'); +const proxy = require('http-proxy-middleware'); + +babelRegister({ + ignore: /\/(build|node_modules)\//, + presets: ['react-app'], +}); + +const express = require('express'); +const path = require('path'); + +const app = express(); + +// Application +if (process.env.NODE_ENV === 'development') { + app.get('/', function(req, res) { + // In development mode we clear the module cache between each request to + // get automatic hot reloading. + for (var key in require.cache) { + delete require.cache[key]; + } + const render = require('./render').default; + res.send(render(req.url)); + }); +} else { + const render = require('./render').default; + app.get('/', function(req, res) { + res.send(render(req.url)); + }); +} + +// Static resources +app.use(express.static(path.resolve(__dirname, '..', 'build'))); + +// Proxy everything else to create-react-app's webpack development server +if (process.env.NODE_ENV === 'development') { + app.use('/', proxy({ + ws: true, + target: 'http://localhost:3001' + })); +} + +app.listen(3000, () => { + console.log('Listening on port 3000...'); +}); + +app.on('error', function(error) { + if (error.syscall !== 'listen') { + throw error; + } + + var bind = typeof port === 'string' + ? '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; + } +}); diff --git a/fixtures/ssr/server/render.js b/fixtures/ssr/server/render.js new file mode 100644 index 0000000000..d002db830a --- /dev/null +++ b/fixtures/ssr/server/render.js @@ -0,0 +1,21 @@ +import React from 'react'; +import {renderToString} from 'react-dom/server'; + +import App from '../src/components/App'; +import assetManifest from '../build/asset-manifest.json'; + +let assets = assetManifest; +if (process.env.NODE_ENV === 'development') { + // Use the bundle from create-react-app's server in development mode. + assets = { + 'main.js': '/static/js/bundle.js', + 'main.css': '', + }; +} + +export default function render() { + var html = renderToString(); + // There's no way to render a doctype in React so prepend manually. + // Also append a bootstrap script tag. + return '' + html; +}; diff --git a/fixtures/ssr/src/components/App.js b/fixtures/ssr/src/components/App.js new file mode 100644 index 0000000000..9b4550d2c8 --- /dev/null +++ b/fixtures/ssr/src/components/App.js @@ -0,0 +1,17 @@ +import React, { Component } from 'react'; + +import Chrome from './Chrome'; +import Page from './Page'; + +export default class App extends Component { + render() { + return ( + +
+

Hello World

+ +
+
+ ); + } +} diff --git a/fixtures/ssr/src/components/Chrome.css b/fixtures/ssr/src/components/Chrome.css new file mode 100644 index 0000000000..b019b57b1d --- /dev/null +++ b/fixtures/ssr/src/components/Chrome.css @@ -0,0 +1,5 @@ +body { + margin: 10px; + padding: 0; + font-family: sans-serif; +} diff --git a/fixtures/ssr/src/components/Chrome.js b/fixtures/ssr/src/components/Chrome.js new file mode 100644 index 0000000000..91b6c782c4 --- /dev/null +++ b/fixtures/ssr/src/components/Chrome.js @@ -0,0 +1,27 @@ +import React, { Component } from 'react'; + +import './Chrome.css'; + +export default class Chrome extends Component { + render() { + const assets = this.props.assets; + return ( + + + + + + + {this.props.title} + + + {this.props.children} +