I use gulp 4.0. And I have, this 'watch' task:
gulp.task("watch", function() {
gulp.watch("scss/*.scss", gulp.series("sass")).on("unlink", function(filepath) {
remember.forget("sass", path.resolve(filepath));
delete cached.caches.sass[path.resolve(filepath)];
});
gulp.watch("templates/**/*.jade", gulp.series("jade")).on("unlink", function(filepath) {
remember.forget("jade", path.resolve(filepath));
delete cached.caches.jade[path.resolve(filepath)];
});
});
So, I have same two lines:
remember.forget("jade", path.resolve(filepath));
delete cached.caches.jade[path.resolve(filepath)];
If it was a pipelines, it would be easy to create new task, and then run them. But here, I have a argument (parameter) - filepath.
So, the question is: How i can replace it with one task? With no repeat of these two lines.
So, the question is: How i can replace it with one task?
You can't. Tasks cannot take arguments. Your only option is to create a function:
function runTask(taskName) {
return function(evt, filepath) {
if (evt === "unlink") {
remember.forget(taskName, path.resolve(filepath));
delete cached.caches[taskName][path.resolve(filepath)];
}
gulp.series(taskName)();
};
}
gulp.task("watch", function() {
gulp.watch("scss/*.scss").on("all", runTask("sass"));
gulp.watch("templates/**/*.jade").on("all", runTask("jade"));
});
Note that the above code presumes that you want to clear the sass and jade caches before running the sass and jade tasks. If it's the other way around you'll have to adjust it accordingly.
Related
I use gulp-notify to trigger notifications when tasks complete. If a task is ran standalone, a notification for that specific task is triggered. If a task is ran as a dependency of another task, a notification for all dependencies is triggered.
In gulp#3, I check if the task is being called as a dependency using gulp.seq, which contains an array of the tasks being ran. Let's say I have three tasks: default, styles, and scripts, with the later two set as dependencies of the first. When running gulp styles, gulp.seq will contain [ 'styles' ]. When running gulp (the default task), gulp.seq will contain [ 'styles', 'scripts', 'default' ]. Knowing that, I then check gulp.seq.indexOf("styles") > gulp.seq.indexOf("default"), which tells me weather or not styles was ran as part of the default task.
With gulp#4, it appears that gulp.seq no longer exists. I've tried digging through the documentation and source code with no luck. It seems like gulp.tree({ deep:true }) (docs) might be what I'm looking for, but I don't see anything in it that returns anything useful.
Is there an equivalent of gulp.seq in gulp#4?
The API gulp.seq was never an official prop exposed by Gulp. With Gulp 4, you cannot do that. gulp.tree({ /* */ }) will not solve this problem for you.
Having said that, if you still need to find whether a task has run during some other task's pipeline, then you will have to decorate every gulp task with your own wrapper using something like this:
let runTasks = [];
function taskWrapper(taskName, tasks, thisTask) {
let callbackTask;
function innerCallback(cb) {
runTasks.push(taskName);
cb();
}
if (thisTask) {
callbackTask = function(cb) {
thisTask(function () {
innerCallback(cb);
});
}
} else {
callbackTask = innerCallback;
}
const newTasks = [ ...tasks, callbackTask ];
gulp.task(taskName, gulp.series(newTasks));
}
// INSTEAD OF THIS
// gulp.task('default', gulp.series('style', 'script', function () { }));
// DO THIS
taskWrapper('default', ['style', 'script'], function(cb) {
console.log('default task starting');
cb();
});
NOTE: Above code snippets has limitation. If you use watch mode, array maintaining the executed tasks i.e. runTasks will keep on growing. Also, it assumes tasks will always run in series. For a parallel mode, the logic gets little complicated.
Finally, you can also have a predefault task to help it further:
taskWrapper('predefault', [], function(cb) {
// RESET runTasks
runTasks = [];
cb();
});
taskWrapper('default', ['predefault', 'style', 'script'], function(cb) {
console.log('default task starting');
cb();
});
Also, I am doubtful if gulp-notify will work with Gulp 4.
Through a bit of luck, I discovered this was possible via the module yargs, which I already have installed.
When running gulp styles, for example, I can check argv._.indexOf("styles") > -1, as it contains ['styles']. When running gulp (i.e the default task), it contains []. In my testing, this works perfectly for my use case.
The official documentation for gulpjs/gulp has a sample gulpfile.js which provides a watch task that has no return statement:
gulp.task('watch', function() {
gulp.watch(paths.scripts, ['scripts']);
gulp.watch(paths.images, ['images']);
});
That approach fits my needs, because I want to watch over multiple sets of files with different tasks associated to each of them.
But I've found in the community a few bold statements saying that gulp.watch should be returned, such as this one, that proposes the following code:
gulp.task('watch', function() {
return gulp.watch('./public/resources/jsx/project/*.js',['application'])
});
I understand that tasks should return, so that other tasks using them as part of their workflow are able to act accordingly to their async nature, but for the special case of a watch task, which is always the last in a list of tasks, there might make sense not to return it, making it possible to have multiple watches.
Does that make sense? Can I safely omit the return from my watch task in order to be able to have multiple instances of gulp.watch in it?
I prefer all task have return statement. Otherwise you can read a false "Finished watch".
When task are complex, it is not possible create a single watch for a several pattern of files. In this cases, my solution is based on to create a supergroup task called "watch" that depends on single watches with its return statements.
gulp.task("watch", [
"watch-css",
"watch-js",
"watch-inject-html"
]);
gulp.task("watch-css", function() {
return gulp.watch(sources.css, ["css"]);
});
gulp.task("watch-js", function() {
return gulp.watch(sources.js, ["js"]);
});
gulp.task("watch-inject-html", function() {
return gulp.watch(sources.inject, ["inject-html"]);
});
For gulp4 you can do this:
gulp.task('watch:images', gulp.parallel(
function () { return gulp.watch(SRC_DIR+'/*', gulp.task('images:copy')); },
function () { return gulp.watch(SRC_DIR+'/svg/**/*', gulp.task('images:svg')); },
function () { return gulp.watch(SRC_DIR+'/backgrounds/**/*', gulp.task('images:backgrounds')); },
function () { return gulp.watch(SRC_DIR+'/heads/**/*', gulp.task('images:heads')); }
));
However the anonymous functions inside gulp.parallel will report as <anonymous> in gulp output.
You can give the functions names and they will show up in gulp output instead of anonymous.
gulp.task('watch:images', gulp.parallel(
function foobar1 () { return gulp.watch(SRC_DIR+'/*', gulp.task('images:copy')); },
function foobar2 () { return gulp.watch(SRC_DIR+'/svg/**/*', gulp.task('images:svg')); },
function foobar3 () { return gulp.watch(SRC_DIR+'/backgrounds/**/*', gulp.task('images:backgrounds')); },
function foobar4 () { return gulp.watch(SRC_DIR+'/heads/**/*', gulp.task('images:heads')); }
));
However it seems that return gulp.watch(/* ... */) is not ideal. When watching if you hit CTRL-C you get a nice big error about those watch tasks not completing.
It seems like if you have a stream you are supposed to return the stream.
e.g. return gulp.src(...).pipe()
...but if you are doing something async or don't have a stream you should be calling the callback instead of returning something.
Would be happy to be pointed to the relevant docs for this (return vs callback) as I didn't see a clear explanation in the gulp docs I read. I tried going all callback (no returning streams) and ran into other issues...but possibly they were caused by something else.
Dealing with multiple watches in a single task the following way doesn't report <anonymous> and also doesn't complain when you CTRL-C while watching. My understanding is that since the watch tasks are open-ended we just inform gulp that as far as gulp cares when it comes to making sure stuff runs in a specific order, these are started and gulp can move on.
gulp.task('watch:images', function (done) {
gulp.watch(SRC_DIR+'/*', gulp.task('images:copy'));
gulp.watch(SRC_DIR+'/svg/**/*', gulp.task('images:svg'));
gulp.watch(SRC_DIR+'/backgrounds/**/*', gulp.task('images:backgrounds'));
gulp.watch(SRC_DIR+'/heads/**/*', gulp.task('images:heads'));
return done();
});
I think you can omit return for watch task. I also don't support structures that has multiple watches. More over you are only going to use watch task on development environment, so go ahead and ignore return for watch task.
I'm learning the gulp way of doing things after using grunt exclusively in the past. I'm struggling to understand how to pass multiple inputs to get multiple outputs w/gulp.
Let's say I have a large project that has specialized js on a per page basis:
The Grunt Way:
grunt.initConfig({
uglify: {
my_target: {
files: {
'dest/everypage.min.js': ['src/jquery.js', 'src/navigation.js'],
'dest/special-page.min.js': ['src/vendor/handlebars.js', 'src/something-else.js']
}
}
}
});
This may be a poor example as it violates the "do only one thing" principle since grunt-uglify is concatenating and uglifying. In any event I'm interested in learning how to accomplish the same thing using gulp.
Thanks to #AnilNatha I'm starting to think with more of a Gulp mindset.
For my case I have a load of files that need to be concatenated. I offloaded these to a config object that my concat task iterates over:
// Could be moved to another file and `required` in.
var files = {
'polyfills.js': ['js/vendor/picturefill.js', 'js/vendor/augment.js'],
'map.js': [
'js/vendor/leaflet.js',
'js/vendor/leaflet.markercluster.min.js',
'js/vendor/jquery.easyModal.js',
'js/vendor/jquery-autocomplete.min.js',
'js/vendor/underscore.1.8.3.js',
'js/map.js'
],
...
};
var output = './build/js';
// Using underscore.js pass the key/value pair to custom concat function
gulp.task('concat', function (done) {
_.each(files, concat);
// bs.reload(); if you're using browsersync
done(); // tell gulp this asynchronous process is complete
});
// Custom concat function
function concat(files, dest) {
return gulp.src(files)
.pipe($.concat(dest))
.pipe(gulp.dest(output));
}
I am new to gulp.
I have written two task that need to be performed. When I run them separately, they work fine. But when I combine them, the "replace" does not work.
gulp.task('bundle-source', function () {
return bundler.bundle(config);
});
gulp.task('bundle-config', function(){
return gulp.src(['config.js'])
.pipe(replace('src/*', 'dist/*'))
.pipe(gulp.dest(''));
});
gulp.task('bundle', ['bundle-config', 'bundle-source']);
I think the issue is that they both manipulate config.js. I think the second task when it saves to disk overwrites the change the first one made. The second task is about 30 seconds.
Gulp tasks are run in parallel by default. So if your tasks are working on the same files, they might step on each others' toes indeed.
You can use gulp's tasks dependencies to have them run one after the other. So if bundle-config should be run before bundle-source :
gulp.task('bundle-source', ['bundle-config'], function () {
return bundler.bundle(config);
});
You can also use a package like run-sequence if you need them to run one after the other :
var seq = require('run-sequence');
gulp.task('bundle', function(cb) {
return seq('bundle-config', 'bundle-source', cb);
});
Finally, You could use gulp 4, which has a built-in mechanism to run tasks in series.
This must be obvious but I can't find it. I want to preprocess my stylus/coffee files with a watcher in the dev environment and in production with a build task (isn't that common to all of us?) and also run a few more minification and uglification steps in production but I want to share the pipe steps common to both dev and production for DRY
The problem is that when I run the task which watches the files, the task which preprocesses does that to all the files since it has its own gulp.src statement which includes all stylus files.
How do I avoid compiling all files on watching while still keeping the compile task separate. Thanks
paths = {
jade: ['www/**/*.jade']
};
gulp.task('jade', function() {
return gulp.src(paths.jade).pipe(jade({
pretty: true
})).pipe(gulp.dest('www/')).pipe(browserSync.stream());
});
gulp.task('serve', ['jade', 'coffee'], function() {
browserSync.init({
server: './www'
});
watch(paths.jade, function() {
return gulp.start(['jade']);
});
return gulp.watch('www/**/*.coffee', ['coffee']);
});
One important thing in Gulp is not to duplicate pipelines. If you want to process your stylus files, it has to be the one and only stylus pipe. If you want to execute different steps in your pipe however, you have multiple choices. One that I would suggest would be a noop() function in conjunction with a selection function:
var through = require('through2'); // Gulp's stream engine
/** creates an empty pipeline step **/
function noop() {
return through.obj();
}
/** the isProd variable denotes if we are in
production mode. If so, we execute the task.
If not, we pass it through an empty step
**/
function prod(task) {
if(isProd) {
return task;
} else {
return noop();
}
}
gulp.task('stylus', function() {
return gulp.src(path.styles)
.pipe(stylus())
.pipe(prod(minifyCss())) // We just minify in production mode
.pipe(gulp.dest(path.whatever))
})
As for the incremental builds (building just the changed files with every iteration), the best way would be to get on the gulp-cached plugin:
var cached = require('gulp-cached');
gulp.task('stylus', function() {
return gulp.src(path.styles)
.pipe(cached('styles')) // we just pass through the files that have changed
.pipe(stylus())
.pipe(prod(minifyCss()))
.pipe(gulp.dest(path.whatever))
})
This plugin will check if the contents have changed with each iteration you have done.
I spend a whole chapter on Gulp for different environments in my book, and I found those to be the most suitable ones. For more information on incremental builds, you can also check on my article on that (includes Gulp4): http://fettblog.eu/gulp-4-incremental-builds/