Webpack 2 and Middleman 4

A quick guide to using Webpack 2 with Middleman 4

I originally wrote this Nov. 29th, 2016 before Webpack 2 was out of beta, so I mostly re wrote it, including new gists, etc on Apr. 10th, 2017

As of version 4, Middleman now supports a variety of front-end/JS tooling via the External Pipeline, outlined here. Previously Middleman only built assets via Rails’ venerable Asset Pipeline aka Sprockets. There is a lot that could be said about Sprockets and why it may or may not be a good choice for Middleman going forward, but even the core Rails team is building in a way to integrate new JS tools like Webpack [edit as of 3/17], and its clear which way the wind is blowing. Having run a previous blog iteration and some clients’ sites on Middleman I’m pretty familiar with it, and I generally think the folks at thoughtbot have done a nice job. If I had more serious content management needs, I also like Jekyll a lot. Finally I use Webpack at work and think the people behind it are all mighty nice, so I decided to go that direction for the New & Shiny™ blog.

The example from the Middleman docs looked simple enough, it basically just involved throwing the snippet below into the config.rb

  activate :external_pipeline,
    name: :webpack,
    command: build? ? './node_modules/webpack/bin/webpack.js --bail' : './node_modules/webpack/bin/webpack.js --watch -d',
    source: ".tmp/dist",
    latency: 1

If you’re thinking to yourself, “surely it can’t be that simple” then you would be correct. From there you still need to set up your webpack.config.js, which isn’t hard per se, but there’s a lot of room for frustration and errors. This is doubly true if you aren’t a JS developer or are new to Webpack. I had a lot of trouble setting up extract-text-webpack-plugin and ultimately had to reach out to the chronically delightful Sean Larkin for help.

I also used a blog post by Ross Kaffenberger for reference. he uses Webpack 1, and his blog setup is worth looking at in general. Here’s a like to his site’s github His webpack.config.js is at the top level and looks a bit different than the one I’ll show below, as it’s for a different version of Webpack, so don’t mix them up! I’m also using Yarn instead of npm, because I like the lockfile.

So starting from the beginning, I’ll assume you have Ruby installed, and if not go here and make sure you have a working Ruby.

Install Middleman 4 and Start a Project

There are some simple instructions here, but it pretty much boils down to running gem install middleman. Once it’s done installing you should be able to run middleman init my_new_project and start a new project. You will also need to install Node and Yarn. If that all worked, congrats you’re ready to go, and you may have broken the bubble on your bingo card.

Next cd my_new_project, and start a new yarn project:

yarn init

Just use all of the defaults by hitting enter until it’s done, they should all be fine. Fore reference, here is what my demo repo looked like at this point. Next you’ll need to install a few packages.

yarn add --dev webpack webpack-dev-server@2

This installs Webpack

yarn add --dev babel-loader babel-core babel-preset-es2015

and this adds Babel, a JS transpiler.

yarn add --dev css-loader extract-text-webpack-plugin sass-loader style-loader

Finally, this installs all of the things you need to do regular CSS and SASS.

Don’t forget to activate the external_pipeline by adding the following to config.rb

  activate :external_pipeline,
    name: :webpack,
    command: build? ?
    "./node_modules/webpack/bin/webpack.js --bail -p" :
    "./node_modules/webpack/bin/webpack.js --watch -d --progress --color",
    source: "build",
    latency: 1

You’ll notice this is a little different than the initial example, basically I’m just using a build directory and adding color to the Webpack output.

Now run touch webpack.config.js and add the following:

  "use strict";
  const path = require("path");
  const webpack = require("webpack");
  const ExtractTextPlugin = require("extract-text-webpack-plugin");
  const cssLoaders = [
    {
      loader: "css-loader",
      options: {
        modules: true,
        minimize: true
      }
    },
    {
      loader: "sass-loader"
    }
  ]
  module.exports = {
    context: __dirname + "/source",
    entry: {
      site: "./javascripts/site.js",
    },
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /(node_modules)/,
          loader: "babel-loader",
        },
        {
          test: /\.(sass|scss)$/,
          use: ExtractTextPlugin.extract({
            fallback: "style-loader",
            use: "css-loader"
          })
        },
      ],//end rules
    },
    output: {
      path: __dirname + "/build/javascripts",
      filename: "[name].bundle.js",
    },

    plugins: [
      new webpack.LoaderOptionsPlugin({
        minimize: true,
        debug: false
      }),
      new ExtractTextPlugin({
        filename:  (getPath) => {
          return getPath("[name].bundle.css").replace("css/js", "css");
        },
        disable: false,
        allChunks: true,
      }),
    ],
  };

Here is a raw gist for easy copying. Warning: opens new tab

The lines beginning with test: /\.(sass|scss)$/, make the sass work, and these lines are basically straight from the extract-text-webpack-plugin readme.

If you run middleman, everything should basically work, and you should see something like the following:

  == The Middleman is loading
  == Executing: `./node_modules/webpack/bin/webpack.js --watch -d --progress --color`
  == View your site at "http://YOUR_ROUTER_NAME:4567", "http://YOUR_IP:4567"
  == Inspect your site configuration at "http://YOUR_ROUTER_NAME.home:4567/__middleman", "http://YOUR_IP:4567/__middleman"
    0% compiling== External: Webpack is watching the files…
  == External: Hash: 57c5cbdd1ea73e6cd381
  == External: Version: webpack 2.3.3
  == External: Time: 610ms
  == External:          Asset    Size  Chunks             Chunk Names
  == External: site.bundle.js  3.2 kB       0  [emitted]  site
  == External:    [0] ./javascripts/site.js 31 bytes {0} [built]

If that worked, you’re all set. If it didn’t go back and make sure all of your config and versions match. And for reference here is my repo at his point. Webpack may be overkill if you don’t do JS or if you only want to use vanilla JS and CSS, but if you want to use es6 or SASS, like me, then its probably worth the effort. If you’d like to see this blog’s source, it’s here on Github.

If you would like to learn more about Webpack, they have a great tutorial and docs.


If you enjoyed this post, follow me on twitter @ChaseGilliam, sometimes I'm funny. You can also find me on Github.