I have a global task that must run in sequence (synchronous) some sub-tasks. I used the task's dependencies mechanism to handle that "synchronous" logic :
// main task
gulp.task('deploy', ['build', 'upload', 'extract', 'migrate'], function() {
// task returns a stream
});
// sub tasks
gulp.task('migrate', ['extract'], function() {
// task returns a stream
});
gulp.task('extract', ['upload'], function() {
// task returns a stream
});
gulp.task('upload', ['build'], function() {
// task returns a stream
});
gulp.task('build', [], function() {
// task returns a stream
});
Dependencies works well and run all in sequence.
But now, how can I call migrate without executing extract>upload>build.
Because, sometimes I'll want to call manually :
gulp build
gulp upload
gulp extract
And I don't want each tasks to re-run all dependencies ...
Thanks
What I've done is define (2) versions of each task I want to isolate, but have them call the same logic so I'm not repeating myself.
For example, in my current project, I have an e2e-tests-development task that depends on build-development, which builds both the server and the client before running e2e tests. Sometimes I just want to re-run the tests and not re-build the entire application.
My e2e-tests-development.js file looks roughly like this:
var gulp = require('gulp');
// Omitted...
gulp.task('e2e-tests-development',
['build-development'],
_task);
gulp.task('e2e-tests-development-isolated',
[], // no dependencies for the isolated version of the task.
_task);
function _task() {
// Omitted...
}
And obviously you'd invoke gulp with e2e-tests-development-isolated instead of e2e-tests-development if you want to just run that task.
(Of course what gulp really needs is a --no-depedencies flag from the command-line...)
In the end, the run-sequence plugin does really well the job :
var runner = require('run-sequence');
// main task
gulp.task('deploy', [], function() {
runner(
'build',
'upload',
'extract',
'migrate'
);
});
// sub tasks
gulp.task('migrate', ['extract'], function() {
// task returns a stream
});
gulp.task('extract', ['upload'], function() {
// task returns a stream
});
gulp.task('upload', ['build'], function() {
// task returns a stream
});
gulp.task('build', ['clean:build'], function() {
// task returns a stream
});
gulp.task('clean:build', [], function() {
// task returns a stream
});
This way I can independently call any sub-task without re-executing previous subtaks...
if you need the 2 version approach like I do, the following solution will do:
gulp.task('deploy:build', ['build'], function () {
gulp.start('deploy');
}
Now I can call deploy without having a dependency on build.
Related
I am super new to using Node.js, NPM and all these modern tools for better productivity and workflow.
So here are the details:
Node version - v8.10.0
Gulp CLI version - 2.0.1
Gulp Local version - 3.9.1
NPM version - 5.6.0
Windows 7
Node.js installed in D:/ProgramFiles
I've tried using gulp and it does work wonderfully with this script
var gulp = require('gulp'),
watch = require('gulp-watch');
gulp.task('default',function(){
console.log('Gulp task created');
});
gulp.task('html' , function() {
console.log('Something useful here');
});
gulp.task('watch', function() {
watch('/app/index.html', function() {
gulp.start('html');
});
});
So typing gulp does respond with the default task message. Typing gulp html does respond too with a console message. However, when i type gulp watch, it does work with following output.
Starting 'watch'...
Finished 'watch' after 7.99 ms
But whenever i make changes and save the index file, the cmd doesn't update. I've tried using Git Bash and other terminals. I've even installed previous node versions and tried solving this issue using those but no luck so far.
I tried editing the dependencies to an older version but that doesn't work too.
If anyone of you can help, I'll be thankful.
Updated with callback method to end process for a graceful exit and not use CTRL-C.
gulp.task('watch',function(){
gulp.watch('./data/index.html', function(){
gulp.start(['someOtherGulpTask']);
});
});
gulp.task('someOtherGulpTask', function () {
gulp.src('./test/sometest.js')
.pipe(gulpmocha(),setTimeout(function() {
cb();
}, 5000))
});
function cb(){
process.exit(1);
}
Original answer below
The order of execution of gulp tasks are -
the default task gets executed and then the subsequent ones unless the task
to be executed is specifically mentioned in the cmdline call to gulp as you
have mentioned - "gulp watch".
you can specify the order in the beginning of the gulpfile like
gulp.task('default', ['watch']);
or, you can start a task explicitly like so,
gulp.task('default',function(done){
console.log('Gulp task created');
gulp.start('watch');
done();
});
done is the callback that allows your waiting task to complete which is missing in your gulp file. Below is the complete gulp file that you can use to execute the watch task as you require.
var gulp = require('gulp'),
watch = require('gulp-watch');
gulp.task('default',function(done){
console.log('Gulp task created');
gulp.start('watch');
done();
});
gulp.task('html' , function() {
console.log('Something useful here');
});
gulp.task('watch', function() {
watch('./data/index.html', function() {
console.log('Some changes done');
});
});
gulp.task('watch', function () {
gulp.watch('./data/index.html', ['html']);
});
Avoid gulp.start, it is not documented or recommended.
I'm trying to run a set of tasks in series, that is one after the other in Gulp 4.0 however when I add my 3rd task my gulp code breaks.
gulp.task('concat-js', (done) => {
gulp.src([
'app1.js',
'app2.js',
])
.pipe(concat("app.js"))
.pipe(gulp.dest('build'))
done();
});
gulp.task('concat-css', (done) => {
gulp.src([
'.styles1.css',
'.style2.css'
])
.pipe(concat("app.css"))
.pipe(gulp.dest('build'))
done();
});
gulp.task('minify-js', (done) => {
gulp.src('./build/app.js')
.pipe(uglify())
.pipe(gulp.dest('./build/'))
done();
})
//this works & creates /build with the app.js & app.css files
gulp.task('build-production-static-resource', gulp.series('concat-js', 'concat-css',, (done) => {
done();
}));
If I delete the build folder to start all over & try adding another task (minfy-js), my third task fails & the build folder also doesn't get created.
//File not found with singular glob: /Users/user/myapp/build/app.js
gulp.task('build-production-static-resource', gulp.series('concat-js', 'concat-css', 'minify-js', (done) => {
done();
}));
The way you're signalling async completion of your tasks is wrong. Read this answer for an overview of the different ways in which you can signal async completion in gulp.
Basically all the streams that you create via gulp.src() are asynchronous. That means you create the stream and your code immediately returns. However the actual processing of the stream starts only after you exit the task function. In order for gulp to know that you're using a stream and that it has to wait for the stream to finish processing, you need to return the stream from your task.
You're doing something else entirely. You're invoking a callback function done which is another way to signal asynchronous task completion to gulp. However that's the completely wrong way in this case, because when you invoke the callback function done the stream you created hasn't even started to process.
That means gulp thinks your concat-js task has finished, although the code inside your task hasn't even really started running. So when the minify-js task runs your ./build/app.js file hasn't been created yet. Boom. Error.
To fix this always return the streams from your tasks:
gulp.task('concat-js', () => {
return gulp.src([
'app1.js',
'app2.js',
])
.pipe(concat("app.js"))
.pipe(gulp.dest('build'))
});
gulp.task('concat-css', () => {
return gulp.src([
'.styles1.css',
'.style2.css'
])
.pipe(concat("app.css"))
.pipe(gulp.dest('build'))
});
gulp.task('minify-js', () => {
return gulp.src('./build/app.js')
.pipe(uglify())
.pipe(gulp.dest('./build/'))
})
gulp.task('build-production-static-resource', gulp.series(
'concat-js', 'concat-css', 'minify-js'
));
I have two sets of files, let's call them base and mods. The mods files override the base files, so when I run the gulp task related to base, I need to run the mods task directly after. My setup is something like this:
gulp.task('base',function(){
return gulp.src('base-glob')
.pipe(...)
.pipe(gulp.dest('out-glob'))
});
gulp.task('mods',function(){
return gulp.src('mods-glob')
.pipe(...)
.pipe(gulp.dest('out-glob'))
});
So I want to run the mods task at the completion of the base task. Note that this is not the same as defining base as a dependency of mods, because if I'm only changing mods files, I only need to run the mods task. I'd prefer not to use a plugin.
I've been reading the docs about callback functions and other suggestions of synchronous tasks, but can't seem to get my head around it.
I know you don't want to use a plugin, but gulp doesn't have a way to run a sequence of tasks in order without a plugin. Gulp 4 will, but in the meantime the stopgap solution is the run-sequence plugin.
gulp.task('all', function() {
runSequence('base', 'mods');
});
This ensures that the tasks run in order as opposed to unordered dependencies.
Now setup a watch:
gulp.task('watch', function() {
gulp.watch('base-glob', ['all']);
gulp.watch('mods-glob', ['mods']);
});
Whenever base-glob changes, gulp will run all task, which will run the sequence base then mods.
Whenever mods-glob changes, gulp will run only mods task.
That sound about right?
runSequence has some weird bugs, it kept complaining that my tasks are not defined.
If you look into the Orchestrator source, particularly the .start() implementation you will see that if the last parameter is a function it will treat it as a callback.
I wrote this snippet for my own tasks:
gulp.task( 'task1', () => console.log(a) )
gulp.task( 'task2', () => console.log(a) )
gulp.task( 'task3', () => console.log(a) )
gulp.task( 'task4', () => console.log(a) )
gulp.task( 'task5', () => console.log(a) )
function runSequential( tasks ) {
if( !tasks || tasks.length <= 0 ) return;
const task = tasks[0];
gulp.start( task, () => {
console.log( `${task} finished` );
runSequential( tasks.slice(1) );
} );
}
gulp.task( "run-all", () => runSequential([ "task1", "task2", "task3", "task4", "task5" ));
I'm trying to conditionally pipe a file stream based on the value of a variable, as a way to define two separate build environments (ie. development and production).
Some tasks can be run individually with a command-line flag like so:
gulp scripts --env production
And will then do some production-only pipeline steps:
gulp.task('scripts', function() {
var jsFilter = filter(['*.js']),
appFiles;
return gulp.src(appFiles)
.pipe(jsFilter)
.pipe(concat('application-build.js'))
.pipe(gulpif(env === 'production', uglify()))
.pipe(size())
.pipe(gulpif(env === 'production', gulp.dest('dist/js'), gulp.dest('tmp/js')))
.pipe(browserSync.reload({ stream: true }));
});
I have a build task that calls a number of other tasks as dependencies (including this scripts task for instance). I want this build task to assign a variable (env, in this case) before running task dependencies. Which means that this:
gulp.task('build', ['scripts', 'styles', 'otherstuff'], function() {
env = 'production';
}
doesn't work, because the dependencies are run before the body of the task.
I currently have it implemented with gulp.start:
gulp.task('build', function() {
env = 'production';
gulp.start('scripts');
});
But the .start method isn't actually part of gulp's public API - it comes from Orchestrator - and isn't intended to be used for anything. Plus, the equivalent method gulp.run was deprecated from the API awhile ago.
So I'm wondering - is there another way I could assign a variable in a task before running its dependencies?
(Or maybe there's a better way to to implement something like build environments in gulp?)
THE RIGHT WAY
I disagree with #Justin. Defining an environmental variable with a task is a hackjob of an idea. This is better done with gutil.env this way.
gulp --env prod task
gulp.task( 'myTask', () => { console.log( gutil.env.env ) } )
Now from this point, you have gulp.env.env set.
Or, alternatively you can do like this example in this ticket.. which addresses this from the developers of Gulp which first suggest to use an environmental variable, but provide this idiom..
function js(shouldMinify) {
return gulp.src('./js/*.js')
.pipe(concat('app.js'))
.pipe(gulpif(shouldMinify, uglify()))
.pipe(gulp.dest('js'));
});
gulp.task('develop', function () {
shouldMinify = false;
return js(shouldMinify);
});
gulp.task('build', function () {
shouldMinify = true;
return js(shouldMinify);
});
That same developer (phated) always says to use env...
Not to mention, you should control this type of logic with environment variables or command line flags. - phated
Presumably, he's referring to the use of gutil.noop() in gulp-util's docs:
// gulp should be called like this :
// $ gulp --type production
gulp.task('scripts', function() {
gulp.src('src/**/*.js')
.pipe(concat('script.js'))
// LOOK BELOW: if we don't send to uglify, we push to noop stream.
.pipe(gutil.env.type === 'production' ? uglify() : gutil.noop())
.pipe(gulp.dest('dist/'));
});
You could create a task specifically to set the environment and run it before your other tasks.
gulp.task('set-production', function() {
env = 'production';
});
// Doesn't quite work because tasks are run in parallel
gulp.task('build', ['set-production', 'scripts', 'styles', 'otherstuff']);
The problem here is that your tasks will be run in parallel, meaning the set-production task may be run after the other tasks. You can solve this problem with the run-sequence package.
var runSequence = require('run-sequence');
gulp.task('build', function(callback) {
runSequence('set-production', ['scripts', 'styles', 'otherstuff'], callback);
});
This will run the set-production task first, then run the scripts, styles, and otherstuff tasks in parallel.
In a nutshell...
I have some grunt tasks with external configs that I want to test (make sure they do what they're supposed to do). So I'd like to write a mocha test running the tasks in the before function and asserting everything went well.
...and with some details
In my gruntfile there is something like
# Load task configs from `grunt` dir.
configs = require('load-grunt-configs') grunt,
config:
src: ['tasks/*{.js,.json,.coffee,.cson}']
# Load all **grunt tasks** with patter `grunt-*`
require('load-grunt-tasks') grunt, pattern: [
'grunt-*'
]
# Pass configs to grunt
grunt.initConfig configs
and afterwards some task registration...
However I now have access to my less task residing as a less.cson in my tasks folder
(which uses grunt-contrib-less).
So far so good. :)
I'm using chai for assertions:
describe('Checking static resources for dev', function () {
describe('less task', function () {
before(function () {
grunt.task.run('less');
});
it('should have a compiled css file for docs', function () {
grunt.file.isFile('public/css/docs.css').should.be.ok;
grunt.file.isFile('public/css/docs.css.map').should.be.ok;
});
As expected, the task completes after the tests were run since it's not async and I don't know a way to tell grunt (without modifying the existing task/config) that it should call a possible done function when ready and then make the assertion.
I also tried grunt-run-task but could not manage to run any task (they abort with a timeout although I gave them more than 20 seconds to complete).
Guess I'm looking for something like
before(function (done) {
grunt.task.run('less', done);
});
Anyone some advice?