| name | grunt-task-runner |
| description | Expert guidance for Grunt task runner automation. Use when setting up Grunt, configuring Gruntfile.js, creating build tasks, optimizing JavaScript/CSS, or automating frontend workflows. |
What is Grunt?
Grunt is a JavaScript task runner that automates repetitive tasks like minification, compilation, unit testing, and linting. It uses a configuration-based approach with plugins to handle various build tasks.
When to Use Grunt
- Automating frontend build processes
- Minifying JavaScript and CSS files
- Concatenating multiple files
- Running tests and linting
- Watching files for changes and auto-reloading
- Deploying static assets
- Legacy projects already using Grunt
Grunt vs Other Tools
- Grunt: Configuration-based, extensive plugin ecosystem, good for legacy projects
- Gulp: Code-based (streams), more modern, better for complex pipelines
- npm scripts: Built into Node.js, simpler for basic tasks
- Webpack: Module bundler with built-in optimization
Installation and Setup
Installing Grunt CLI
npm install -g grunt-cli
Installing Grunt in a Project
npm install grunt --save-dev
Basic Project Structure
myproject/
├── package.json
├── Gruntfile.js
├── src/
│ ├── js/
│ ├── css/
│ └── images/
└── dist/
├── js/
├── css/
└── images/
Gruntfile.js Structure
Basic Template
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['concat', 'uglify']);
};
Loading Plugins Automatically
require('load-grunt-tasks')(grunt);
Common Tasks
Concatenation (grunt-contrib-concat)
concat: {
options: {
separator: ';',
},
dist: {
src: ['src/js/*.js'],
dest: 'dist/js/bundle.js',
},
}
Minification (grunt-contrib-uglify)
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n',
},
dist: {
files: {
'dist/js/bundle.min.js': ['<%= concat.dist.dest %>'],
},
},
}
CSS Minification (grunt-contrib-cssmin)
cssmin: {
target: {
files: [{
expand: true,
cwd: 'src/css',
src: ['*.css', '!*.min.css'],
dest: 'dist/css',
ext: '.min.css',
}],
},
}
File Watching (grunt-contrib-watch)
watch: {
scripts: {
files: ['src/js/**/*.js'],
tasks: ['concat', 'uglify'],
options: {
spawn: false,
},
},
styles: {
files: ['src/css/**/*.css'],
tasks: ['cssmin'],
options: {
spawn: false,
},
},
}
Copying Files (grunt-contrib-copy)
copy: {
main: {
files: [
{expand: true, cwd: 'src/', src: ['**'], dest: 'dist/'},
],
},
}
Cleaning (grunt-contrib-clean)
clean: {
dist: ['dist/*'],
build: ['build/*'],
}
Running Tests (grunt-contrib-jasmine)
jasmine: {
src: ['src/**/*.js'],
options: {
specs: 'specs/**/*[Ss]pec.js',
helpers: 'spec/helpers/**/*.js',
},
}
Advanced Configuration
Dynamic File Mappings
files: {
expand: true,
cwd: 'src/',
src: ['**/*.js'],
dest: 'dist/',
ext: '.min.js',
flatten: false,
filter: 'isFile',
}
Template Variables
banner: '/*! <%= pkg.name %> v<%= pkg.version %> | <%= grunt.template.today("yyyy-mm-dd") %> */',
Multi-Task Configuration
uglify: {
options: {
mangle: false,
},
dev: {
files: {
'dist/js/dev.min.js': ['src/js/**/*.js'],
},
},
prod: {
options: {
mangle: true,
compress: true,
},
files: {
'dist/js/prod.min.js': ['src/js/**/*.js'],
},
},
}
Common Plugins
Essential Plugins
grunt-contrib-concat - Concatenate files
grunt-contrib-uglify - Minify JavaScript
grunt-contrib-cssmin - Minify CSS
grunt-contrib-watch - Watch files for changes
grunt-contrib-copy - Copy files
grunt-contrib-clean - Clean directories
grunt-contrib-jshint - JavaScript linting
grunt-contrib-csslint - CSS linting
grunt-contrib-connect - Start a web server
grunt-contrib-imagemin - Optimize images
Useful Plugins
grunt-babel - Transpile ES6+
grunt-sass - Compile Sass/SCSS
grunt-postcss - PostCSS processing
grunt-browserify - Browserify bundling
grunt-nodemon - Auto-restart Node.js apps
grunt-mocha-test - Run Mocha tests
grunt-git - Git commands
grunt-gh-pages - Deploy to GitHub Pages
Best Practices
Task Organization
- Group related tasks together
- Use descriptive task names
- Create separate tasks for dev and prod environments
- Use
grunt.registerTask to create composite tasks
grunt.registerTask('dev', ['jshint', 'concat', 'watch']);
grunt.registerTask('build', ['jshint', 'concat', 'uglify', 'cssmin']);
grunt.registerTask('default', ['build']);
Performance Optimization
- Use
spawn: false in watch tasks for better performance
- Cache file operations where possible
- Use
grunt-newer to only process changed files
- Parallelize independent tasks
grunt.loadNpmTasks('grunt-newer');
watch: {
scripts: {
files: ['src/js/**/*.js'],
tasks: ['newer:jshint', 'newer:concat'],
options: {
spawn: false,
},
},
}
Error Handling
- Use
--force flag to continue on errors (with caution)
- Add error handlers in custom tasks
- Use
grunt.fail.fatal() for critical errors
grunt.registerTask('custom', function() {
try {
} catch (e) {
grunt.fail.fatal('Custom task failed: ' + e.message);
}
});
Configuration Management
- Keep configuration in separate files for large projects
- Use
grunt.option() for command-line arguments
- Environment-specific configurations
var env = grunt.option('env') || 'dev';
grunt.config.set('env', require('./config/' + env + '.js'));
Common Gotchas
File Path Issues
- Always use forward slashes in paths (even on Windows)
- Use
expand: true for dynamic file mappings
- Be careful with
flatten: true - it removes directory structure
Async Tasks
- Grunt tasks are async by default
- Call
this.async() for tasks that need async handling
- Always call the done callback
grunt.registerTask('async-task', function() {
var done = this.async();
setTimeout(function() {
console.log('Async operation complete');
done();
}, 1000);
});
Plugin Loading
- Load plugins before registering tasks
- Use
load-grunt-tasks to auto-load all grunt-* plugins
- Be aware of plugin version compatibility
Watch Task Issues
- Use
spawn: false for better performance
- Be careful with infinite loops in watch tasks
- Use
interrupt: true to stop running tasks on file changes
Integration with Other Tools
With npm Scripts
{
"scripts": {
"build": "grunt build",
"dev": "grunt dev",
"watch": "grunt watch"
}
}
With Gulp
- Can run Grunt tasks from Gulp using
gulp-grunt
- Use Gulp for streaming operations, Grunt for configuration-heavy tasks
With Webpack
- Use Grunt for pre/post-processing
- Use Webpack for module bundling
- Can run Webpack as a Grunt task using
grunt-webpack
When to Migrate from Grunt
Consider migrating to Gulp or npm scripts when:
- Your build process is becoming too complex
- You need better performance with streaming
- You want code-based configuration instead of config objects
- Your team prefers modern JavaScript tooling
Keep Grunt when:
- The project already has extensive Grunt configuration
- You need the extensive plugin ecosystem
- Configuration-based approach fits your workflow
- Performance is not a critical concern