Skip to content

Instantly share code, notes, and snippets.

@jarednorman
Last active May 13, 2020 18:24
Show Gist options
  • Save jarednorman/11a1cbb11b389fb0501e to your computer and use it in GitHub Desktop.
Save jarednorman/11a1cbb11b389fb0501e to your computer and use it in GitHub Desktop.
Webpack with Rails Part 1

Webpack with Rails Part 1

I don't like Sprockets, but it's the easiest and best choice for lots of Ruby on Rails projects. When looking for a better way to manage my JavaScript assets and an improved developer story, my current tool of choice is webpack. While I wish Rails supported drop-in asset pipelines the way that Phoenix does, it's not hard to use webpack with Rails.

Disclaimer: There are lots of other guides for getting webpack working with Rails. Those guides I've read missed details I felt were important, or configured webpack in ways that I was not happy with. I wrote this as a guide to provide the reader with a solid starting point that wasn't too opinionated.

I had the following requirements:

  • I wanted Babel support; it makes working with JavaScript far more enjoyable.

  • I didn't want my "source" assets to be in a folder that Sprockets manages; while some might prefer to have these files in app/assets/javascripts I don't think Sprockets should know anything about them.

  • I wanted the entries output by webpack to be in a subfolder of app/assets/javascripts rather than straight into public so that they get fingerprinted to avoid caching issues.

This article was written using Rails 4.2.4 and should work for any version of Rails 4.2, and should need little if any modification for working against other 4.x versions. I used webpack 1.12.2, and babel-loader 5.3.2.

Install webpack

Run npm init and follow the instructions. Next, install webpack and babel-loader by running npm install --save babel-loader webpack.

Configure webpack

Here's a minimal webpack.config.js. Drop it into the root of your project.

// webpack.config.js

"use strict";

var path = require('path');
var config = module.exports = {};

config.context = __dirname;

// Specify your entries, I store all my webpack managed JavaScript in
// app/webpack, as per my earlier requirements.
config.entry = {
  cart: './app/webpack/cart.js'
};

// This outputs an entry named 'foobar' into
// app/assets/javascripts/entries/foobar.js.
config.output = {
  path: path.join(__dirname, "app/assets/javascripts/entries"),
  filename: "[name].js"
}

// Use babel-loader for our *.js files.
config.module = {
  loaders: [
    { test: /\.js$/,
      exclude: /node_modules/,
      loader: "babel-loader" }
  ]
}

Rails time

Now, if you write some ES6 in app/webpack/cart.js you can run node node_modules/.bin/webpack and your entry will be compiled to app/assets/javascripts/entries/cart.js. (You may need to create that folder.)

// app/webpack/cart.js

// Write some ES6!
let foo = "bar";
console.log("bar");

To include this entry in your Rails views/layouts, you'll need to open up config/initializers/assets.rb and add entries/cart.js to your procompile list, and then restart your rails server.

# config/initializers/assets.rb

Rails.application.config.assets.precompile += %w( entries/cart.js )

You can include the javascript tag as you would any other, <%= javascript_include_tag "entries/cart" %> if you're using ERB.

Finishing touches

Since you're far too lazy/efficient run webpack every time you modify your assets, you want to run the watcher. Open up your package.json and add a scripts entry:

// package.json

{
  // ...
  "scripts": {
    "watch": "webpack --watch --colors --progress"
  },
  // ...
}

This allows you to start the webpack watcher by running npm run watch. Make sure to include this in your README so new users on the project know how to get things working.

Deployment

I won't include any specifics for handling deployment as the details would vary too much between environments. In most configurations you'll want to either override/modify your assets:precompile rake task to run webpack first, or modify your deploy system to run webpack before assets:precompile is run.

Write ES6, Be Happy

Webpack and Babel provide a powerful system for breaking up your JavaScript into self-contained entry points and isolating individual components, with an excellent developer story.

Webpack allows you to build much more complex pipelines to meet your application's requirements. For example Webpack's ecosystem works exceptionally well when building React and Flux/Redux applications, offering tools like React Hot Loader.

@crimson-knight
Copy link

So, I read this quickly and almost missed that this was written for Rails 4, so that may be the reason for your approach here but, I have a few questions about this configuration you've chosen.

Are you using the asset pipeline for the Webpack transpiled ES6 because there isn't the image_pack_tag or javascript_pack_tag helpers available in Rails 4?

Doesn't Webpack already minify it's output? It seems like putting the output into the assets pipeline to re-minify/process the already processed JS seems unnecessary and you could just put the transpiled output into your Public folder.

Have you made anything so that when running in development and making changes to the JS, Rails will know that it's JS assets need to be transpiled and will trigger Webpack? Since this is available in newer rails apps (out of the box this is how it works in Rails 6) so I was just curious if you'd made any adaptations to get the same behavior.

@jarednorman
Copy link
Author

jarednorman commented May 13, 2020

@crimson-knight This is a stale guide from before Webpacker existed.

Compiling into the sprockets controlled folder was an easy way to allow reference the assets using javascript_include_tag so that fingerprinting would happen. Unless you set it up yourself there would be no initial minification from Webpack unless you set it up yourself, because I was using Webpack directly and not getting any of the "default behaviour" that Webpacker gives you.

Unless you're living in world where you can't use Webpacker, nothing here should be useful to you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment