Getting import/export working ES6 style using Browserify + Babelify + Gulp = -5hrs of life

aaron
4 min readApr 9, 2016

--

Web browsers have traditionally been quite slow to keep up with the times. Back in the early days, there was a huge surge of development of features (IE vs Netscape) all to gain the largest market share. This probably took the efforts to solidify web standard slightly backwards as developers simply has no choice but to support which ever browser was the most feature complete. Some of those features even became the pseudo-standard (or just quirks) like how CSS renders or even how HTML tags / attributes are to be used.

We’re now in 2016 and its a great time to be a developer… or so I thought… There’s a sense for the web community to really push the next iteration of standards HTML5, CSS3, ES6 (aka ES2015). Most browsers nowadays ship with (or have experimental) builds that involve unconfirmed standard features. This leads to a bit of quirks but documentation these days is very good so you know if you can rely on it or not (e.g: http://caniuse.com/).

HTML5 and CSS3 have caught up and really changed what’s possible on the web (I love animations and Browser API support). However one key element of the web still lags behind a bit in terms of uniformity and support; the next iteration of Javascript (an implementation of ECMAScript, the language specification) — ES6.

One recent example where I lost so much time is using the new ES6 ‘import’ and ‘export’ module syntax, which I took for granted when using boilerplates or templates made by others (e.g. React). In NodeJS, ‘require’ was such an easy way to modularise your code into files as well as the ability to treat these modules as libraries (via a package manager like npm). In the Browser, this thing just doesn’t exist. And if you think about it, its quite difficult to achieve. There’s been efforts to take care of this but they’ve always been a bit awkward and complicated (I’ve RequireJS before and it was always a pain to setup and ended up being extremely fragile).

Here comes Browserify, a library which lets you ‘require’ code/modules in the same manner as you do in Node. Basically ‘Node modules for the client’. But there’s a catch; you can’t simply do require(‘myModule’) in the browser, because where is the browser going to fetch that resource from? So you actually gotta include the module somewhere on the page. Thus we end up having to bundle the javascript and send it all down to the client. Easy, we’ve been doing this type of stuff for a while using Gulp so its easy to collect files and process them. So that’s using Browserify + Gulp to get ‘require()’ working on the client.

// gulpfile.jsimport gulp from 'gulp';
import browserify from 'browserify';
import source from 'vinyl-source-stream';
import buffer from 'vinyl-buffer';
gulp.task('scripts', () => {
browserify(['myEntryPoint.js', 'myModule.js'])
.pipe(source('bundle.js')
.pipe(gulp.dest('dist/scripts'));
});
// index.html
<script src="dist/bundle.js></script>

We haven’t gotten to the ES6 syntax ‘import’ and ‘export’ keywords are much nicer and substantially more powerful than require() is — albeit the syntax isn’t as uniform/straightforward as I’d like it to be. Anyway, now if we want to write our code in ES6, since the majority of browsers don’t yet fully support ES6, we need to transpile it into ES5 via a compiler called Babel. This tool is like a pre-compiler, it’ll scan your files, converting the ES6 syntax into ES5 equivalents. However there’s one problem; ‘require()’ is not even in the ES5 syntax… So even if we compile we can’t use it… Guess what, you need to use a tool which does understand require(), and Browserify does.

This leads us to Babel + Browserify + Gulp. Seems like a simple enough path right? Wrong… Browserify isn’t very good with Babel directly and so I found out that you gotta use another library called ‘Babelify’ >_> Anyway, its a transform tool which Browserify uses to convert ES6 to ES5 and then does the magical dependency resolution.

// gulpfile.jsimport gulp from 'gulp';
import babelify from ‘babelify’;
import browserify from 'browserify';
import source from 'vinyl-source-stream';
import buffer from 'vinyl-buffer';
gulp.task('scripts', () => {
browserify(['myEntryPoint.js', 'myModule.js'])
.transform(babelify)
.bundle()
.pipe(source('bundle.js')
.pipe(gulp.dest('dist/scripts'))
.pipe(buffer()) // You need this if you want to continue using the stream with other plugins
});
// index.html
<script src="dist/bundle.js></script>

So… why did I suddenly lose 5 hours? Probably due to lack of proper research and understanding. But honestly, I think its due to the fact that there are just so many ways to do things that its hard to know where things go wrong or which way to take. This is one of the joys and frustrations that comes from working on the (literally) bleeding edge tools and features of the web. Its a shame that during my endless hunts there wasn’t as much concrete documentation regarding what everything is and how it fits together. Hopefully this information is useful for someone. If anything, I hope it helps future Aaron.

TL;DR: Browserify lets you use NodeJS-like requires in your code. If you want to use ES6 modules, you’ll need Babel/Babelify to convert your ES6 code into ES5, such that Browserify can understand it. Gulp is your friend in sequencing these tasks but there’s a couple of quirks due to the differences in libraries and output. Vinyl source and buffer help this.

Bunch of resources which helped me piece these things together (in rough order of usefulness for me)

--

--

aaron

Full-stack software engineer @ DMM.com. Enjoys Tea, Sake and a good sketching session