JavaScript & Rails: a `webpacker` evaluation

An article, posted about 6 years ago filed in javascript, ruby, rails, ruby on rails, programming, es2015, coffeescript, gem & assets.

Webpacker is still opt-in for new Rails projects. But this might change. The JavaScript ecosystem is moving fast and new JavaScript frameworks are pushing customer’s expectations to higher levels. To use these frameworks with your Rails app, you had a few options:

A small praise for Rails Assets

I have been using rails-assets.org the past few years to keep my JavaScript dependencies up to date. It thought it was smart solution; instead of requiring individual developers to maintain Gem-wrappers, Gem wrappers are created on the fly by RailsAssets.org. It was smart and light weight on the developers side and worked perfectly with the Rails’ Asset pipeline, which, while not being really advanced, worked just fine for most projects that needed a few JavaScript libraries.

Sadly, being based on the bower-stack, it’s foundation got deprecated quite some time ago. More modern tools are no longer begin updated for Bower. Also, with growing library-sizes and number of libraries included, more advanced minification strategies, such as tree-shaking, are becoming a must-have. Just uglifying and combining the JavaScript files, like the asset pipeline did for us, is no longer good enough. Hence I believe that, in its current state, RailsAssets.org is no longer a solid base for future development.

JavaScript & Rails: a `webpacker` evaluation

How to use webpacker with your existing Rails-app

It really isn’t that hard, the webpacker README.md describes it pretty simply already:

  1. Add gem 'webpacker', '~> 3.0’ to your Gemfile and run bundle
  2. run bin/rails webpacker:install
  3. (optional) add CoffeeScript support: bin/rails webpacker:install:coffee (I am migrating, so quite some CoffeeScript written already here)
  4. add <%= javascript_pack_tag 'application' %> and <%= stylesheet_pack_tag 'application' %> to your application
  5. start writing JavaScript and adding anything from the npm-repository, or straight from github, using yarn add and start importing them from the application.js file in app/javascripts/packs.

So let’s have a look at the developer experience.

The ugly

run two servers?!

Next to rails s you’ve got to run:

./bin/webpack-dev-server

Which brings nice benefits like auto reloading, but it is just one extra thing you need to think of. To me both ruby and Ruby on Rails are all about reducing cognitive load. Maybe rails s should run webpack-dev-server automatically in development mode when webpacker is loaded.

javascript/**/*.css

Call me old school, but I’m no fan of CSS in JS. Not that this is implied by simply the directory structure, and the actual CSS comes from what many would consider a JavaScript Package library (npm), but still. When this is an attempt to integrate Rails and JavaScript properly, the directory structure should be different.

IE compatibility

Using rails assets (Bower), mostly pre-compiled JavaScript files are being included, ready for production use. With webpacker you typically get to work with the raw untranspiled source code, typically written using ES2015+. And IE doesn’t like that as much as dev’s do. This requires transpilation, and this is where Babel comes in to play. See next.

Webpack and .babelrc configuration is ugly

While I think, from a ruby standpoint, Webpack and .babelrc configuration are ugly the webpacker gem adds another .yml file with another set of configuration options. And it doesn’t replace the standard Webpack and .babelrc config files, hence making it even harder to comprehend. As an example: I was migrating an existing project using Zurb Foundation to webpacker. As its JS-files are using some modern JavaScript features, their JavaScript also had to run through webpacker and Babel. Sadly, files imported from the node modules directory are excluded by default (probably an optimisation strategy?). Where do we start to fix this (properly?).

Fix: add the following line to config/webpack/environment.js:

 environment.loaders.get('babel').exclude = [];

UglifyJS & Webpack don’t play nicely

Currently the Ugflify JS configuration for webpack comes with IE11 unfriendly settings; after Babeling it to IE11 compatible JS, Uglifier adds shorthand object notations and more, breaking IE11 again (really, this took me days… :( ).

Fix: add the following line to config/webpack/production.js:

 environment.plugins.get('UglifyJs').options.uglifyOptions.ecma = 5

The Bad

JavaScript is a mess (when compared to ruby)

There are great and exciting things happening in JS world, but it goes (or has been going) ugly fast and while new versions are approaching rubyesque expressiveness, you need something like Babel to translate it for you. And then you need to include polyfills for extensions to make it run everywhere. There is too much manual configuration and testing involved at this moment. How much easier things were with just CoffeeScript, an expressive language translating predictably to JS that pretty much works everywhere and out of the box with Rails.

npm

(don’t worry, there is also a ‘good’ titled npm…) NPM is a mess. Probably due the popularity and rapid changes in technologies. But while with rubygems github repo-names typically equal the name to require it and its name at rubygems.org, npm has turned out to be pretty much unpredictable when it comes to naming. You really have to make sure you add the right package and import it properly. It’s being complicated because there are also many similarly named projects. There are obviously more developers creating packages, but maybe the ruby-devs are also a bit more creative on the naming side?

The good

npm

Really, access to everything from the npm repo’s is a big advantage. There is no alternative to npm that I know of.

Live reloading

This was available before of course, but webpack makes it just a bit faster. And it is a freebie working out of the box (albeit it requires running that second server, as mentioned in the ‘ugly’).

Modern JavaScript(!)

Present day JS has become much more fun to write. After being configured correctly, we can now use it without much thought in our Rails projects.

Overall

It is great to see work is being done to connect Rails to the modern JavaScript stack. But the lack of focus on developer happiness in the JavaScript world is in stark contrast with the happiness that ruby (and also Rails) brings. I’ve been developing 100% JavaScript-based frontends for several of my clients as a front-end developer and was bothered by the initial complexity. Yet it wasn’t as apparent to me until now, using them together. The webpacker-gem, however, adds another layer of complexity in its attempt to hide some of these complexities. Sadly the current state of that abstraction is not good enough to make me a happier programmer. I’m not sure what the right direction should be: hoping for a simpler webpack (the latest version will accept no configuration at all) and other JavaScript technologies, and dumbing down webpacker (the Rails integration) and ditching the webpacker.yml, or abstract as much of the configuration behind a simple predictably “ruby-native” .yml file, using well selected defaults (including transpilation of JavaScript sources inside node_modules). While configuring in .yml-files is the most Rails-like approach, the webpacker-gem hasn’t reached maturity yet and adding yet another configuration file to check simply increases the complexity.

Because the JS-ecosystem is still a moving target it may be hard to predefine best practices, and standardize on these practices in default webpacker configurations. Hence, easier and probably better to comprehend in the short term would have been a more bare-bones approach to making yarn and Rails work together. Simply generating a good webpacker native configuration could have been enough.

I will re-evaluate my findings in a next project and see if there are places where I can contribute.

Op de hoogte blijven?

Maandelijks maak ik een selectie artikelen en zorg ik voor wat extra context bij de meer technische stukken. Schrijf je hieronder in:

Mailfrequentie = 1x per maand. Je privacy wordt serieus genomen: de mailinglijst bestaat alleen op onze servers.