Levelling Up with Grunt: Project Scaffolding with grunt-init

Published on , in Tooling with no comments .

A few weeks ago when I announced my new design I promised a follow up post on my Grunt setup. If you don’t know what Grunt is, or if you aren’t sure if you’ll benefit from it, then I highly recommend you check out these two articles from 24 Ways and Integralist.

My grunt setup isn’t really anything special but I’ve noticed that when people post about Grunt there’s one thing they usually don’t cover and that’s grunt-init.

Grunt-init is a project scaffolding tool. I find that I usually use the same plugins and configuration for everything I build so grunt-init allows me to create a boilerplate project structure to use whenever I’m starting a new project. The directory structure, basic files (such as boilerplate HTML & CSS) and the Gruntfile (with configured plugins) are automatically created. This means I don’t have to manually configure Grunt with each new project.

Creating a Template

Templates for grunt-init should be located at ~/.grunt-init/ or %USERPROFILE%\.grunt-init\ for Windows users.

Templates have the following required structure.


/my-template
	template.js
	rename.json
	/root

The root sub-directory is your main template directory. All your files that you want to be copied to your project directory should be in there.

Rename.json is an optional file that allows you to specify the source path/name of a file and the destination path/name. So if you have a file that you want to store in a different location or with a different name in your project directory then you can specify it in here. You can also specify files that shouldn’t be copied by default. This can then be overridden based on what you enter in the prompts when running grunt-init. More on this later.

Template.js is the main template file where you define prompts etc.

A Sample Template

Here’s a sample template.json based on one of my own. You can see on lines 47–50 I check the answer given for the “Susy” prompt. If it was “yes” then Susy specific files get copied over to my new project. Those two files are listed in my rename.json to be ignored by default. I won’t go into any more details since the code is commented.


'use strict';

// Basic template description.
exports.description = 'Scaffolds a new project with GruntJS, SASS, MODX and optionally SUSY.';

// Template-specific notes to be displayed after question prompts.
exports.after = 'You should now install project dependencies with _npm ' +
				'install_. After that, you may execute project tasks with _grunt_. For ' +
				'more information about installing and configuring Grunt, please see ' +
				'the Getting Started guide:' +
				'\n\n' +
				'http://gruntjs.com/getting-started';

// Any existing file or directory matching this wildcard will cause a warning.
exports.warnOn = '*';

// The actual init template.
exports.template = function(grunt, init, done) {

	init.process({}, [
		// Prompt for these values.
		init.prompt('name'),
		init.prompt('title'),
		init.prompt('description'),
		init.prompt('version'),
		init.prompt('SUSY', 'no')
	], function(err, props) {
		// Files to copy (and process).
		var files = init.filesToCopy(props);

		// Actually copy (and process) files.
		init.copyAndProcess(files, props);

		// Empty folders won't be copied over so make them here
		grunt.file.mkdir('assets/img');
		grunt.file.mkdir('assets/css/plugins');
		grunt.file.mkdir('assets/js/vendor');
		grunt.file.mkdir('assets/js/plugins');
		grunt.file.mkdir('assets/js/src-map');
		grunt.file.mkdir('assets/modx');
		grunt.file.mkdir('assets/modx/chunks');
		grunt.file.mkdir('assets/modx/snippets');
		grunt.file.mkdir('assets/modx/plugins');

		// Copy files/folders depending on any options chosen
		if(props.SUSY == 'yes') {
			init.copy('config.rb');
			init.copy('assets/css/scss/_base.scss');
		}

		// Generate package.json file, used by npm and grunt.
		init.writePackageJSON('package.json', {
			name: props.name,
			description: props.description,
			version: props.version,
			devDependencies: {
				"grunt-contrib-concat": "~0.3.x",
				"grunt-contrib-uglify": "~0.2.x",
				"grunt-contrib-cssmin": "~0.6.x",
				"grunt-contrib-sass": "~0.4.x",
				"grunt-contrib-jshint": "~0.6.x",
				"grunt-contrib-watch": "~0.5.x"
				"jshint-stylish": "~0.1.4"
			},
		});

		// All done!
		done();
	});
};

My Gruntfile

Here’s my commented Gruntfile. I’m using the JSHint, Concat, Uglify, Sass, CSSmin & Watch plugins. I’m also using the JSHint Stylish reporter for a more readable output.


// Wrapper function with one parameter
module.exports = function(grunt) {
	// This banner gets inserted at the top of the generated files, such a minified CSS
	var bannerContent = '/*!\n' +
						' * <%= pkg.name %>\n'+
						' * Version: <%= pkg.version %>\n'+
						' * Build date: <%= grunt.template.today("yyyy-mm-dd HH:MM:ss") %>\n'+
						' */\n\n';

	// Project configuration.
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		jshint: {
			files: ['assets/js/src/*.js'],
			options: {
				forin: true,
				noarg: true,
				noempty: true,
				eqeqeq: true,
				bitwise: true,
				undef: true,
				unused: true,
				curly: true,
				browser: true,
				devel: true,
				jquery: true,
				indent: true,
				maxerr: 25,
				reporter: require('jshint-stylish')
			},
		},
		concat: {
			options: {
				banner: bannerContent
			},
			app: {
				src: ['assets/js/src/*.js'],
				dest: 'assets/js/app.js'
			},
			plugins: {
				src: ['assets/js/plugins/*.js'],
				dest: 'assets/js/plugins.min.js'
			}
		},
		uglify: {
			options: {
				banner: bannerContent,
				sourceMapRoot: 'assets/js/src/*.js',
				sourceMap: 'assets/js/src-map/app.min.js.map'
			},
			target : {
				src : ['assets/js/src/*.js'],
				dest : 'assets/js/app.min.js'
			}
		},
		sass: {
			dev: {
				options: {
					style: 'expanded',
					compass: true
				},
				files: {
					'assets/css/styles.css': 'assets/css/scss/styles.scss',
				}
			}
		},
		cssmin: {
			options: {
				banner: bannerContent
			},
			target : {
				src : ['assets/css/styles.css'],
				dest : 'assets/css/styles.min.css'
			}
		},
		watch: {
			sass: {
				files: ['assets/css/scss/*.scss'],
				tasks: ['sass', 'cssmin'],
			},
			scripts: {
				files: ['assets/js/src/*.js'],
				tasks: ['jshint', 'concat', 'uglify'],
			},
		}
	});

	grunt.loadNpmTasks('grunt-contrib-jshint');
	grunt.loadNpmTasks('grunt-contrib-uglify');
	grunt.loadNpmTasks('grunt-contrib-concat');
	grunt.loadNpmTasks('grunt-contrib-cssmin');
	grunt.loadNpmTasks('grunt-contrib-sass');
	grunt.loadNpmTasks('grunt-contrib-watch');

	grunt.registerTask('default', ['jshint', 'concat', 'uglify', 'sass', 'cssmin']);
	grunt.registerTask('jslint', ['jshint']);
};

Using a Template

To create a new project using a grunt-init template just run the following command in the root of your project directory:


$ grunt-init {template name}

Afterwards you just need to run npm install and the plugins defined in the package.json will be installed.

Leave a Comment