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 intopublic
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
.
Run npm init
and follow the instructions. Next, install webpack and
babel-loader by running npm install --save babel-loader 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" }
]
}
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.
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.
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.
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 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.