grunt is a great tool, and I have written lots of plugins for it. One problem that bothered me was the separation between regular tasks and multi-purpose tasks.
A regular task requires no configuration, just needs to be listed by name in the pipeline. For example my grunt-deps-ok can be used very easily
1 | grunt.loadNpmTasks('grunt-deps-ok'); |
The default pipeline itself was created using registerTask function!
Most grunt plugins on the other hand use configurable multi task method. A single task can have multiple options configured under different names, providing great flexibility. For example, you can use different jshint options for source and test files
1 | grunt.initConfig({ |
Using multi tasks allows to run each target separately: grunt jshint:source
or
grunt jshint:test_files
.
The problem is when the majority of use cases can be covered by the default options, and only a small number of cases needs additional configuration. In this case, multi task adds boilerplate code. For example, my grunt-nice-package plugin caused a lot of projects to copy / paste empty boilerplate code
1 | grunt.initConfig({ |
Omitting nice-package property from the grunt config prevented the task from running. Finally I found the way to initial same task both as a multi task and as a regular task depending on the config object.
Place loadNpmTasks
call AFTER grunt.initConfig
call to allow plugin access
to the config data. I usually use matchdep
to load tasks:
1 | grunt.initConfig({ ... }); |
When a grunt task is registered it receives an instance of grunt object, which includes the configuration information. We can inspect the config to see if the user has specified options or not:
1 | module.exports = function (grunt) { |
You can see a working example in nice-package.js.
Naturally, both tasks reuse the same function internally to validate package.json. We get the best of two worls: simple to use default task and the ability to pass any additional options if needed.
Passing options
If same task has to work as multi task and default task, you need to make sure verbose and force
command line arguments still work. In multi task, we use this.options
call to combine command line
and task config options.
1 | if (grunt.config.data[taskName]) { |
But default task does NOT have this.options
method. Thus if you need command line arguments, you
must parse them yourself
1 | function isVerbose(option) { |