09 Jul 2012

Try Harmony modules today

If you're interested in trying out the new proposed module syntax for ECMAScript Harmony, James Burke has developed a useful plugin that will transform it into regular JavaScript for use in any browser.

For this example, we're going to use the Jam JavaScript package manager to install the Harmony Modules plugin, but you can also set this up manually by grabbing the source and doing some config. Get Jam, create a new directory, then cd to the new directory and do the following:

jam install hm

You're now ready to write some Harmony modules!

Hello, world!

The first thing to note, is that we're going to use the .hm extension to differentiate it from regular JavaScript. Create a file called hello.hm and add the following:

export function say(name) {
    alert('Hello, ' + name + '!');
}

Now, let's create index.html:

<script src="jam/require.js"></script>

<script>
    require(['hm!hello'], function (hello) {
        hello.say('world');
    });
</script>

The hm! part just tells the require function to use the Harmony modules plugin to load hello.

Testing it out

The hm plugin uses AJAX during development to get the source of your Harmony code and transform it before execution. This is great, because it lets you edit your Harmony modules and just hit refresh in the browser to see your changes. However, this does mean you can't use the file:/// protocol. Instead, we're going to use a web server. For this kind of testing I like to use the http-server module for Node.js, which comes with a handy little command-line web server:

npm install -g http-server
http-server .

If you already have one installed, feel free to use that instead. Next, open up http://localhost:8080/ in your browser and you should see an alert box saying "Hello, world!". Success!

Requiring modules

So, we've defined a simple hello module with a single exported function say. How do we use this module in other Harmony code? Let's create another module called app.hm:

module hello = 'hello';

export function init() {
    hello.say('solar system');
}

Next, we update index.html to require the new app.hm module:

<script src="jam/require.js"></script>

<script>
    require(['hm!app'], function (app) {
        app.init();
    });
</script>

Refresh the page in your browser, and you should now see "Hello, solar system!".

Importing specific functions

Harmony modules let you import specific exported properties from a module too. Let's update app.hm to use import for the hello.say function:

import {say} from 'hello';

export function init() {
    say('galaxy');
}

Another refresh, and you should see "Hello, galaxy!". You can also change the local reference for the function you're importing. Since say isn't very meaningful inside app.hm, let's use sayHello instead:

import {say: sayHello} from 'hello';

export function init() {
    sayHello('universe');
}

See the transformation

If you're interested in seeing how the hm plugin transforms your code into regular JavaScript. Add the following config options to index.html:

<script src="jam/require.js"></script>

<script>
    require.config({
        config: {
            hm: {logTrasform: true}
        }
    });
    require(['hm!app'], function (app) {
        app.init();
    });
</script>

Now when you refresh the page, open up your browser console (CTRL+SHIFT+I in Chrome), and you should see the before and after source code displayed.

Compilation

So, you've had some fun trying out parts of the proposed module syntax for Harmony. You want to release something, but loading modules via AJAX and transforming on the fly is too slow for production. This is where the Jam compile command comes in.

Pre-compiling our example app:

jam compile -i hm\!app -o jam/require.js

If you refresh the page and take a look at the network tab, you should see that require.js is now the only script being loaded. You can continue to add and use new modules without having to run this step again, just be sure to run it each time before you use it in "production". If you're not going to require any uncompiled modules you can greatly reduce the filesize by excluding the hm plugin. Of course, I really wouldn't recommend using this for anything that matters. This is not a new standard yet!

Shortcomings

The require-hm project is still a work in progress, as is the Harmony modules syntax. As such, not every part of the proposed spec is supported by the hm plugin yet. I suggest you take a look at the README on GitHub for more information on what you can and cannot use. Have fun, and discussion on the new module syntax is most welcome in the comments.