Testing for unused functions with Grunt
I’m relatively new to Grunt. I had used it before in several projects but it was already set up and working fine, to I didn’t really look into it. Besides, the sheer amount of options/plugins available put me off a bit. That and time being money.
But in between projects I have a bit of breathing space so the last couple of days I’ve been getting my hands really dirty.
I’ve used Ant before, but usually I just write some .bat files to run tasks. Although that always bugged me a bit. I mean DOS, come on… I used that thirty years ago when I first touched Windows and I’m still using it! But not any more (well a lot less anyway).
Grunt and Node are my new best friends.
So anyway, after Grunting a dummy project I fixed it for TinySort. In the lib folder of that project I found jquery.zen.min.js, a script I quickly threw together in the beginning of this year. So I decided to turn that into a new project (removing the jQuery depencency).
My Zen script is for using Emmet (formerly know as Zen) in your regular browser JavaScripts. Emmet, although written in JavaScript, is really intended for IDE plugins, and does much more than just expand HTML abbreviations. Which finally brings us to the title of this post.
Even though I just included a subset of the scripts I still ended up with 44kB in my minified result. The subset I used was still about 5000 lines but a lot of it was redundant.
So I needed a way to find out what functions were not used and remove them. And I had to do it automatically so I wouldn’t have any problems updating to a newer Emmet version (also, I had no intention of sifting through those 5000 lines by hand).
Finding the unused functions was tricky, but relatively simple. I injected a method call to each function telling me the line number it was called from. Then I wrote a test suite using every possible Emmet call that the script could encounter. This told me 274 out of 433 functions were not used.
So now what?
Finding unused functions is one thing, removing them is quite another. We’re dealing with named and anonymous functions, plus the named ones can be either expressions or declarations (function foo(){} -or- var foo = function(){};). I thought about prepending a dummy function and replacing all the unused functions with the dummy one. But that would not work for declarations because they could be referenced elsewhere.
Then there was the problem of selecting the entire function for removal. A simple regex wouldn’t cut it because you could can have nested functions, statements, comments and all those can have curly braces. I knew where the function started, so the only thing I could think of was meticulously step through every subsequent character and count up and down for every valid curly brace until I’d reach zero.
Then I had a brainfart: since it’s only the minified version that really counts, why not start every redundant function with ‘return false;’ and let Uglify do the heavy lifting? A few minutes later I had gone from 44kB to 26kB.
It left the file littered with occurrences of ‘function(a,b){return!1}’. Replacing those expressions with a dummy shaved off another 3kB.
So there you have it: a 50% decrease in filesize using code injection and Uglify.
The script is called ‘unused-functions’ and is up at NPM and Github.