Automated JavaScript Minification with Fabric

I love JavaScript compression and I try to use Julien Lecomte’s YUI Compressor as much as I can. Unfortunately, compressing every file manually on each deployment, or at each update is a hard habit to get into, and it’s also extremely boring. Now that I have implemented automated deployments using Fabric, I decided to automatically minify all my JavaScript files before each deployment as well.

The first step in the process was to get the YUI Compressor working on my Ubuntu development environment. I downloaded the latest version (2.4.1) from here, then I installed the GNU Java bytecode interpreter:

sudo apt-get install gij

After the installation is done, we can compress a JavaScript file like this:

java -jar /path/to/yuicompressor-2.4.1.jar core.js -o core.min.js

Finally, I put together a very simple Python script that finds all .js files in a given directory and passes them through the compressor:

#!/usr/bin/env python
 
# JS Minify
# Python script that can process all JavaScript files in a directory
# through the YUI Compressor
 

import os
 
from optparse import OptionParser
 

def main():
# Setup the option parser
parser = OptionParser(
usage="Usage: %prog [options] --dir=SOURCE_DIR",
version="%prog 0.1"
)

parser.set_defaults(
compressor="/path/to/yuicompressor-2.4.1.jar",
)

parser.add_option("--dir", dest="dir", help="Directory to look for JavaScript files")
parser.add_option("--compressor", dest="compressor", help="Path to the YUI Compressor jar file")

(options, args) = parser.parse_args()

if not options.dir:
parser.error("You have to specify a directory.")

# Loop through all files with .js extension
for file in [x for x in os.listdir(options.dir) if os.path.splitext(x)[1].lower() == ".js"]:
print "Compressing %s" % file

# Setup the input/output paths
input = os.path.join(options.dir, file)
output = os.path.join(options.dir, os.path.splitext(file)[0] + ".min.js"

# Pass the file through the YUI Compressor, save the output as .min.js
os.system("java -jar %(compressor)s %(input)s -o %(output)s" % {
"compressor": options.compressor,
"input": input,
"output": output,
}))

# Replace the original file with the minified version
os.rename(output, input)
 

if __name__ == "__main__":
main()

The last step is to call this script from my fabfile. I ended up having a bit of a convoluted setup, because I export the release using git-archive, which tarballs it (there is no other option), then I untar that file, call the script to compress JavaScript files, then tarball it again. Yeah, I know, it’s weird, but until I find a better solution, it does the job:

# Create a temporary local directory, export the given commit using git archive
local("mkdir ../tmp")
local("cd ..; git archive --format=tar --prefix=deploy/ $(hash) conf build | gzip > tmp/archive.tar.gz")
 
# Untar the archive to minify js files
local("cd ../tmp; tar -xzf archive.tar.gz; rm -f archive.tar.gz")
local("python /path/to/jsminify.py --dir=../tmp/deploy/build/media/js")
 
# Tarball the release again
local("cd ../tmp; tar -cf archive.tar deploy; gzip archive.tar")

And just like that, I never have to think about minifying my JavaScript again, it’s all done for me right before I deploy. I also enabled mod_compress on my lighttpd media server so this site should be scoring pretty high on bandwidth optimization now.

Comments (1)

Speak Your Mind

  • Your address won't be shown, it will be used for Gravatar icons if available.

  • Markdown is allowed.

Header 1    Header 2
========    --------

*italic*    **bold**

> A single level blockquote
>> A nested quote

A link: [example](http://url.com "title")
Or a quick URL: <http://url.com>

1. Numbered list item 1
2. Numbered list item 2

* Unordered list item 1
* Unordered list item 2

Any paragraph with 4 spaces to 
the left is a preformatted code block

Language specific code blocks with 
syntax highlighting are supported too:

@@ python
import antigravity
@@ end