I'm pretty excited about using ECMAScript 6 features now via Babeljs - in particular, I'd love to start making my JavaScript code more modular using the new modules feature.
Here's what I've written so far:
// ECMAScript 6 code - lib.js
export const sqrt = Math.sqrt;
export function square (x) {
return x * x;
}
export function diag (x, y) {
return sqrt(square(x) + square(y));
}
// ECMAScript 6 code - main.js
import { square, diag } from 'lib';
console.log(square(11));
console.log(diag(4, 3));
I understand that I can transpile this code from ES6 to ES5 via babel on the command line:
babel lib.js > lib6to5.js
babel main.js > main6to5.js
But what do I need to do to use this code within my HTML?
For example, what would this index.html file look like:
<!-- index.html -->
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>ECMAScript 6</title>
<!-- What goes here?
How do I include main6to5.js and lib6to5.js to make this work in the browser? -->
<script src="?????"></script>
</head>
<body>
</body>
</html>
Thank you
Without using Modules:
If you are not using modules (imports/exports), then you can simply transpile your code into ES5 and include those ES5 files in your html.
Example:
// ES6 - index.js
// arrow function
var result = [1, 2, 3].map(n => n * 2);
console.log(result);
// enhanced object literal
var project = "helloWorld";
var obj = {
// Shorthand for ‘project: project’
project,
// Methods
printProject() {
console.log(this.project);
},
[ "prop_" + (() => 42)() ]: 42
};
console.log(obj.printProject());
console.log(obj);
Transpile to es5: babel index.js > es5.js
In index.html
, include <script src="es5.js"></script>
Will print out the following in console:
[2,4,6]
helloWorld
{"project":"helloWorld","prop_42":42}
Using Modules: Now if you are using modules (which is your case with lib.js
and main.js
), after converting your code into ES5 you also have to bundle them (from AMD/CommonJS/Modules to code that your browser can understand). You can do this with various build systems like gulp, webpack, browserify, etc. I'm going to use browserify as an example here.
Say my folder structure looks like this:
es6
|- src
|- lib.js
|- main.js
|- compiled
|- index.html
I run babel to transpile my files /src
to /compiled
folder: babel src --out-dir compiled
.
Now I have my ES5 code in the compiled folder. I install browserify in the cmd line and then bundle my main.js (entry point) in my compiled folder
~/es6 » npm install --global browserify
~/es6 » browserify ./compiled/main.js -o ./bundle.js
Now I have bundle.js
which is look like this:
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
"use strict";
exports.square = square;
exports.diag = diag;
var sqrt = exports.sqrt = Math.sqrt;
function square(x) {
return x * x;
}
function diag(x, y) {
return sqrt(square(x) + square(y));
}
Object.defineProperty(exports, "__esModule", {
value: true
});
},{}],2:[function(require,module,exports){
"use strict";
var _lib = require("./lib");
var square = _lib.square;
var diag = _lib.diag;
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5
},{"./lib":1}]},{},[2]);
Then in your index.html:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>ECMAScript 6</title>
<script src="./bundle.js"></script>
</head>
<body>
</body>
</html>
Then simply open up your index.html
, and your console should give you the following:
121 bundle.js:27
5 bundle.js:28
I started out by trying the same thing, but eventually found that Gulp really helped out a lot.
One thing to keep in mind: babel source.js > destination.js
won't polyfill new ES6 syntax. Your code right now isn't using any let
statements, destructured assignment, generator functions, or anything like that; but if you do add that at a later stage, you will need a more sophisticated transformation.
Here is an answer that explains how to setup the gulp file: Javascript 6to5 (now Babel) export module usage (Disclaimer: It is one of my answers :P)
Here are the steps specific to your case:
- Create a file called
gulpfile.js
in your directory with the following in it:
var gulp = require('gulp');
var browserify = require('browserify');
var babelify= require('babelify');
var util = require('gulp-util');
var buffer = require('vinyl-buffer');
var source = require('vinyl-source-stream');
var uglify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps');
gulp.task('build', function() {
browserify(['./lib.js', './main.js'], { debug: true })
.add(require.resolve('babel/polyfill'))
.transform(babelify)
.bundle()
.on('error', util.log.bind(util, 'Browserify Error'))
.pipe(source('app.js'))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(uglify({ mangle: false }))
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('./'));
});
gulp.task('default', ['build']);
- Run
npm install gulp browserify babel babelify gulp-util vinyl-buffer vinyl-source-stream gulp-uglify gulp-sourcemaps
to install the needed dependencies.
- Run
gulp
to bundle everything together.
- Use the bundled script in your HTML with this element:
<script src="app.js"></script>
The nice thing in addition to polyfills being added is that the code is minified, and you get sourcemaps, which means even in your developer tools, you can debug the ES6 code itself.
Note: While the import statement you are using is correct according to the ES6 draft specification, Babel won't like it. You will need to add a ./
, so that it looks something like this:
import { square, diag } from './lib';
I suspect this is because the transformation happens in Node.js, and this differentiates a file from a node module. As a side point, you can write ES6 for node do requires with import statements :)
[Note: I realise my answer is poor as it doesn't fully reflect
the questioners intention to use ES6 modules via babeljs in a front-end workflow.
Keeping the answer her for those that want to use ES6 modules within a webpage]
Try using jspm.io for loading the ES6 modules in the browser without transpiling upfront with babel.
A plunker can be found here
jspm works on top on of system.js which tries to be a loader for any module format (ES6, AMD, CommonJS)..
To make this work in my browser, I based myself on this jspm ES6 demo. Just copied System.js and es6-module-loader.js in a js/lib folder and copied your es6 js files in the js folder. Then the html just looks like this:
<html>
<body>
Test .. just open up the Chrome Devtools console to see the ES6 modules output using jspm
<script src="js/lib/system.js"></script>
<script>System.import("main");</script> </body>
</html>
A plunker can be found here
Would one of the --modules
switches compile into JS that can be directly included in a web page in the same way that the --script
flag will compile to browser JS (--script
can not be used with modules)? See https://github.com/google/traceur-compiler/wiki/Options-for-Compiling