Finally we decided to embrace the latest JavaScript improvements and use EcmaScript 6 on all new applications. First, we started applying it to Tracker (view source code on Github). Rails ecosystem provides Webpacker, a gem that allows packing assets using modern tools like yarn and writing the latest flavour of JavaScript via Babel.

Tracker runs on Ruby on Rails 5.2.0.beta2 at the moment, and I'd like to list here the steps we took to install Webpacker.

Installation

Firstly we add webpacker gem to Gemfile:

ruby
# Gemfile

gem 'webpacker', '~> 3.3'

then we install it in the app:

Folder structure

There seem to be variances on folder structure among implementers, here's our own:

Stylesheets

The app/javascript/application.js links all assets in both JavaScript and stylesheets. For stylesheets we'll use a manifest file, where we import all our stylesheets:

css
// app/javascript/stylesheets/application.scss

@import 'layout';

Then we import that file in our JavaScript manifest:

js
// app/javascript/packs/application.js

import '../stylesheets/application'

We need to add the new manifests to out layout file, app/views/layout/application.html.erb:

html
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

Here we replaced the stylesheet_link_tag and javascript_include_tag helpers with the new stylesheet_pack_tag and javascript_pack_tag respectively. This replaces the asset pipeline with Webpack. If we run the app now, we should be able to see assets served by Webpack.

Webpacker notices when we change our assets, and compiles them when we refresh the page. This may take slightly longer that to what we are used to. To avoid, we tend to run bin/webpack-dev-server on another console tab, which watches for changes, compiles the files automatically and also refreshes the web page, so we don't have to.

Bootstrap

Now we install some common front-end libraries. The best way to do this is to use yarn:

Yarn install Bootstrap, jQuery and Popper, needed by Bootstrap to run. We need to add them as plugins too:

js
// config/webpack/environment.js

const webpack = require('webpack')
environment.plugins.append(
  'Provide',
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    'window.jQuery': 'jquery',
    Popper: ['popper.js', 'default']
  })
)

We then import them:

js
// app/javascript/packs/application.js

import 'jquery'
import 'bootstrap'

On the stylesheet side, we just add the parts of Bootstrap we need to a bootstrap_custom file:

css
// app/javascript/stylesheets/bootstrap_custom.scss

@import 'bootstrap/functions';
@import 'bootstrap/variables';
@import 'bootstrap/mixins';
@import 'bootstrap/root';
@import 'bootstrap/reboot';
@import 'bootstrap/type';
// @import 'bootstrap/images';
// @import 'bootstrap/code';
@import 'bootstrap/grid';
// @import 'bootstrap/tables';
@import 'bootstrap/forms';
@import 'bootstrap/buttons';
@import 'bootstrap/transitions';
@import 'bootstrap/dropdown';
// @import 'bootstrap/button-group';
// @import 'bootstrap/input-group';
// @import 'bootstrap/custom-forms';
@import 'bootstrap/nav';
@import 'bootstrap/navbar';
@import 'bootstrap/card';
// @import 'bootstrap/breadcrumb';
// @import 'bootstrap/pagination';
@import 'bootstrap/badge';
// @import 'bootstrap/jumbotron';
@import 'bootstrap/alert';
// @import 'bootstrap/progress';
// @import 'bootstrap/media';
@import 'bootstrap/list-group';
@import 'bootstrap/close';
@import 'bootstrap/modal';
@import 'bootstrap/tooltip';
// @import 'bootstrap/popover';
// @import 'bootstrap/carousel';
@import 'bootstrap/utilities';
@import 'bootstrap/print';

and add that to the application.scss manifest:

css
// app/javascript/stylesheets/application.scss

@import 'bootstrap_custom';
@import 'layout';

Images

Images are also included within the application.scss:

js
// app/javascripts/packs/application.js

require.context('../images/', true, /\.(gif|jpg|png|svg|eot|ttf|woff|woff2)$/i)

Fonts

Font files are added to the app/javascripts/images/fonts folder. In Tracker, we use Montserrat so we link them to the stylesheet:

css
// app/javascripts/stylesheets/fonts.scss

@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 400;
  src: url("../images/fonts/montserrat-v12-latin/montserrat-v12-latin-regular.eot"); /* IE9 Compat Modes */
  src: local('Montserrat Regular'), local('Montserrat-Regular'),
       url("../images/fonts/montserrat-v12-latin/montserrat-v12-latin-regular.eot?#iefix") format('embedded-opentype'), /* IE6-IE8 */
       url("../images/fonts/montserrat-v12-latin/montserrat-v12-latin-regular.woff2") format('woff2'), /* Super Modern Browsers */
       url("../images/fonts/montserrat-v12-latin/montserrat-v12-latin-regular.woff") format('woff'), /* Modern Browsers */
       url("../images/fonts/montserrat-v12-latin/montserrat-v12-latin-regular.ttf") format('truetype'), /* Safari, Android, iOS */
       url("../images/fonts/montserrat-v12-latin/montserrat-v12-latin-regular.svg#Montserrat") format('svg'); /* Legacy iOS */
}

@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 700;
  src: url("../images/fonts/montserrat-v12-latin/montserrat-v12-latin-700.eot"); /* IE9 Compat Modes */
  src: local('Montserrat Regular'), local('Montserrat-Regular'),
       url("../images/fonts/montserrat-v12-latin/montserrat-v12-latin-700.eot?#iefix") format('embedded-opentype'), /* IE6-IE8 */
       url("../images/fonts/montserrat-v12-latin/montserrat-v12-latin-700.woff2") format('woff2'), /* Super Modern Browsers */
       url("../images/fonts/montserrat-v12-latin/montserrat-v12-latin-700.woff") format('woff'), /* Modern Browsers */
       url("../images/fonts/montserrat-v12-latin/montserrat-v12-latin-700.ttf") format('truetype'), /* Safari, Android, iOS */
       url("../images/fonts/montserrat-v12-latin/montserrat-v12-latin-700.svg#Montserrat") format('svg'); /* Legacy iOS */
}

Then add the fonts file to the stylesheet manifest:

css
// app/javascript/stylesheets/application.scss

@import 'fonts';

Unobtrusive JS and Turbolinks

Rails comes with rails-ujs and turbolinks gems, which we don't need anymore and can remove from the Gemfile file. We can also remove the gem uglifier used to parse the assets.

Since they are normal JavaScript files, we include them via yarn:

The we link them inside the manifest and start them:

js
// app/javascript/packs/application.js

import Rails from 'rails-ujs'
import Turbolinks from 'turbolinks'

Rails.start()
Turbolinks.start()

Sources