Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at cf.Objective() 2013 (Bloomington, MN) with:

ClosureCompiler.cfc - A ColdFusion Facade For Google's Closure Compiler

By Ben Nadel on
Tags: ColdFusion

The other day, I took a look at calling Google's Closure compiler from a ColdFusion context. The code, in that example, was fairly complex, involved a lot of Java-Casting, and required a good deal of trial and error to get running. After it was complete, I figured it would be nice to create a ColdFusion component facade for the Closure compiler that would greatly simplify the interactions with the Java code.

Project: View the ClosureCompiler.cfc project on my GitHub account.

The goal of the ClosureCompiler.cfc project was not to create a one-to-one mapping of ColdFusion components to Java objects; rather, the goal was to significantly decrease the complexity of compiling JavaScript code. As such, I tried to move all the relevant methods (that I had experience with) into the ClosureCompiler.cfc; then, I let the facade deal with coordinating interactions between all the underlying Java classes.

Furthermore, unless a given method has to return a significant value (ie, is a "Getter" method), it returns the ClosureCompiler.cfc reference such that the vast majority of methods can be chained together to tell a story. So far, here are the public methods that I have created:

  • addExternContent( filename, content )
  • addExternFile( filepath )
  • addJavaScriptContent( filename, content )
  • addJavaScriptDirectory( directoryPath [, filter = "\.js$" ] )
  • addJavaScriptFile( filepath )
  • compile() :: String
  • getErrors() :: Array
  • getWarnings() :: Array
  • hasErrors() :: Boolean
  • hasWarnings() :: Boolean
  • setInputDelimiter( inputDelimiter )
  • setPrettyPrint( isPrettyPrint )
  • stripConsoleDebugging()
  • stripNamePrefix( namePrefix )
  • stripNamePrefixes( namePrefixes )
  • stripNameSuffix( nameSuffix )
  • stripNameSuffixes( nameSuffixes )
  • stripType( type )
  • stripTypePrefix( typePrefix )
  • stripTypePrefixes( typePrefixes )
  • stripTypes( types )
  • useAdvancedOptimizations()
  • useDefaultExterns()
  • useSimpleOptimizations()
  • useWhiteSpaceOptimizations()

While the ClosureCompiler.cfc doesn't expose the underlying compiler options object (at this time), it does expose three methods that correspond to the primary levels of compilation aggressiveness:

  • useAdvancedOptimizations()
  • useSimpleOptimizations()
  • useWhiteSpaceOptimizations()

These three methods are meant to be used in a mutually-exclusive manner. You wouldn't want to call more than one of these methods for any given compilation event.

To see how the ClosureCompiler.cfc ColdFusion component can simplify the use of Google's Closure compiler, let's take a look at one of the demos that comes with the project. In this case, I'm going to run the "Advanced Optimizations" example since that will most closely correspond to my previous blog post:

  • <cfscript>
  •  
  •  
  • // Create our Closure compiler facade.
  • compiler = new lib.ClosureCompiler();
  •  
  • // Configure the compiler and get code. Because our JavaScript
  • // code uses a number of native JavaScript methods, we have to
  • // use the "default exptens".
  • code = compiler
  • .setPrettyPrint( true )
  • .useAdvancedOptimizations()
  • .addJavaScriptFile( expandPath( "./js/input.js" ) )
  • .addExternFile( expandPath( "./js/extern.js" ) )
  • .useDefaultExterns()
  • .stripConsoleDebugging()
  • .compile()
  • ;
  •  
  • // Output the compiled code (remember, we have pretty-print on).
  • writeOutput(
  • "<pre>" &
  • htmlEditFormat( code ) &
  • "</pre>"
  • );
  •  
  • // If there were any arrors, output them.
  • for ( error in compiler.getErrors() ) {
  •  
  • writeDump( error.toString() );
  •  
  • }
  •  
  •  
  • </cfscript>
  •  
  • <!--- Try to consume the compiled script. --->
  • <script type="text/javascript" src="./js/extern.js"></script>
  • <script type="text/javascript">
  •  
  •  
  • // Include the code we compiled.
  • <cfoutput>#code#</cfoutput>
  •  
  • console.log(
  • "tricia ann smith ...",
  • getInitials( "tricia ann smith" )
  • );
  •  
  •  
  • </script>

As you can see, the ClosureCompiler.cfc methods tell a story that is fairly easy to understand.

NOTE: The stripConsoleDebugging() is a facade method for stripping all the "types" associated with the "console" and "window.console" usage. Essentially, it removes all references to console logging within the compiled code.

In most of the demos in the ClosureCompiler.cfc project, I am assuming that the compiler.jar file is part of the ColdFusion class paths; but, the ClosureCompiler.cfc is architected such that this assumption can be overridden. Internally, all of the Java object creation and initialization is quarantined within individual private methods. This means that if you need to use an external class loader, all you have to do is sub-class the ClosureCompiler.cfc and override the "create" methods for the various Java objects. In fact, the ClosureCompiler.cfc project comes with a "loader" example that uses Mark Mandel's famous JavaLoader.

As I become more familiar with the Closure compiler, I am sure that the features of this ColdFusion facade will expand; but, I intend to keep it as simple as possible. Probably, I'll end up adding methods to expose the underlying Java objects in case someone needs to get much more granular with their interactions.




Reader Comments

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.