31 minutes read

Every project begins simply—you install some packages, write your code, test it, and add new features. But soon, you find yourself repeating many manual tasks, small or large. These tasks can include refreshing your browser to see the latest changes, committing code to version control, concatenating and minifying JavaScript files to reduce the size, optimizing images, ensuring CSS files are properly autoprefixed, bundling your files into a single file —the list goes on.

While these tasks might seem easy to do at first, they soon get in the way, consuming valuable time and breaking your workflow. This is where build systems come into play, automating these repetitive tasks and allowing you to build your own custom workflow. Taking the first step to use a build system can feel daunting, much like starting to write tests for the first time. However, the rewards are worth it: you'll save time and create a more efficient development process.

One such build system is Gulp.js. In this topic, we will take that first step together and explore how to set up and use Gulp.js to streamline your development workflow.

Gulp.js

Gulp.js is a build system specifically designed for front-end developers. It automates repetitive tasks in your development workflow. But what sets gulp apart from other such systems?

  • Streams

    Streams in Gulp allow you to process files through a series of transformations in an efficient, chained manner. Instead of writing temporary files at each step, streams enable direct piping from one task to the next, which speeds up the build process and reduces disk usage.

  • Code over Configuration

    Gulp’s "code over configuration" philosophy means you write JavaScript code to define your tasks. If you're already familiar with JavaScript for front-end development, you can leverage the same language to manage your build processes. This approach allows for greater complexity and flexibility than traditional configuration files, as you can use the full power of JavaScript to customize and extend your workflow.

Typical use cases for Gulp include compiling Sass or LESS into CSS, auto-prefixing CSS for cross-browser compatibility, bundling and minifying JavaScript, and live-reloading your browser during development. Gulp achieves this through a rich ecosystem of plugins that are easy to use and well-maintained, ensuring safety and reliability in your build process. These plugins cover a wide range of tasks, allowing you to customize and extend your workflow as needed.

Setting up Gulp

To get started with Gulp, you'll need to have Node.js and npm (Node Package Manager) installed on your machine. Once you have them set up, follow these steps to install Gulp in your project:

  1. Initialize npm: Open a terminal and navigate to your project's directory. If you haven't already initiated npm in your project, run the command:

    npm init

    Follow the prompts to provide the necessary information.

  2. Install Gulp.js: Install Gulp.js as a development dependency by running the command:

    npm install gulp --save-dev

    This will add Gulp to your project's package.json file.

  3. Install Gulp CLI: Install the gulp command line utility globally with:

    npm install --global gulp-cli

    This will add Gulp to your project's package.json file.

  4. Create Gulp Configuration File: Create a new file named gulpfile.js in your project's root directory. This file will contain your Gulp configuration and tasks.

Here's a basic example of a gulpfile.js:

function defaultTask(cb) {
  // place code for your default task here
  cb(); //callback
}

exports.default = defaultTask

Using --save-dev in npm install will add the package to the development dependencies. This is because Gulp is typically used during the development phase to automate tasks and is not required in the production environment.

Gulp.js Plugins

Gulp plugins are essential tools that enhance the functionality of Gulp by automating various tasks in your build process. Whether you need to minify files, compile code, or optimize images, there’s likely a Gulp plugin designed to handle it. These plugins save you time and effort by performing specialized tasks and can be combined to create a custom workflow that suits your project's needs.

Using plugins is straightforward: you simply import them and use them in your Gulp tasks. We'll cover how to do this in detail in the next section. For now, let's look at how to install them.

You can find a wide range of Gulp plugins on the Gulp.js plugins page. Each plugin is tested by the Gulp team and optimized for its specific function. To install a plugin, use npm in the same way you installed Gulp: npm install <plugin-name> --save-dev. For example, to install gulp-clean-css, a plugin for minifying CSS, you would run npm install gulp-clean-css --save-dev.

Creating Gulp Tasks

Gulp tasks are the core of what makes Gulp powerful and flexible. A Gulp task is a JavaScript function that performs a specific action in your build process. To better understand how Gulp works, it's useful to see it in action with a real example.

Let's consider a typical front-end development scenario: you have a project with HTML, CSS, and JavaScript files. Manually processing these files can be tedious and error-prone. Tasks like combining CSS files into one, minifying your JavaScript, and ensuring your HTML is optimized for production are repetitive and time-consuming. Gulp can automate these tasks efficiently.

Example Project Structure

We'll create a simple project with src and dist folders. The src folder contains our source files (HTML, CSS, and JavaScript), and the dist folder will hold the processed files.

