You are reading a page on Ben Weaver's Website.

Use the Google Closure Service With Node.js

2010.10.06 Read More javascript web node.js

Here's a short Node.js method for compressing javascript with Google's Closure Compiler Service. Besides being practical, it's a good example of sending a POST request using Node's http module.

First, here's an example application that compresses itself. The script reads itself into a string and passes result to closure.compile(). If an error is passed to the callback, it's thrown. Otherwise, the result is logged to the console.

var fs = require('fs'),
    closure = require('../lib/closure'),
    self = fs.readFileSync(__filename);

closure.compile(self, function(err, code) {
  if (err) throw err;

  var smaller = Math.round((1 - (code.length / self.length)) * 100);
  console.log('Myself, compiled (%d% smaller)', smaller);
  console.dir(code);
});

The compile() procedure accepts a string of javascript code and a next procedure. Most of the work is setting up the request and processing the response. The http.createClient() and Client.request() methods are used to start a POST request. HTTP-level and service-level errors are converted to node Error objects and passed directly to next.

function compile(code, next) {
  try {
    var qs = require('querystring'),
        http = require('http'),
        host = 'closure-compiler.appspot.com',
        body = qs.stringify({
          js_code: code.toString('utf-8'),
          compilation_level: 'ADVANCED_OPTIMIZATIONS',
          output_format: 'json',
          output_info: 'compiled_code'
        }),
        client = http.createClient(80, host).on('error', next),
        req = client.request('POST', '/compile', {
          'Host': host,
          'Content-Length': body.length,
          'Content-Type': 'application/x-www-form-urlencoded'
        });

    req.on('error', next).end(body);

    req.on('response', function(res) {
      if (res.statusCode != 200)
        next(new Error('Unexpected HTTP response: ' + res.statusCode));
      else
        capture(res, 'utf-8', parseResponse);
    });

    function parseResponse(err, data) {
      err ? next(err) : loadJSON(data, function(err, obj) {
        var error;
        if (err)
          next(err);
        else if ((error = obj.errors || obj.serverErrors || obj.warnings))
          next(new Error('Failed to compile: ' + sys.inspect(error)));
        else
          next(null, obj.compiledCode);
      });
    }
  } catch (err) {
    next(err);
  }
}

The capture and loadJSON methods are helpers that convert a Stream to a string and JSON to an Object.

Get the code here.