Transpiling ES6 Modules to AMD & CommonJS Using Babel & Gulp

Ravi
Ravi
Share

ECMAScript 6 (a.k.a ECMAScript 2015 or ES6), the specification for next version of JavaScript has been approved and browser vendors are hard at work implementing it. Unlike the previous versions of ECMAScript, ES6 comes with a huge set of changes to the language to make it a good fit for the scale at which it is used today. Sitepoint has a number of articles covering these features.

Although browsers haven’t implemented all of the features yet, we can already take advantage of ES6 during development and convert it to a version that browser understands before shipping the application. Babel and Traceur are two of the leading transpilers used for this purpose. Microsoft’s typed superset of JavaScript, TypeScript can also be used as an ES6 transpiler.

I covered how ES6 can be used today to write Angular 1.x applications in one of my previous articles. In that article I used Traceur’s on-the-fly transpiler to run the application. Although it works, it is always better to transpile beforehand and reduce the amount of work to be done in the browser. In this article, we will see how the same sample application can be transpiled to ES5 and the modules into either CommonJS or, AMD using Babel to make it run on today’s browsers. Though the sample is based on Angular, the techniques of transpilation can be used with any valid ES6 code.

As ever, you can find the code to accompany this article on our GitHub repo.

The Importance of Modules

One of the key features in any language used to write large applications, is the ability to load different pieces of the application in the form of modules. Modules not only help us keep the code cleaner but they also play a role in reducing the usage of global scope. The contents of a module are not made available to any other module unless the other module explicitly loads it.

The importance of modules is not limited to applications. Even large JavaScript libraries can take advantage of the module system to export their objects as modules and the applications using the libraries import these modules as required. Angular 2 and Aurelia have started using this feature.

If you’d like a quick primer on using modules in ES6, please read: Understanding ES6 Modules

About the Sample Application

The subject of our sample application is a virtual book shelf. It consists of the following pages:

  1. Home page: shows a list of active books that can be marked as read, or moved to the archive.
  2. Add book page: adds a new book to the shelf by accepting title of the book and name of author. It doesn’t allow a duplicate titles.
  3. Archive page: lists all archived books.

The application is built using AngularJS 1.3 and ES6. If you look at any of the files in the app folder, you will see the keywords export and import used to export objects from the current module and to import objects from other modules. Now, our job is to use Babel’s Gulp tasks to convert these modules to one of the existing module systems.

But I’m Not Using Angular. I Just Want to Convert ES6 Modules to CommonJS/AMD

No worries! We got you covered. With a minor amount of tweaking the recipes demonstrated below can be used in any project involving ES6 modules. Angular is quite unimportant here.

Converting to CommonJS

CommonJS is a module system defined by the CommonJS group. It is a synchronous module system, in which the modules are loaded using the require function and exported using the exports property of the module object. The module object is expected to be available in all modules by default.

Node.js uses this module system, so it defines the module object natively and makes it available to your application. As browsers don’t have this object defined, we need to use a utility called Browserify to fill the gap.

Before we start, we will also need to install a few npm packages. These will enable us to use Babel and Browserify in conjunction with Gulp to convert our ES6 modules to one of the common module formats and package the application as a single file for the browser to consume.

  • gulp-babel — converts ES6 code into vanilla ES5
  • Browserify — lets you require('modules') in the browser by bundling up all of your dependencies
  • vinyl-source-stream — handles the Browserify module directly, avoiding need for gulp-browserify wrapper
  • vinyl-buffer — converts stream to a buffer (necessary for gulp-uglify which doesn’t support streams)
  • gulp-uglify — minifies files
  • del — lets you delete files and folders
  • gulp-rename — a plugin to let you rename files

You can get this lot by typing:

npm install gulp-babel browserify gulp-browserify vinyl-source-stream vinyl-buffer gulp-uglify del gulp-rename --save-dev

