Skip to content
This repository was archived by the owner on Aug 1, 2024. It is now read-only.

goog.module: an ES6 module like alternative to goog.provide

John Lenz edited this page Sep 24, 2015 · 14 revisions

goog.module

goog.module is a new way of structuring JavaScript files with a few advantages over the traditional goog.provide/goog.require structure. Basic example:

before:

goog.provide(‘foo’);

goog.require(‘baz.Quux’);

foo.Bar = function() { /* … */ };

after

goog.module(‘foo’);

var Quux = goog.require(‘baz.Quux’);

exports.Bar = function() { /* … */ };

goog.modules are similar to CommonJS or ES6 modules. Main features:

  • Inside a goog.module file, goog.require now has a return value: it returns the exports object of the required module. (If you goog.require a non-module file, the return value is the associated namespace.)
  • Top-level declarations are file-scoped, not global.
  • Module exports are not globals. They are accessed through the module exports object.
  • module files are always in strict mode.

The design doc for this should be http://go/goog.module

Here is a typical goog.module for a class:

goog.module('some.module.identifier');

// Require a traditional namespace:
var array = goog.require('goog.array');
// Require a module dependency:
var SomeClass = goog.require('my.namespace.SomeClass');

exports = goog.defineClass(SomeClass, {
  constructor: function() {
    doSomething();
  }
});

Here is a typical goog.module for a namespace:

goog.module('some.module.identifier');

// Require a traditional namespace:
var array = goog.require('goog.array');
// Require a module dependency:
var SomeClass = goog.require('my.namespace.SomeClass');

exports.method1 = function() {};
exports.method2 = function() {};

Who can use goog.module now

goog.module requires special loading. If you use custom bundling logic, it will need to be updated to handle goog.module. These techniques are already compatible with goog.module:

  • Karma test runner
  • Closure Compiler compiled code

FAQ

How do I use a goog.module from a traditional Closure file?

From within a goog.module, another goog.module’s exports are returned by the goog.require for that module. However, this isn’t the case from a traditional file. Here is an example of how to access the file:

goog.provide('my.namespace.Foo');

goog.require('some.module');

goog.scope(function() {
  var namespace = my.namespace;
  var module = <strong>goog.module.get('some.module')</strong>;

  namespace.Foo = function() {
    use(module);
  }
});

To migrate code it is possible to use goog.module.declareLegacyNamespace to allow the use of a goog.module in place of a traditional file without migrating them all in advance.

How do I write a unit test for a goog.module?

Traditionally, jsunit test files declared global functions using top level declarations, however a goog.module (like other module systems) top level declaration are hidden (the test runner will complain if it doesn’t see any test methods, so failing to export the tests shouldn't go unnoticed).

A closure style unit test would look like this:

goog.module('goog.baseModuleTest');
goog.setTestOnly('goog.baseModuleTest');

var jsunit = goog.require('goog.testing.jsunit');
var testSuite = goog.require('goog.testing.testSuite');

testSuite({
  testMethod: function() {},
});

goog.setTestOnly() isn’t required but is good practice. This pattern will work for ES6 modules as well.

Known Issues

goog.module causes problems with Angular injected classes

By default a goog.module files exports are sealed. If the export object is a constructor, then AngularJS won’t be able to add the $inject property it expects to. This happens both during development (AngularJS adds the $inject property to cache injections) and during production when using --angular_pass. You can work around this by disabling using the goog.SEAL_MODULE_EXPORTS define.

Custom Code Bundlers need to be updated

Common logic for building bundles with goog.module can be found here: https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/deps/ClosureBundler.java

Same limitations as goog.scope

Currently, goog.module is rewritten by the Closure Compiler as a goog.scope. We expect the compiler at some point to include first class support for goog.module. File bugs if this causes you problems.

Closure Debug loader uses synchronous XHR

Bundling is preferred.

Writing async tests

AsyncTestCase doesn't work well with goog.module and in fact it is no longer needed. Instead regular test can return a promise which should be resolved once test is finished. Example:

var Promise = goog.require('goog.Promise');
var testSuite = goog.require('goog.testing.testSuite');
testSuite({
  testAsyncMethod: function() {
    return new Promise(function(resolve) {
      // finish test after 1 second.
      setTimeout(function() { resolve(); }, 1000);
    })
  }
});