Skip to main content
Ben Nadel at cf.Objective() 2012 (Minneapolis, MN) with: Shawn Slaughter
Ben Nadel at cf.Objective() 2012 (Minneapolis, MN) with: Shawn Slaughter

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

By on
Tags:

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.

Want to use code from this post? Check out the license.

Reader Comments

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel