Tech

How to manage your JavaScript dependencies with Sprockets in 2021

Guillaume Briday profile picture

Guillaume Briday

It’s 2021, and you probably need to use JavaScript in your Rails applications more than ever. JavaScript’s ecosystem drastically changed over the last few years, in the good way.

Module bundlers have completely taken over the task runners, Babel is nearly everywhere and ES6 changed the way we write our code.

But on the down side, the overall complexity to manage our assets with these new tools has exploded. So in a big application like Per Angusta, it could become very complex to upgrade and probably not necessarily at all. Let’s see why and how we manage dependencies at Per Angusta.

Before starting, I’m Guillaume Briday, lead developer at Per Angusta and proud member of the Rails organization to manage and contribute to Webpacker.

Photo: Maxwell Nelson

Why you need dependencies at all?

To build a Website these days, we’ll need thousand lines of code. It could be CSS or JavaScript but in both cases we want to use robust code written by other people. It’s a good thing, Open source is meant for that!

For instance, we might want to install Bootstrap for quick prototyping or Flatpickr if you want a powerful datetime picker. It would take months or years to write it by ourselves. Because these libraries are used by thousands or millions of people, bugs are almost nonexistence and performance is probably high.

These packages have other dependencies that also have other dependencies and so on. All these dependencies might need different versions of the same library and you guess it, it will become very hard to manage this manually and upgrades might become almost impossible.

Most of the time, we ended up creating a vendor folder with every single dependencies in it with their dependencies included and no one touches it for years. It could be a security flaw and you won't be able to enjoy your favorites packages new features.

Package manager: Yarn

Yarn (and npm) is to JavaScript what Bundler is to Ruby. A Package Manager.

Its role is to deal with all the dependencies you might need in your projects. Instead of copy pasting tons of files in your vendor folder, Yarn will handle that for you in a nice way. If you have dependency conflicts, it will intelligently manage that for you and you could have two versions of the same package if it's required.

Moreover, having the well known vendor folder in your VCS makes finding package's version and upgrade in Pull Requests very tough.

With a package manager, you will declare all the dependencies you need with their version in a JSON file called package.json. It will be the single source of truth about your dependencies information.

Yarn will use this list of dependencies to install and manage them. No more vendor folder in your VCS and easier Pull Requests review and upgrades!

It’s definitely a huge win in our developer workflow. Moreover, it brings a real way to audit security vulnerabilities with automated tools like yarn audit or dependabot which is almost impossible with vendors folder. You can also audit your license compatibility with Licensed automatically.

Here is an example of a package.json:

{
  "dependencies": {
    "bootstrap": "4.4.1",
    "select2": "4.0.13",
    "trix": "1.3.1"
  }
}

Instead of having our vendor folder looking something like this with (potentially) unknown versions:

├── bootstrap
│   ├── bootstrap.min.css
│   └── bootstrap.min.js
├── select2
│   ├── select2.min.css
│   └── select2.min.js
└── trix
    ├── trix.min.css
    └── trix.min.js

After the installation, a node_modules folder will contain all your dependencies.

JavaScript dependencies in Rails applications

A few years ago, npm did not exist nor Yarn. We used to install our JavaScript and CSS dependencies as we just saw above or through bundler as Gem. It was handled via Sprockets and everything worked fine.

Unfortunately, JavaScript and CSS packages can’t be used “as is” with Bundler and Sprockets. Creators of packages have to make a Gem version of their packages. This drastically reduces the amount of packages available because almost no developer bother to do it since Yarn exists. Here is, for instance, the official Bootstrap gem.

So that would be cool to be able to use Yarn and Sprockets together, right?

How to use Yarn with Rails and Sprockets

But, what is Sprockets in the first place?

Sprockets is a Ruby library for compiling and serving web assets for Rails. Put simply, Sprockets does two things, it preprocesses then merges your assets into production-ready files. These assets might come from gems or from your vendor folder.

//= require bootstrap
//= require jquery
//= require whatever

Now instead of retrieving these files in the vendor folder, we could retrieve them in the node_modules folder and let Yarn manage it for us! All we have to do is to add additional assets in the Sprockets load path. In the config/initializers/assets.rb add this line:

Rails.application.config.assets.paths << Rails.root.join('node_modules')

Now we can install some packages. For instance:

$ yarn add handsontable

And link it in the Sprockets’ manifest.js file:

//= link handsontable/dist/handsontable.full.js

This link requires the file node_modules/handsontable/dist/handsontable.full.js under the hood!

Is it that simple? Yes! Sprockets is really straightforward.

Why not Webpacker only?

If you are using Rails 5.2+, you might have noticed that Webpacker is in the default Gemfile.

Webpacker is a wrapper around Webpack to make an easy integration with Rails. Webpacker is not meant to replace Sprockets.

With the quick evolution of JavaScript over the years, the JavaScript ecosystem has found the needs to go further than just compiling libraries into the same file for production.

Webpack is much more powerful than Sprockets when it comes managing JavaScript assets. With Webpack, you can:

  • Deal with modules instead of polluting global scope.
  • Tree-shake your code for better performance and avoid massive payloads.
  • Split files so only the JavaScript needed on the page will be loaded.
  • Write JSX or have completely new file format like Vue’s SFC.
  • And so on…

These features are cool but not necessarily required for your projects nor free for your teams.

The complexity of Webpack is far beyond Sprockets! Never forget it.

How to write modern JavaScript with Sprockets?

Sprockets 4 introduced many new features for writing modern JavaScript. One of them is the support of ES6 syntax with the Babel prepocessor included. It means that you can write ES6 JavaScript and transpile it for browsers that don’t support it natively. It’s completely transparent and happens a build time.

But depending on the browsers you target, you would probably not need transpilation at all.

For instance, this piece of code is written in ES6:

class PerAngusta {
  sayHello() {
    console.log('Hello')
  }
}

new PerAngusta().sayHello() // #=> 'hello'

const getUserName = ({ name }) => {
  console.log(name)
}
getUserName({ name: 'Steve' }) // #=> 'Steve'

In almost all browsers, this code will run “as is”. Which is perfect! But if you still need to support Internet Explorer, this code will completely crash.

And here’s come Browserslist. You can specify targeted browsers in a file called .browserslistrc:

IE >= 10, last 2 versions

With that configuration, we can tell Babel the we need to make the JavaScript compiled for IE 10 and greater and the latest 2 versions supported by all other browsers. Our initial code will be automatically transpiled to:

var PerAngusta = (function () {
  function PerAngusta() {}

  var _proto = PerAngusta.prototype

  _proto.sayHello = function sayHello() {
    console.log('Hello')
  }

  return PerAngusta
})()

new PerAngusta().sayHello() // #=> 'hello'

var getUserName = function getUserName(_ref) {
  var name = _ref.name
  console.log(name)
}

getUserName({ name: 'Steve' }) // #=> 'Steve'

This code is compatible with all browsers! It is ES5 JavaScript like we used to write manually in good old days. 😅

Browserlist is also used by multiple tools like Autoprefixer. So be sure to add this file in your projects even if you don’t write ES6.

Conclusion

Before going all the way down to the fancy new tool that pop up in your twitter’s feed you definitely should stick with what is working for your team and especially your customers. Sprockets is truly awesome and with importmap and jsbundling coming, you might not need to switch to Webpacker at all.