Loading Google Closure libraries from Node.js

January 10, 2012 at 01:24 AM | JavaScript | View Comments

While developing remora (which will be released "soon") I had to load Google Closure libraries from Node.js. As it turns out, I'm the first one who has had to do this, so a blog post seemed in order.

I've loaded my Closure library in Node by creating a Node module which loads Closure's base.js, tells Closure how to load JavaScript files, loads my Closure library, then exports everything (the goog name space and my namespace) through module.exports.

First, an execfile function is needed, which will load and evaluate a JavaScript file using a particular namespace:

var vm = require("vm");
var fs = require("fs");

function execfile(path) {
  var data = fs.readFileSync(path);
  vm.runInThisContext(data, path);

Note: for various reasons (one of which being that you can't control the value of 'this' from the 'vm' module) vm.runInThisContext must be used to load the scripts into the global context. Trying to load the into a sandbox seems to fail for reasons I can't fully explain yet.

Next, the Closure base.js and deps.js files are loaded and CLOSURE_IMPORT_SCRIPT is set to a thin wrapper around execfile:

var base_basedir = "../src/browser/";
execfile(base_basedir + "base.js");
execfile(base_basedir + "deps.js");
goog.global.CLOSURE_IMPORT_SCRIPT = function(path) {
  execfile(base_basedir + path);
  return true;

Now Closure libraries which use goog.require(...) and goog.provide(...) can be loaded:


(note: only the base library needs to be loaded; its dependencies will be taken care of by goog.require(...)).

Normal JavaScript packages can also be loaded:


Finally, module.exports is set:

module.exports = {
    goog: GLOBAL.goog,
    remora: GLOBAL.remora,

Note: because Closure scripts expect that require/provide will inject items into the global namespace, the global names created (ex, goog and remora) cannot be deleted from the global namespace.

And that's all there is to it. This file can now be loaded like any other Node module (ex, var my_closure_library = require("./closure_compat.js")).

For a complete example, see remora/src/node/remora.dev.js.

Permalink + Comments