Grunt will output exit codes and that's fantastic for scripts executing grunt tasks but I want the ability to handle failed grunt tasks after grunt completes them.
I was expecting to find some type of error handling function that I could set in the initConfig somewhere but I don't see anything. Likewise, even a "finally" function would work nicely.
Basically, I have an alias task that is a set of tasks that I execute and one of them temporarily changes content of a file and I write the content back to disk after everything completes. I want to still be able to at least attempt to write the content back to disk even if tasks after the mutation occurs, fail.
Something to this affect would be great.
grunt.initConfig({
onError: function (error) {
// execute my file cleanup
},
// this is essentially a "finally" function that executes once grunt
// finishes with all tasks.
onComplete: function () {
// execute my file cleanup
}
});
I am pretty sure, that there is no such feature. But it is a popular request: 1, 2.
What can be done by now? You can write a custom grunt-task, something like the following:
var errorCb = function() { callback(); }
var doneCb = function() { callback(); }
grunt.initConfig({
task_runner: {
before: ...,
beforeEach: ...,
run: ['other_task1', 'other_task2'],
after: ...,
afterEach: ...,
onError: errorCb,
onComplete: doneCb
},
other_tasks: { ... }
});
And register your task:
grunt.registerTask('task_runner', "Tasks' lifecycle", function(task, message) {
// foreach task in tasks: grunt.task.run(task)
...
// except(`grunt.util.error`)
});
As I know, there is no beatiful way to get the result of a task run. So here comes the monkey-patching. It is possible to hook these functions: grunt.fail and grunt.log.error.
Here's some inspiration: 1, 2. Also, have a look at grunt-then.
All in all, I can not say that it is an easy task. I hope someday Grunt will have events (by now: "Note that Grunt doesn't yet emit any events, but can still be useful in your own tasks.").
P.S. Have you considered Gulp for your project?
P.S.S If you are going to write your custom task, feel free to ask me for a contribution.
Related
According to this article here:
https://dweldon.silvrback.com/common-mistakes
Subscriptions don't block
Many aspects of the framework seem like magic. So much so that it may
cause you to forget how web browsers work. Take this simple example:
Meteor.subscribe('posts');
var post = Posts.findOne();
The idea that post will be undefined is the root cause of roughly one
in twenty meteor questions on stackoverflow.
So then why doesn't subscribe have a callback, and if it does, why isn't it referenced more often by the Meteor literati?
Why don't we have:
Meteor.subscribe('posts', function(err, posts){
//when the items/posts actually arrive
});
I hope my question makes sense.
Maybe I don't get the question, but the Meteor.Subscribe function has callbacks named onError and onReady methods.
Optional. May include onError and onReady callbacks. If a function is
passed instead of an object, it is interpreted as an onReady callback.
From docs.
For example.
Meteor.subscribe("posts", {
onReady: function () { console.log("onReady And the Items actually Arrive", arguments); },
onError: function () { console.log("onError", arguments); }
});
Also check this GitHub issue
Note: I have updated the article after reading this question.
While subscribe does have an optional callback, I intentionally avoided it in the original article because there aren't currently any common patterns that use it. In other words, I didn't want readers to come away from the article thinking that callbacks were actually the right solution to this problem.
In production applications, subscriptions typically come in two flavors:
Global: initiated as soon as the client starts, or maybe in an autorun.
Route: initiated as via a subscriptions or waitOn option.
It's also worth noting that in recent weeks, the template subscription pattern has emerged, though it hasn't seen wide adoption yet.
In all of these cases, the subscription is started and then can either be asynchronously checked for a reactive ready state, or ignored with the use of guards to prevent reference errors.
Because ready is reactive, this effectively gives us the same benefits of a callback, but with fewer lines of code. Let's look at two examples:
example 1
Meteor.subscribe('posts', function() {
Session.set('postsReady', true);
});
Tracker.autorun(function() {
if (Session.get('postsReady'))
showFancyAnimation();
});
example 2
var handle = Meteor.subscribe('posts');
Tracker.autorun(function() {
if (handle.ready())
showFancyAnimation();
});
Both examples demonstrate the same concept - subscribing and then reactively testing the state of the subscription. As you can see there really isn't a benefit to the callback.
Finally, (as I now point out in the article), subscriptions are often spatially separated from the code which uses them. You'll typically subscribe in your route code and consume the results in your templates. For this reason you almost never see code which looks like:
Meteor.subscribe('posts', function() {
showFancyAnimation();
});
In fact, the only place I ever encounter code like the above is in SO answers because the author is trying to make a quick demonstration rather than trying to show a typical usage pattern.
Meteor.subscribe has been enhanced since v1.2. One of its callbacks onError is now replaced with onStop in Meteor v1.2.0.2 documentation
callbacks Function or Object
Optional. May include onStop and onReady callbacks. If there is an
error, it is passed as an argument to onStop. If a function is passed
instead of an object, it is interpreted as an onReady callback.
Having that enhancement, Meteor.subscribe is used with callbacks as an object
Meteor.subscribe( 'collection', {
onStop: function( error /* optional */ ) {
// when the sub terminates for any reason,
// with an error argument if an error triggered the stop
},
onReady: function() {
// when ready
}
});
However, onError still works for backward compatibility. Be aware that some popular packages, such as SubsManager still uses onError. That being said such snippet below is now deprecated but doesn't break .
Meteor.subscribe( 'collection', {
onError: function( error ) {
// if the subscribe terminates with an error
},
onReady: function() {
// when ready
}
});
On the other hand, Meteor.subscribe can be used with a callback as a function as before
Meteor.subscribe( 'collection', function() {
// when ready
});
As my personal notice, if Meteor.subscribe happens to be passed with careless multiple callback functions, only the last one takes effect as the onReady callback.
Meteor.subscribe( 'collection', function() {
// this doesn't execute.
}, function() {
// when ready.
});
The associated Git commitment is listed here for a reference.
I have something like this:
gulp.task('default', ['css', 'browser-sync'] , function() {
gulp.watch(['sass/**/*.scss', 'layouts/*.css'], function() {
gulp.run('css');
});
});
but it does not work, because it watches two directories, the sass and the layouts directory for changes.
How do I make it work, so that gulp watches anything that happens inside those directories?
gulp.task('default', ['css', 'browser-sync'] , function() {
gulp.watch(['sass/**/*.scss', 'layouts/**/*.css'], ['css']);
});
sass/**/*.scss and layouts/**/*.css will watch every directory and subdirectory for any changes to .scssand .css files that change. If you want to change that to any file make the last bit *.*
You can write a watch like this.
gulp.task('watch', function() {
gulp.watch('path/to/file', ['gulp task name for css/scss']);
gulp.watch('path/to/file', ['gulp task name for js']);
});
This way you can set up as many tasks as you want via the file path of what you want to watch followed by the name of the task you created. Then you can write your default like this:
gulp.task('default', ['gulp task name for css/scss', 'gulp task name for js']);
If you want to simply watch for various file changes, then just watch files using glob like *.css in your task.
One problem that has arisen for multiple people (including me) is that adding a gulp.filter outside of the task causes gulp.watch to fail after the first pass. So if you have something like this:
var filter = gulpFilter(['fileToExclude.js'])
gulp.task('newTask', function(){ ...
Then you need to change it to:
gulp.task('newTask', function(){
var filter = gulpFilter(['fileToExclude.js'])
The filter has to be included in the task function. Hope that helps someone.
This works for me (Gulp 4):
function watchSass() {
return gulp.watch(sassGlob, { ignoreInitial: false }, buildCss)
}
function watchImages() {
return gulp.watch(imagesGlob, copyImages)
}
exports.watch = gulp.parallel(watchSass, watchImages)
#A.J Alger's answer worked for me when using Gulp v3.x.
But starting with Gulp 4, The following appears to work for me.
Notice that each task has to return a value or call "done()". The main task in this example is 'watchSrc' which in parallel calls the other tasks.
gulp.task('watchHtml', function(){
return watch('src/**/*.html', function () {
gulp.src('src/**/*')
.pipe(gulp.dest(BUILD_DIR))
})
})
gulp.task('watchJS', function(){
return watch('src/**/*.js', 'devJS')
})
gulp.task('watchCSS', function(){
return watch(['src/**/*.css', 'src/**/*.scss'], 'buildStyles')
})
gulp.task('watchSrc', gulp.parallel('watchHtml', 'watchJS', 'watchCSS'), function(done)
{
done()
})
As of gulp 4, this is another option:
const { watch, series, parallel } = require('gulp');
function watchTask(cb) {
// this will execute all task on any changes
watch(['src/**/*'],
series(parallel(jsTask, htmlTask, assetTask),
));
// this will run specific task based on file type or folder
watch(['src/**/*.js'], series(jsTask));
watch(['src/**/*.html'], series(htmlTask));
watch(['assets/**/*'], series(assetTask));
}
exports.default = series(parallel(jsTask, htmlTask, assetTask), watchTask);
If you convert your tasks into functions
function task1(){
return gulp...
...
}
There are then 2 useful methods you can use:
GULP.SERIES will run the tasks synchronously
gulp.task('default', gulp.series(task1,task2));
GULP.PARALLEL will run them asynchronously
gulp.task('default', gulp.parallel(task1,task2));
is there a way to bypass the task in the grunt build process using options??
In below example i want to bypass the test and do only package and buildArtifacts tasks.
Example:
grunt.registerTask('test', [
'karma:continuous' ,
'e2e-test'
]);
grunt.registerTask('build', function (target) {
grunt.task.run(['force:on','package','test','force:off','buildArtifacts']);
});
Afterall its a .js file, i think you can write a custom js function to edit what you want to run based on "option"
As earlier said, you can edit the Gruntfile.js to create a new task.
grunt.registerTask('build-bypass-test', ['package','buildArtifacts']);
On command link just execute the below command.
grunt build-bypass-test
Updated Answer
var test = grunt.option('test');
grunt.registerTask('build',function() {
if(test) {
grunt.log.writeln('Running without test');
grunt.task.run(['force:on','package','force:off','buildArtifacts']);
} else {
grunt.log.writeln('Running with test');
grunt.task.run(['force:on','package','test','force:off','buildArtifacts']);
}
});
Hope this helps you. :)
I'm using grunt-contrib's concat and uglify modules to process some javascript. Currently if src/js/ is empty, they will still create an (empty) concat'd file, along with the minified version and a source map.
I want to task to detect if the src/js/ folder is empty before proceeding, and if it is, then the task should skip (not fail). Any ideas how to do this?
The solution may not be the prettiest, but could give you an idea. You'll need to run something like npm install --save-dev glob first. This is based on part of the Milkshake project you mentioned.
grunt.registerTask('build_js', function(){
// get first task's `src` config property and see
// if any file matches the glob pattern
if (grunt.config('concat').js.src.some(function(src){
return require('glob').sync(src).length;
})) {
// if so, run the task chain
grunt.task.run([
'trimtrailingspaces:js'
, 'concat:js'
, 'uglify:yomama'
]);
}
});
A gist for comparison: https://gist.github.com/kosmotaur/61bff2bc807b28a9fcfa
With this plugin:
https://www.npmjs.org/package/grunt-file-exists
You can check file existence. (I didn't try, but the source looks like supporting grunt expands. (*, ** ...)
For example like this::
grunt.initConfig({
fileExists: {
scripts: ['a.js', 'b.js']
},
});
grunt.registerTask('conditionaltask', [
'fileExists',
'maintask',
]);
But maybe if the file doesn't exist it will fail with error instead of simple skip.
(I didn't test it.)
If this is a problem you can modify a bit the source of this plugin to run the related task if the file exists:
The config:
grunt.initConfig({
fileExists: {
scripts: ['a.js', 'b.js'],
options: {tasks: ['maintask']}
},
});
grunt.registerTask('conditionaltask', [
'fileExists',
]);
And you should add this:
grunt.task.run(options.tasks);
In this file:
https://github.com/alexeiskachykhin/grunt-file-exists/blob/master/tasks/fileExists.js
after this line:
grunt.log.ok();
Maybe this is just a more up-to-date answer as the others are more than a year old, but you don't need a plugin for this; you can use grunt.file.expand to test if files matching a certain globbing pattern exist.
Update of #Kosmotaur's answer (path is just hard-code here though for simplicity):
grunt.registerTask('build_js', function(){
// if any file matches the glob pattern
if (grunt.file.expand("subdir/**/*.js").length) { /** new bit here **/
// if so, run the task chain
grunt.task.run([
'trimtrailingspaces:js'
, 'concat:js'
, 'uglify:yomama'
]);
}
});
I'm using grunt-watch to re-build my less style sheets:
watch: {
less: {
files: ['media/less/**/*.less'],
tasks: ['less'],
options: {
atBegin: true,
spawn: false
}
}
}
But if there is a syntax error in any of the .less files, the task just loops, trying to re-build the .less files every second… which makes debugging fairly difficult, because the error messages scroll past very quickly.
Is there any way fix that, so grunt-watch will only re-run the task once the .less files have been changed again?
This is using:
grunt#0.4.2
grunt-contrib-less#0.8.3
grunt-contrib-watch#0.5.3
I think the issue you're describing is this one, which was fixed in master but hasn't yet been released (as of 2013/12/17).
Well, for debugging purposes you could do a simple envelope of the less task with a custom task:
grunt.registerTask('myless', 'my less task', function() {
// do whatever debugging you want and stop the loop if needed.
grunt.task.run(['less']);
});
Then use your myless task in the watch.
UPDATE:
the idea is that since any repeated call to less now goes through your code - you can do whatever needed to either provide a more concrete output or preven repeated calls if failing is "desired" outcome and should fail, but not loop.
UPDATE 2:
Something like this:
watch: {
`less`: {
files: ['**/*.less'], // or whatever the extension is
tasks: ['myless'] // your envelope task
}
}
var flag;
grunt.registerTask('myless', 'My LESS task', function() {
if(flag === true) {
// if you are here - it means watch just invoked you repeatedly
// do whatever you need to analyze the issue (includig running an additional task)
flag = false;
return; // you exit task without altering any less files again -
// that should NOT trigger watch again
} else {
flag = true;
grunt.task.run(['less']);
}
});