Here’s the folder structure:

Gulp-project folder structure

Let’s fill the files with the following dummy content so we can check the resulting output.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sample Project</title>
    <link rel="stylesheet" href="css/styles.css">
</head>
<body>
    <h1>Hello, Gulp!</h1>
    <script src="js/scripts.js"></script>
</body>
</html>
body {
    font-family: Arial, sans-serif;
}
h1 {
    color: blue;
}
console.log('Hello from JavaScript!');

Install Plugins

Before we start writing the gulpfile.js, let’s install the plugins we’ll be using.

  • gulp-htmlmin for minifying HTML

  • gulp-clean-css for minifying CSS

  • gulp-terser for minifying JavaScript

  • gulp-sourcemaps for generating source maps(This provide a way to map the minified or compiled code back to the original source code.)

Install these plugins with npm:

npm install gulp-htmlmin gulp-clean-css gulp-terser gulp-sourcemaps --save-dev

General Structure of a gulpfile.js

A typical gulpfile.js includes:

  1. Imports: Importing the necessary modules and plugins.

  2. Tasks: Defining the tasks that will process your files.

  3. Exports: Exporting the tasks so they can be run by Gulp.

Here’s how you can set up your gulpfile.js:

// 1. Import necessary modules and plugins
const gulp = require('gulp');                                                                    //1a
const htmlmin = require('gulp-htmlmin');
const cleanCSS = require('gulp-clean-css');
const terser = require('gulp-terser');
const sourcemaps = require('gulp-sourcemaps');

// 2. Define tasks

// Task to minify HTML
function minifyHTML() {                                                                           //2a
  return gulp.src('src/*.html') // src() points to the source files                               //2b
    .pipe(htmlmin({ collapseWhitespace: true })) // Minifies the HTML files                       //2c
    .pipe(gulp.dest('dist')); // dest() specifies the destination for the processed files         //2d
}

// Task to minify CSS
function minifyCSS() {
  return gulp.src('src/css/*.css')
    .pipe(sourcemaps.init())
    .pipe(cleanCSS({ compatibility: 'ie8' }))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('dist/css'));
}

// Task to minify JavaScript
function minifyJS() {
  return gulp.src('src/js/*.js')
    .pipe(sourcemaps.init())
    .pipe(terser())
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('dist/js'));
}

// 3. Export tasks
exports.default = gulp.parallel(minifyHTML, minifyCSS, minifyJS);                                   //3a,b

Explanation(each tab correspond to the comments in the code above):

First, we import the necessary Gulp modules and plugins.

Gulp task

In Gulp, tasks are functions that define specific actions to be performed on our files. Each task typically starts by specifying the source files it will work on, processes those files through a series of plugins, and then outputs the processed files to a destination folder. Tasks are typically written as functions that return a stream, which is a series of operations that your files go through. For example, the minifyHTML task takes HTML files from the src directory, processes them by minifying the content, and then outputs the optimized files to the dist directory.

How to Run the Gulp Tasks

To run the Gulp tasks, navigate to your project directory in the terminal and execute the default task with: Gulp, this command will run all the exported tasks(in our case, default).

While plugins simplify many tasks, you can also integrate Node.js modules into your Gulp tasks for added flexibility.

gulp.src():

The src() function in Gulp is used to specify the source files that a task will operate on. It defines where Gulp should look for the files to be processed. This function takes a glob pattern, which is a powerful way to match multiple files using wildcard characters.

Globs are patterns that help match file paths. For instance, 'src/css/*.css' is a glob pattern where:

  • src/css/ specifies the directory to look in.

  • *.css matches all files with a .css extension in that directory.

