Category Archives: Google Closure Compiler

Combining and Minifying JavaScript Files with Google Closure Compiler

GOAL

The goal is to combine and minify several JS files into one JS file in the right order.

PROBLEM

Let’s assume we have the following directory structure with three JS files.

Directory Structure

appdev
└── src
    ├── appdev.blackcow.js
    ├── appdev.js
    └── appdev.whitesheep.js

appdev.js

var AppDev = {
    modules : [],

    start : function () {
        var moduleName;
        for ( moduleName in AppDev.modules ) {
            if ( AppDev.modules.hasOwnProperty( moduleName ) ) {
                AppDev.modules[moduleName]();
            }
        }
    }
};

appdev.blackcow.js

AppDev.modules.blackcow = function ( config ) {
    console.log( 'in black cow module...', config );
};

appdev.whitesheep.js

AppDev.modules.whitesheep = function ( config ) {
    console.log( 'in white sheep module...', config );
};

SOLUTION

To pull this off, we will leverage Google Closure Compiler.

There are multiple ways to use this compiler, but if you are using Mac, the easiest approach, in my opinion, is to install this compiler using Brew.

brew install closure-compiler

Once installed, navigate to the appdev directory and run the following command:-

closure-compiler --js `find src/**/*.js` --js_output_file appdev.min.js

Now, a new file called appdev.min.js will be created.

appdev
├── appdev.min.js
└── src
    ├── appdev.blackcow.js
    ├── appdev.js
    └── appdev.whitesheep.js

The reformatted file content looks like this:-

AppDev.modules.blackcow = function ( a ) {
    console.log( "in black cow module...", a )
};
var AppDev = {
    modules : [], start : function () {
        for ( var a in AppDev.modules ) {
            if ( AppDev.modules.hasOwnProperty( a ) ) {
                AppDev.modules[a]()
            }
        }
    }
};
AppDev.modules.whitesheep = function ( a ) {
    console.log( "in white sheep module...", a )
};

The generated code is going to cause problem because the Closure Compiler basically appends each file content based on the order of the JS file names.

To fix this, we have to specify the dependencies so that the Closure Compiler will auto-sort the files correctly.

appdev.js

goog.provide('AppDev');

var AppDev = {
    modules : [],

    start : function () {
        var moduleName;
        for ( moduleName in AppDev.modules ) {
            if ( AppDev.modules.hasOwnProperty( moduleName ) ) {
                AppDev.modules[moduleName]();
            }
        }
    }
};

appdev.blackcow.js

goog.require('AppDev');

AppDev.modules.blackcow = function ( config ) {
    console.log( 'in black cow module...', config );
};

appdev.whitesheep.js

goog.require('AppDev');

AppDev.modules.whitesheep = function ( config ) {
    console.log( 'in white sheep module...', config );
};

After rerunning the compiler, the file content for appdev.min.js now looks correct.

var AppDev = {
    modules : [], start : function () {
        for ( var a in AppDev.modules ) {
            if ( AppDev.modules.hasOwnProperty( a ) ) {
                AppDev.modules[a]()
            }
        }
    }
};
AppDev.modules.blackcow = function ( a ) {
    console.log( "in black cow module...", a )
};
AppDev.modules.whitesheep = function ( a ) {
    console.log( "in white sheep module...", a )
};