The main thing I had to change in Closure Library is how goog.require() scripts are loaded. Native ES6 modules are loaded asynchronously as if they had the 'defer' attribute, but the standard good.require() implementation uses document.write so must run in a synchronous script to ensure everything's loaded in the right order. So I made an ES6 module that replaces Closure's default script importer with one that loads them asynchronously. It still ensures they're loaded in the correct order, with the one caveat that they will NOT run before whatever native ES6 module loaded them.
This means you can't immediately reference a goog.require()'d package in the root scope of an ES6 module, so this won't work:
goog.require("my.View");
export default class PageView extends my.View {...}
// ReferenceError: Can't find variable: my
But I made a callback function to help:
import whenScriptsLoaded from "./devLoader.js";
goog.require("my.app");
// You only need to do this once, since this callback will run after all packages
// required by this module and all other modules it imports have finished loading.
whenScriptsLoaded(() => {
my.app.init();
});
The actual execution order when run as raw source in-browser:
- All ES6 modules (in imported order)
- All Closure Library packages/modules (in goog.require'd order)
- The whenScriptsLoaded() callback.
You also can't load an ES6 module into a goog.require()'d package at all (at least not in a format that works with both web browsers and Closure Compiler), since only files loaded as true ES6 modules can use import.
It seems to build OK in Closure Compiler too, including with advanced compilation turned on. I did have to do some refactoring to get type annotations working right. One difference is Closure Compiler has to actually import an ES6 module in order to use it in a type annotation, but it can't handle circular dependencies. So if Class A imports Class B, you can't then have Class B annotate something with /** @type {./A} */ -- because now Class B will have an implied circular import of Class A. Instead, you need to define an interface class in a third file, have Class A @implement InterfaceA, and then Class B use @type {InterfaceA} in its annotations.
Here's the experimental loader module I wrote. Import this into your ES6 module before you start goog.require()ing stuff.