How to Migrate to Gulp.js 4.0
Despite competition from webpack and Parcel, Gulp.js remains one of the most popular JavaScript task runners. Gulp.js is configured using code which makes it a versatile, general-purpose option. As well as the usual transpiling, bundling and live reloading, Gulp.js could analyze a database, render a static site, push a Git commit, and post a Slack message with a single command. Learn how to migrate to Gulp.js 4.0.
For an introduction to Gulp, take a look at the following:
- An Introduction to Gulp.js
- How to Use Gulp.js to Automate Your CSS Tasks
- Develop WordPress Themes Faster with Gulp
Gulp.js 4.0
Gulp.js 3.x has been the default for around half a decade. Until recently, npm install gulp
would have installed 3.9.1 — the version referenced in the tutorials above.
Gulp.js 4.0 has been available throughout that time, but had to be explicitly installed with npm install gulp@next
. This was partly owing to ongoing development and because Gulp.js 4 gulpfile.js
configuration files are not compatible with those developed for version 3.
On December 10, 2018, Gulp.js 4.0 was announced as the default and published to npm. Anyone using npm install gulp
on a new project will receive version 4.
Is it Necessary to Migrate to Gulp.js 4?
No. Gulp.js 3 has been deprecated and is unlikely to receive further updates, but it can still be used. Existing projects won’t update unless the version is explicitly changed in the dependencies
section of package.json
. For example:
"dependencies": {
"gulp": "^4.0.0"
}
You an also install Gulp.js 3 in new projects using:
npm install gulp@^3.9.1
It’s possibly best to stick with Gulp.js 3.x if you have a particularly complex, mission-critical build system.
However, existing Gulp.js plugins should be compatible and most gulpfile.js
configurations can be migrated in an hour or two. There are several benefits to upgrading, which will become apparent throughout this tutorial.
Upgrade to Gulp.js 4.0
Update your package.json
dependencies as shown above, then run npm install
to upgrade. You can also update the command-line interface using npm i gulp-cli -g
, although this hasn’t changed at the time of writing.
To check the installation, enter gulp -v
at the command line:
$ gulp -v
[15:15:04] CLI version 2.0.1
[15:15:04] Local version 4.0.0
Migrating gulpfile.js
Running any task is now likely to raise scary-looking errors. For example:
AssertionError [ERR_ASSERTION]: Task function must be specified
at Gulp.set [as _setTask] (/node_modules/undertaker/lib/set-task.js:10:3)
at Gulp.task (/node_modules/undertaker/lib/task.js:13:8)
at /gulpfile.js:102:8
at Object.<anonymous> (/gulpfile.js:146:3)
at Module._compile (internal/modules/cjs/loader.js:688:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
at Module.load (internal/modules/cjs/loader.js:598:32)
at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
at Function.Module._load (internal/modules/cjs/loader.js:529:3)
at Module.require (internal/modules/cjs/loader.js:636:17)
at require (internal/modules/cjs/helpers.js:20:18)
at execute (/gulp-cli/lib/versioned/^4.0.0/index.js:36:18)
at Liftoff.handleArguments (/gulp-cli/index.js:175:63)
at Liftoff.execute (/gulp-cli/node_modules/liftoff/index.js:203:12)
at module.exports (/gulp-cli/node_modules/flagged-respawn/index.js:51:3)
at Liftoff.<anonymous> (/gulp-cli/node_modules/liftoff/index.js:195:5)
It’s daunting, but you can ignore everything except the first reference of gulpfile.js
, which shows the line where an error was encountered (102 in this example).
Fortunately, most of these errors are caused by the same type of problem. The following sections use the CSS tasks tutorial code as an example. The code is available on GitHub and provides the original Gulp.js 3 gulpfile.js and the migrated Gulp.js 4 equivalent.
Convert Task Arrays to series() Calls
Gulp.js 3 allowed arrays of synchronous tasks to be specified. This was typically used when a watch
event was triggered or a task had dependencies. For example, run the images
task before the css
task:
gulp.task('css', ['images'], () =>
gulp.src(cssConfig.src)
// .other plugins
.pipe(gulp.dest(cssConfig.build));
);
Gulp.js 4.0 introduces the series()
and parallel()
methods to combine tasks:
series(...)
runs tasks one at a time in the order specified, andparallel(...)
runs tasks simultaneously in any order.
Complex tasks can be nested to any level — something which would have been difficult to achieve in Gulp.js 3. For example, run tasks a
and b
in parallel then, once both are complete, run tasks c
and d
in parallel:
gulp.series( gulp.parallel(a, b), gulp.parallel(c, d) )
The css
task above can be migrated to Gulp.js 4 by changing the array to a series()
method call:
gulp.task('css', gulp.series('images', () =>
gulp.src(cssConfig.src)
// .other plugins
.pipe(gulp.dest(cssConfig.build));
)); // remember the extra closing bracket!
Async Completion
Gulp.js 3 permitted synchronous functions but these could introduce errors which were difficult to debug. Consider tasks which don’t return the gulp
streamed value. For example, a clean
task to delete the build
folder:
const
gulp = require('gulp'),
del = require('del'),
dir = {
src : 'src/',
build : 'build/'
};
gulp.task('clean', () => {
del([ dir.build ]);
});
Gulp.js 4.0 throws a warning because it needs to know when a task has completed in order to manage series()
and parallel()
sequences:
[15:57:27] Using gulpfile gulpfile.js
[15:57:27] Starting 'clean'...
[15:57:27] The following tasks did not complete: clean
[15:57:27] Did you forget to signal async completion?
To solve this, Gulp.js 4 will accept a returned Promise (which is supported by the del
package):
gulp.task('clean', () => {
return del([ dir.build ]);
});
// or use the simpler implicit return:
// gulp.task('clean', () => del([ dir.build ]) );
Alternatively, pass a callback function which is executed on completion (del
also provides a synchronous sync()
method):
gulp.task('clean', (done) => {
del.sync([ dir.build ]);
done();
});
Here’s a more complex Gulp.js 3 example with watch tasks:
gulp.task('default', ['css', 'server'], () => {
// image changes
gulp.watch(imgConfig.src, ['images']);
// CSS changes
gulp.watch(cssConfig.watch, ['css']);
});
These can be migrated to Gulp.js 4 series()
methods and a done()
callback:
gulp.task('default', gulp.series('css', 'server', (done) => {
// image changes
gulp.watch(imgConfig.src, gulp.series('images'));
// CSS changes
gulp.watch(cssConfig.watch, gulp.series('css'));
done();
}));
Bonus Tip: Convert Task Methods to ES6 Modules
Most gulpfile.js
configurations will work in Gulp.js 4.0 once task arrays are converted to series()
calls and asynchronous functions signal completion.
Although the task()
definition method is still supported, the newer ES6 module exports
pattern offers several benefits:
- Private tasks can be defined which can be called within
gulpfile.js
but not from thegulp
command. - Functions can be passed by reference rather than a string name so syntax errors can be highlighted by editors.
- The same function can be referenced using any number of task names.
- It’s easier to define complex dependencies in
series()
and/orparallel()
.
Take the following Gulp.js 3 images
and css
tasks:
gulp.task('images', () =>
gulp.src(imgConfig.src)
// .other plugins
.pipe(gulp.dest(imgConfig.build))
);
gulp.task('css', ['images'], () =>
gulp.src(cssConfig.src)
// .other plugins
.pipe(gulp.dest(cssConfig.build))
);
These could be converted to use the Gulp.js 4 module pattern:
function images() {
return gulp.src(imgConfig.src)
// .other plugins
.pipe(gulp.dest(imgConfig.build));
}
exports.images = images;
exports.pics = images;
function css() {
return gulp.src(cssConfig.src)
// .other plugins
.pipe(gulp.dest(cssConfig.build));
}
exports.css = gulp.series(images, css);
exports.styles = exports.css;
Note: return
statements must be added because the ES6 arrow =>
functions with an implicit return have been changed to standard function definitions.
In this Gulp.js 4 example:
- either
gulp images
orgulp pics
can be used to run theimages()
task - either
gulp css
orgulp styles
will runimages()
followed bycss()
.
Set exports.default
to define a default task run when gulp
is executed from the command line without a specific task.
Gulp Goodness
The CSS tasks example, which optimizes images, compiles Sass, minifies CSS, and live-reloads on changes, took less than one hour to convert. Examine the code on GitHub to see:
- the original Gulp.js 3 gulpfile.js, and
- the migrated Gulp.js 4 gulpfile.js.
It’s taken a while to (properly) arrive, but Gulp.js 4 provides opportunities for defining tasks which would have been impractical in version 3. Updating software can seem like a waste of development effort, but you’ll be rewarded with a faster, more robust set of tasks, which will save time in the long run.