Stop being the grunt, use the grunt
With that command, Grunt linted my files, minified CSS & Javascript, ran unit tests, fired up an express server, and watches any changes to css files to automatically update my browser.
You've used some sort of task runner before. Think:
(Most) Anything a human can do, Grunt can do (better)
This file is used by Node.js and list the project dependencies and their versions
{
"name": "AutomateMe",
"version": "0.1.0",
"devDependencies": {
"grunt": "~0.4.2",
"grunt-contrib-jshint": "~0.6.3",
"grunt-contrib-nodeunit": "~0.2.0",
"grunt-contrib-uglify": "~0.2.2"
}
}
A Javascript file that wraps a grunt function and configures a series of plugins. Any valid javascript can go here.
Every Gruntfile (and gruntplugin) uses this basic format, and all of your Grunt code must be specified inside this function:
module.exports = function(grunt) {
// Do grunt-related things in here
};
Most Grunt tasks rely on configuration data defined in an object passed to the grunt.initConfig method. The <% %> template strings may reference any config properties, configuration data like filepaths and file lists may be specified this way to reduce repetition
grunt.initConfig({
foo_files: ['./*.js'],
bar_files: ['./*.css'],
//concat config for concatenating files
//command line via 'grunt concat'
concat: {
foo: {
// concat task "foo" target options and files go here.
files: '<%= foo_files %>'
},
bar: {
// concat task "bar" target options and files go here.
files: '<%= bar_files %>'
},
},
}
Any plugins you use or tasks you want to import, you use as such:
// Load the plugin that provides the "concat" task.
grunt.loadNpmTasks('grunt-contrib-concat');
//Register the task to run
grunt.registerTask('compile', ['clean', 'concat', 'jshint', 'karma', 'uglify', 'preprocess', 'shell:build']);
//OPTIONAL
//Loads .js files in ./tasks directory
grunt.loadTasks("tasks");
//tasks folder - compile.js
module.exports = function(grunt) {
grunt.registerTask('compile', ['clean', 'concat', 'jshint', 'karma', 'uglify', 'preprocess', 'shell:build']);
};
You can define custom tasks with Javascript or multiple tasks with targets
module.exports = function(grunt) {
// A very basic default task.
grunt.registerTask('log', 'Log some stuff.', function() {
grunt.log.write('Logging some stuff...').ok();
});
};
grunt.initConfig({
log: {
foo: [1, 2, 3],
bar: 'hello world',
baz: false
}
});
grunt.registerMultiTask('log', 'Log stuff.', function() {
grunt.log.writeln(this.target + ': ' + this.data);
});
You can think of Grunt options as the command line parameters you pass to grunt for calling tasks. They can be used anywhere in
module.exports = function(grunt) {
//grunt express --host=server.raisemore.com
var host = grunt.option('host');
};
module.exports = function(grunt) {
//Standard javascript - retrieve a command line parameter
//Say the user typed 'grunt --foo=bar'
var foo = grunt.option('bar') || 'fubar';
grunt.initConfig({
// Arbitrary non-task-specific properties.
my_property: 'whatever',
our_file_list: ['./js/*.js'],
pkg: grunt.file.readJSON('package.json'),
// jshint task configuration here - you can specify several targets per task
jshint: {
all: {
options: {
smarttabs: false //specify options for just this target
},
files: '<%= our_file_list %>' //Special Grunt syntax to get a config variable
},
just_a_few: {
files: ['./js/index.js', './js/nav.js']
}
// specify global options for all targets
options: {
smarttabs: true,
eqnull: true,
eqeqeq: false
}
}
}
});
// Load the plugin that provides the "jshint" task.
grunt.loadNpmTasks('grunt-contrib-jshint');
// Default task(s).
grunt.registerTask('default', ['jshint']);
grunt.registerTask('custom', 'This is how you define a custom task.', function(arg1, arg2){
//Javascript here folks
});
};
Grunt mainly runs from its command line interface in the working directory the Gruntfile
npm install -g grunt-cli
If your working directory has a Gruntfile - just install project dependencies and view the defined tasks!
npm install
grunt --help
Create your package.json file - contains all project depenencies
npm init
Here I'd recommend taking the sample Gruntfile from their website, and start adding in tasks as you see fit
Find the plugin you want to use, and just run npm install
//We use the --save-dev flag to save this entry to our package.json file
npm install grunt-contrib-uglify --save-dev
Now that we added a plugin via npm, we must include in Gruntfile
grunt.loadNpmTasks('grunt-contrib-watch');
The best thing about Grunt is the hundreds of plugins that are easy to configure and use. Lets dive in a few common tasks
Install the plugin
npm install grunt-contrib-uglify --save-dev
//...snip previous config...
uglify: {
subset: {
files: {
'dest/output.min.js': ['src/input1.js', 'src/input2.js']
}
},
all: {
files: {
'dest/all.min.js': ['']
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt uglify
Specify which uglify task target to run
grunt uglify:subset
grunt uglify:all
Just like we saw at Thunder Plains, we can use Sass with the sass ruby gem and the grunt sass plugin.
gem install sass
npm install grunt-contrib-sass --save-dev
sass: {
dist: {
files: {
'./src/stylesheets/styles.css': './src/stylesheets/styles.scss'
}
}
}
grunt sass
Lets watch our SASS / JS / CSS files and when they change, automatically compile or minify.
Using the Grunt Watch plugin, we can monitor file changes and execute tasks on file change events.
//Command Line to install plugin
npm install grunt-contrib-watch --save-dev
//Gruntfile to load plugin
grunt.loadNpmTasks('grunt-contrib-watch');
// ...snip prior config...
watch: {
main: {
files: [ 'Gruntfile.js', 'js/reveal.js', 'css/reveal.css' ],
tasks: 'default'
},
edits: {
files: [ 'css/editable.css' ],
options: {
livereload: 35729,
}
}
},
Include livereload.js file - server pushes file changes to browser
/* Edit the CSS here and click save to live reload */
#editable_css {
color: white;
border: solid 1px red;
}
Grunt has a wide variety of plugins to assist with testing. Perhaps a good solution would be to set up watch to see any javascript changes, and on those changes execute the unit tests
Run jasmine specs headlessly through PhantomJS.
jasmine: {
pivotal: {
src: 'src/**/*.js',
options: {
specs: 'spec/*Spec.js',
helpers: 'spec/*Helper.js'
}
}
}
Run QUnit unit tests in a headless PhantomJS instance.
// Project configuration.
grunt.initConfig({
qunit: {
all: ['test/**/*.html']
}
});
Run all your unit tests for Jasmine/QUnit/Mocha through multiple browsers Chrome/Firefox/Safari/Phantom
karma: {
options: {
configFile: 'karma.conf.js',
runnerPort: 9999,
browsers: ['Chrome', 'Firefox']
},
continuous: {
singleRun: true,
browsers: ['PhantomJS']
},
dev: {
reporters: 'dots'
}
}
It is worth mentioning that there is a Grunt plugin for Git-hooks - enforce testing on users before they commit.
githooks: {
all: {
'pre-commit': 'test'
}
}
Say you want to resize images, or minify them. There are Grunt Plugins for those.
image_resize: {
android_small: {
options: {
height: 426,
width: 320
},
files: {
//Destination : source
'./android/res/drawable/splash.png': resize_file
}
},
android_normal: {
options: {
height: 470,
width: 320
},
files: {
//Destination : source
'./android/res/drawable-mdpi/splash.png': resize_file
}
},
android_large: {
options: {
height: 640,
width: 480
},
files: {
//Destination : source
'./android/res/drawable-ldpi/splash.png': resize_file,
'./android/res/drawable-hdpi/splash.png': resize_file
}
}
imagemin: {
static: {
options: {
optimizationLevel: 3
},
files: {
'dist/img.png': 'src/img.png', // 'destination' : 'source'
'dist/img.jpg': 'src/img.jpg',
'dist/img.gif': 'src/img.gif'
}
},
dynamic: {
files: [{
expand: true,
cwd: 'src/',
src: ['**/*.{png,jpg,gif}'],
dest: 'dist/'
}]
}
}
Theres a Yeoman generator to help you write your own Grunt plugins - https://github.com/yeoman/generator-gruntplugin