Search code examples
webpackgoogle-closure-compilergoogle-closure-librarytranspiler

How could I effectively convert google-closure javascript to modern ES6?


I have a code base that uses the google-closure style modules goog.provide and classes, etc.

I want to modernize this code base but it's roughly 15k lines of code and I was hoping a tool could help me.

These tools are broken, I tried many things to get them to work but they just wont. It's a lost cause. https://github.com/angular/clutz https://github.com/DreierF/closure-es6-converter

I think If I had a tool to update the closure classes and modules to the ES6 variants, I could do everything else by hand.

This tool works to update to a modern ES6 class syntax, but notably doesn't update the closure modules. https://github.com/lebab/lebab

Would this tool allow me to convert closure modules to es6 modules? Or does it only produce a minified output. https://webpack.js.org/plugins/closure-webpack-plugin/#usage-example


Solution

  • There are 2 documents you should read about this:

    ES6 modules and Closure interop

    and

    Migrating from goog.modules to ES6 modules

    Some highlights from each:

    ES6 modules can use goog.require like goog.modules to reference Closure files:

    const math = goog.require('goog.math');
    export const MY_CONSTANT = math.sum(1, 1, 2, 3, 5, 8, 13, 21);
    

    The Closure Library has the function goog.declareModuleId(/** string */ id), which, when called in an ES6 module, associates the ES6 module with a goog.module-like id (e.g. something that can be goog.require'd, goog.module.getd, goog.forwardDeclared, and goog.requireTyped, and does not create any global symbols). goog.requires for these symbols will return the entire module object, as if bound by import * as.

    goog.declareModuleId('example.es6');
    export const MY_CONSTANT = 'Hello!';
    

    And

    Closure module default exports Closure modules can have a "default" export via exports = value;. When using this syntax the module's exports become the default export. This is frequently used with modules that export a single class.

    goog.module('my.Class');
    class Class {}
    // Default export. goog.require for this module returns this class.
    exports = Class; 
    

    What about migrating Closure modules that call declareLegacyNamespace? goog.module.declareLegacyNamespace is not supported in ES6 modules. It breaks a fundamental principle of modules: that modules do not create global values. Its purpose was to allow migration from goog.provide to Closure modules. We do not support it when moving to ES6 modules.

    It is recommended you migrate all goog.provided files that are relying on the declareLegacyNamespace call to Closure or ES6 modules first, or at the very least have them call goog.module.get in a goog.scope to get references to modules. If this is not possible and migration of the Closure module is still desired, you'll need to follow similar steps to migrating a Closure module with default exports, except your *_shim.js file will call declareLegacyNamespace. You will only be able to delete this file once all the goog.provide files have migrated.