Search code examples
javascriptecmascript-6gulpgoogle-closure-compileres6-modules

Using Closure Compiler on ES6 Modules with Properties


So after a day of testing I'm almost there: using google-closure-compiler-js and gulp.js to minify / compile my ES6 module based "library".

There is just one problem left. How to call the properties of an imported module?

My test case consists of 3 files:

gulpfile.js

const gulp = require("gulp");
const compiler = require("google-closure-compiler-js").gulp();
const sourcemaps = require("gulp-sourcemaps");

gulp.task("default", function() {
// wildcards don't seem to work yet with the js version
// files need to be in reverse order of dependency...
return gulp.src(["src/boilerplate/main.js", "src/app.js"])
    .pipe(sourcemaps.init())
    .pipe(compiler({
        jsOutputFile: "app.min.js",
        compilationLevel: "ADVANCED",
        warningLevel: "VERBOSE",
        createSourceMap: true
    }))
    .pipe(sourcemaps.write("/"))
    .pipe(gulp.dest("./dist"));
});

src/boilerplate/main.js

var mod = {};
export default mod;

var foo = "FOO";
var bar = "BAR";

function foobar(join) {
    join = (typeof join === "string") ? join : "";
    return foo + join + bar;
}
Object.defineProperty(mod, "foobar", {value: foobar});

src/app.js

import boilerplate from "./boilerplate/main.js";

console.log(boilerplate["foobar"]("+"));
console.log(boilerplate.foobar("+"));

If you log boilerplate here, you see that it has the property "foobar". Symbol intact and all. Great!

Hence app.js' first call – bracket notation ["foobar"] – works perfectly fine. However, the dot notation does not. Calls to an imported module's property get minified by Closure Compiler!
You get your classic "Hi. I'm new to ADVANCED mode" error: TypeError: a.a is not a function.

How do I prevent this? (& let's assume exporting foobar directly isn't viable, for real world's sake.)

  • Do I need an exports file? How exactly would I do that? My first ventures in that direction did not take me far... is there a trick specific to google-closure-compiler-js?

  • Is there some kind of hack that allows for dot notation, until Closure Compiler adds a feature that automatically does not rename calls to properties of imports?

  • And what does this closure-compiler/wiki's article about JS-Modules#type-references advise me to do? Is this the answer I fail to implement?

    /** @param {foo.Foo} Foo */ function(Foo) {}


Solution

  • For ADVANCED mode compatibility, use Object.defineProperties to avoid quoted strings.

    Object.defineProperties(mod, {
      foobar: {value: foobar}
    });
    

    Anytime you use quoted strings, you will be required to use brackets (or other techniques) or risk violating the consistent property access rules.