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);
});
Related
Below you can see simplified view of an issue. Basically, I'm able to call task1.js using gulp.series in tasks task2,3.js, but once I add same code to call task1.js in task4.js - Task never defined: task1 error gets thrown.
There are more tasks in the tasks folder than in file structure example below.
I've got three tasks,
...
/tasks
build.js
clean.js
dev.js
gulpfile.babel.js
...
all of them required in gulpfile.babel.js using the require-dir package
import requireDir from 'require-dir';
requireDir('./tasks', {recurse: true});
This allows me to call a task from clean.js at dev.js, and it works fine.
import gulp from 'gulp';
gulp.task('dev', gulp.series('clean');
But after I add same code structure at build.js.
import gulp from 'gulp';
gulp.task('build', gulp.series('clean');
it somehow breaks gulp stream (I guess), so now on any task call I get:
$gulp dev
-AssertionError [ERR_ASSERTION]: Task never defined: clean.
$gulp -v
[11:50:11] CLI version 2.0.1
[11:50:11] Local version 4.0.0
For those migrating from gulp v3 to v4 or are using gulp.task() to define tasks in gulp v4 and get this error message: Task never defined, the problem usually lies here:
Forward references
A forward reference is when you compose tasks, using string
references, that haven't been registered yet. This was a common
practice in older versions, but this feature was removed to achieve
faster task runtime and promote the use of named functions. In newer
versions, you'll get an error, with the message "Task never defined",
if you try to use forward references. You may experience this when
trying to use exports for your task registration and composing tasks
by string. In this situation, use named functions instead of string
references.
During migration, you may need to use the forward reference registry.
This will add an extra closure to every task reference and
dramatically slow down your build. Don't rely on this fix for very
long.
From gulpjs documentation re: gulp.series and gulp.parallel documentation.
Here is what that means. There are two ways to create tasks:
1. gulp.task('someStringAsTask', function() {..})
2. function myNamedFunction () {…}
When you use version 1 (gulp.task…) you cannot refer to that task by its string name until it has been registered. So you cannot do this:
exports.sync = gulp.series('sass2css', serve, watch);
// or gulp.task('dev', gulp.series('sass2css', serve, watch); doesn't work either
gulp.task('sass2css', function() {
return gulp.src(paths.sass.stylesFile)
.pipe(sass().on("error", sass.logError))
.pipe(gulp.dest(paths.css.temp))
.pipe(reload({ stream: true }));
})
Results in
AssertionError [ERR_ASSERTION]: Task never defined: sass2css
That is a forward reference, composing a task (using gulp.series or gulp.parallel) and referring to a task by its string name (in the above case 'sass2css') before it has been registered. (calling "gulp.task(…..)" is the act of registering) Putting the gulp.task('sass2css',...) first fixes the problem.
If you use version two of defining a task:
function sass2css() {
return gulp.src(paths.sass.stylesFile)
.pipe(sass().on("error", sass.logError))
.pipe(gulp.dest(paths.css.temp))
.pipe(reload({ stream: true }));
}
you are now using a named function to register a task and do not need to use its name as a string. So this now works:
exports.sync = gulp.series(sass2css, serve, watch);
// gulp.task('dev', gulp.series(sass2css, serve, watch); this also works
followed by (or preceded by - either works):
function sass2css() {
return gulp.src(paths.sass.stylesFile)
.pipe(sass().on("error", sass.logError))
.pipe(gulp.dest(paths.css.temp))
.pipe(reload({ stream: true }));
}
The original OP used this and it worked:
import gulp from 'gulp';
gulp.task('dev', gulp.series('clean');
Noted that clean.js got imported before dev.js so that was okay.
This didn't work:
import gulp from 'gulp';
gulp.task('build', gulp.series('clean');
because the string-referenced task, 'clean' gets imported (and thus registered) after build.js where it is referenced - thus creating an illegal forward reference to a string-referenced task.
So there are two standard ways to fix this error:
Use named functions to define tasks not gulp.task('someTask',...). Then it doesn't matter the order of using those named functions when composing other tasks, i.e., when using gulp.series or gulp.parallel. And there are other advantages to using named functions, such as passing arguments, so this is the best option.
If you do use the older gulp v3 gulp.task method of creating tasks with string references, be careful to not refer to those tasks until after the task is actually created.
Also see my answer at task never defined error for fixing another problem which results in the same error message. Specifically using gulp.task('someTask', ['anotherTask'], function(){}) synatx in a gulp4 file.
The series and parallel functions of gulp 4 do not create a task definition as its README seems to suggest, but instead they both run the tasks in parameter. In order to work as intended, one need to surround the call with a closure.
So, to fix the excerpt
gulp.task('build', gulp.series('clean'));
it is necessary to add the closure:
// Older EcmaScripts:
gulp.task('build', function() { return gulp.series('clean') });
// EcmaScript 6:
gulp.task('build', () => gulp.series('clean'));
I had a similar setup where I had recursively require'd all tasks under a directory. And after updating to gulp 4 started getting error Task never defined.
I tried Pedro solution, but this caused another error:
The following tasks did not complete: default
Did you forget to signal async completion?
The solution was fairly simple for me, just import the missing tasks.
import gulp from 'gulp';
import './clean';
gulp.task('build', gulp.series('clean'));
The easiest solution might be using the official undertaker-forward-reference package:
const gulp = require("gulp");
const FwdRef = require("undertaker-forward-reference");
gulp.registry(FwdRef()); // Or gulp.registry(new FwdRef());
gulp.task("firstRegisteredTask", gulp.series("laterRegisteredTask")); // Works thanks to undertaker-forward-reference
gulp.task("laterRegisteredTask", () => {
return gulp.src("someGlob").pipe(gulp.dest("someFolder"));
});
This solution might negatively affect performance though (source):
This will add an extra closure to every task reference and dramatically slow down your build.
I've specified some Gulp tasks in a gulpSequence like this:
gulp.task('default', function(done){
gulpSequence(
'private:clean',
'private:copy-app-package-file',
'private:copy-app-main-file',
'private:copy-libs',
'private:copy-app-files',
'private:copy-css-files',
'private:build-html',
'private:package-app',
done);
});
I thought that they should run one after another. The build-html tasks needs the libs, app-files and css-files, because they get included within one html file.
But if I run the default task, the build-html task is missing some files. For example the task can only inject 10 files in the html file but there are 16 files.
Is there another way to specify dependent tasks. I thought the gulpSequence will handle this.
You're on the right lines, but the npm package you are trying to use is depreciated. Might be worth considering checking out run-sequence (https://www.npmjs.com/package/run-sequence) which waits for a previous task to complete before starting.
So in your instance, this would become
gulp.task('default', function( cb ) {
$.runSequence(
'private:clean',
'private:copy-app-package-file',
'private:copy-app-main-file',
'private:copy-libs',
'private:copy-app-files',
'private:copy-css-files',
'private:build-html',
'private:package-app',
cb
)
});
You specified that all tasks should run in sequence with no parallelism at all. A new task starts as soon as a former task shows completion.
Probably you have some issues in a task before the build-html task. Do they handle the completion (done() callback) correctly? In most cases I saw there was some async action triggered and the callback was called before the async task finished. So the sequence continues before the task is really over...
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);
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 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'));
});