grunt
grunt copied to clipboard
Multitask option to set the default behavior when a subtask is not specified
Sometimes it's dangerous or undesired to run all of a multitask's subtasks by default.
Proposal: an option to set the default behavior when a subtask is not specified instead of iterating through all the subtasks.
Default deploy
to use dev
when a subtask is not specified:
deploy: {
options: {
default: 'dev'
},
dev: { .. },
qa: { .. },
prod: { .. }
}
$ grunt deploy # only runs deploy:dev instead of all of them
Example: Don't run any bump subtasks if the subtask is not specified.
bump: {
options: {
default: false
},
major: { .. },
minor: { .. },
patch: { .. },
build: { .. }
}
$ grunt bump # returns an error that the bump subtask was not specified
:+1:
This would also be helpful when you have a bunch of subtasks for clean
and running them all at once is an uncommon scenario.
:+1:
I have a case where I'd like to have some additional watch targets for specific deployment targets, but I'd like the generic watch task not to run any of them, and an option like the one described by @dylang would be very useful.
+1
I agree, I actually thought this was the default behavior when I first started using grunt, it just made sense. options
is a grunt reserved keyword now, maybe defaults
could be as well?
grunt.initConfig({
myMultiTask: {
options: {
clean: true,
dir: ""
},
defaults: {
clean: false.
dir: "foo"
},
bamboo: {
dir: "bar"
},
jenkins: {
dir: "baz"
}
}
});
Invoke:
$ grunt myMultiTask
-- Running myMultiTask...
$ grunt myMultiTask:bamboo
-- Running myMultiTask (subtask bamboo)...
$
@cowboy: Does this make any sense or are there perhaps any plans on a different approach that enables one to run this without having to define a new task that runs a specific subtask?
:+1:
Having the exact same situation with clean
as described by @NickHeiner.
+1 for default subtask
:+1:
I don't like the idea of a named option, because it will 1. override any existing task's defaults
option and 2. be meaningless once options are merged down to the target level.
This problem could be solved with another reserved key next to the options
key, like default
.
Another possible solution: Right now, properties starting with _
are ignored in multi-tasks and are invalid targets. Maybe I could change that behavior to make them non-iterable, but still valid.
Here's a sample Gruntfile:
module.exports = function(grunt) {
grunt.initConfig({
foo: {
a: {},
b: {},
_c: {},
}
});
grunt.registerMultiTask('foo', function() {
console.log(this.nameArgs);
});
};
And some sample CLI output.
$ grunt foo
Running "foo:a" (foo) task
foo:a
Running "foo:b" (foo) task
foo:b
Done, without errors.
$ grunt foo:_c
Running "foo:_c" (foo) task
foo:_c
Done, without errors.
Note that in the current Grunt, running grunt foo:_c
will error. This would be a bit of a change.
I love the underscore option. It maps to the way partials work in Sass, in a way.
Nate Eagle
On Jan 22, 2014, at 3:12 PM, Ben Alman [email protected] wrote:
I don't like the idea of a named option, because it will 1. override any existing task's defaults option and 2. be meaningless once options are merged down to the target level.
This problem could be solved with another reserved key next to the optionskey, like default.
Another possible solution: Right now, properties starting with _ are ignored in multi-tasks and are invalid targets. Maybe I could change that behavior to make them non-iterable, but still valid.
Here's a sample Gruntfile:
module.exports = function(grunt) { grunt.initConfig({ foo: { a: {}, b: {}, _c: {}, } });
grunt.registerMultiTask('foo', function() { console.log(this.nameArgs); });};
And some CLI output.
$ grunt foo Running "foo:a" (foo) task foo:a
Running "foo:b" (foo) task foo:b
Done, without errors.
$ grunt foo:_c Running "foo:_c" (foo) task foo:_c
Done, without errors.
It's just a thought, for now at least.
— Reply to this email directly or view it on GitHubhttps://github.com/gruntjs/grunt/issues/815#issuecomment-33062465 .
@cowboy how would _
options help with default behavior? If you run grunt foo
is it still running all subtasks?
I like using "default" as an extra keyword. Though that could break code in the wild I guess.
The next question is whether the value should be a string that references the default task or just be the task object itself. I suppose it could be both dependant on the type the user uses.
+1 for default target.
I'm using a build task that has several targets, named 'package1' to 'packageN'. Each package target builds a list of modules, many of which are built into multiple packages.
Running the build task without a target would build a lot of the modules more than once. Thats why im using an additional 'all' target that builds every module.
Being able to flag this target would be exactly what i need.
The underscore option is fine, but in my situation it would lead to the use of a lot of underscore target names (_packageX) - which seems awkward.
:+1: A default target is needed and makes sense.
+1 Why is this not a thing yet? :)
Here's a dirty hack until something more official comes along. Create a file (tasks/multitask-default.js) and call grunt.loadTasks('tasks') in your Gruntfile.js. Use this for the contents of the file:
/**
* Enable default multitask support. Define your default task by adding another task
* with the empty string ('') as its name and the name of the default task as its value.
*
* Example: (publish is a multi-task)
* // In your Gruntfile.js
* grunt.initConfig({
* publish: {
* options: {
* // some options
* },
* '': 'dev',
* dev: {
* // definition
* },
* release: {
* // definition
* }
* }
* });
*
* // Running "grunt publish" will only invoke publish:dev
*
*/
module.exports = function (grunt) {
var originalRunAllTargets = grunt.task.runAllTargets;
grunt.task.runAllTargets = function(taskname, args) {
var targets = grunt.config.getRaw(taskname) || {},
target;
function isValidMultiTaskTarget(name) {
return !/^_|^options$/.test(name);
}
target = targets[''];
if (typeof target === 'undefined') {
originalRunAllTargets(taskname, args);
} else {
if (! isValidMultiTaskTarget(target)) {
throw new Error('Invalid default target "' + target + '" specified.');
}
grunt.task.run([taskname, target].concat(args || []).join(':'));
}
};
};
:+1:
How about naming it defaultTarget
.
I would even prefer to change the current behavior and make running all targets explicit:
grunt.initConfig({
myMultiTask: {
defaultTarget: 'jenkins',
options: {
clean: true,
dir: ""
},
bamboo: {
dir: "bar"
},
jenkins: {
dir: "baz"
}
}
});
-
myMultiTask:bamboo
runs the bamboo target -
myMultiTask
runs the default target (jenkins). If no defaultTarget is defined,myMultiTask
will raise an error -
myMultiTask:*
runs bamboo and jenkins
I like the * designation to make it explicit that you want to run all of the tasks, too.
+1
I would like to write independent grunt-exec tasks, where only the specified subtask runs by default, similar to how a traditional Makefile behaves.
+1 for default subtask.
:+1: This would be a great feature.
Hi, I have the following scenario:
My app is built for multiple clients, so I'm setting up grunt to handle commands like:
grunt scsslint --client=company1
grunt scsslint --client=company2
Gruntfile.js
module.exports = function (grunt) {
var client = grunt.option('client') || 'company1';
var scsslint = {
options: {
config: 'scss-lint-config.yml',
reporterOutput: 'scss-lint-report.xml'
},
core: ['core/**/*.scss']
};
scsslint[client] = [client + '/**/*.scss'];
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
config: grunt.file.readJSON(client + '-config.json'),
scsslint: scsslint,
grunt scsslint --client=company1
should be the same as running grunt scsslint:company1
.
However, grunt scsslint --client=company1
run all sub-tasks. grunt scsslint:company1
works just fine.
Any ideas to accomplish what I want?
+1 for the defaultTarget
+1 for myMultiTask:*
to all the targets explicitly
grunt.initConfig({
myMultiTask: {
defaultTarget: 'jenkins',
options: {
clean: true,
dir: ""
},
bamboo: {
dir: "bar"
},
jenkins: {
dir: "baz"
}
}
});
myMultiTask:bamboo
runs the bamboo target
myMultiTask
runs the default target (jenkins).
If defaultTarget is not provided / defined, myMultiTask
will raise an error
myMultiTask:*
runs bamboo and jenkins
:+1: Would solve some problems we are having really nicely
+1 for defaultTarget
Especially in the case of multiple watch targets this would be really helpful.
It's easy to overcome this limitation by monkeypatching:
module.exports = (grunt) ->
grunt.registerMultiTask 'test', 'Docs...', ->
...
decoratedFn = grunt.task._tasks['test'].fn
decorator = (target) ->
args = grunt.util.toArray(arguments)
if not target
args = ['all'].concat(args.slice(1)) # running just 'grunt test'
else if not grunt.config(['test', target])
args = ['arbitrary', target].concat(args.slice(1)) # running arbitrary test, e.g. 'grunt:test/my-specific-test-file.coffee'
@nameArgs = "test:#{args[0]}"
decoratedFn.apply(@, args)
grunt.task._tasks['test'].fn = decorator
Config:
test:
options:
reporter: 'spec'
timeout: 120000
all:
options: ...
arbitrary:
options: ...
unit:
src: 'test/*-test.coffee'
options:
timeout: 30000
Now I'm able to do:
grunt test:path/to/my/test.coffee # unknown target, assuming path to file, target 'arbitrary'
grunt test:unit # specific target 'unit'
grunt test # target 'all', thus the same as 'grunt test:all'
+1 This would be a useful feature.
How come this is still not a thing?