What does .once(string, function) do? - gulp

I am analyzing the VS Code tests.
'use strict';
const gulp = require('gulp');
const mocha = require('gulp-mocha');
gulp.task('test', function () {
return gulp.src('test/all.js')
.pipe(mocha({ ui: 'tdd', delay: true }))
.once('end', function () { process.exit(); });
});
I have not found any documentation for once(string, function). What does it do? Where are the docs?
Having searched the gulp source reveals the use of but not the definition of once.

once() is a feature/function of Node.js not Gulp.
Here's the documentation.
In Streams, it can be used to add a one time listener. Once the event is triggered, the listener is invoked and removed.

Related

only last callback is being called in gulp file

In gulpfile, I've 3 tasks, when I run the following code, it only executes the callback of the last task. I want that if I run the gulp command, after completion of clean task, it should execute callback of copy:db & default task.
Gulp.task('clean', function () {
console.log('Clean');
return Gulp.src("./dbSchema/*")
.pipe(VinylPaths(Del));
})
Gulp.task('copy:db', Gulp.series("clean"), function () {
console.log('Copy DB');
return Gulp.src("./db/*")
.pipe(Gulp.dest("./dbSchema"));
})
Gulp.task('default', Gulp.series("copy:db"), function () {
console.log('defaulp');
return TypeScriptProject.src()
.pipe(TypeScriptProject())
.js.pipe(Gulp.dest('dist'));
});
When I run the command gulp, it shows the following log.
[12:46:37] Starting 'default'...
[12:46:37] Starting 'copy:db'...
[12:46:37] Starting 'clean'...
Clean
[12:46:37] Finished 'clean' after 26 ms
[12:46:37] Finished 'copy:db' after 28 ms
[12:46:37] Finished 'default' after 31 ms
Can anyone tell me where am I going wrong?
To get your code working how you’ve described, the callback functions need to be passed as a paramater to .series(). Eg:
Gulp.task('clean', function () {
console.log('Clean');
return Gulp.src("./dbSchema/*")
.pipe(VinylPaths(Del));
})
Gulp.task('copy:db', Gulp.series(clean, function () {
console.log('Copy DB');
return Gulp.src("./db/*")
.pipe(Gulp.dest("./dbSchema"));
}))
Gulp.task('default', Gulp.series(copy:db, function () {
console.log('defaulp');
return TypeScriptProject.src()
.pipe(TypeScriptProject())
.js.pipe(Gulp.dest('dist'));
}));
IMHO, it would be simpler to have three totally separate tasks:
Gulp.task('clean', function () {
console.log('Clean');
return Gulp.src("./dbSchema/*")
.pipe(VinylPaths(Del));
});
Gulp.task('copy:db', function () {
console.log('Copy DB');
return Gulp.src("./db/*")
.pipe(Gulp.dest("./dbSchema"));
});
Gulp.task('default', function () {
console.log('defaulp');
return TypeScriptProject.src()
.pipe(TypeScriptProject())
.js.pipe(Gulp.dest('dist'));
});
and then call them with either:
Gulp.task('default', gulp.series(clean, copy:db, js));
or
Gulp.task('default', gulp.series(clean, gulp.parallel(copy:db, js)));
Hope that helps :)
Additional Notes:
The naming convention for gulp/vars is normally camelCase, eg: gulp and typeScriptProject not Gulp or TypeScriptProject.
You can totally remove the need to ever write gulp. by using: var {gulp, task, src, dest, watch, series, parallel} = require('gulp');
Rather than defining your tasks directly, you can make your code easier to read by using CommonJS exports module notation to declare tasks.
Makes life a little easier if you are consistent when with quotes, rather than mixing singles and doubles. Both allow globbing
Following Gulp’s own documentation is perhaps the place to start, their sample code on github has some great examples of setting up a basic gulpfile.
If you wrap all that up you get this:
/*
* Example of requires with gulp methods also requiring gulp.
*/
var {
gulp,
dest,
series,
parallel,
src,
task,
watch
} = require('gulp'),
vinylPaths = require('vinyl-paths'), // may not be required, see note in clean func.
del = require('del'),
ts = require('gulp-typescript');
/*
* Added a basic TypeScript Project so the example is complete and could run.
*/
var typeScriptProject = ts.createProject({
declaration: true
});
/*
* Your tasks converted to plain/standard js functions.
*/
function clean () {
return src('dbSchema/*')
.pipe(vinylPaths(del));
// Looking at your example code the vinylPaths is redundant,
// as long as you’re using del ^2.0 it will return its promise,
// so you could replace the above with:
return del([ 'dbSchema' ]);
}
function copyDb () {
return src('db/*')
.pipe(dest('dbSchema'));
}
function scripts () {
// example src path
return src('lib/*.ts')
.pipe(typeScriptProject())
.pipe(dest('dist'));
}
/*
* By defining all the tasks separately it makes it really clear how the tasks will run.
*/
var build = gulp.series (
clean,
gulp.parallel (
copyDb,
scripts
)
);
/*
* Example of using `exports` module notation to declare tasks.
*/
exports.clean = clean;
exports.copyDb = copyDb;
exports.scripts = scripts;
exports.build = build;
/*
* Good practise to define the default task as a reference to another task.
*/
exports.default = build;

