Code.Art.Web

Code.Art.Web

Profile Picture

Lorefnon

A minimal setup for using ES6 modules in Rails

Abstract

ES6 modules are the future of modular code organization in javascript. Let's explore how to use them today in Rails.

TL;DR

browserify-rails + babelify is a hassle-free solution if you are planning to use ES6 modules with sprockets, compared to the officially recommended sprockets-es6.

Using ES6 and ES7 features today with Babel

While ES6 adoption is progressively improving across browsers, and the sprockets team is planning to integrate ES6 features into Rails asset pipeline in near future, using a widely popular transpiler: Babel we can leverage many of those features right away. This ES6 compatibility table establishes babel as argubaly the best solution for transpiling ES6/ES7.

The specific aspect of interest for this post is ES6 modules feature which provides a standardized module system for javascript.

ES6 modules in Babel

While babel does have a solution for ES6 modules, rather than handling dependency resolution itself - it transpiles the modules to existing javascript based module systems - the most popular ones being AMD and CommonJS. This post does not go into a compartive analysis of them, but there is an excellent article by Addy Osmani which provides an in-depth elaboration on the topic.

Solutions for integrating Sprockets with babel

The solution recommended by the Babel team for using babel with rails, is through an experimental sprockets-es6 gem, which is intended to be a PoC for future work to be integrated into Sprockets. Quoting from the README:

This plugin is primarily experimental and will never reach a stable 1.0. The purpose is to test out BabelJS features on Sprockets 3.x and include it by default in Sprockets 4.x.

Javascript bundling woes

Apart from the experimental status, the key issue with using this gem is that it is non-trivial to get ES6 modules to work with it. The primary reason being that, as mentioned above, even though babel transpiles ES6 modules to CommonJS (or AMD), we still need to provide an implementation of the relevant module system that will enable the browsers to recognize the modules. This means we will have to include another dependency like sprockets-commonjs. However there is a caveat:

One caveat to the approach this library takes, is that dependencies loaded through require() will not be added to the dependency graph. This library will not parse the AST tree for require calls. This decision has been made for a variety of reasons, but it does mean you need to require files through both CommonJS and Sprockets.

Using AMD modules with requirejs-rails is something that works, however javascript community has largely adopted npm for package management framework. For example - jQuery plugin repository now states:

The jQuery Plugin Registry is in read-only mode. New plugin releases will not be processed. We recommend moving to npm, using "jquery-plugin" as the keyword in your package.json. The npm blog has instructions for publishing your plugin to npm.

Browserify-rails as an alternative

There is however a simpler solution: Using the gem browserify-rails which bridges sprockets and browserify. Browserify is a javascript bundler that leverages CommonJS :

Browserify lets you require('modules') in the browser by bundling up all of your dependencies

Browserify and the ecosystem around transforms

The great thing about browserify is that we can hook in transforms which can take care of additional pre-processing before the required files are bundled up. Of particular interest to us, is the browserify transform for babel - babelify which allows us to sidestep the caveat above. We need to have a node installation on the system though, just having a javascript runtime is not sufficient - but this is not much of an issue because node.js is now widely supported on all widely used platforms.

Installation

To get this to work we need to add browserify-rails to Gemfile:

gem "browserify-rails"

as well as a package.json in project root:

{
    "name": "something",
    "license": "MIT",
    "engines": {
      "node": ">= 0.10"
    },
    "dependencies": {
        "babel-preset-es2015": "^6.1.18",
        "babelify": "^7.2.0",
        "browserify": "~> 10.2.4",
        "browserify-incremental": "^3.0.1"
    }
}

If we want to use other javascript libraries available through npm we can include them directly in the package.json.

Configuring browserify and babelify

In addition to the above, we will also have to pass a configuration option to browserify to use babelify. This can be done by adding the following line to config/application.rb :

config.browserify_rails.commandline_options = "-t babelify"

You may have noticed that we have included the babel-preset-es2015 in our dependencies. While babel is a generic javascript transformation utility, it is this present that encorporates the plugins required to transform the syntax for next generation javascript to javascript that browsers of today can understand. The recommended way to specify options for babel transformer is through a .babelrc file in the project root:

{
  "presets": ["es2015"]
}

That is all the setup we needed. Now we can move on to the application code. There is a single caveat though: We can not directly start using ES6 modules in our top level files (typically application.js) but only in our required files:

Using ES6 modules in application code

So our application.js can be fairly minimal with a single require statement:

require('./main')

Now we can use ES6 modules in main.js

main.js:

import hello from './hello'

hello()

hello.js:

function hello() {
    alert('hello world');
}

export default hello;

If we run the server now and visit the home page, we should be greeted with a hello prompt.

comments powered by Disqus
Separator line
Separator line
Lorefnon

Full stack web developer and polyglot programmer with strong interest in dynamic languages, web application development and user experience design.


Strong believer in agile methodologies, behaviour driven development and efficacy of open source technologies.


© 2013 - 2015 Gaurab Paul


Code licensed under the The MIT License. Content and Artwork licensed under CC BY-NC-SA.


The opinions expressed herein are my personal viewpoints and may not be taken as professional recommendations from any of my previous or current employers.


Site is powered by Jekyll and graciously hosted by Github