Demystifying tasks and targets
Here, we have the basics of any Gruntfile.
module.exports = function(grunt) {
grunt.initConfig({
// tasks will go here. A "task" is run by a grunt "plugin"
});
};
Let's say we want to run one task:
jshint
: to lint our javascripts
Here is how we would define it (without any options or files yet):
module.exports = function(grunt) {
grunt.initConfig({
jshint: {}, // this is where the `jshint` task configuration will go
});
};
Before we add options and specify the files we want to lint, we need to install the plugins that will actually run our task.
In the command line run:
npm install grunt-contrib-jshint --save-dev
This will install the plugin in node_modules
(the --save-dev
part adds it to devDependencies
in your project's package.json
file).
Now we need to add grunt.loadNpmTasks()
for our task:
module.exports = function(grunt) {
grunt.initConfig({
jshint: {}
});
// Right here!
grunt.loadNpmTasks('grunt-contrib-jshint');
};
Technically, Grunt describes these as "Grunt plugins" (even though it's loadNpmTasks
, not loadNpmPlugins
), and you can only load one at a time (despite loadNpmTasks
being plural). I digress....
To simplify, I'll leave out the "wrapper" code here
To properly specify the actual options and files to run against, Grunt convention is to define targets inside each task. It's easiest to understand by way of example.
Step by step:
Define a task:
grunt.initConfig({
// This is a task
jshint: {},
});
Add some task-level options:
jshint: {
options: {...} // <= here
}
DON'T DO THIS
jshint: {
options: {},
files: {} // <= don't put "files" here, explained below
}
There are a few Grunt plugins that allow this, but generally defining files at the task-level is bad practice, and often won't work anyway. Just make sure you read the docs for how to configure the plugin and you'll be fine.
DO THIS instead.
jshint: {
options: {},
// this is a target
dev: {
files: {}
},
// this is a target
prod: {
files: {}
}
}
Why have both task
and target
level options?
- task-level options: this is a convenience that allows you to specify "common" options that should be used on all targets.
- target-level options: offer a way to override task level options, or to specify options that only apply to a given target.
Needless to say, both task and target options are, well, optional.
Here is how our task would look if we decked it out with options at all levels:
jshint: {
// These options are NOT inside any of our targets.
// e.g. these are "task-level" options
options: {
jshintrc: '.jshintrc' // each target will use this
},
dev: {
options: {
// only the dev target will use this
reporter: 'checkstyle'
},
files: {...}
},
prod: {
options: {
// only the prod target will use this
force: true,
},
files: {...}
}
}
In other words, in the previous example:
- jshint is the task
- dev is a target
- prod is a target
you rule. this is exactly what i needed. 😃 thanks again!