Gulp - conditional task inside task - gulp

I'd like to set up my default task to run with watch task if environment is set production. I can't find solution to make my gulp-if conditional work in a tasks stack. Here's my code:
gulp.task('default', ['styles', 'scripts', 'video' ], function() {
gulpif(isProduction, gulp.task('watch'));
});

Instead of gulp.task('watch') use gulp.start('watch') although you usually don't want to call a task from another task. A better way would be for you to create a function and call the function instead.
Also note: the gulp.start() method will no longer work with gulp4
Update:
Here is an example of how to use functions within gulp:
var someFile = require('./someFile.js');
gulp.task('my-custom-task', function () {
someFile.doSomething('foo', 'bar');
});
If your function does something asynchronously, it should call a callback at the end, so gulp is able to know when it’s done:
var someFile = require('./someFile.js');
gulp.task('my-custom-task', function (callback) {
someFile.doSomething('foo', 'bar', callback);
});

Related

Can I use Gulp 4 to run tasks in a serial fashion?

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.

gulp.watch - To return or not return

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.

How to run gup task in series

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.

Gulp: how to pass parameters from watch to tasks

With gulp you often see patterns like this:
gulp.watch('src/*.jade',['templates']);
gulp.task('templates', function() {
return gulp.src('src/*.jade')
.pipe(jade({
pretty: true
}))
.pipe(gulp.dest('dist/'))
.pipe( livereload( server ));
});
Does this actually pass the watch'ed files into the templates task? How do these overwrite/extend/filter the src'ed tasks?
I had the same question some time ago and came to the following conclusion after digging for a bit.
gulp.watch is an eventEmitter that emits a change event, and so you can do this:
var watcher = gulp.watch('src/*.jade',['templates']);
watcher.on('change', function(f) {
console.log('Change Event:', f);
});
and you'll see this:
Change Event: { type: 'changed',
path: '/Users/developer/Sites/stackoverflow/src/touch.jade' }
This information could presumably be passed to the template task either via its task function, or the behavior of gulp.src.
The task function itself can only receive a callback (https://github.com/gulpjs/gulp/blob/master/docs/API.md#fn) and cannot receive any information about vinyl files (https://github.com/wearefractal/vinyl-fs) that are used by gulp.
The source starting a task (.watch in this case, or gulp command line) has no effect on the behavior of gulp.src('src-glob', [options]). 'src-glob' is a string (or array of strings) and options (https://github.com/isaacs/node-glob#options) has nothing about any file changes.
Hence, I don't see any way in which .watch could directly affect the behavior of a task it triggers.
If you want to process only the changed files, you can use gulp-changed (https://www.npmjs.com/package/gulp-changed) if you want to use gulp.watch, or you cold use gulp-watch.
Alternatively, you could do this as well:
var gulp = require('gulp');
var jade = require('gulp-jade');
var livereload = require('gulp-livereload');
gulp.watch('src/*.jade', function(event){
template(event.path);
});
gulp.task('templates', function() {
template('src/*.jade');
});
function template(files) {
return gulp.src(files)
.pipe(jade({
pretty: true
}))
.pipe(gulp.dest('dist/'))
}
One of the possible way to pass a parameter or a data from your watcher to a task. Is through using a global variable, or a variable that is in both blocks scops. Here is an example:
gulp.task('watch', function () {
//....
//json comments
watch('./app/tempGulp/json/**/*.json', function (evt) {
jsonCommentWatchEvt = evt; // we set the global variable first
gulp.start('jsonComment'); // then we start the task
})
})
//global variable
var jsonCommentWatchEvt = null
//json comments task
gulp.task('jsonComment', function () {
jsonComment_Task(jsonCommentWatchEvt)
})
And here the function doing the task work in case it interest any one, But know i didn't need to put the work in such another function i could just implemented it directly in the task. And for the file you have your global variable. Here it's jsonCommentWatchEvt. But know if you don't use a function as i did, a good practice is to assign the value of the global variable to a local one, that you will be using. And you do that at the all top entry of the task. So you will not be using the global variable itself. And that to avoid the problem that it can change by another watch handling triggering. When it stay in use by the current running task.
function jsonComment_Task(evt) {
console.log('handling : ' + evt.path);
gulp.src(evt.path, {
base: './app/tempGulp/json/'
}).
pipe(stripJsonComments({whitespace: false})).on('error', console.log).
on('data', function (file) { // here we want to manipulate the resulting stream
var str = file.contents.toString()
var stream = source(path.basename(file.path))
stream.end(str.replace(/\n\s*\n/g, '\n\n'))
stream.
pipe(gulp.dest('./app/json/')).on('error', console.log)
})
}
I had a directory of different json's files, where i will use comments on them. I'm watching them. When a file is modified the watch handling is triggered, and i need then to process only the file that was modified. To remove the comments, i used json-comment-strip plugin for that. Plus that i needed to do a more treatment. to remove the multiple successive line break. Whatever, at all first i needed to pass the path to the file that we can recover from the event parameter. I passed that to the task through a global variable, that does only that. Allow passing the data.
Note: Even though that doesn't have a relation with the question, in my example here, i needed to treat the stream getting out from the plugin processing. i used the on("data" event. it's asynchronous. so the task will mark the end before the work completely end (the task reach the end, but the launched asynchronous function will stay processing a little more). So the time you will get in the console at task end, isn't the time for the whole processing, but task block end. Just that you know. For me it doesn't matter.

How to start Gulp task with params?

I need to apply a build task for specific files. For finding them, I use the typical template. But I can't understood how to pass the arguments (file path) from gulp.src.
Desirable solution.
gulp.task('bundles', function() {
gulp.src('bundles/**/*.js').
pipe(gulp.start('build', file.path));
});
gulp.task('build', function (path) {
// use here
});
Question is a bit stale and I am not sure I totally understand what you're trying to achieve here, but I think what you're looking for is lazypipe
You might want to clarify your question if that's not what you're looking for
Example Usage:
var lazypipe = require('lazypipe'),
g = require('gulp-load-plugins')({lazy: true}),
jsTransformPipe = lazypipe()
.pipe(g.jshint) // <-- Notice the notation: g.jshint, not g.jshint()
.pipe(g.concat, 'bundle.js'), // <-- Notice how the param is passed to g.concat, as a second param to .pipe()
jsSourcePipe = lazypipe()
.pipe(gulp.src, './**/*.js');
gulp.task('bundle', function() {
jsSourcePipe()
.pipe(jsTransformPipe()) // <-- You execute the lazypipe by calling it as a function
.pipe(gulp.dest('../build/');
});
With lazypipe you basically create a pipe for future use; hope this help
(Can't comment because of rep, sorry)
I assume that your sample code isn't filled with everything, but why don't you merge those tasks and use your gulp.src() in your build task instead of calling another task.
Maybe it's useful for you but with what you're showing I can't find an explanation for why you do this instead of simply going with something like :
gulp.task('build', function (path) {
gulp.src('bundles/**/*.js)
//Your code for this task
});
Of course, it removes the bundles task, but it's not useful as is.
Don't hesitate to comment if I'm wrong and I'll try to help you as much as I can.
First off, gulp.task('build', function (path) won't ever work. The only valid argument for gulp tasks is a callback to signal asynchronous task completion. If you tried to do run the above, gulp would expect path to be a function and the task would never complete unless that function was called. In this example, the 'build' task should be a regular function called from the 'bundles' pipe, not a task.
The better question would be: How do I run a custom function inside a gulp pipe? Plugins like gulp-tap might get you close, but it's not difficult to create what is essentially an inline gulp plugin to call your function.
Gulp pipes receive a through2 object stream containing a vinyl file object, an encoding and a callback. Here's a basic skeleton for calling any arbitrary function against the files in a gulp pipe:
var gulp = require('gulp');
var through = require('through2');
gulp.task('stack', function() {
return gulp.src('./src/*.js')
.pipe(through.obj(function(file, enc, cb) {
// file.path is the full path to the file
myBuildFunction(file.path);
cb(null, file);
}))
.pipe(gulp.dest('./build/'));
})
This can be incredibly powerful. To modify the file's contents, just change the file.contents buffer. To rename or relocate the file, change file.path. Everything can be done in gulp's native pipes.