I have a Gulp build process that runs through roughly 10 tasks, including browserify and watch. It currently builds a common-bundle.js, and common-libs.js. It uses browser-sync to give me sub-second rebuilds.
Now I want to also build a project that depends on the common project. I want to retain the live rebuilds of both common and this project so that I could work on both of them at the same time. I want to keep the build process itself as DRY as possible and reuse the tasks i created to build common.
For example, a sample task:
var config = require('../config');
gulp.task('styles', function () {
return gulp.src(config.styles.src) // if i could tell it to get config elsewhere...
...
I can't pass a parameter into each task to tell it, go run the task but use:
var config = require('../config').common;
vs.
var config = require('../config').projectA;
I don't think tasks can take parameters.
Is there a different way to structure this?
git/gist link would be highly appreciated.
For now I am trying this approach - each task js file has 2 tasks defined, but at least the logic of the task is reused. I still wish for something cleaner.
./gulp/task/style.js:
function styles(config){
return gulp.src(config.styles.src)
...
}
}
gulp.task('styles', function () {
styles(config.common);
});
gulp.task('stylesProject1', function () {
styles(config.project1);
});
devTask.js:
runSequence(['styles', 'stylesProject1], 'watch', callback);
Related
I recently upgraded to gulp 4 and I am trying to solve a long standing issue of with my export process.
In short I have 3 (or more) independent folders in my project. By independent I mean that they each have their own bundle.js and global.css file. I have setup a target variable in my gulpfile which is used to create all the paths gulp needs for that target.
In the current situation when I want to export my entire project I need to manually change the target variable in the gulpfile and then run the export task.
I need something that works like the following (as the other_folders array can change)
/*---------- Exports current target ----------*/
gulp.task('export', gulp.series(to_prod,'export_files', 'export_scripts_and_styles', 'export_fonts', 'export_core'));
/*---------- Exports all targets ----------*/
gulp.task('export_all', function(done){
var needs_exporting = other_folders.concat("website");
needs_exporting.forEach(function(export_this){
target = export_this;
set_paths();
// Here it needs to fire the generic export task
gulp.series('export');
});
done();
});
The problem is that I cannot seem to find a way to call a gulp task in the forEach loop. Is there a way to do this or do I need a workaround?
Calling gulp.series('export') doesn't immediately start the export task. It just returns a function that you have to call in order to start the export task.
However calling the returned function doesn't start the export task immediately either. The function is asynchronous. Only later is the export task actually started.
The easiest way to run an asynchronous function for each element of a collection in series is to use the eachSeries() function that's provided by the async package:
var async = require('async');
gulp.task('export_all', function(done){
var needs_exporting = other_folders.concat("website");
async.eachSeries(needs_exporting, function(export_this, cb) {
target = export_this;
set_paths();
gulp.series('export')(cb);
}, done);
});
I have a gulp watch task that uses gulp-sass to convert SASS to CSS. For development, separated CSS files are all I want.
For release, I have a min tasks that uses gulp-cssmin and concat to bundle and minify the CSS. However, the min task doesn't deal with the SASS files. It assumes they have already been converted to CSS.
Is this a valid assumption? What if an automated build gets the latest source (which does not have .scss files) and runs min? Is there a race condition here as to whether the watch task will run in time for min to pick up the SASS files?
If your min task is also watching the SASS files, your assumption is not necessarily valid. By default, all the tasks will run at the same time. You can however define dependencies in your task, so that a task does not start until another task is finished. This could solve your problem. The documentation about dependencies can be found here.
There are also two other solutions:
Watch the CSS files for the min task.
Run the logic in your min task at the end of the task that compiles the SASS.
Edward! Better use optional minification to prevent extra reading from the disk.
var env = require('minimist')(process.argv.slice(2));
var noop = require('readable-stream/passthrough').bind(null, { objectMode: true });
gulp.task('style', function () {
return gulp.src('...')
.pipe(process())
.pipe(env.min ? uglify() : noop())
.pipe(gulp.dest('...'))
});
Also I don't recommend these tools from gulp-util which will be deprecated
Even if gulp guaranteed that it would wait for watch tasks to complete before starting dependent tasks, a race could still occur during the interval from when a source file is changed until the OS notifies gulp. So I decided not to use the SASS → CSS transform output. For as infrequently as I need to build the minimized versions, there's no payoff for saving those milliseconds.
Instead I used stream-combiner2 to reuse the transform logic:
var combiner = require('stream-combiner2');
function scssTransform() {
return combiner(
sourcemaps.init(),
sass(),
sourcemaps.write());
}
I use the transform for my non-mimized "sandbox" for local development:
gulp.task('sandbox:css', function () {
return gulp.src(paths.scss)
.pipe(scssTransform())
.pipe(gulp.dest(dirs.styles));
});
My task for serving the site locally depends on and watches the task:
gulp.task('serve.sandbox', ['sandbox:css' /* ... more ... */], function () {
// ... Start the web server ...
gulp.watch(paths.scss, ['sandbox:css']);
// ... Watch more stuff ...
});
I also use the transform for my minimized deployment:
gulp.task('deploy:css', function () {
return gulp.src(paths.scss)
.pipe(scssTransform())
.pipe(concat(files.cssMin))
.pipe(cssmin())
.pipe(gulp.dest(dirs.deploy));
});
Everytime I run gulp, I see this message gulp.run() has been deprecated. Use task dependencies or gulp.watch task triggering instead.
Example code:
var watch = require('gulp-watch');
watch(['public/**/*.js','!public/**/*.min.js'],function(){
gulp.run('compressjs');
gulp.run('bs-reload');
});
How can I avoid using gulp.run() with gulp-watch?
You shouldn't use run. Here is an alternative (to address that part of your answer), but not what you need to do:
gulp
.start('default')
.once('task_stop', function(){
//do other stuff.
});
If you really must fire an ad hoc task, but can literally use run...You can use .start with the task name, and also attach to the task_stop handler to fire something when the task is complete. This is nice when writing tests for gulp tasks, but that's really it.
however in day to day gulp usage, this is an antipattern.
Normally, you build smaller tasks and composite them. This is the right way. See this:
var gulp = require('gulp'),
runSequence = require('run-sequence');
function a(){
//gulpstuff
}
function b(){
//gulpstuff
}
function d(callback){
runSequence('a', 'b', callback)
}
gulp
.task('a', a) // gulp a -runs a
.task('b', b) // gulp b runs b
.task('c', ['a', 'b']) //gulp c runs a and b at the same time
.task('d', d); //gulp d runs a, then b.
basically if c or d was a watch task, you'd achieve the same goal of firing the already registered smaller gulp tasks without .run
gulp.run() was deprecated because people were using it as a crutch. You are using it as a crutch!
I'm not sure why you're using gulp-watch, the built in gulp.watch would be far more appropriate for what you're using it for. Have a look at the documentation for .watch: https://github.com/gulpjs/gulp/blob/master/docs/API.md#gulpwatchglob--opts-tasks-or-gulpwatchglob--opts-cb
Here's what you should have written. Please understand why you're using it instead of just copying it:
gulp.watch(['public/**/*.js','!public/**/*.min.js'], ['compressjs', 'bs-reload'])
You can always just use plain old javascript functions. From what i've read this is considered to be a more "gulp-ish" way of doing things.
I ran into a similar situation once and basically solved it with something like this:
var watch = require('gulp-watch');
watch(['public/**/*.js','!public/**/*.min.js'], function(){
compress();
bsReload();
});
And then these functions are basically wrappers around the guts of what would have been your original gulp tasks:
var compress = function () {
return gulp.src("stuff/**")
.pipe(gulp-compress())
.pipe(gulp.dest("./the_end/");
};
Its easy to become caught up in the idea that one has to use gulp tasks for everything otherwise you are "doing it wrong" but if you need to use something like this go for it.
If you also want a gulp task with the same functionality then whip up something like this:
gulp.task("compress", function () {
return compress();
});
and you can still take advantage of gulp task dependecies when using the same code if you need it somewhere else.
I'm having some trouble with a couple of gulp tasks. i've tried everything and followed the documentation but nothing seems to work. Hoping someone can help.
What i'm trying to do is have sass minification happen, then rsync the result to a local vagrant folder (NOT a remote server), then reload browser sync.
The problem i am having is that I'm fighting the fact that gulp wants to run all the tasks together. I need them to happen one after the other. All the tasks work on their own, i am just having trouble making them run sequentially. I've tried callbacks and playing with dependency but i'm obviously missing something.
My setup is complicated, all my tasks are in separate js files but i've tried to combine what i have into this single github gist so people can help. Thanks for any assistance.
https://gist.github.com/CodeStalker/9661725dcaf105d2ed6c
The only way I have got this to work is to pipe rsync onto the end of the sass magnification, and wrap it in gulp-if using a server: true variable so it only does the rsync if it knows its running on a VM. Not ideal.
This is a common issue people run into with Gulp, especially when coming from Grunt. Gulp is async to the max, it always wants to do as many things as it can all at the same time.
The first step to getting tasks to run sequentially is to let gulp know when your task ends, this can be done a couple different ways.
1) Return a stream, gulp will wait for the stream's "end" event as the trigger for that task being done. Example:
gulp.task( "stream-task", function(){
return gulp.src( srcGlob )
.pipe(sass())
.pipe(compressCSS())
.pipe(gulp.dest( destGlob ));
});
2) Return a Promise, gulp will wait for the promise to enter a resolved state before signaling the task as done. Example: (not perfect just to get the point across)
gulp.task( "promise-task", function() {
return new Promise(function(resolve, reject){
fs.readFile( "filename", function( err, data ){
if( err ){ return reject(err); }
return resolve( data );
});
});
});
3) Call the task callback, if the function doesn't return anything but the function signature takes an argument, that argument will be a callback function you can call to signal the task as done. Example:
gulp.task( "cb-task", function( done ){
fs.readFile( "filename", function( err, data ){
// do stuff, call done...
done();
});
});
Most often you are going to be returning a stream like example 1, which is what a typical gulp task looks like. Options 2 and 3 are more for when you are doing something that isn't really a traditional stream based gulp task.
The next thing is setting the "dependency" of one task for another. The gulp docs show this as the way you do that:
gulp.task( "some-task-with-deps", [ "array-of-dep", "task-names" ], function(){ /* task body */ });
Now I don't know the current status but there were some issues with these dependency tasks not running in the proper order. This was originally caused by a problem with one of gulp's dependencies (orchestrator I believe). One kind gentleman out there in NPM land made a nice little package to be used in the interim while the bugs were being worked out. I started using it to order my tasks and haven't looked back.
https://www.npmjs.com/package/run-sequence
The documentation is good so I won't go into a lot of detail here. Basically run-sequence lets explicitly order your gulp tasks, just remember it doesn't work if you don't implement one of the three options above for each of your tasks.
Looking at your gist adding a couple missing return statements in your tasks may just do the trick, but as an example this is what my "dev" task looks like for my project at work...
gulp.task( "dev", function( done ){
runSequence(
"build:dev",
"build:tests",
"server:dev",
[
"less:watch",
"jscs:watch",
"lint:watch",
"traceur:watch"
],
"jscs:dev",
"lint:dev",
"tdd",
done // <-- thats the callback method to gulp let know when this task ends
);
});
Also for reference my "build:dev" task is another use of run-sequence
gulp.task( "build:dev", function( done ){
runSequence(
"clean:dev",
[
"less:dev",
"symlink:dev",
"vendor:dev",
"traceur:dev"
],
done // <-- thats the callback method to let know when this task ends
);
});
If the tasks need to be run in order the task name gets added as its own argument to runSequence if they don't conflict send them in as an array to have the tasks run at the same time and speed up your build process.
One thing to note about watch tasks! Typically watch tasks run indefinitely so trying to return the "infinite" stream from them may make gulp think that the task never ends. In that case I'll use the callback method to make gulp think the task is done even if it is still running, something like...
gulp.task( "watch-stuff", function( done ){
gulp.watch( watchGlob )
.on( "change", function( event ){ /* process file */ });
done();
});
For more on watch tasks and how I do incremental builds check out an answer I wrote the other day, Incremental gulp less build
Here you go, I believe it should work now. If not let me know, I might have made a typo.
Your gist fixed
I think you are hitting (i don't have enough reputation points to post link, google issues #96) which is fixed in 4.x which isn't out yet :-). That said, check this hack out: My post about Gulp v3.x bug
I'm having a hard time to understand on how to process multiple gulp sources in a single task. In a task like this:
gulp.task('task1', function (cb) {
gulp.src('src/js/**/*').pipe(gulp.dest('dist'));
gulp.src('src/css/**/*').pipe(gulp.dest('dist'));
...
});
I would like to process all the different source files and then mark the task as finished, so the others tasks can depend on it's completion.
I'm aware of the possibility, to using individual tasks for each individual source but this would make everything more complicated and bloat the orchestrator with a huge number of tasks that are actually not needed individually.
You can pass an array of globs to gulp.src if you are doing the same things to all files. For example,
gulp.task('task1', function () {
return gulp.src(['src/js/**/*', 'src/css/**/*']).pipe(gulp.dest('dist'));
});
Be sure to return the stream so the orchestrator knows when that task is complete.
If you are doing different things to the different sets of files, you can merge the streams like this in one task:
var es = require('event-stream');
gulp.task('fancy-task', function () {
return es.merge(
gulp.src('*.js').pipe(some-js-plugin()),
gulp.src('*.css').pipe(some-style-plugin))
.pipe(gulp.dest('dist'));
});