Using glob patterns, you can easily target groups of files without listing each one individually. Here are a few more examples to illustrate how globs work:

  • src/js/*.js matches all JavaScript files in the src/js directory.

  • src/**/*.html matches all HTML files in the src directory and any of its subdirectories.

  • src/img/*.{png,jpg} matches all PNG and JPG files in the src/img directory.

pipe()

The pipe() function in Gulp is a key method that allows you to chain together multiple operations on your files. Think of it as a pipeline where the output of one operation becomes the input for the next. This makes it easy to create a sequence of processing steps, transforming your files step-by-step.

In this minifyHTML task, the pipe() function is used to pass the HTML files through the htmlmin plugin. The htmlmin plugin is used to minify HTML files. Minification is the process of removing unnecessary characters from the code (like spaces, newline characters, comments, etc.) without affecting its functionality. This reduces the file size and improves load times.

The { collapseWhitespace: true } option passed to htmlmin is an argument that specifies how the plugin should operate. In this case, it tells the plugin to collapse all the whitespace in the HTML files, making them more compact. There are many other options you can use with htmlmin, and each plugin typically has its own set of configurable options.

To find out more about the arguments and options available for a specific plugin, you can refer to the plugin's documentation at plugins documentation.

gulp.dest()

The dest() function in Gulp specifies the destination folder where the processed files will be written. This function tells Gulp where to save the final output after all the processing steps have been applied. It's the last step in your Gulp task, ensuring that your transformed files are saved in the right location.

Here, gulp.dest('dist') means that after the HTML files are minified, they will be written to the dist folder.

gulp.parallel()

In Gulp, the gulp.parallel() and gulp.series() functions are used to control the execution order of tasks. These functions allow you to define whether tasks should run concurrently or sequentially.

  • gulp.parallel():

This function allows tasks to run simultaneously. It’s useful when tasks do not depend on each other and can be executed at the same time to save processing time. For example:

task = gulp.parallel(minifyHTML, minifyCSS, minifyJS);

In this case, minifyHTML, minifyCSS, and minifyJS will all run at the same time.

  • gulp.series():

This function ensures that tasks run one after the other, in a specific sequence. This is useful when tasks are dependent on the completion of other tasks. For instance:

task = gulp.series(task1, task2, task3);

Here, task1 will complete before task2 starts, and task2 will complete before task3 starts.

exports.default = gulp.parallel(minifyHTML, minifyCSS, minifyJS);

Exporting tasks in Gulp is crucial as it makes them available for Gulp to execute. Using exports.default, we specify the default task that will run when you simply type gulp in the command line. This is a necessary step to let Gulp know what task to call on. You can make other tasked using the same format:

exports.build = gulp.series(minifyHTML, minifyCSS, minifyJS);

Gulp Watch and Live Reloading

Gulp also has watch functionality, which allows you to monitor changes in your project's files and automatically run tasks when changes are detected. This saves you from manually running tasks every time you make a change. One area where this can be particularly useful is in live reloading.

Live reloading automatically refreshes your browser whenever changes are made to your project's files. This provides a seamless development experience by eliminating the need to manually refresh the browser to see the latest changes.

To achieve live reloading, we can use BrowserSync. After installing BrowserSync using npm, we can add a task to our gulpfile.js that utilizes it.

Installation:

npm install --save-dev browser-sync

And here's how you can set up Gulp to watch for changes and enable live reloading using BrowserSync:

//other imports
const browserSync = require('browser-sync').create();   // Import BrowserSync and create an instance

//..other tasks defined above

// Initialize BrowserSync and set it up to serve files from the 'dist' directory
function browserSyncInit(done) {
  browserSync.init({
    server: {
      baseDir: "./dist" 
    }
  });
  done();
}

// Reload the browser
function reload(done) {
  browserSync.reload();
  done();
}

// Watch task
function watchFiles() {
  //Watch for changes in the files and run the minifying tasks defined previously, then reload the browser
  gulp.watch('src/css/**/*.css', gulp.series(minifyCSS, reload)); 
  gulp.watch('src/js/**/*.js', gulp.series(minifyJS, reload));
  gulp.watch('src/*.html', gulp.series(minifyHTML, reload));
}

// Default task
exports.default = gulp.series(
  gulp.parallel(minifyHTML, minifyCSS, minifyJS)
  browserSyncInit,
  watchFiles
);

As you can see from the code above, plugins aren't always needed. You can use Node.js packages and create tasks that interact with plugins and other module package tasks, providing a flexible and powerful build process. You can read more on this at the official documentation.

To see everything in action, run gulp in your project directory. Your browser will automatically refresh and display the latest changes every time you save your files.

Conclusion

Gulp is a build tool that simplifies and speeds up frontend development tasks. By using Gulp and its plugins, you can automate repetitive tasks, optimize your code, and improve your development workflow.

Key concepts covered in this topic:

  • Installing and setting up Gulp in a project

  • Creating and running Gulp tasks to automate common frontend tasks

  • Using Gulp plugins to add functionality and simplify task creation

  • Setting up watch tasks and live reloading for a more efficient development process

The best way to become familiar with Gulp is to start using it in your own projects, whether they are small or large. As you get comfortable with it, you'll be able to customize your workflow to fit your specific needs.

How did you like the theory?
Report a typo