Package Exports
- universal-webpack
- universal-webpack/build/chunks plugin
- universal-webpack/build/server configuration
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (universal-webpack) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
universal-webpack
[![NPM Version][npm-badge]][npm] [![Build Status][travis-badge]][travis]
Helps setting up isomorphic (universal) Webpack build: the one that's working both on client and server.
Motivation
In summer 2015 I wrote webpack-isomorphic-tools to make isomorphic (universal) React rendering work on server-side when the project was built with Webpack.
The goal was met and many people started using it to implement isomorphic (universal) rendering in their apps.
Still it lacked some funky Webpack features like variuos Webpack plugins and other edge cases.
So I did some research on Webpack builds for Node.js and came up with this proof-of-concept solution which seems to work good enough. It supports all Webpack features (all plugins, etc).
Installation
npm install universal-webpack --saveUsage
Suppose you have a typical webpack.config.js file. Create two new files called webpack.config.client.js and webpack.config.server.js with the following contents:
webpack.config.client.js
import { client_configuration } from 'universal-webpack'
import settings from './universal-webpack-settings'
import configuration from './webpack.config'
export default client_configuration(configuration, settings)webpack.config.server.js
import { server_configuration } from 'universal-webpack'
import settings from './universal-webpack-settings'
import configuration from './webpack.config'
export default server_configuration(configuration, settings)universal-webpack-settings.js
export default
{
server:
{
input: './source/server.js',
output: './build/server/server.js'
}
}Use webpack.config.client.js instead of the old webpack.config.js for client side Webpack builds.
The server_configuration() function takes the client-side Webpack configuration and tunes it a bit for server-side usage (target: "node").
The server-side bundle (settings.server.output file) is generated from settings.server.input file by Webpack when it's run with the webpack.config.server.js configuration. An example of settings.server.input file may look like this (it must export a function):
source/server.js
// express.js
import path from 'path'
import http from 'http'
import express from 'express'
import http_proxy from 'http-proxy'
// react-router
import routes from '../client/routes.js'
// Redux
import store from '../client/store.js'
// The server code must export a function
// (`parameters` may contain some miscellaneous library-specific stuff)
export default function(parameters)
{
// Create HTTP server
const app = new express()
const server = new http.Server(app)
// Serve static files
app.use(express.static(path.join(__dirname, '..', 'build/assets')))
// Proxy API calls to API server
const proxy = http_proxy.createProxyServer({ target: 'http://localhost:xxxx' })
app.use('/api', (req, res) => proxy.web(req, res))
// React application rendering
app.use((req, res) =>
{
// Math current URL to the corresponding React page
react-router.match(routes, req.originalUrl).then((error, result) =>
{
if (error)
{
res.status(500)
return res.send('Server error')
}
// Render React page
const page = redux.provide(result, store)
res.status(200)
res.send('<!doctype html>' + '\n' + ReactDOM.renderToString(<Html>{page}</Html>))
})
})
// Start the HTTP server
server.listen()
}The last thing to do is to create a startup file for the server side. This is the file you're gonna run with Node.js, not the file provided above.
source/start-server.js
import { server } from 'universal-webpack'
import settings from '../universal-webpack-settings'
import configuration from '../webpack.config'
server(configuration, settings)Calling source/start-server.js will basically call the function exported from source/server.js built with Webpack.
In the end you run all the above things like this (in parallel):
webpack-dev-server --hot --inline --config "./webpack.config.client.js --port XXXX --colors --display-error-details"webpack --watch --config "./webpack.config.server.js" --colors --display-error-detailsnodemon "./source/start-server" --watch "./build/server"The above three commands are for development mode. For production mode the single command will be:
webpack --config "./webpack.config.client.js" --colors --display-error-details
webpack --config "./webpack.config.server.js" --colors --display-error-details
node "./source/start-server"Chunks
This library will pass the chunks() function parameter (inside the parameters argument of the server-side function) which returns webpack-compiled chunks filename info:
build/webpack-chunks.json
{
javascript:
{
main: `/assets/main.785f110e7775ec8322cf.js`
},
styles:
{
main: `/assets/main.785f110e7775ec8322cf.css`
}
}These filenames are required for <script src=.../> and <link rel="style" href=.../> tags in case of isomorphic (universal) rendering on the server-side.
Example project
You may refer to this sample project as an all-in-one illustration of using this little library.
Drawbacks
It will output double the amount of all assets included in the project: one complete bundle for client-side build and one complete bundle for server-side build. E.g. an asset ./images/dog.jpg will be output both into ./build/client/9059f094ddb49c2b0fa6a254a6ebf2ad.jpg and ./build/server/9059f094ddb49c2b0fa6a254a6ebf2ad.jpg. If you find a way to avoid that, drop me a line.
Also, it will perform two Webpack builds instead of one, but this shouldn't be much of an issue since developers' machines are highly multicore these days.
Advanced configuration
{
// By default, all `node_modules` are marked as `external`
// for server-side Webpack build.
//
// With this setting one can explicitly define which modules
// aren't gonna be marked as `external` dependencies.
// (and therefore are gonna be compiled with `babel-loader` by Webpack)
//
// Can be used for ES6-only `node_modules`.
// A more intelligent solution would be accepted:
// https://github.com/halt-hammerzeit/universal-webpack/issues/10
//
exclude_from_externals:
[
'lodash-es',
/^lodash-es$/
]
}Source maps
I managed to get source maps working in my Node.js server-side code using source-map-support module.
source/start-server.js
// Enables proper source map support in Node.js
import 'source-map-support/register'
// The rest is the same as in the above example
import { server } from 'universal-webpack'
import settings from '../universal-webpack-settings'
import configuration from '../webpack.config'
server(configuration, settings)Without source-map-support enabled it would give me No element indexed by XXX error (which means that by default Node.js thinks there are references to other source maps and tries to load them but there are no such source maps).
devtool is set to source-map for server-side builds.
Nodemon
I recommend using nodemon for running server-side Webpack bundle. Nodemon has a --watch <directory> command line parameter which restarts Node.js process each time the <directory> is updated (e.g. each time any file in that directory is modified).
In other words, Nodemon will relaunch the server every time the code is rebuilt with Webpack.
There's one little gotcha though: for the --watch feature to work the watched folder needs to exist by the time Nodemon is launched. That means that the server must be started only after the settings.server.output path folder has been created.
To accomplish that this library provides a command line tool: universal-webpack. No need to install in globally: it is supposed to work locally through npm scripts. Usage example:
package.json
...
"scripts": {
"start": "npm-run-all prepare-server-build start-development-workflow",
"start-development-workflow": "npm-run-all --parallel development-webpack-build-for-client development-webpack-build-for-server development-start-server",
"prepare-server-build": "universal-webpack --settings ./universal-webpack-settings.js prepare",
...The prepare command creates settings.server.output path folder, or clears it if it already exists.
License
MIT [npm]: https://www.npmjs.org/package/universal-webpack [npm-badge]: https://img.shields.io/npm/v/universal-webpack.svg?style=flat-square [travis]: https://travis-ci.org/halt-hammerzeit/universal-webpack [travis-badge]: https://img.shields.io/travis/halt-hammerzeit/universal-webpack/master.svg?style=flat-square [coveralls]: https://coveralls.io/r/halt-hammerzeit/universal-webpack?branch=master [coveralls-badge]: https://img.shields.io/coveralls/halt-hammerzeit/universal-webpack/master.svg?style=flat-square