It would be helpful – and likely even necessary – to be able to use goog.require to import ES6 modules in order to migrate a large codebase to the ES6 module system.
The wiki mentions interop and demonstrates doing a CommmonJS require of an ES6 module, but doesn't mention using goog.require: https://github.com/google/closure-compiler/wiki/JS-Modules#module-interoperation
In basic experiments, importing a module by its transformed path (e.g. goog.require('module$path$to$module$file')) compiles correctly. This doesn't seem like a stable or supported way of importing a module though.
I put together some experiments at https://gist.github.com/rictic/f042c2adf30101597fa5bd34a23d992d (buildable internal link: https://goto.google.com/js-module-interop-experiment) demonstrating different combinations of goog.module, goog.provide and ES6 modules as best as I understand the situation today.
Related bug: https://github.com/google/closure-compiler/issues/1877
I really need to go document this. It works with this form:
import * as Event from 'goog:goog.event';
@ChadKillingsworth and that works for any namespace in your build, not just Google Closure Library ones?
This works for any namespace defined with goog.provide or goog.module.
However, we will probably restrict this so that it does not work with a module resolution mode of BROWSER. That syntax will never run in a browser without a build step.
But I'd also ask why goog.require doesn't work?
var Event = goog.require('goog.event');
@ChadKillingsworth This issue is for going the other direction. What should a user do, who wants to import an ES6 module from a file that uses goog.provide or goog.module?
In the terms of the gist, how would goog-user.js or global-user.js depend on es6-lib.js?
Here are all the options that I can see.
loadModule() and asynchronous import().So the core problem here: an import statement can only be used in a module, but a file with goog.provide is intended to execute in the global scope. I don't know how to reconcile these two things without a module loader.
Oddly enough, you can use CommonJS and the require statement to bridge these. Which is what babel and other transpilers do anyway.
some/goog/provide.js
goog.provide('some.goog.provide');
const foo = require('/some/esm/module.js').default;
console.log(foo);
some/esm/module.js
export default 'bar';
If you limit yourself to require statements, you can use it as a module loader and bridge between script and module seamlessly.
Now how this gets used internal to Google - someone else will have to answer that.
goog.provide should still work from an ES6 module because it explicitly writes to the toplevel window object, which is allowed and has the intended effect. Code that works fine under use strict, and is explicit about its exports via either goog.provide or window.foo = should be fine in that respect.
There are some more subtle things, like document.currentScript won't work, but from my experimenting so far I'm starting to suspect that most well behaved code wouldn't fail when loaded as a module, and almost all cases where it would could be caught by existing compiler warnings.
This appears to be working, I think this bug can be closed.
I'm able to do const es6Exports = goog.require('./es6-module.js'); from within a goog.module file. I can't require an ES6 module directly from a file that is not a goog.module file, but I can create a bridge goog.module that just reexports an es6 module, which is good enough for me.
Most helpful comment
I really need to go document this. It works with this form: