More Webpack
Following on from yesterdays experiments with webpack and react, I’ve changed things round a little today in an effort to make things even simpler.
webpack.ProvidePlugin
One of the annoying aspects of splitting the single file into smaller pieces was the need to add the require lines to every one. I thought there must be a simpler way – and there is! The ProvidePlugin allows you to tell webpack that certain requires should be added as needed. To use it a few changes are needed in the webpack configuration.
First you’ll need to make sure that webpack is available, so add the require at the top of the file.
var webpack = require('webpack');
Then add a plugins section with the plugin and it’s configuration.
plugins: [ new webpack.ProvidePlugin({ React: "react", ReactDOM: "react-dom" }), ],
Following this change, the javascript files can be simplified by removing the require lines for react or react-dom, so /components/Main.js becomes just
module.exports = React.createClass({ render: function() { return (
Hello World!
) } });This proved very useful for the project as a few components used jquery, but remembering which ones and to include the require line wasn’t an issue once this plugin had been added – with the appropriate configuration line ($: “jquery”).
Source Maps
It always seems like a good idea to create a .map file, so it’s as simple as adding a line telling webpack to do just that.
devtool: "source-map",
CSS
Handling CSS is something that never seems as simple as it should/could be, but initially webpack seems to offer a solution. 2 new loaders are needed, so install them via npm.
npm install --save-dev style-loader css-loader
Then we need to tell webpack that we can use them for css files, by adding a section to the loaders.
module: { loaders: [ { test: /components/.+.jsx?$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ['react'] } }, { test: /.+.css$/, loader: 'style-loader!css-loader' } ] }
As we’re using the resolve to keep require usage simple, we should update that as well (I’ve added the css files in an inventively named css directory),
resolve: { modulesDirectories: ['node_modules', 'components', 'css'], extensions: ['', '.js', '.jsx', '.css'] },
After making these changes, we need to actually add a require for some css. This was done in App.js as follows,
require('style.css');
However, running webpack produced a small surprise and a touch of confusion.
$ webpack Hash: f4a6a633c9be19bddd79 Version: webpack 1.12.13 Time: 1717ms Asset Size Chunks Chunk Names bundle.js 689 kB 0 [emitted] main bundle.js.map 808 kB 0 [emitted] main + 164 hidden modules
Where was the CSS? The answer is simple, but also, to my mind, not obvious. It’s merged into the bundle.js and if you open it in an editor you’ll find it there. However, I want the css in a seperate file for a number of reasons, so this solution only partly works. The answer turns out to be another plugin.
npm install --save-dev extract-text-webpack-plugin
Once installed there are quite a few changes required to use it. First off, we need to extract the css rather than let it be bundled with the javascript. To do this we need to change the css loader.
{ test: /.+.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader') }
Before we can use the ExtractTextPlugin we need to require it, so this line needs to be added at the top of the file.
var ExtractTextPlugin = require("extract-text-webpack-plugin");
Finally we also need to output our extracted css, so we need to add this entry to the plugins.
new ExtractTextPlugin("bundle.css")
Running webpack now gives the expected results.
$ webpack Hash: 55234e19dea7f3c8f04f Version: webpack 1.12.13 Time: 1757ms Asset Size Chunks Chunk Names bundle.js 679 kB 0 [emitted] main bundle.css 107 bytes 0 [emitted] main bundle.js.map 794 kB 0 [emitted] main bundle.css.map 87 bytes 0 [emitted] main + 164 hidden modules Child extract-text-webpack-plugin: + 2 hidden modules
With this approach I can simply add a require line into any component javascript file for required css and it will be automatically bundled. This works well but the ordering of things being added isn’t always as I’d wish it, so more care will need to be taken with how I write the css. This approach also opens up the prospect of moving to a LESS/SASS based approach as the css can be processed before being added to the bundle.
I’m reasonably sure I don’t need a map file for the css, but I haven’t found any simple solutions yet. Answers on a postcard.