Crunching/minifying HTML, CSS and JavaScript in Node.js with the minify module

; Date: 2013-01-01 12:33

Tags: Node.JS »»»» Minimized JavaScript »»»» AkashaCMS »»»» Minification »»»» Minimized CSS »»»» Minimized HTML

I've just added HTML/CSS/JavaScript minification to AkashaCMS (link is external), to minimize the size of static website files rendered by AkashaCMS. This will reduce page load times by decreasing the number of bytes required to be downloaded. We humans like blank space because spreading out information makes it easier to understand. With programming languages that becomes all the white space and indenting conventions. But the white space makes no difference to the computers that interpret the web pages, in fact it makes the web browsing experience slower because it takes longer to download or execute the web pages, or the CSS or JavaScript referenced by the web pages.

The technique for minifying files with Node.js is pretty simple, thanks to the minify (link is external) module.

The module minizes the size of HTML, CSS, and JavaScript files. To do so it uses the UglifyJS (link is external), clean-css (link is external), and html-minifier (link is external) modules. I'd been planning on using the UglifyJS module and hoping to find a module for HTML and CSS minification, and am glad to find someone had already done it.

Out of the box minify doesn't do its job over a while directory structure. Also, it writes the minimized version to a second file rather than writing back to the same file. For AkashaCMS, I wanted to minimize the output directory in-place.

The other thing minify doesn't do out of the box is iterate over a directory structure, and minimize everything it sees.

Fortunately it is straight-forward to implement a solution that solves both of these issues.

First thing is to pull in the modules

var fs         = require('fs');
var minify     = require('minify');
var filewalker = require('filewalker');

Now for the function - pulled from the AkashaCMS source

/**
 * Minimize a directory tree using the minify library.
 **/
module.exports.minimize = function(options, done) {
    filewalker(options.root_out, {
          maxPending: -1, maxAttempts: 3, attemptTimeout: 3000
    })
    .on('file', function(path, s, fullPath) {
        var stat = fs.statSync(fullPath);
        minify.optimize([fullPath], {
            cache: true,
            callback: function(pMinData) {
                fs.writeFile(fullPath, pMinData, 'utf8', function (err) {
                    if (err) done(err);
                    else {
                        fs.utimesSync(fullPath, stat.atime, stat.mtime);
                    }
                });
            }
        });
    })
    .on('error', function(err) {
        if (err) done(err);
        else { done(); }
    })
    .walk();
}

The filewalker module makes it fairly straight-forward to walk a tree of files. It emits events on everything it finds within the file tree. All we're interested in is the regular files found by filewalker.

The cache:true value passed to minify means it won't write the minified data out to a file, but store it in an in-memory cache. The callback is also invoked, receiving the data. That makes it a simple matter to write the minimized data back to the same file.

Notice we're also preserving file access and modify times.