Detect outputs in closure compiler's gulp plugin

I am using gulp and google closure compiler e.g.:
var compilerPackage = require('google-closure-compiler');
var closureCompiler = compilerPackage.gulp();
gulp.task('js-compile', function () {
return closureCompiler({
js: './src/js/**.js',
externs: compilerPackage.compiler.CONTRIB_PATH + '/externs/jquery-1.9.js',
compilation_level: 'ADVANCED',
warning_level: 'VERBOSE',
language_in: 'ECMASCRIPT6_STRICT',
language_out: 'ECMASCRIPT5_STRICT',
output_wrapper: '(function(){\n%output%\n}).call(this)',
js_output_file: 'output.min.js'
})
.src() // needed to force the plugin to run without gulp.src
.pipe(gulp.dest('./dist/js'));
});
Is it possible do detect outputs (warning and error)? For example like this:
.on("error", function(err) {})
.on("warning", function(warn) {});
Thank you for your help

Gulp error: The following tasks did not complete: Did you forget to signal async completion?

I have the following gulpfile.js, which I'm executing via the command line gulp message:
var gulp = require('gulp');
gulp.task('message', function() {
console.log("HTTP Server Started");
});
I'm getting the following error message:
[14:14:41] Using gulpfile ~\Documents\node\first\gulpfile.js
[14:14:41] Starting 'message'...
HTTP Server Started
[14:14:41] The following tasks did not complete: message
[14:14:41] Did you forget to signal async completion?
I'm using gulp 4 on a Windows 10 system. Here is the output from gulp --version:
[14:15:15] CLI version 0.4.0
[14:15:15] Local version 4.0.0-alpha.2
Since your task might contain asynchronous code you have to signal gulp when your task has finished executing (= "async completion").
In Gulp 3.x you could get away without doing this. If you didn't explicitly signal async completion gulp would just assume that your task is synchronous and that it is finished as soon as your task function returns. Gulp 4.x is stricter in this regard. You have to explicitly signal task completion.
You can do that in six ways:
1. Return a Stream
This is not really an option if you're only trying to print something, but it's probably the most frequently used async completion mechanism since you're usually working with gulp streams. Here's a (rather contrived) example demonstrating it for your use case:
var print = require('gulp-print');
gulp.task('message', function() {
return gulp.src('package.json')
.pipe(print(function() { return 'HTTP Server Started'; }));
});
The important part here is the return statement. If you don't return the stream, gulp can't determine when the stream has finished.
2. Return a Promise
This is a much more fitting mechanism for your use case. Note that most of the time you won't have to create the Promise object yourself, it will usually be provided by a package (e.g. the frequently used del package returns a Promise).
gulp.task('message', function() {
return new Promise(function(resolve, reject) {
console.log("HTTP Server Started");
resolve();
});
});
Using async/await syntax this can be simplified even further. All functions marked async implicitly return a Promise so the following works too (if your node.js version supports it):
gulp.task('message', async function() {
console.log("HTTP Server Started");
});
3. Call the callback function
This is probably the easiest way for your use case: gulp automatically passes a callback function to your task as its first argument. Just call that function when you're done:
gulp.task('message', function(done) {
console.log("HTTP Server Started");
done();
});
4. Return a child process
This is mostly useful if you have to invoke a command line tool directly because there's no node.js wrapper available. It works for your use case but obviously I wouldn't recommend it (especially since it's not very portable):
var spawn = require('child_process').spawn;
gulp.task('message', function() {
return spawn('echo', ['HTTP', 'Server', 'Started'], { stdio: 'inherit' });
});
5. Return a RxJS Observable.
I've never used this mechanism, but if you're using RxJS it might be useful. It's kind of overkill if you just want to print something:
var of = require('rxjs').of;
gulp.task('message', function() {
var o = of('HTTP Server Started');
o.subscribe(function(msg) { console.log(msg); });
return o;
});
6. Return an EventEmitter
Like the previous one I'm including this for completeness sake, but it's not really something you're going to use unless you're already using an EventEmitter for some reason.
gulp.task('message3', function() {
var e = new EventEmitter();
e.on('msg', function(msg) { console.log(msg); });
setTimeout(() => { e.emit('msg', 'HTTP Server Started'); e.emit('finish'); });
return e;
});
An issue with Gulp 4.
For solving this problem try to change your current code:
gulp.task('simpleTaskName', function() {
// code...
});
for example into this:
gulp.task('simpleTaskName', async function() {
// code...
});
or into this:
gulp.task('simpleTaskName', done => {
// code...
done();
});
You need to do one thing:
Add async before function.
const gulp = require('gulp');
gulp.task('message', async function() {
console.log("Gulp is running...");
});
THIS WORKED!
The latest update on Feb 18, 2021, I found the problem after using the elder solution below, then I have fixed it by using the following instead for the next gulp version.
File: Package.json
...,
"devDependencies": {
"del": "^6.0.0",
"gulp": "^4.0.2",
},
...
File: gulpfile.js Example
const {task} = require('gulp');
const del = require('del');
async function clean() {
console.log('processing ... clean');
return del([__dirname + '/dist']);
}
task(clean)
...
Elder Version
gulp.task('script', done => {
// ... code gulp.src( ... )
done();
});
gulp.task('css', done => {
// ... code gulp.src( ... )
done();
});
gulp.task('default', gulp.parallel(
'script',
'css'
)
);
I was getting this same error trying to run a very simple SASS/CSS build.
My solution (which may solve this same or similar errors) was simply to add done as a parameter in the default task function, and to call it at the end of the default task:
// Sass configuration
var gulp = require('gulp');
var sass = require('gulp-sass');
gulp.task('sass', function () {
gulp.src('*.scss')
.pipe(sass())
.pipe(gulp.dest(function (f) {
return f.base;
}))
});
gulp.task('clean', function() {
})
gulp.task('watch', function() {
gulp.watch('*.scss', ['sass']);
})
gulp.task('default', function(done) { // <--- Insert `done` as a parameter here...
gulp.series('clean','sass', 'watch')
done(); // <--- ...and call it here.
})
Hope this helps!
This is an issue when migrating from gulp version 3 to 4, Simply you can add a parameter done to the call back function , see example,
const gulp = require("gulp")
gulp.task("message", function(done) {
console.log("Gulp is running...")
done()
});
I cannot claim to be very knowledgeable on this but I had the same problem and have resolved it.
There is a 7th way to resolve this, by using an async function.
Write your function but add the prefix async.
By doing this Gulp wraps the function in a promise, and the task will run without errors.
Example:
async function() {
// do something
};
Resources:
Last section on the Gulp page Async Completion: Using async/await.
Mozilla async functions docs.
You need to do two things:
Add async before function.
Start your function with return.
var gulp = require('gulp');
gulp.task('message', async function() {
return console.log("HTTP Server Started");
});
Workaround: We need to call the callback functions (Task and Anonymous):
function electronTask(callbackA)
{
return gulp.series(myFirstTask, mySeccondTask, (callbackB) =>
{
callbackA();
callbackB();
})();
}
Basically v3.X was simpler but v4.x is strict in these means of synchronous & asynchronous tasks.
The async/await is pretty simple & helpful way to understand the workflow & issue.
Use this simple approach
const gulp = require('gulp')
gulp.task('message',async function(){
return console.log('Gulp is running...')
})
Here you go: No synchronous tasks.
No synchronous tasks
Synchronous tasks are no longer supported. They often led to subtle mistakes that were hard to debug, like forgetting to return your streams from a task.
When you see the Did you forget to signal async completion? warning, none of the techniques mentioned above were used. You'll need to use the error-first callback or return a stream, promise, event emitter, child process, or observable to resolve the issue.
Using async/await
When not using any of the previous options, you can define your task as an async function, which wraps your task in a promise. This allows you to work with promises synchronously using await and use other synchronous code.
const fs = require('fs');
async function asyncAwaitTask() {
const { version } = fs.readFileSync('package.json');
console.log(version);
await Promise.resolve('some result');
}
exports.default = asyncAwaitTask;
My solution: put everything with async and await gulp.
async function min_css() {
return await gulp
.src(cssFiles, { base: "." })
.pipe(concat(cssOutput))
.pipe(cssmin())
.pipe(gulp.dest("."));
}
async function min_js() {
return await gulp
.src(jsFiles, { base: "." })
.pipe(concat(jsOutput))
.pipe(uglify())
.pipe(gulp.dest("."));
}
const min = async () => await gulp.series(min_css, min_js);
exports.min = min;
Solution is simple, but I outline the changes I made, the error I was getting, my gulpfile before and after, and the package versions--therefore making it appear very long.
I solved this by following the directions of multiple previous answers, in addition to following the error outputted when I would save my .scss file.
In short:
I changed how gulp-sass was imported—see (A)
I changed all functions to ASYNC functions—see (B)
(A) Changes made to gulp-sass import:
Before: var sass = require('gulp-sass)
After: var sass = require('gulp-sass')(require('sass'));
(B) Simply convert functions to ASYNC—
What my gulpfile looked like before:
'use strict';
// dependencies
var gulp = require('gulp');
var sass = require('gulp-sass');
var minifyCSS = require('gulp-clean-css');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
var changed = require('gulp-changed');
var SCSS_SRC = './src/Assets/scss/**/*.scss';
var SCSS_DEST = './src/Assets/css';
function compile_scss() {
return gulp.src(SCSS_SRC)
.pipe(sass().on('error', sass.logError))
.pipe(minifyCSS())
.pipe(rename({ suffix: '.min' }))
.pipe(changed(SCSS_DEST))
.pipe(gulp.dest(SCSS_DEST));
}
function watch_scss() {
gulp.watch(SCSS_SRC, compile_scss);
}
gulp.task('default', watch_scss); //Run tasks
exports.compile_scss = compile_scss;
exports.watch_scss = watch_scss;
What my gulpfile looked like after:
'use strict';
// dependencies
var gulp = require('gulp');
//var sass = require('gulp-sass');
var sass = require('gulp-sass')(require('sass'));
var minifyCSS = require('gulp-clean-css');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
var changed = require('gulp-changed');
var SCSS_SRC = './src/Assets/scss/**/*.scss';
var SCSS_DEST = './src/Assets/css';
async function compile_scss() {
return gulp.src(SCSS_SRC)
.pipe(sass().on('error', sass.logError))
.pipe(minifyCSS())
.pipe(rename({ suffix: '.min' }))
.pipe(changed(SCSS_DEST))
.pipe(gulp.dest(SCSS_DEST));
}
async function watch_scss() {
gulp.watch(SCSS_SRC, compile_scss);
}
gulp.task('default', watch_scss); // Run tasks
exports.compile_scss = compile_scss;
exports.watch_scss = watch_scss;
Package Versions:
"gulp": "^4.0.2",
"gulp-changed": "^4.0.3",
"gulp-rename": "^2.0.0",
"gulp-uglify": "^3.0.2",
"gulp-clean-css": "^4.3.0",
"gulp-sass": "^5.0.0",
"sass": "^1.38.0"
Error I was getting:
Error in plugin "gulp-sass"
Message:
gulp-sass 5 does not have a default Sass compiler; please set one yourself.
Both the `sass` and `node-sass` packages are permitted.
For example, in your gulpfile:
var sass = require('gulp-sass')(require('sass'));
[14:00:37] The following tasks did not complete: default, compile_scss
[14:00:37] Did you forget to signal async completion?
I got that solved, It's Pretty simple just add the below code snippet.
var gulp = require('gulp');
gulp.task('message', async function() {
console.log("HTTP Server Started");
});
I was struggling with this recently, and found the right way to create a default task that runs sass then sass:watch was:
gulp.task('default', gulp.series('sass', 'sass:watch'));
In gulp version 4 and over, it is required that all gulp tasks tell Gulp where the task will end. We do this by calling a function that is passed as the first argument in our task function
var gulp = require('gulp');
gulp.task('first_task', function(callback) {
console.log('My First Task');
callback();
})
Add done as a parameter in default function. That will do.
For those who are trying to use gulp for swagger local deployment, following code will help
var gulp = require("gulp");
var yaml = require("js-yaml");
var path = require("path");
var fs = require("fs");
//Converts yaml to json
gulp.task("swagger", done => {
var doc = yaml.safeLoad(fs.readFileSync(path.join(__dirname,"api/swagger/swagger.yaml")));
fs.writeFileSync(
path.join(__dirname,"../yourjsonfile.json"),
JSON.stringify(doc, null, " ")
);
done();
});
//Watches for changes
gulp.task('watch', function() {
gulp.watch('api/swagger/swagger.yaml', gulp.series('swagger'));
});
For me the issue was different: Angular-cli was not installed (I installed a new Node version using NVM and simply forgot to reinstall angular cli)
You can check running "ng version".
If you don't have it just run "npm install -g #angular/cli"
I know the problem was presented 6 years ago but probabily you missed return in your function.
I fixed this issue this morning with eslint, that gave me the same message after running "gulp lint" in my working directory.
Example:
function runLinter(callback)
{
return src(['**/*.js', '!node_modules/**'])
.pipe(eslint())
.on('end', ()=>
{
callback();
});
}
exports.lint = runLinter;
So I got the same error with Gulp 4, but the solution was different. I had the error:
"Did you forget to signal async completion?"
but before the error it also says:
"gulp-sass no longer has a default Sass compiler; please set one yourself"
I completely missed that part at first.
So I had this in the gulfile.js:
const sass = require('gulp-sass')
This should be changed to:
const sass = require('gulp-sass')(require('sass'));
Now it works.

Gulp - start task with an argument

So I'm trying to create a gulp workflow and I'd like to implement options for some tasks, like gulp copy-images --changed. Now, I've created a watch task that obviously watches all image files and it should start the copy-images with the --changed flag.
Ideally, I want to do something like this:
gulp.task('copy-images', function(){
// some code
});
gulp.task('watch', function(){
gulp.watch(config.images, ['copy-images --changed']);
});
I'm also very aware that I could do:
gulp.task('copy-images', function(){
// some code
});
gulp.task('copy-images-changed', function(){
// some code
});
gulp.task('watch', function(){
gulp.watch(config.images, ['copy-images']);
});
but this means duplicate code.
Anyone with a solution or maybe some advice?
Thanks in advance!
Gulp does not provide a built-in way of specifying options for tasks. You have to use an external options parser module like yargs. See this question for more on that topic.
This also means that passing something like ['copy-images --changed'] to gulp.watch() will not work. The entire string will just be interpreted as a task name.
The best approach for you would be to factor out the code of your task into a function and then call this function from both your task and your watch:
var argv = require('yargs').argv;
function copyImages(opts) {
if (opts.changed) {
// some code
} else {
// some other code
}
}
gulp.task('copy-images', function() {
copyImages(argv);
});
gulp.task('watch', function(){
gulp.watch(config.images, function() {
copyImages({changed:true});
});
});
The above should cover all of your bases:
gulp copy-images will execute //some other code.
gulp copy-images --changed will execute //some code.
gulp watch will execute //some code any time a watched file is changed.

How to run a gulp task from another task?

I am aware of the ability to declare dependencies that will run before the task, e.g.
gulp.task('a', () => {});
gulp.task('b', () => {});
gulp.task('c', ['a', 'c'], () => {});
Tasks 'a' and 'b' will be run every time after task 'c' is called and before task 'c' is executed.
However, how do I programmatically call an arbitrary task from within a gulp.task?
I did it using gulp.start(); like this:
gulp.task('test1', function(){
gulp.start('test2');
})
gulp.task('test2', function(){
// do something
})
I was using gulp 3.9.1 if it matters. Looks like gulp.start() may be removed or deprecated; but it hasn't happened yet.
Update
If I were to break things out into separate functions, as Novellizator suggested, it would be something like this:
function doSomething(){
// do something
}
gulp.task('test1', function(){
doSomething();
})
gulp.task('test2', function(){
doSomething();
})
It is simple and avoids the use of gulp.start().
From Gulp 4+ I found this to work well as a replacement for gulp.start:
const task1 = () => { /*do stuff*/ };
const task2 = () => { /*do stuff*/ };
// Register the tasks with gulp. They will be named the same as their function
gulp.task(task1);
gulp.task(task2);
...
// Elsewhere in your gulfile you can run these tasks immediately by calling them like this
(gulp.series("task1", "task2")());
// OR
(gulp.parallel("task1", "task2")());
On the general theme of "don't" from the other answers, it depends what you're doing.
If you're thinking:
gulp.task("a", () => {
doSomethingInstant()
return doTaskB()
})
You want:
gulp.task("meta-a", () => {
doSomethingInstant()
})
gulp.task("a", ["meta-a", "b"])
This works because all the tasks are started in order, so if you're not doing anything async in "meta-a", it will finish before "b" starts. If it's async then you need to be much more explicit:
gulp.task("meta-a", () => {
return doSomethingAsync()
})
function taskB() {
// Original task B code...
}
gulp.task("b", taskB)
gulp.task("a", ["meta-a"], taskB)
On a related note, you can initiate the tasks using either gulp.series or gulp.parallel in gulp v4. Although it's not a direct replacement for gulp.start, it achieves the same objective.
Example:
Before v4
gulp.task('protractor', ['protractor:src']);
After v4
gulp.task('protractor', gulp.series('protractor:src'));
As JeffryHouser stated above using the following method works but is deprecated and will be removed in future versions. I just attempted the following on 3.9.1 and it works fine.
Code:
// Variables
var gulp = require('gulp');
var less = require('gulp-less');
var watch = require('gulp-watch');
// Task to compile less -> css: Method 1- The bad way
gulp.task('default', function(){
gulp.src('src/*.less')
gulp.start('lessToCss');
});
gulp.task('lessToCss', function() {
gulp.src('src/*.less')
.pipe(less())
.pipe(gulp.dest('src/main.css'));
});
The second way mentioned in the comment by Novellizator is:
"Break tasks out into functions, then reuse them in other tasks if you need to" piece of advice from the aforementioned issue
I do this by use of lazypipe() https://www.npmjs.com/package/lazypipe
Code:
// Variables
var gulp = require('gulp');
var less = require('gulp-less');
var watch = require('gulp-watch');
var lazypipe = require('lazypipe');
var fixMyCSS = lazypipe()
.pipe(less); // Be CAREFUL here do not use less() it will give you an errror
// Task to compile less -> css: Method 2 - The nice way
gulp.task('default', function(){
//gulp.start('lessToCss');
gulp.src('src/*.less')
.pipe(fixMyCSS())
.pipe(gulp.dest('src/main.css'));
});
A brief comparison
Method 1 yielding the following:
Starting 'default'...
[22:59:12] Starting 'lessToCss'...
[22:59:12] Finished 'lessToCss' after 13 ms
[22:59:12] Finished 'default' after 48 ms
For a total of 61 ms to complete
Method 2?
- [23:03:24] Starting 'default'...
- [23:03:24] Finished 'default' after 38 ms
Practically half that at 38 ms
For a total difference of 23 ms
Now why this is the case? I honestly don't know enough about gulp to say, but let's say method 2 is clearly the more readable,maintainable and even the faster choice.
They are both easy to write so long as you understand not to call a stream directly within the lazypipe().
The best way form me is:
task('task1', () => {
// Buy potatoes
});
task('task2', ['task1'], () => {
// Cut potatoes
});
The result of the task2 it will be: Buy potatoes and Cut potatoes
There are a couple of ways to do this:
The first one: just invoke a new task call for the other:
gulp.task('foo', function() {
gulp.task('caller', ['bar']); // <- this calls the other existing task!
}
gulp.task('bar', function() {
//do something
}
The second, less cleaner one: Gulp is Javascript, so you just set a couple of instructions as a random function and call it!
gulp.task('task', function() {
recurrent_task();
}
gulp.task('task2', function() {
recurrent_task();
}
function recurrent_task() {
//do something
}