Thursday, June 23, 2016

Using ES6 Modules in CoffeeScript

As we saw in my last post, https://blog.darkoverlordofdata.com/2016/06/mixing-globls-with-es6-module.html, we can use the TypeScript compiler to transpile import/export statements in our ES6 files. But were not really limited to ES6.

We can also use CoffeeScript as a pre-processor for the TypeScript compiler. To use modules in CoffeeScript is pretty easy, we can use the tick (`) to escape javascipt. Hopefully this will fully supported soon at the syntax level.

Suppose I create a module like this:

`import Batch from 'gdx/graphics/g2d/Batch'`

class SpriteBatch extends Batch 


`export default SpriteBatch`

and then I compile like this, where my jsconfig.json file references the output from coffee:

coffee -o src/js -bc src/coffee && tsc -p jsconfig.json 

And something like this is emitted by typescript into the outpur file:

define("gdx/graphics/g2d/SpriteBatch", ["require", "exports", "gdx/graphics/g2d/Batch"], function (require, exports, Batch_1) {
    "use strict";
    SpriteBatch = (function (superClass) {
        extend(SpriteBatch, superClass);
        function SpriteBatch() {
            return SpriteBatch.__super__.constructor.apply(this, arguments);
        }
        return SpriteBatch;
    })(Batch_1.default);
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.default = SpriteBatch;
});
```

Using typescript to build my coffescript. Why does that seem so wrong?

Tuesday, June 21, 2016

Mixing Globals with ES6 Module Implementation

Typescript 1.8 adds the new jsconfig.json format. So now, when the compilerOptions.module field is set to 'system' or 'amd' and compilerOptions.outFile is also set, typescript will bundle all of my modules, transpiling import/export polyfills. But it incudes no implementation for those polyfills. I could use SystemJs, but that is another external, and it's overkill - I only need to use module syntax internally, to see other modules in the same bundle.

My project consumes globals, such as PIXI.js. And to expose my API to scala.js, my library needs to be a global. But within my library, I want to use all the new es6 features, including import and export.

So my jsconfig.json looks like this:

{
    "compilerOptions": {
        "target": "es6",
        "module": "amd",
        "rootDir": "src",
        "outFile": "js/app.js"
    },
    "files": [
        "src/define.js",
        "src/lib/lib.js",
        "src/lib/shapes/Circle.js",
        "src/lib/utils/functions.js"
    ]
}

Typescript normalizes the interface, so no parameter checking is needed. For amd modules, the define
function is less than 10 lines of code:
After running tsc, the outFile js/app.js:

var define = (function (modules) {
    return (name, deps, callback) => {
        modules[name] = { id: name, exports: {} };
        let args = [(name) => modules[name].exports, modules[name].exports];
        for (let i = 2; i < deps.length; i++)
            args.push(modules[deps[i]].exports);
        callback.apply(modules[name].exports, args);
    };
}({}));
define("lib/shapes/Circle", ["require", "exports"], function (require, exports) {
    "use strict";
    //------ lib.js ------
    class Circle {
        constructor(radius) {
            this.radius = radius;
        }
    }
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.default = Circle;
});
define("lib/utils/functions", ["require", "exports"], function (require, exports) {
    "use strict";
    //------ lib2.js ------
    exports.sqrt = Math.sqrt;
    function square(x) {
        return x * x;
    }
    exports.square = square;
    function diag(x, y) {
        return exports.sqrt(square(x) + square(y));
    }
    exports.diag = diag;
});
define("lib/lib", ["require", "exports", "lib/shapes/Circle", "lib/utils/functions"], function (require, exports, Circle_1, functions) {
    "use strict";
    /**
     * Re-export it all to a namespace-like object hierarchy
     */
    window.lib = {
        shapes: {
            Circle: Circle_1.default
        },
        utils: {
            functions: functions
        }
    };
});