Since I discovered it shortly after moving into the world of Hipchat (and later Slack) from the world of IRC, Hubot has been one of my favorite tools to make my life better. I’ve always enjoyed ChatOps, from the early days when we simply called it “writing eggdrop scripts,” and Hubot brought ChatOps into the modern age with its infinite flexibility and common platform that everyone could build on top of.

Hubot itself is written in CoffeeScript, and traditionally, most scripts have also been written in CoffeeScript. Unfortunately, I don’t like CoffeeScript much—I’ve always found it to be an ill-fitting crutch for Ruby developers who didn’t want to learn JavaScript, lest they cut themselves on the sharp edges of braces. Meanwhile, ES6 (ES2015, really, but I’m set in my ways…) has brought some really nice things to JavaScript development. I’m not going to list any here, but take a look at kangax’s ES6 compatibility table for an exhaustive list of everything ES6 brings to the table.

I’ve been slowly converting the scripts we use internally at Xamarin to at least be written in JavaScript, if not ES6—there hasn’t really been any good guidance on how to plug ES6 scripts into Hubot until recently, and what there was seemed like it was only half of the story. Today I sat down and figured out what needed to be done to make ES6 scripts automatically work.

Step 1: Install a few packages

Install babel-register, babel-preset-es2015, and babel-plugin-add-module-exports. Visit the respective module sites to learn more about them, but the short story is that the first two will make sure Babel works, and the last package makes sure Babel exports CommonJS-style defaults so that simple require calls will work.

Step 2: Create a .babelrc

At the top-level of your Hubot repo, create a .babelrc file with the following contents:

  "presets": [ "es2015" ],
  "plugins": [ "add-module-exports" ]

This will enable the ES6 preset and the module.exports plugin you installed earlier.

Step 3: Make sure Babel gets loaded early

Create a script that will be always be loaded first—I chose to name mine 000-import-es6.js. You can also make this a CoffeeScript script if you’d like, but I stuck with plain old JavaScript. The contents should look like this:

module.exports = function es6(robot) {};

The function export is not, in fact, required—it just makes Hubot shut up about expecting a function but receiving an object when checking module.exports.

Step 4: Profit!

You can now write scripts using ES6—all of the features are available to you to use. Put your scripts in the standard location for Hubot and they will happily be loaded and compiled at runtime—you’ll still get correct line numbers in stack traces though, for which I am infinitely thankful.

For Module Authors

If you’re authoring a Hubot module outside of your Hubot source tree, the process is almost exactly the same—at step 3, instead of creating a 000-import-es6.js file, you can create an index.js in the root of your package, with contents similar to this:

var realDefault = require("./src/foo");
module.exports = realDefault;

A possible alternate solution is to require babel-register, then export a function that uses Hubot’s robot.loadFile method to load your actual script entry point—I haven’t tried this, so I don’t know how well it would work, but I suspect it would be just fine.