Rails with Webpack - not for everyone

Published Jan 06, 2018Last updated Apr 20, 2018
Rails with Webpack - not for everyone

TL;DR

Webpack may be an excellent choice for front-end web apps. But in a standard Rails app I find it to be volatile (configuration still changes) and surprisingly unfriendly (follow my adventure below). Sprockets 4 was a better trade-off for me in a standard Rails project.

Yarn in Rails on Heroku

I've been using Yarn in Rails for some time now, so I can pull in CSS and JS dependencies directly from the source npm packages. The advantage is that I don't have to wait for someone to update a ruby gem wrapper whenever a new version comes out. In fact, I don't necessarily want my Gemfile to be bloated with gems that wrap frontend assets at all.

Deploying a Ruby app that uses Node on Heroku without buildpack is luckily as trivial as adding gem "webpacker" to your Gemfile. However, just to use Yarn, I now needed Webpack. In fact, I needed Webpack to be there without actually doing anything, because my assets were still compiled with Sprockets.

So, I ran rake webpacker:install and, at that time, the config files looked very cluttered. But I somehow managed to have rake assets:precompile run Webpack as a no-op by creating some empty files here and there. All this just so that I could use yarn out of the box.

And... I forgot about it (I did not, however, forget about the cluttered config files spread all over my Rails application.)

Fast-forward 6 months.

Obstacles when replacing Sprockets with Webpack

I'm in a project with several hundred SASS files and every time I change one of them, I need to wait 4 seconds for every browser reload because Sprockets re-compiles my CSS. Five days later I had to fix this.

Somewhere I read that Webpack is smart and fast, so I thought I'd give it a more serious attempt. If you google "Rails Webpack" you will find happy adventurous blog entries about how to replace the asset pipeline with Webpack in Rails 5.1. It had a hype air of "finally we're free from sprockets" and "using modern technology" and "custom JS processing" about it. Sure, why not.

So I ran rake webpacker:install (again) and began moving my JS files to app/javascript/packs. But then came my first stumble block. Would I really create a directory called app/javascript/packs/stylesheets? My SASS files don't feel happy in a directory that includes javascript in its name. But I continued.

The next pebble on my way was that there is no glob import support for SASS in Webpack, so I couldn't do import something/**/* (while this is usually frowned upon, there are valid use-cases such as Harry Roberts ITCSS where components don't conflict with each other). I noticed that webpacker uses sass-loader which in turn recommends another loader to achieve glob imports.

A quick google for "webpack sass glob" made me suspicious. There were no less than 4 Webpack loaders that did the same thing: import-glob-loader, node-sass-glob-importer, import-glob, sass-resources-loader. (This made me feel as if the JS community was the Wild West that has not reached the same level of conformity than the Ruby community).

When you use Webpack in Rails, there are a bunch of loaders pre-configured for you. Each of these reads your asset file, may modify it, and passes it on to the next loader. By this magic, globbing can be achieved if a loader parses your import **/* and replaces it with individual import statements for each file.

Unfortunately, all of these loaders only work with .scss files and not .sass which was exclusively used in the current project. Ok, I thought, let me create a few entry files (such as application.scss and admin.scss) where I use SCSS instead of SASS. It's a shame, but I won't add number 5 to the list of import glob loaders.

Yet, because loaders work the way they do, it parses the initial SCSS file, resolves the glob statements, but then the regular sass compiler takes over and every included file cannot use glob statements. For example, my application.scss would use **/* to load a components.scss but in the latter I could not use **/* any more. But I can work even around that. I just need to adapt the way I work to the framework (right?).

Even more obstacles

Then I had a really bad feeling when I had to add this to my JS files:

# This is a Javascript file
import 'stylesheets/something.scss'
import 'images/background.png'

I had to seriously import CSS and blog image files in Javascript in order to tell Webpack that they exist? This may make sense in some rich-client app, but not in my standard Rails project.

But, I got temporarily distracted by this error message:

invalid_config.png

I know that Webpack was created by a German but this error message reminded me a little bit too much of theoretical courses from university.

Invalid configuration object.

"Ah, my configuration file must have invalid YAML syntax", I thought. But after an hour, or so, lost on googling "debugging webpack", I finally, and accidentally, fixed the mistake. What was the problem? My webpacker.yml had a source_entry_path: some_path where some_path was a directory that did not exist. That's all.

Why did Webpack not tell me that a directory is missing? I can only assume that this would have been a no-brainer in other contexts. But I still did not give up my hopes that the JS community would eventually become more user-friendly. So I ran bin/webpack and all assets compiled in the end. At least I think so, because the output was, again, not as clear as I would have wished and compiling errors were so long that I had to scroll up 3 pages in my Terminal. (I will omit how I struggled to load the URL of a background image into my CSS, due to lacking documentation).

(Just to be clear, I am by no means badmouthing Webpack, but I present to you my experience with how Webpack presented itself to me.)

Yet, why were all my compiled JS files empty? Because of a bug reported in 2015 that has not been solved yet. If two files exist with the same basename, e.g. application.sass and application.js, then some plugin will remove all content from the JS file.

So I went through this "out-of-the-box" Webpack experience and gained... a SASS compiling improvement from 4 to 3 seconds. But I had to keep an eye on the Terminal window where the Webpack compiling took place, before I could reload the browser. So I didn't gain much at all.

I give up for now

At this point I called my buddy who is an Ember.js consultant and asked him about his experience with Webpack. He just laughed and said: "Webpack is terrible. All these loaders, you never know which one actually runs, then everybody creates their own little loader functions, you spend a year configuring it and the output is not beautiful and you don't even notice whether SASS compiles via Ruby or C."

There he summarized my last weeks exact experience in one sentence.

Finally, I realized that Sprockets 4 (which has been hiding as beta on Rubygems for two whole years now) supports SassC as a drop-in replacement of the Sass compiler implemented in Ruby. I gave it a try and, to be fair, my SASS compiling time has still not gone below 3 seconds. But at least I'm enjoying a true out-of-the-box experience for all assets.

Summary

  • Webpack is more difficult to configure than it should be. I really can't point my finger at it, except that it is far from trivial even for a bug-hunter. My experience with Ember-CLI makes Webpack look like a dragon in comparison.
  • Sprockets automatically runs on browser request. No need to have a webpacker-dev-server running that looks for file changes, then compiles, and then you may reload your browser window (no, I don't want my browser to auto-reload after compiling).
  • I can't get rid of the feeling that Webpack is primarily concerned with JS and not as much with PNG, WOFF, SVG, etc. In that respect, the Rails Asset Pipeline (apple) is just not comparable with Webpack (pair pear).
  • I can not confirm that Webpack overall is faster than Sprockets in a large project. After all, it's still underlying libraries such as Babel or SASS that do the actual work.

(Title image: "Eugène von Guérard - Lake Wakatipu with Mount Earnslaw", public domain from the Google Art Project.)

Discover and read more posts from Marius
get started