webpack-isomorphic-tools
webpack-isomorphic-tools
THIS PACKAGE IS DEPRECATED. LOOK ELSEWHERE.
webpack-isomorphic-tools
is a small helper module providing very basic support for isomorphic (universal) rendering when using Webpack. It was created a long time ago when Webpack was v1
and the whole movement was just starting. Therefore webpack-isomorphic-tools
is a hacky solution. It allowed many projects to set up basic isomorphic (universal) rendering in the early days but is now considered deprecated and new projects shouldn’t use it. This library can still be found in legacy projects. For new projects use either universal-webpack
or all-in-one frameworks like Next.js.
Topics
- What it does
- A simple example
- Installation
- Usage
- A working example
- Configuration
- Configuration examples
- What are webpack-assets.json?
- What are Webpack stats?
- What’s a “module”?
- API
- Troubleshooting
- Miscellaneous
- References
- Contributing
What it does
Suppose you have an application which is built using Webpack. It works in the web browser.
Should it be “isomorphic” (“universal”)? It’s better if it is. One reason is that search engines will be able to index your page. The other reason is that we live in a realtime mobile age which declared war on network latency, and so it’s always better to fetch an already rendered content than to first fetch the application code and only then fetch the content to render the page. Every time you release a client-side only website to the internet someone writes a frustrated blog post.
So, it’s obvious then that web applications should be “isomorphic” (“universal”), i.e. be able to render both on the client and the server, depending on circumstances. And it is perfectly possible nowadays since javascript runs everywhere: both in web browsers and on servers.
Ok, then one can just go ahead and run the web application in Node.js and its done. But, there’s one gotcha: a Webpack application will usually crash when tried to be run in Node.js straight ahead (you’ll get a lot of SyntaxError
s with Unexpected token
s).
The reason is that Webpack introduces its own layer above the standard javascript. This extra layer handles all require()
calls magically resolving them to whatever it is configured to. For example, Webpack is perfectly fine with the code require()
ing CSS styles or SVG images.
Bare Node.js doesn’t come with such trickery up its sleeve. Maybe it can be somehow enhanced to be able to do such things? Turned out that it can, and that’s what webpack-isomorphic-tools
do: they inject that require()
magic layer above the standard javascript in Node.js.
Still it’s a hacky solution, and a better way would be to compile server-side code with Webpack the same way it already compiles the client-side code. This is achieved via target: “node”
configuration option, and that’s what universal-webpack
library does. However, webpack-isomorphic-tools
happened to be a bit simpler to set up, so they made their way into many now-legacy projects, so some people still use this library. It’s not being maintained anymore though, and in case of any issues people should just migrate to universal-webpack
or something similar.
webpack-isomorphic-tools
mimics (to a certain extent) Webpack’s require()
magic when running application code on a Node.js server without Webpack. It basically fixes all those require()
s of assets and makes them work instead of throwing SyntaxError
s. It doesn’t provide all the capabilities of Webpack (for example, plugins won’t work), but for the basic stuff, it works.
A simple example
For example, consider images. Images are require()
d in React components and then used like this:
// alternatively one can use `import`, // but with `import`s hot reloading won't work // import imagePath from '../image.png' // Just `src` the image inside the `render()` method class Photo extends React.Component { render() { // When Webpack url-loader finds this `require()` call // it will copy `image.png` to the build folder // and name it something like `9059f094ddb49c2b0fa6a254a6ebf2ad.png`, // because Webpack is set up to use the `[hash]` file naming feature // which makes browser asset caching work correctly. return <img src={ require('../image.png') }/> } }
It works on the client-side because Webpack intelligently replaces all the require()
calls with a bit of magic.
But it wouldn’t work on the server-side because Node.js only knows how to require()
javascript modules. It would just throw a SyntaxError
.
To solve this issue one can use webpack-isomorphic-tools
. With the help of webpack-isomorphic-tools
in this particular case the require()
call will return the real path to the image on the disk. It would be something like ../../build/9059f094ddb49c2b0fa6a254a6ebf2ad.png
. How did webpack-isomorphic-tools
figure out this weird real file path? It’s just a bit of magic.
webpack-isomorphic-tools
is extensible, and finding the real paths for assets is the simplest example of what it can do inside require()
calls. Using custom configuration one can make require()
calls (on the server) return anything (not just a String; it may be a JSON object, for example).
For example, if one is using Webpack css-loader modules feature (also referred to as “local styles”) one can make require(*.css)
calls return JSON objects with generated CSS class names maps like they do in este and react-redux-universal-hot-example.
Installation
webpack-isomorphic-tools
are required both for development and production
$ npm install webpack-isomorphic-tools --save
Usage
First you add webpack-isomorphic-tools
plugin to your Webpack configuration.
webpack.config.js
var WebpackIsomorphicToolsPlugin = require('webpack-isomorphic-tools/plugin') var webpackIsomorphicToolsPlugin = // webpack-isomorphic-tools settings reside in a separate .js file // (because they will be used in the web server code too). new WebpackIsomorphicToolsPlugin(require('./webpack-isomorphic-tools-configuration')) // also enter development mode since it's a development webpack configuration // (see below for explanation) .development() // usual Webpack configuration module.exports = { context: '(required) your project path here', module: { loaders: [ ..., { test: webpackIsomorphicToolsPlugin.regularExpression('images'), loader: 'url-loader?limit=10240', // any image below or equal to 10K will be converted to inline base64 instead } ] }, plugins: [ ..., webpackIsomorphicToolsPlugin ] ... }
What does .development()
method do? It enables development mode. In short, when in development mode, it disables asset caching (and enables asset hot reload), and optionally runs its own “dev server” utility (see port
configuration setting). Call it in development webpack build configuration, and, conversely, don’t call it in production webpack build configuration.
For each asset type managed by webpack-isomorphic-tools
there should be a corresponding loader in your Webpack configuration. For this reason webpack-isomorphic-tools/plugin
provides a .regularExpression(assetType)
method. The assetType
parameter is taken from your webpack-isomorphic-tools
configuration:
webpack-isomorphic-tools-configuration.js
import WebpackIsomorphicToolsPlugin from 'webpack-isomorphic-tools/plugin' export default { assets: { images: { extensions: ['png', 'jpg', 'gif', 'ico', 'svg'] } } }
That’s it for the client side. Next, the server side. You create your server side instance of webpack-isomorphic-tools
in the very main server javascript file (and your web application code will reside in some server.js
file which is require()
d in the bottom)
main.js
var WebpackIsomorphicTools = require('webpack-isomorphic-tools') // this must be equal to your Webpack configuration "context" parameter var projectBasePath = require('path').resolve(__dirname, '..') // this global variable will be used later in express middleware global.webpackIsomorphicTools = new WebpackIsomorphicTools(require('./webpack-isomorphic-tools-configuration')) // initializes a server-side instance of webpack-isomorphic-tools // (the first parameter is the base path for your project // and is equal to the "context" parameter of you Webpack configuration) // (if you prefer Promises over callbacks // you can omit the callback parameter // and then it will return a Promise instead) .server(projectBasePath, function() { // webpack-isomorphic-tools is all set now. // here goes all your web application code: // (it must reside in a separate *.js file // in order for the whole thing to work) require('./server') })
Then you, for example, create an express middleware to render your pages on the server
import React from 'react' // html page markup import Html from './html' // will be used in express_application.use(...) export function pageRenderingMiddleware(request, response) { // clear require() cache if in development mode // (makes asset hot reloading work) if (process.env.NODE_ENV !== 'production') { webpackIsomorphicTools.refresh() } // for react-router example of determining current page by URL take a look at this: // https://github.com/catamphetamine/webapp/blob/master/code/server/webpage%20rendering.js const pageComponent = [determine your page component here using request.path] // for a Redux Flux store implementation you can see the same example: // https://github.com/catamphetamine/webapp/blob/master/code/server/webpage%20rendering.js const fluxStore = [initialize and populate your flux store depending on the page being shown] // render the page to string and send it to the browser as text/html response.send('<!doctype html>n' + React.renderToString(<Html assets={webpackIsomorphicTools.assets()} component={pageComponent} store={fluxStore}/>)) }
And finally you use the assets
inside the Html
component’s render()
method
import React, {Component, PropTypes} from...