Now let’s start using these packages in our gulpfile.js. We need to write a task to take all ES6 files and pass them to Babel. The default module system in Babel is CommonJS, so we don’t need to send any options to the babel function.

var babel = require('gulp-babel'),
    browserify = require('browserify'),
    source = require('vinyl-source-stream'),
    buffer = require('vinyl-buffer'),
    rename = require('gulp-rename'),
    uglify = require('gulp-uglify'),
    del = require('del');

gulp.task('clean-temp', function(){
  return del(['dest']);
});

gulp.task('es6-commonjs',['clean-temp'], function(){
  return gulp.src(['app/*.js','app/**/*.js'])
    .pipe(babel())
    .pipe(gulp.dest('dest/temp'));
});

Hopefully there is nothing too confusing here. We are declaring a task named es6-commonjs which grabs any JavaScript files in the app directory and any of its sub directories. It then pipes them through Babel, which in turn converts the individual files to ES5 and CommonJS modules and copies the converted files into the dest/temp folder. The es6-commonjs task has a dependency named clean-temp, which will remove the dest directory and any files in it, before the es6-commonjs task runs.

If you want to make the code more explicit and specify the module system, you may modify usage of Babel as:

.pipe(babel({
  modules:"common"
}))

Now we can create a single bundled file from these individual files by applying Browserify and then minifying the output using the uglify package. The following snippet shows this:

gulp.task('bundle-commonjs-clean', function(){
  return del(['es5/commonjs']);
});

gulp.task('commonjs-bundle',['bundle-commonjs-clean','es6-commonjs'], function(){
  return browserify(['dest/temp/bootstrap.js']).bundle()
    .pipe(source('app.js'))
    .pipe(buffer())
    .pipe(uglify())
    .pipe(rename('app.js'))
    .pipe(gulp.dest("es5/commonjs"));
});

The above task has two dependencies: the first is the bundle-commonjs-clean task, which will delete the directory es5/commonjs, the second is the previously discussed es6-commonjs task. Once these have run, the task places the combined and minified file app.js in the folder es5/commonjs. This file can be referenced directly in index.html and the page can be viewed in a browser.

Finally, we can add a task to kick things off:

gulp.task('commonjs', ['commonjs-bundle']);

Converting to AMD

The Asynchronous Module Definition (AMD) system is, as the name suggests, an asynchronous module loading system. It allows multiple dependent modules to load in parallel and it doesn’t wait for one module to be completely loaded before attempting to load other modules.

Require.js is the library used to work with AMD. RequireJS is available through Bower:

bower install requirejs --save

We also need the Gulp plugin for require.js to bundle the application. Install the gulp-requirejs npm package for this.

npm install gulp-requirejs --save-dev

Now we need to write the tasks for converting the ES6 code to ES5 and AMD and then to bundle it using RequireJS. The tasks are pretty much similar to the tasks created in the CommonJS section.

var requirejs = require('gulp-requirejs');

gulp.task('es6-amd',['clean-temp'], function(){
    return gulp.src(['app/*.js','app/**/*.js'])
    .pipe(babel({ modules:"amd" }))
    .pipe(gulp.dest('dest/temp'));
});

gulp.task('bundle-amd-clean', function(){
  return del(['es5/amd']);
});

gulp.task('amd-bundle',['bundle-amd-clean','es6-amd'], function(){
  return requirejs({
    name: 'bootstrap',
    baseUrl: 'dest/temp',
    out: 'app.js'
  })
  .pipe(uglify())
  .pipe(gulp.dest("es5/amd"));
});

gulp.task('amd', ['amd-bundle']);

To use the final script on index.html page, we need to add a reference to RequireJS, the generated script and then load the bootstrap module. The bootstrap.js file inside app folder bootstraps the AngularJS application, so we need to load it to kick start the AngularJS application.

<script src="bower_components/requirejs/require.js" ></script>
<script src="es5/amd/app.js"></script>
<script>
  (function(){
    require(['bootstrap']);
  }());
</script>

Conclusion

