Use Grunt to Develop a Web Application

Sstart web development recently and use Grunt to run tasks automatically. Once the the configuration file Gruntfile.js is set up, everything will run smoothly. It's straightforward to get started. And there're plenty of plugins that just make life much easier. I'm using jshint, htmlhint, less, watch. They're providing grunt task and you can use them out of box. Besides these plugins, you can also use any npm modules as you use them in your regular JavaScript file. I don't plan to repeat the setup of Grunt and the tasks I list above. I'm going to write down some changes I make to meet my requirements, as well as something that is worth noting.

1. Only compile changed files.

This is done by the task watch and there's an example here

var changedFiles = Object.create(null);
var onChange = grunt.util._.debounce(function() {
    grunt.config('jshint.all.src', Object.keys(changedFiles));
    changedFiles = Object.create(null);
}, 200);
grunt.event.on('watch', function(action, filepath) {
    changedFiles[filepath] = action;

It only checks changes in JavaScript files. Below are the problems I need to solve.

1. Watch changes on other files.

2. Handle the case when a file is deleted.

Let's first look at the parameters that are passed to the event handler for watch. they're action, filepath and target. The parameter target is listed in the above example. It's the target in the task watch which will execute when the file filepath is changed. And the action is "added", "changed", "deleted". And below is the solution I used for solving the above problem.

1. Record the target.
Use a dictionary of a target to a list of changed files instead of the other way as in the example. That is:

grunt.event.on('watch', function(action, filepath, target) {
    var filesForTarget = changedFilesForTargets[target];

    if (filesForTarget === undefined || filesForTarget === null) {
        filesForTarget = [];
        changedFilesForTargets[target] = filesForTarget;


Then in onChange, I iterate through the keys in changedFilesForChanges, and then change the corresponding task/target's file paths.

var targets = Object.keys(changedFilesForTargets);
for (var i = 0; i < targets.length; ++i) {
    switch (targets[i]) {
    case 'target1':
       // similar to the example.
    // more targets.

Note the target name here is the target you set in the task watch.
That's done for adding/changing any file.
2. Handle the action "deleted".
I have all source files (js, less, html) in one folder (let's say src/). And need to compile and copy the result files to another folder (let's say build/). When I delete a file in src/, I also want to delete it from build/. When the action is "deleted", I add the file path to a different list. And in onChange(), I compute the path in build/ and delete the file there. I use fs.unlinkSync() to delete a file. fs is an internal module in node.js

2. File path separator

I want to have it work cross platform. And I want to only copy the changed files from source folder to destination folder. I also need to copy files in other scenarios. So I need to build my own path. I found some cases where separators are mixed on Windows. '/', '\' are used in the same path. I ended up using a node.js internal module path to build the path.

3. Work on cygwin

This may be the trickiest one. I used to run grunt watch in cygwin. When I deleted a file, the watch task didn't start. I tried to debug a couple times and didn't understand it. But when I tried the node.js environment using Windows console. It picked up the deleted changes. So I just used the Windows command line now.

4. Log and debug

I log verbose message with grunt.verbose.writeln() and run commands with option "--verbose" to see more information.

I'm glad I found a task grunt-debug-task. After installed, I can run "grunt debug task:target" to start debugging. It requires node-inspector to run. It will start the Chrome browser and you can debugger your Gruntfile.js like a client side JavaScript file.

The above are what I do and what I learn during my project. It's always good to practice to learn. Grunt is working as expected in my project. Next will be to make it work with cordova and require.js optimization.


Leave a comment

Your email address will not be published. Required fields are marked *