Register to get access to free programming courses with interactive exercises

Basic Gulp Functions Gulp

In the last lesson, we plugged in the parallel() function built into the Gulp package, which allowed us to combine several functions into one task:

const { parallel } = require('gulp');

const sassCompile = (done) => {
  console.log('Compile SASS to CSS');


const pugCompile = (done) => {
  console.log('Compile Pug to HTML');


const imagesOptimize = (done) => {
  console.log('Optimize Images');


exports.default = parallel(sassCompile, pugCompile, imagesOptimize);

We will cover several other functions inside the Gulp package in this lesson. There's no need to describe them separately, so let's look at the most common ones.

The series() function

It is very similar in functionality to the parallel() function we learned in the previous lessons. But if there are two different functions, they must have differences, right? They do! The difference lies in the way we call the functions within the task. There are two approaches here:

  • The parallel() function executes functions simultaneously. In the example above, the sassCompile(), pugCompile(), and imagesOptimize() functions won't queue up or wait for each other to finish but will start working together. This approach is convenient if the tasks are in no way related to each other and the result of one doesn't depend on the others:

Parallel execution of Gulp tasks

  • The series() function implements series execution. It means a function won't start until the other one finishes. It helps when we use multiple subtasks, directly affecting what function will be next:

Consistent execution of Gulp tasks

Let us pay attention to the moment when tasks started in the screenshots. With parallel execution, Gulp shows all the tasks running simultaneously, and when using the series() function, the execution of a new task starts only after the previous one finishes.

The src() and dest() functions

Many of the tasks in Gulp are related to file processing. Whether it's SASS, Pug, or another tool, we should process them into a format understandable for browsers. To do this, we specify which file to process and where to move it after processing. Two functions are responsible for this in Gulp:

  • src() to specify the path to the file we are processing
  • dest() to specify the path where to put the file we have already processed

There are no particular surprises here. These functions are responsible for handling file paths and communication. They're much more flexible than they may seem at first, but the rest of the functionality, such as caching, is used very rarely and in only large projects. Let's look at a basic example of a file-copying operation:

const { src, dest } = require('gulp');

const copyFile = () => {
  return src('src/sass/app.scss')

exports.copy = copyFile;

After completing the task, the src/sass/app.scss file will be copied to the build/styles/ directory:

├── build/
│   ├── styles/
│   │   └── app.scss
├── src/
│   ├── sass/
│   │   └── app.scss
│   ├── pages/
│   │   ├── index.pug
│   │   ├── sections/
│   │   │   ├── head.pug
│   │   │   └── footer.pug
├── gulpfile.js
├── package.json
└── node_modules/

You don't need it there now because the build directory will get the processed files, but now you know how they'll get there :)

The done() function isn't called here, unlike it was in the previous examples. It is because we used the keyword return. You may have read more about the return keyword in the Introduction to Programming course.


In the example above, we had a clear path to the file we wanted to copy. For small projects, this can be a simple and efficient solution. But it is often necessary to process not just one but all files from a particular directory or even a directory tree. For example, we can have the following style structure:

├── src/
│   ├── sass/
│   │   ├── global.scss
│   │   ├── mobile.scss
│   │   ├── desktop.scss

How can these files be processed correctly? There are two options:

  1. Process each file individually
  2. Process all files within one function

With the first option, it's simple: we create three functions, combine them into a single task and execute:

const { src, dest, parallel } = require('gulp');

const copyGlobalScss = () => {
  return src('src/sass/global.scss')

const copyMobileScss = () => {
  return src('src/sass/mobile.scss')

const copyDesktopScss = () => {
  return src('src/sass/desktop.scss')

exports.copy = parallel(copyGlobalScss, copyMobileScss, copyDesktopScss);

Programmers are okay with solutions with only three files, but it's not the best practice. After all, we perform the same operation on each file. There will be changes only in the processed file.

To specify multiple files, we use path templates called Globs. It is a small package that converts templates to paths and is built into Gulp by default. You only really need to know a couple of tricks that you can use to select almost any file and in any quantity.

The first construction is the asterisk *, which indicates that you should select everything that doesn't conflict with the specified path. In the last example, we can replace the filename with an asterisk, and then Gulp will select all three files:

  • global.scss
  • mobile.scss
  • desktop.scss
const { src, dest } = require('gulp');

const copyScss = () => {
  return src('src/sass/*.scss')

exports.copy = copyScss;

You can make copying files even better. Suppose the SASS files are in different places, and we want to merge them into one. You have to go through all the available directories, see if there is a file with the extension scss/sass, and copy it. It uses a ** construction designed to iterate through directories. For example, you can modify the code as follows:

const { src, dest } = require('gulp');

const copyScss = () => {
  return src('src/**/*.scss')

exports.copy = copyScss;

When the task starts, we will search for files in all directories within src with the extension .scss. We look through the src subdirectories and the src directory. For example, we select the following files:

  • src/styles.scss
  • src/project/app/styles/app.scss
  • src/sass/mobile.scss and so on

Imagine we use the method that searches for files in different directories when transferring them using dest(). In this case, Gulp keeps the nesting it had. For example, the src/project/app/styles/app.scss file will end up in the build/styles/project/app/styles/app.scss path when we run the last example. This feature helps to avoid mistakes when working on a project.

It works fine, but there's one significant problem – there may be directories inside our src that we don't need to process files from.

For example, let us observe the directories with npm packages. We can deal with this problem using Globs. It has a directory exclusion method specified with a logical negation sign !. In this case, we specify the desired paths and exceptions as an array of strings. Here we exclude the src/project directory from copying:

const { src, dest } = require('gulp');

const copyScss = () => {
  // ['src/**/*.scss', '!src/project/**'] — It is a string array
  // which excludes the src/project directory and all directories nested within it
  return src(['src/**/*.scss', '!src/project/**'])

exports.copy = copyScss;

The characters /** go to the end of the excluded directory. We exclude not only the directory itself but also every subdirectory in it.

The pipe() function

Without going into the fine details, pipe() allows you to link read and write streams to each other. If we go back to the file copying example, it's through pipe() that we can get a file and give it to dest() for further processing.

The same is true for file processing with plugins, as in the case of the compilation of SASS into CSS. We can bind the whole chain using the pipe() function.

In the context of working with the Gulp package, you can build a typical chain, or template, of tasks:

const task = () => {
  return src('the file we are working with)
    .pipe(pluginOne()) // Processing the first plugin
    .pipe(pluginTwo()) // Processing by the second plugin
    .pipe(pluginN()) // Processing another plugin
    .pipe(dest('the path where we put the processed file'));

Do it yourself

Add multiple directories and files to the tutorial project. Create tasks to copy and move them to different directories. Practice the Globs patterns you have learned.

Recommended materials

  1. Built-in Functions in Gulp

Hexlet Experts

Are there any more questions? Ask them in the Discussion section.

The Hexlet support team or other students will answer you.

About Hexlet learning process

For full access to the course you need a professional subscription.

A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.

Get access
hours of theory

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions

Our graduates work in companies:

<span class="translation_missing" title="translation missing:">Bookmate</span>
<span class="translation_missing" title="translation missing:">Healthsamurai</span>
<span class="translation_missing" title="translation missing:">Dualboot</span>
<span class="translation_missing" title="translation missing:">Abbyy</span>
Suggested learning programs
Layout with the latest CSS standards
5 months
from scratch
under development
Start at any time

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending this form, you agree to our Personal Policy and Service Conditions
Toto Image

Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.