Last active
September 25, 2022 16:55
-
-
Save braska/9e04975fd0c2c1ec09f904035b51c6d9 to your computer and use it in GitHub Desktop.
Express React SSR
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
doctype html | |
<html !{helmet.htmlAttributes.toString()}> | |
head. | |
!{helmet.title.toString()} | |
!{helmet.meta.toString()} | |
!{helmet.link.toString()} | |
<body !{helmet.bodyAttributes.toString()}> | |
#root. | |
!{content} | |
script(src=assets['manifest.js']) | |
script(src=assets['polyfill.js']) | |
script(src=assets['vendor.js']) | |
script(src=assets['main.js']) | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const NODE_ENV = process.env.NODE_ENV || 'development'; | |
const express = require('express'); | |
const React = require('react'); | |
const Helmet = require('react-helmet').Helmet; | |
const renderToString = require('react-dom/server').renderToString; | |
const ServerStyleSheet = require('styled-components').ServerStyleSheet; | |
const path = require('path'); | |
const requireFromString = require('require-from-string'); | |
let ReactApp; | |
let manifest; | |
const app = express(); | |
app.set('view engine', 'pug'); | |
app.set('views', path.join(__dirname, 'views')); | |
/* eslint-disable import/no-extraneous-dependencies, global-require, import/no-unresolved */ | |
if (NODE_ENV === 'development') { | |
const webpack = require('webpack'); | |
const webpackConfig = require('./webpack.config'); | |
const compiler = webpack(webpackConfig); | |
app.use(require('webpack-dev-middleware')(compiler, { | |
noInfo: true, publicPath: '/', | |
})); | |
app.use((req, res, next) => { | |
const content = compiler.compilers[1].outputFileSystem.readFileSync(path.join(__dirname, 'dist', 'server', 'app.js'), 'utf8'); | |
ReactApp = requireFromString(content); | |
manifest = JSON.parse(compiler.compilers[0].outputFileSystem.readFileSync(path.join(__dirname, 'dist', 'client', 'manifest.json'), 'utf8')); | |
next(); | |
}); | |
app.use(require('webpack-hot-middleware')(compiler)); | |
} else { | |
ReactApp = require('./dist/server/app'); | |
manifest = require('./dist/client/manifest.json'); | |
app.use(express.static(path.join(__dirname, 'dist', 'client'))); | |
} | |
/* eslint-enable import/no-extraneous-dependencies, global-require */ | |
app.get('/', (req, res) => { | |
const sheet = new ServerStyleSheet(); | |
const appString = renderToString(sheet.collectStyles(React.createElement(ReactApp.default))); | |
const css = sheet.getStyleTags(); | |
const helmet = Helmet.renderStatic(); | |
res.render('index', { | |
assets: manifest, | |
styles: css, | |
helmet, | |
content: appString, | |
}); | |
}); | |
app.listen(3000); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const NODE_ENV = process.env.NODE_ENV || 'development'; | |
const webpack = require('webpack'); | |
const CopyWebpackPlugin = require('copy-webpack-plugin'); | |
const CleanWebpackPlugin = require('clean-webpack-plugin'); | |
const ManifestPlugin = require('webpack-manifest-plugin'); | |
const nodeExternals = require('webpack-node-externals'); | |
const path = require('path'); | |
const addHash = (template, hash) => (NODE_ENV === 'production' ? template.replace(/\.[^.]+(\.map)?$/, `.[${hash}]$&`) : template); | |
const clientConfig = { | |
name: 'client', | |
context: path.resolve(__dirname, 'src'), | |
entry: { | |
polyfill: ['babel-polyfill'], | |
main: (files => (NODE_ENV !== 'production' ? [ | |
'react-hot-loader/patch', | |
// activate HMR for React | |
'webpack/hot/only-dev-server', | |
// bundle the client for hot reloading | |
// only- means to only hot reload for successful updates | |
] : []).concat(files))(['./js/app']), | |
vendor: ['./js/vendor'], | |
}, | |
output: { | |
path: path.resolve(__dirname, 'dist', 'client'), | |
publicPath: '/', | |
filename: addHash('assets/js/[name].js', 'chunkhash'), | |
sourceMapFilename: addHash('assets/js/[name].js.map', 'chunkhash'), | |
}, | |
devtool: 'source-map', | |
resolve: { | |
extensions: ['.js', '.jsx', '.json'], | |
modules: ['shared', 'node_modules'], | |
}, | |
module: { | |
rules: [ | |
{ | |
enforce: 'pre', | |
test: /\.jsx?$/, | |
use: 'eslint-loader', | |
exclude: /node_modules/, | |
}, | |
{ | |
test: /\.jsx?$/, | |
use: 'babel-loader', | |
exclude: /node_modules/, | |
}, | |
{ | |
test: /\.css$/, | |
use: ['css-loader'], | |
}, | |
{ | |
test: /\.(jpe?g|png|gif)$/, | |
use: 'file-loader', | |
}, | |
], | |
}, | |
plugins: [ | |
new CleanWebpackPlugin(path.join('dist', 'client')), | |
new webpack.optimize.CommonsChunkPlugin({ | |
name: ['vendor', 'manifest'], | |
minChunks: Infinity, | |
}), | |
new webpack.NoEmitOnErrorsPlugin(), | |
new webpack.NamedModulesPlugin(), | |
// prints more readable module names in the browser console on HMR updates | |
new webpack.DefinePlugin({ | |
__DEV__: JSON.stringify(NODE_ENV !== 'production'), | |
}), | |
new CopyWebpackPlugin([ | |
{ | |
from: 'static', | |
}, | |
]), | |
new ManifestPlugin({ | |
publicPath: '/', | |
}), | |
], | |
}; | |
if (NODE_ENV !== 'production') { | |
clientConfig.entry.main.splice(1, 0, 'webpack-hot-middleware/client?path=/__webpack_hmr&name=client'); | |
clientConfig.plugins.push(new webpack.HotModuleReplacementPlugin()); | |
} | |
const clientSSR = { | |
name: 'client SSR', | |
target: 'node', | |
context: path.resolve(__dirname, 'src'), | |
entry: ['./js/app/app'], | |
output: { | |
path: path.resolve(__dirname, 'dist', 'server'), | |
publicPath: '/', | |
filename: 'app.js', | |
libraryTarget: 'commonjs2', | |
}, | |
plugins: [ | |
new CleanWebpackPlugin(path.join('dist', 'server')), | |
new webpack.NoEmitOnErrorsPlugin(), | |
new webpack.NamedModulesPlugin(), | |
], | |
resolve: { | |
extensions: ['.js', '.jsx', '.json'], | |
modules: ['shared', 'node_modules'], | |
}, | |
externals: [nodeExternals()], | |
module: { | |
rules: [ | |
{ | |
test: /\.jsx?$/, | |
use: 'babel-loader', | |
exclude: /node_modules/, | |
}, | |
{ | |
test: /\.(jpe?g|png|gif)$/, | |
use: 'file-loader?emitFile=false', | |
}, | |
], | |
}, | |
}; | |
module.exports = [clientConfig, clientSSR]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment