Archive for the ‘shell’ Category

Articles

Combining and minifying files (in order) using Shell Scripts

In shell,Uncategorized on October 10, 2013 by Matt Grande Tagged: , , , , ,

I’ve been working on an application using Backbone.js recently. I really enjoy it, however I realised today that I now have over 100 javascript files in my project. This means that every time my page loads, the browser is making 100 separate HTTP requests. Very quickly, your site slows down.

The obvious solution is to combine all my scripts into one file and minify. I’ve done this several times before, but I needed to make sure that certain files loaded in a certain order, so I whipped up a shell script to ensure some of the files are pushed to the front of the line.

Having never really done that much shell scripting before, this wasn’t the easiest task for me. To help save others some time, I’ve attached my script and documented it pretty well. Let me know if you have any questions!

screenshot

Above, you’ll see our (sample) folder structure. Under the models subfolder, we have tab.js, which needs to be loaded before subtab.js.

Below, you’ll see the gist of what I’m doing. If you have any sort of shell experience, I’m sure this isn’t a revelation, but I wish I had something like this when I started writing earlier today!


#!/bin/bash
# This method returns 1 if a string is contained in an array, and 0 if it is not.
containsElement () {
local e
# For each element in the second argument to this method
# The ${} indicates that this is an array, the @ indicates we want all
# elements, and the 2 denotes that this is the second argument to this
# method.
for e in "${@:2}"
do
# If the current element, $e, is equal to the first argument passed
# into this method, $1, return 1
[[ "$e" == "$1" ]] && return 1
done
# We'ver reached the end of the loop, return 0
return 0
}
# The directory your JavaScript files are in.
JS_DIR='js'
# A temporary file that will be deleted upon every run
JS_TEMP="$JS_DIR/_combined.js"
# The final output file
JS_COMBINED_FILE="$JS_DIR/combined.js"
# Get an array of all of the javascript files.
allFiles=(models/*.js)
# Create a new array of just the items that need to be moved to the front.
modelsToBeMovedForwards=(models/tab.js)
# Get the length of the allFiles array. There's a lot going on in this line.
# The $ indicates that we're 'getting' (rather than setting)
# {} indicates that this is an array
# The # says that we want the length
# [@] says we want all of the items in the array
length=${#allFiles[@]}
# Loop through the first array. Remove the items that need to be moved to the front.
for ((i=1; i<$length; i++))
do
# Call containsElement, declared above
containsElement "${allModels[$i]}" ${modelsToBeMovedForwards}
# If the results of the method, $?, is 1, remove this item from the array.
if [[ "$?" == "1" ]]; then
unset allModels[$i]
i=$i-1
fi
done
# Concatonate the arrays.
final=("${modelsToBeMovedForwards[@]}" "${allFiles[@]}")
# Loop through each item…
for F in ${final[@]}; do
CURR_FILE="$JS_DIR/$F"
# … and shove it in our temp file using `cat`
cat $CURR_FILE >> $JS_TEMP
done
# Clear the old combined file
> $JS_COMBINED_FILE
# Compress the temp file into the real file using the YUI Compressor
java -jar yuicompressor-2.4.8.jar $JS_TEMP -o $JS_COMBINED_FILE
# Remove the temp files
rm $JS_TEMP
# Optional: Add the file to git.
# git add $JS_COMBINED_FILE

view raw

compress.sh

hosted with ❤ by GitHub

A few things to note:

  1. I highly recommend adding this code as a git hook, if you want your combined JS to be in source control
  2. The heavy lifting is done with the fabulous YUI Compressor, which requires Java to be installed (It also does CSS minification).
  3. Remember to chmod your compression script to allow you to execute or else it won’t work!