Skip to content

Instantly share code, notes, and snippets.

@jonjaques
Last active February 26, 2018 05:28
Show Gist options
  • Save jonjaques/0b118d527b7527f7973f1ea108ae42a4 to your computer and use it in GitHub Desktop.
Save jonjaques/0b118d527b7527f7973f1ea108ae42a4 to your computer and use it in GitHub Desktop.
Reload your app on the server too; `web.js` is the entrypoint on the browser, `renderer.js` is a verrry simple middleware for server rendering. Ideally you wouldn't be passing content in the entry points, only config.
import React from 'react'
import Foo from './foo'
import {hot} from 'react-hot-loader'
export default hot(module)(App)
function App(props) {
return <h1>
{props.message}
<Foo foo={100} />
</h1>
}
import Path from 'path'
import * as Pirates from 'pirates'
import Webpack from 'webpack'
import WebpackDevMiddleware from 'webpack-dev-middleware'
import WebpackHotMiddleware from 'webpack-hot-middleware'
export default function DevMiddleware() {
const env = process.env.NODE_ENV || 'development'
const [web, server] = require('../../webpackfile.babel')
const webCompiler = Webpack(web(env))
const serverCompiler = Webpack(server(env))
const devMiddleware = WebpackDevMiddleware(webCompiler, {
logLevel: 'warn',
publicPath: web(env).output.publicPath
})
const hotMiddleware = WebpackHotMiddleware(webCompiler)
const serverMiddleware = WebpackServerMiddleware(serverCompiler, 'dist/node.js')
return [
devMiddleware,
hotMiddleware,
serverMiddleware
]
}
// Only cares about one module
function WebpackServerMiddleware(compiler, path) {
const appPath = Path.resolve(path)
// Build an instance of the dev middleware so we can
// plunder it's memory filesystem
const dev = WebpackDevMiddleware(compiler, {logLevel: 'warn'})
const booty = dev.context.compiler.outputFileSystem
// Hook into require so we can use the memoryFs provided by webpack
Pirates.addHook(hook, { exts: ['.js', '.jsx'], matcher })
return middleware
function hook (code, filename) {
return booty.readFileSync(filename).toString()
}
function matcher (filename) {
return booty.existsSync(filename)
}
function middleware (req, res, next) {
// We actually use the dev middleware here not because we need it, but because it
// has code to hold up the request until the bundle is finished compiling
dev(req, res, (err) => {
try {
// Blow away cached module
delete require.cache[appPath]
// Attach the module to the response for use
// further down the middleware chain
res.locals.app = require(appPath)
} catch(err) {
console.log(`Error compiling app: \n` + err.stack)
} finally {
next(err)
}
})
}
}
import React from 'react'
import ReactDOM from 'react-dom/server'
export default function Renderer() {
return (req, res, next) => {
if (!res.locals.app) throw new Error("Couldn't locate app")
const App = res.locals.app.default
const tree = <App message="Hello, friend" />
res.type('html').send(`
<!doctype html>
<div id="application">${ReactDOM.renderToString(tree)}</div>
<script src="/assets/web.js"></script>
`.trim())
}
}
import Path from 'path'
import Express from 'express'
import compression from 'compression'
import {json} from 'body-parser'
import devMiddleware from './middleware/dev'
import renderer from './middleware/renderer'
import errorHandler from './middleware/errors'
const app = Express()
export default app
app.use(compression())
app.use(json())
if (__DEVELOPMENT__) {
app.use(devMiddleware())
}
app.use('/assets',
Express.static(Path.resolve('dist')),
Express.static(Path.resolve('assets'))
)
app.use(renderer())
app.use(errorHandler())
import React from 'react'
import ReactDOM from 'react-dom'
import Foo from './app'
const pageNode = document.getElementById('application')
ReactDOM.hydrate(<Foo message="Hello, friend" />, pageNode)
import Path from 'path'
import Webpack from 'webpack'
import NodeExternals from 'webpack-node-externals'
module.exports = [
config('web'),
config('node'),
]
export function config(target = 'web') {
return env => {
const c = {
target,
context: Path.resolve('app'),
entry: {
[target]: target === 'web' ? ['./web.js'] : ['./index.js'],
},
output: {
filename: '[name].js',
path: Path.resolve('dist')
},
module: {
rules: [
{ test: /\.jsx?$/,
loader: 'babel-loader',
include: [
Path.resolve('app'),
Path.resolve('lib')
]
}
]
},
externals: [],
plugins: [],
}
if (target === 'web') {
c.entry[target].unshift('babel-polyfill')
c.output.publicPath = '/assets'
if (env === 'production') {
c.plugins.push(new Webpack.optimize.UglifyJsPlugin())
}
else {
c.entry[target].unshift('webpack-hot-middleware/client')
c.plugins.push(new Webpack.HotModuleReplacementPlugin())
}
}
else if (target === 'node') {
c.output.libraryTarget = 'commonjs2'
c.externals = [NodeExternals()]
c.node = false
}
return c
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment