2009-12-30

Closure Compiler

I've been trying out Google's supposedly wicked Closure Compiler. It seems to work well for normal things; but, there still is a bit farther to go. I don't know if there is something wrong with the way that I write javascript; but, it seems that the Closure Compiler just can't handle it properly...

By the way, there was a very eye-opening and interesting post on the Google Code Blog. Basically, they came up with a wonderful idea: send all modules, split-up and commented-out, to the client, then eval() them on-the-fly. Now, why the hell didn't anyone think of that before!? D'OH!!! Anyway, props to the thinker who came up with that solution.

Well, okay, back to the point of this blog.

In order to see how well the compiler works, I wrote some slightly bloated code (see code appendices below).

For some reason, though, the output is really not as optimized as I would have hoped...

For example:
if((b=b)&&f===b.tagName){a=b.innerHTML||"";b.parentNode.removeChild(b)}a=(a=a)?a.replace(/^\s*\/\*\s*(.*?)\s*\*\/\s*$/,"$1"):void 0;if(a){a=["{",a,"}"].join("");eval(a)}

Could probably be further reduced to:
if(b&&f===b.tagName){a=b.innerHTML||"";b.parentNode.removeChild(b)}if(a){eval(["{",a.replace(/^\s*\/\*\s*(.*?)\s*\*\/\s*$/,"$1"),"}"].join(""))}

And even further yet to:
if(b&&f===b.tagName){eval(["{",(b.innerHTML||"").replace(/^\s*\/\*\s*(.*?)\s*\*\/\s*$/,"$1"),"}"].join(""));b.parentNode.removeChild(b)}

Maybe there's some cross-browser stuff (of which I should mention I am not really familiar). I dunno.

All-in-all, from what I've seen so far, it's really good at reducing unnecessary, or "dead", code. As such, I'd have say that I want to start using the Closure Compiler for my own projects! At this point it looks like nothing beats a hand-coder for optimization, though--I'll have to go over the output and manually fix all the (a = a) stuff.





Appendix


module.js
// ==ClosureCompiler==
// @compilation_level ADVANCED_OPTIMIZATIONS
// @output_file_name default.js
// @formatting pretty_print
// ==/ClosureCompiler==

// ADD YOUR CODE HERE
var xvn = function(doc){
 var SCRIPT = doc.createElement('script').tagName;

 var __loaded_modules = {};
 function mark_as_loaded(id) {
  __loaded_modules[id] = true;
 }
 function is_loaded(id) {
  return (id) && (__loaded_modules[id]);
 }

 function get_elm(id) {
  var elm;
  if (id) {
   elm = doc.getElementById(id);
  }
  return elm;
 }

 function grab_js(elm) {
  var js;
  if (elm && SCRIPT === elm.tagName) {
   js = elm.innerHTML || "";
   elm.parentNode.removeChild(elm);
  }
  return js;
 }

 function clean_js(js) {
  if (!js) return;

  return js.replace(/^\s*\/\*\s*(.*?)\s*\*\/\s*$/,"$1");
 }

 function eval_js(js) {
  if (!js) return;

  var new_js = ['{', js, '}'].join("");
  eval(new_js);
 }

 return {
  'load_module':function(id){
   if (!is_loaded(id)) {
    eval_js(clean_js(grab_js(get_elm(id))));
    mark_as_loaded(id);
   }
  }
 };
};
window['xvn'] = xvn;

module-compiled.js
window.xvn=function(d){var f=d.createElement("script").tagName,e={};return{load_module:function(c){if(!(c&&e[c])){var a;var b;if(c)b=d.getElementById(c);if((b=b)&&f===b.tagName){a=b.innerHTML||"";b.parentNode.removeChild(b)}a=(a=a)?a.replace(/^\s*\/\*\s*(.*?)\s*\*\/\s*$/,"$1"):void 0;if(a){a=["{",a,"}"].join("");eval(a)}e[c]=true}}}};

index.html
<!DOCTYPE html>
<html>
<head>
 <title>Module Loading Tests</title>
 <script type="text/javascript" src="./module-compiled.js"></script>
</head>
<body>
 <div id="click-me-man" onclick="my_xvn.load_module('mod1');">Load Mod1</div>

 <script type="text/javascript" id="mod1">
 /*
  alert("Hey!");
 */
 </script>

 <script type="text/javascript">
  var my_xvn = new xvn(document);
 </script>
</body>
</html>