by

Grunt.js: Minify, rename, and put it in another folder

Grunt Logo copyrighted, all rights reserved by Bocoup
Grunt Logo copyrighted, all rights reserved by Bocoup

If you know Grunt.js, this post is for you. Grunt is a super awesome node.js-based task runner. It makes development easier, your productivity faster, and your attractiveness… attractiver.

I don’t want to go through all 3,000+ reasons that Grunt is fantastic, so I’ll just give you one: You’re writing some JavaScript files. You’d like them to be minified, renamed, and placed in another directory… Every time you save a file. But, the Uglify task doesn’t do that!

Oh, but it does…

Your prerequisites

Before you get started, make sure you have the following things squared away:

  • Node.js is installed
  • You have a package.json file in your directory
  • Grunt is installed in your directory
  • you’ve run either of these from your command line
    • npm install grunt-contrib
    • npm install grunt-contrib-uglify
  • You’ve already started up a Gruntile.js

The Use-Case

I outlined this a little bit in the introduction, but let’s outline the exact requirements in more detail:

  • You want grunt to minify some JavaScript files
  • You want grunt to rename the minified files
  • You want grunt to put those minified files in a different directory

This means that /private/myFile.js gets minified, renamed to myFile.min.js, and then moved to the  /public folder. So, /private/myFile.js becomes /public/myFile.min.js. The kicker here is that we aren’t creating a directory inside of the current one, we’re moving stuff to a different directory entirely.

The Solution

Start with the Grunt API

When you look over Grunt’s Uglify documentation, you don’t see any examples with this scenario. That’s because renaming and moving files aren’t part of the Uglify API, but rather the Grunt API itself. We need to find out how to access information about a file.

Uglify and grunt.file

The grunt.file object is our friend here.

In particular, we need to use the expandMapping() method. This method takes three arguments, like so:

grunt.file.expandMapping(patterns, dest [, options])

So, what we need to do is wrap this in grunt’s uglify task. So we’ll start off with this:

grunt.initConfig({
	pkg: grunt.file.readJSON('package.json'),
	uglify: {
		min: {
		    files: grunt.file.expandMapping(['private/*.js'], 'public/', {})
		}
	}
});

All it amounts to is that we put grunt.file.expandMapping() as the value for the files property.

Add the options

That third argument is the options object. The real magic here is in the options. If you look at grunt.file.expandMapping, you’ll see two options in particular which are useful to us:

  • flatten: boolean
  • rename:function(dest, matchedSrcPath, options) {}

flatten will remove the the path component from the source files. And rename()…well, that’s pretty obvious.

So now, we want this:

grunt.initConfig({
	pkg: grunt.file.readJSON('package.json'),
	uglify: {
		min: {
		    files: grunt.file.expandMapping(['private/*.js'], 'public/', {
		    	flatten: true,
		        rename: function(destBase, destPath) {}
		    })
		}
	}
});

So we have an uglify task:

  • Inside is the minify task called min.
  • The min task has a filesproperty that’s using the grunt.file.expandMapping()method.
  • That method is going to look in my private/ directory for JavaScript files
  • and has public/as my destination
  • expandMapping() will flatten these files, and it will also give the option to rename them

Start renaming

The last piece here is the renaming. We want the rename property to return the new name, which we can do with this:

		        rename: function(destBase, destPath) {
		            return destBase+destPath.replace('.js', '.min.js');
		        }

The first argument is a destination, the second is the source’s path.

Put it all together

So what we’ll do is put this all together with a watch task, and we get this result:

module.exports = function (grunt) {
grunt.initConfig({
	pkg: grunt.file.readJSON('package.json'),
	uglify: {
		min: {
		    files: grunt.file.expandMapping(['private/*.js'], 'public/', {
		    	flatten: true,
		        rename: function(destBase, destPath) {
		            return destBase+destPath.replace('.js', '.min.js');
		        }
		    })
		}
	},
	watch: {
		scripts: {
			files: 'private/*.js',
			tasks: ['uglify:min']
		}
	}
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['watch']);
grunt.registerTask('min', ['uglify:min']);
};

The Big Idea

I did a lot of googling to figure out how to do this. StackOverflow had a question on this, and one answer in particular was actually pretty close to what I wanted. But that was an answer to the question, “how do I rename it?” It didn’t address, “how do I also move it out of the current working directory?”

This solution will minify, rename, and move the file. Happy grunting!