Basically, I have tasks split in two. The first is for development. There’s no magic, just basic stuff to make it work. It’s fast and great. The latter is for production. It’s slow, and I only want to run it periodically for testing, debugging, optimisation and production. Hence they are two separate tasks.
The latter, the task for production, does rely on the former task, however, I’d like to continue the stream, as if the latter task is an addition to the former.
gulp.task('styles', function() {
return gulp.src('src/sass/**/*.scss')
.pipe(sass())
.pipe(autoprefixer())
.pipe(gulp.dest('dist/css'));
});
gulp.task('styles-production', function() {
return runTaskAndUseStream('styles')
// Continue where the styles task ended
.pipe(uncss({
...
}))
.pipe(gulp.dest('dist/css'));
});
I was hoping the undocumented gulp.start('task') method would return a stream, but unfortunately it doesn’t. Many modules does allow you to fiddle with streams, but none of them returns a gulp task as a stream.
What do?
One thing that is (most likely) considered best practice would be incorporating the uncss task into your styles task, but running it only when certain flags are set:
var gutil = require('gulp-util');
function production(task) {
return (gutil.env.production ? task : gutil.noop());
}
gulp.task('styles', function() {
return gulp.src('src/sass/**/*.scss')
.pipe(sass())
.pipe(autoprefixer())
.pipe(production(uncss({
})))
.pipe(gulp.dest('dist/css'));
});
Packages you need: gulp-util for the noop task, and argument parsing
run it with gulp styles --production
(untested, but you get the concept)
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.
My code currently uses run-sequence to run a series of tasks. From what I was told I can now do this using native Gulp 4. Can someone confirm if this is possible and if so advise how I could do this.
var runSequence = require('run-sequence');
gulp.task('make-prod-ex1', function () {
makeAppHtml('app/**/*ex1', function () {
runSequence(
'makeTemplate',
'clean-css',
'make-css-files',
'make-css-bundle',
'rename-css-bundle',
'clean-js',
'make-js-bundle',
'rename-js-bundle',
'rename-index',
function () {
console.log("Completed");
});
});
});
In Gulp 4.0 gulp.series() and gulp.parallel() completely replace the task dependency mechanism that was used in Gulp 3.x. That means you can't write things like gulp.task('task1', ['task2']) anymore.
Instead of telling Gulp which tasks depend on each other and letting Gulp decide the execution order based on the dependency graph, you now have to explicitly define the execution order by composing gulp.series() and gulp.parallel() calls. Both can accept task names as well as functions and return functions themselves:
gulp.task('make-prod-ex1', gulp.series(
function(done) {
makeAppHtml('app/**/*ex1', done);
},
'makeTemplate',
'clean-css',
'make-css-files',
'make-css-bundle',
'rename-css-bundle',
'clean-js',
'make-js-bundle',
'rename-js-bundle',
'rename-index',
function (done) {
console.log("Completed");
done();
}));
As usual you have to make sure to signal async termination by either calling a done callback or returning streams in your tasks/functions. From personal experience it seems that Gulp 4.0 is a lot more quick to complain about this than Gulp 3.x was.
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 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/