Gulp4. "AssertionError : Task never defined" when calling or importing tasks
Asked Answered
W

4

33

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
Whangee answered 24/5, 2018 at 9:5 Comment(0)
H
65

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:

  1. 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.

  2. 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.

Heliotropism answered 1/3, 2019 at 6:31 Comment(1)
Freaking phenomenal answer. Bow in respect! Thanks a ton.Chemarin
M
16

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'));
Maclaine answered 29/9, 2018 at 3:49 Comment(1)
This gives the type error "TS2345: Argument of type '() => TaskFunction' is not assignable to parameter of type 'TaskFunction'."Thoroughgoing
G
3

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'));
Gretagretal answered 1/3, 2019 at 3:51 Comment(0)
T
1

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.

Thoroughgoing answered 21/2, 2023 at 2:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.