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));
Related
Ok, so I am using the typical image minification process. I have functions for jpg, pngs and I have a separate function to convert those to webp.
I have the following gulpfile (only the relevant parts are here):
function clean(done) {
rimraf(folder.public_img, done);
}
function images() {
return gulp.src([folder.preimages+'/**/*.{gif,png,jpg,svg}'])
.pipe(cache(imagemin([
imageminPngquant({
speed: 1,
quality: [0.95, 1]
}),
imageminZopfli({
more: true
}),
imageminMozjpeg({
quality: 65
}),
])))
.pipe(gulp.dest(folder.public_img));
}
gulp.task('webp', () =>
gulp.src([folder.preimages+'/**/*.{gif,png,jpg}'])
.pipe(webp({
quality: 65,
method: 6
}))
.pipe(gulp.dest(folder.public_img))
);
function serve() {
gulp.watch(folder.preimages+"/**/*.{gif,png,jpg,svg}", images);
gulp.watch(folder.preimages+"/**/*.{gif,png,jpg}", webp);
}
gulp.task('clean', clean);
gulp.task('images', images);
gulp.task('serve', gulp.series('clean', 'images', 'webp', serve));
gulp.task('default', gulp.series('clean', 'images', 'webp', serve));
So, basically when I run gulp the first time, all the processes run fine and the webp images are generated. BUT, if I add a new image, the images() function runs, but for some reason the webp() function doesn't execute, it's as if it doesn't see changes, and I have it configured the same way as images.
I tried two alternatives:
I added the webp process directly inside the images() function, but that way my JPG and PNG files were directly converted to webp and I didn't have fallback images.
I tried to create the webp function with the same format as the others function webp() { ... }, but when I tried to run gulp it showed this error:
The following tasks did not complete: default,
Did you forget to signal async completion?
So I found that I could use the format above gulp.task('webp', () =>... and that works, but doesn't respect the watch function. I think it could be related to the function to name assignation in the end of the file, but I am not that familiar with the syntax.
What should I change so that it watches correctly?
I was able to recreate your code and get it to work as follows:
In creating your gulp tasks, something goofy seems to be happening because you've created a series of tasks to run with your gulp.task('serve'). Instead, you can write out your tasks like this:
gulp.task(clean);
gulp.task(images);
gulp.task(serve);
gulp.task('default', gulp.series(clean, images, webp, serve));
You may want to call your webp function something else - I was getting a conflict because of also importing gulp-webp as const = webp. You also don't need to put quotation marks around the name of the function in the task and then also reference it in the task again. So gulp.task(clean) works just fine. You don't need gulp.task('clean', clean).
You can probably also write your default task as follows:
gulp.task('default', gulp.series(clean, serve);
This had the same effect for me as also including the images and webp tasks in the series. This setup worked for me. Running gulp, it first ran the clean function to remove the public_img folder, then ran images followed by webp. It worked up the initial run as well as when I added a new image to the preimages folder.
Update
Here's the complete gulpfile I got to work:
const gulp = require('gulp');
const imagemin = require('gulp-imagemin');
const rimraf = require('rimraf');
const webp = require('gulp-webp');
const folder = {
preimages: 'src/images',
public_img: 'src/public_img',
};
function clean(done) {
rimraf(folder.public_img, done);
}
function images() {
return gulp
.src([folder.preimages + '/**/*.{gif,png,jpg,svg}'])
.pipe(imagemin())
.pipe(gulp.dest(folder.public_img));
}
function webpTask() {
return gulp
.src([folder.preimages + '/**/*.{gif,png,jpg}'])
.pipe(webp())
.pipe(gulp.dest(folder.public_img));
}
function serve() {
gulp.watch(folder.preimages + '/**/*.{gif,png,jpg,svg}', images);
gulp.watch(folder.preimages + '/**/*.{gif,png,jpg}', webpTask);
}
gulp.task('clean', clean);
gulp.task('images', images);
gulp.task('default', gulp.series('clean', 'images', webpTask, serve));
Notes: I removed the gulp serve task. I couldn't get that to work under any configuration. I think you are running into a conflict by naming both the task and the function serve, although I'm not 100% positive. I changed the webp task name to webpTask, as I was getting a conflict by naming both the webp import and the webp function webp. I didn't include any of the webp() or imagemin() configuration options, but that shouldn't matter.
You should also be able to remove the calls for both the images and webpTask functions in the gulp default task, as running the serve function should trigger both of those.
This setup worked for me both when adding new images to my preimages folder while the gulp default task was running as well as when restarting the gulp default task.
FYI: I'm using gulp version 4.0.1., but I don't think that should make a difference for this.
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.
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']);
}
});