Modules are a long overdue feature in JavaScript. They will be arriving in ES6, but unfortunately, their native browser support is currently poor. That does not however, mean that you cannot use them today. In this tutorial I have demonstrated how to use Gulp, Babel and a variety of plugins to convert ES6 modules to the CommonJS and AMD format that you can run in your browser.

And as for ES6? ES6 has gained a lot of attention in the community since it was announced. It is already used by several JavaScript libraries or, frameworks including Bootstrap’s JavaScript plugins, Aurelia, Angular 2 and several others. TypeScript has also added support for a handful number of ES6 features including modules. Learning about and using ES6 today, will reduce the effort required to convert the code in future.

Frequently Asked Questions (FAQs) on Transpiling ES6 Modules to AMD & CommonJS Using Babel & Gulp

What is the purpose of transpiling ES6 modules to AMD and CommonJS using Babel and Gulp?

Transpiling ES6 modules to AMD and CommonJS using Babel and Gulp is a process that allows developers to write code in the latest version of JavaScript (ES6) and then convert it into a version that can be understood by various JavaScript engines. This is particularly useful because not all browsers or environments support the latest ES6 features. By transpiling the code, developers can ensure that their applications will run smoothly across different platforms.

How does Babel help in transpiling ES6 modules?

Babel is a JavaScript compiler that is primarily used to convert ECMAScript 2015+ (ES6+) code into a backwards compatible version of JavaScript that can be run by older JavaScript engines. When it comes to transpiling ES6 modules, Babel provides plugins like “babel-plugin-transform-modules-commonjs” that transform ES6 module syntax into CommonJS syntax, which is widely supported.

What role does Gulp play in the transpilation process?

Gulp is a task runner that can be used to automate the transpilation process. It can be configured to watch for any changes in your ES6 files and automatically run the Babel transpiler on them. This saves developers from having to manually run the transpiler every time they make changes to their code.

Can I transpile ES6 modules to AMD instead of CommonJS?

Yes, you can transpile ES6 modules to Asynchronous Module Definition (AMD) instead of CommonJS. Babel provides a plugin called “babel-plugin-transform-modules-amd” for this purpose. AMD is particularly useful for handling large numbers of modules in a web browser environment.

What are the differences between AMD and CommonJS?

AMD and CommonJS are both module formats. The main difference between them is how they handle dependencies. AMD supports asynchronous loading of dependencies, which can improve performance in a browser environment. On the other hand, CommonJS loads dependencies synchronously, which is simpler and works well in server environments like Node.js.

How can I specify which ES6 features to transpile?

Babel uses a configuration file called .babelrc (or babel.config.js) where you can specify which ES6 features to transpile. You can list the plugins or presets you want to use in this file. For example, to transpile ES6 modules, you would include “babel-plugin-transform-modules-commonjs” or “babel-plugin-transform-modules-amd” in your configuration.

What are the benefits of using ES6 modules?

ES6 modules bring a number of benefits to JavaScript development. They introduce a standard syntax for importing and exporting functions, objects or values from modules, which can make your code more organized and easier to manage. They also support static analysis, which can improve performance and catch errors at compile time rather than at runtime.

How can I debug transpiled code?

Debugging transpiled code can be challenging because the code that is executed is not the same as the code you wrote. However, Babel provides a solution to this problem in the form of source maps. A source map is a file that maps the transpiled code back to the original source code, allowing you to debug your code as if it was running the original ES6 code.

Can I use Babel and Gulp with other JavaScript frameworks?

Yes, Babel and Gulp are not tied to any specific JavaScript framework. They can be used with any framework that supports ES6, including React, Angular, Vue.js, and more.

Are there alternatives to Babel and Gulp for transpiling ES6 modules?

Yes, there are several alternatives to Babel and Gulp for transpiling ES6 modules. These include TypeScript, Traceur, and Rollup. Each of these tools has its own strengths and weaknesses, so the best choice depends on your specific needs and preferences.