The simplicity of CfStatic

7 October 2011

RIAforge hosts many great ColdFusion-related OSS projects, but it's actually not that often that I come across one which directly and comprehensively scratches a particular itch. It's even rarer for a project to hit two nails on the head, but Dominic Watson's recently released CfStatic does just that.

1. Minifies and concatenates Javascript and CSS

Even before Google tweaked its algorithms to favour faster sites I'd been interested in improving page loading times and used YSlow and PageSpeed to identify ways in which I could optimise client-side performance.

GZipping, web server caching and static domains are among the good practices I've adopted, but the two that kept eluding me were:

  • Minify your JS/CSS
  • Concatenate your JS/CSS files to reduce the number of HTTP requests

One of these days, I told myself, I will learn ANT and get it to deal with that on deployment so that I'm still able to edit the separated, un-minified files.

That day has never arrived, but now there's no need as CfStatic will handle both the minification and concatenation ("stitching together") of my CSS and JS files. And it does this dynamically: in real-time as you develop, not just as a final deployment step.

Integrating CfStatic into your apps is straightforward:

  1. Prepare your CSS/JS by grouping them into a sensible folder structure and marking any dependencies between files.
  2. Create an instance of CfStatic, telling it about your folder structure.
  3. Tell CfStatic which files you want to include for specific requests.
  4. Use CfStatic to output the selected CSS and JS at the appropriate points in your HTML.

CfStatic allows quite a bit of flexibility to match your own approach, but here's a simple example using the default config options just to give you an idea.

1. Prepare your CSS/JS

Organise your static files into a folder structure that will make sense to you as you develop. Remember that splitting up files is fine because they will be joined together by CfStatic at runtime.

Screenshot of expanded folder structure

It's generally a good idea to group files that need to be output together into folders (or "packages") which will be compressed and joined together as a single file. In each of the CSS and JS folders I have a core package which I will want to output on every page, and a specific package just for my contact page.

To ensure files are processed in the right order, you can specify that a given file depends on another by using JavaDoc style comments (acceptable in both CSS and JS) at the top of the dependent file. For example, if layout.css should come before forms.css, add the following to the top of forms.css.


/**
* Description: Styles for forms
*
* @depends /core/layout.less
*/

The @depends attribute will be read by CfStatic which will make sure layouts.css is processed first.

You'll also want to create a folder where CfStatic can write the minified files. By default this will be min below your main static assets folder.

2. Instantiate CfStatic

Next we need to create an instance of CfStatic so that we can use it. Caching it in the Application scope will often be a good idea, but having multiple, shorter-lived instances may be appropriate in cases where the configuration needs to vary between requests.

Application.cfc


<cfcomponent>
<cfscript>
	this.name	=	"cfStaticTest";
	
	function onApplicationStart()
	{
		var config	=	
		{
			staticDirectory=GetDirectoryFromPath( GetCurrentTemplatePath() ) & "assets\"
			,staticUrl="/assets/"
		};
		application.cfstatic	=	New org.cfstatic.CfStatic( argumentCollection=config );
	}

</cfscript>
</cfcomponent>

You'll need to create a mapping so that org.cfstatic.CfStatic points to the CfStatic CFC and package you've downloaded and extracted. (NB: I'm using CF9 style instantiation via New(), but CfStatic supports older versions and CreateObject() syntax will work just as well.)

The config tells CfStatic how to find and address the main static assets folder. By default it will concatenate all files in the same folder (package) into one minifed file, but other options are available.

3. Select files to include

Now we can tell CfStatic which files we want to include on each page view using the include() method:


<cfsilent>
<cfscript>
	application.cfstatic.include( "/css/core/" ).include( "/js/core/" );
</cfscript>
</cfsilent>
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="utf-8"/>
	<title>CfStatic test</title>
</head>
<body>
	<p>CfStatic Test</p>
</body>
</html>

Notice how I've chained the include() calls so that the CSS and JS files are all specified with just one line of code.

If I wanted to add page-specific files, such as for my Contact page, I'd just include those as well on that particular request:


application.cfstatic
	.include( "/css/core/" )
	.include( "/js/core/" )
	.include( "/css/contact/" )
	.include( "/js/contact/" );

4. Output the CSS and JS

Having told CfStatic which files we want for a given request, all that's left is to get it to output links to the processed files in our HTML view using the renderIncludes() method. For optimal performance, stylesheets are typically output in the <head>, while Javascripts are normally placed just before the closing </body> tag.


<cfsilent>
<cfscript>
	application.cfstatic.include( "/css/core/" ).include( "/js/core/" );
</cfscript>
</cfsilent>
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="utf-8"/>
	<title>CfStatic test</title>
	<cfoutput>#application.cfstatic.renderIncludes( "css" )#</cfoutput>
</head>
<body>
	<p>CfStatic Test</p>
	<cfoutput>#application.cfstatic.renderIncludes( "js" )#</cfoutput>
</body>
</html>

When we run the page, CfStatic will generate the minified/concatenated files in the min folder from our "source" files in css and js:

Screenshot of min folder showing generated files

If we view the HTML source of the page, we see that CfStatic has added <link> and <script> tags which include the generated files in the min folder. Instead of five separate http requests to our unminified core JS/CSS, we now have just two and the contents are minified.


<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="utf-8"/>
	<title>CfStatic test</title>
	<link rel="stylesheet" href="/assets/min/core.min.css?20111006052201" media="screen, projection" />
</head>
<body>
	<p>CfStatic Test</p>
	<script type="text/javascript" src="/assets/min/core.min.js?20111006051712"></script>
</body>
</html>

Notice the query string added to each url. This is based on the time the source files were last modified so that browsers will be encouraged to flush their local cached versions whenever you update the source. The minified files are also regenerated automatically whenever a change is detected.

There's more...

This is just a simple example of usage and there are other features/options I haven't touched on, so please do consult the documentation.

Bleeding edge improvements

Bear in mind also that CfStatic is still new and in active development. At the time of writing the latest downloadable release is Beta 1.1, but Dominic's been working on a number of issues and minor improvements—relating to the "cacheBusting" in particular—and I'd recommend getting the latest committed version from GitHub.

2. Compiles LESS files on the server

The second of my itches satisfyingly scratched by CfStatic is server-side compilation of LESS... but I'll discuss that in another post: The simplicity of LESS CSS with CfStatic.

Comments

  • Formatting comments: See this list of formatting tags you can use in your comments.
  • Want to paste code? Enclose within <pre><code> tags for syntax higlighting and better formatting and if possible use script. If your code includes "self-closing" tags, such as <cfargument>, you must add an explicit closing tag, otherwise it is likely to be mangled by the Disqus parser.