How to run a task ONLY on modified file with Gulp watch
GulpGulp WatchGulp Problem Overview
I have the current gulp task which I use in a gulpfile. Path are from a config.json and everything works perfectly:
//Some more code and vars...
gulp.task('watch', function() {
gulp.watch([config.path.devfolder+"/**/*.png", config.path.devfolder+"/**/*.jpg", config.path.devfolder+"/**/*.gif", config.path.devfolder+"/**/*.jpeg"], ['imagemin-cfg']);
})
//Some more code ...
gulp.task('imagemin-cfg', function () {
return gulp.src([config.path.devfolder+"/**/*.png", config.path.devfolder+"/**/*.jpg", config.path.devfolder+"/**/*.gif", config.path.devfolder+"/**/*.jpeg"], {read: false})
.pipe(imagemin({
progressive: true,
svgoPlugins: [{removeViewBox: false}],
use: [pngcrush()]
}))
.pipe(gulp.dest(buildType))
.pipe(connect.reload());
});
But I still have an issue, the number of images in my project is huge and this task takes ages. I'm looking for a way to run my task ONLY on modified files. If I had an image or modify it, imagemin()
will only run on this image, and not on all.
Once again everything is working perfectly fine, but the run time is really long.
Thanks.
Gulp Solutions
Solution 1 - Gulp
"gulp-changed" is not a best solution for doing this, because it watch only modified filed in "buildType" folder.
Try gulp-cached instead.
Solution 2 - Gulp
Another option is to used the gulp.watch on("change") option.
gulp
.watch([config.path.devfolder+"/**/*.png", config.path.devfolder+"/**/*.jpg", config.path.devfolder+"/**/*.gif", config.path.devfolder+"/**/*.jpeg"])
.on("change", function(path) {
gulp
.src(path)
.pipe(imagemin({
progressive: true,
svgoPlugins: [{removeViewBox: false}],
use: [pngcrush()]
}))
.pipe(gulp.dest(buildType))
.pipe(connect.reload());
});
Solution 3 - Gulp
No need for plugins, this can be achieved with just gulp.watch.
With gulp.watch, you can target the changed file like this.
gulp.watch(["src/**/*"], function (obj) {
return gulp.src(obj.path, {"base": "src/"})
.pipe(gulp.dest("dest"));
});
Edit: for Gulp v4.0.2 - Now fixed:
const { watch, src, dest } = require('gulp');
var watcher = watch(["src/**/*"]);
watcher.on('change', function(fileName){
return src(fileName, {base: 'src/'})
.pipe(dest('dest'));
});
Solution 4 - Gulp
Incremental builds are supported natively in Gulp without any plugins since version 4.0. Here is the example taken from the project README:
const paths = {
...
images: {
src: 'src/images/**/*.{jpg,jpeg,png}',
dest: 'build/img/'
}
}
function images() {
return gulp.src(paths.images.src, {since: gulp.lastRun(images)})
.pipe(imagemin())
.pipe(gulp.dest(paths.images.dest));
}
function watch() {
gulp.watch(paths.images.src, images);
}
Solution 5 - Gulp
May I suggest gulp-newy in which you can manipulate the path and filename in your own function. Then, just use the function as the callback to the newy()
. This gives you complete control of the files you would like to compare.
This will allow 1:1 or many to 1 compares.
newy(function(projectDir, srcFile, absSrcFile) {
// do whatever you want to here.
// construct your absolute path, change filename suffix, etc.
// then return /foo/bar/filename.suffix as the file to compare against
}
Solution 6 - Gulp
Yeah, gulp-changed does exactly that:
var changed = require('gulp-changed');
gulp.task('imagemin-cfg', function () {
return gulp.src([config.path.devfolder+"/**/*.png", config.path.devfolder+"/**/*.jpg", config.path.devfolder+"/**/*.gif", config.path.devfolder+"/**/*.jpeg"], {read: false})
.pipe(changed(buildType))
.pipe(imagemin({
progressive: true,
svgoPlugins: [{removeViewBox: false}],
use: [pngcrush()]
}))
.pipe(gulp.dest(buildType))
.pipe(connect.reload());
});
Solution 7 - Gulp
Here's a real-world example I use all the time to do this without the need for any additional packages. I use this to minify, rename and compile any .js file in the js
directory. Compiled files are saved to a dist
directory with .min
appended before the extension.
// Compile JS
var compileJS = function( file ) {
var currentDirectory = process.cwd() + '/';
var modifiedFile = file.path.replace( currentDirectory, '' );
gulp.src( modifiedFile )
.pipe( uglify() )
.pipe( rename( {
suffix: ".min"
} ) )
.pipe( livereload() )
.pipe( gulp.dest( 'dist' ) );
};
// Watch for changes
gulp.watch( 'js/*.js', [ 'js' ] ).on( "change", compileJS );
The above answers seemed partially incomplete and a little unclear to me, hopefully this is helpful for anyone else looking for a basic example.
Solution 8 - Gulp
In the task or callback, you'll have an event parameter, which has a type property, which will tell you if the file was added, deleted or changed. Best bet is to make use of that in a conditional on your task.
gulp.watch('path to watch', function(event){
if(event.type === 'changed') {
gulp.start('your:task');
}
};
Solution 9 - Gulp
Define the main task so that it accepts a parameter for the .src()
input pattern. Define a wrapper function to pass a default src value for the task so that you can still call it directly like gulp images
:
const imageFilePattern = '/src/path/to/input';
function images() {
getImagesTask(imageFilePattern);
}
function imagesTask(src) {
return gulp.src(src)
.pipe(imagemin())
.pipe(gulp.dest('dest'));
}
Now you can easily define the watch task to only process changed files:
function watch() {
return gulp.watch(imageFilePattern).on("change", imagesTask);
}