DEV Community

Cover image for Gulp 4 Tutorial
Ganesh Shinde
Ganesh Shinde

Posted on

Gulp 4 Tutorial

Table of Contents

What is Gulp?

  • Gulp is an open source Javascript toolkit and task runner
  • It was built on Node.js and NPM
  • Used for time consuming and repetitive tasks
  • Hundreds of plugins available for different tasks

Common Tasks

  • Minification of styles and scripts
  • Concatenation
  • Cache busting
  • Testing, linting and optimization

Prerequisites

Install Node.js

Install Gulp globally using following command in command line.
By default latest version will get installed.

npm install -g gulp
Enter fullscreen mode Exit fullscreen mode

Check if Gulp is installed properly, Then we are good to move forward.

npm gulp --version
Enter fullscreen mode Exit fullscreen mode

Create package.json

First create new directory for our project, if you already have one no need to create new.
Open command prompt and change current working directory as our project directory using below command (change path accordingly).

# Absolute path of directory
cd c:/projects/my-project
Enter fullscreen mode Exit fullscreen mode

Now create package.json file for our project using below command.

npm init -y
Enter fullscreen mode Exit fullscreen mode

Install Packages

Install required packages and save them as development dependencies using below command.

npm install --save-dev gulp gulp-concat gulp-rename gulp-replace gulp-imagemin gulp-sourcemaps gulp-sass postcss gulp-postcss autoprefixer cssnano gulp-terser
Enter fullscreen mode Exit fullscreen mode

Find below, Purpose of installed packages.

gulp gulp-concat gulp-rename gulp-replace - Basic file operations such as concatenation, file renaming and file content replacement.

gulp-imagemin - Image optimization.

gulp-sourcemaps - Sourcemaps creation for styles and scripts.

gulp-sass postcss gulp-postcss autoprefixer cssnano - sass/scss compilation, Add vendor prefixes and minify styles.

gulp-terser - Minify scripts.

After this our package.json file will contain data something like below.

  "devDependencies": {
    "autoprefixer": "^10.2.5",
    "cssnano": "^5.0.2",
    "gulp": "^4.0.2",
    "gulp-concat": "^2.6.1",
    "gulp-imagemin": "^7.1.0",
    "gulp-postcss": "^9.0.0",
    "gulp-rename": "^2.0.0",
    "gulp-replace": "^1.1.3",
    "gulp-sass": "^4.1.0",
    "gulp-sourcemaps": "^3.0.0",
    "gulp-terser": "^2.0.1",
    "postcss": "^8.2.15"
  }
Enter fullscreen mode Exit fullscreen mode

Create gulpfile.js

Open project in code editor and create gulpfile.js.
In this file we will define all the tasks that we want to automate.

Import Packages

Write below code inside gilpfile.js.
It will import all those installed packages, So that we can use them to perform tasks.

const gulp = require('gulp');
const { src, dest, watch, series, parallel } = require('gulp');
const imagemin = require('gulp-imagemin');
const sourcemaps = require('gulp-sourcemaps');
const concat = require('gulp-concat');
const rename = require('gulp-rename');
const replace = require('gulp-replace');
const terser = require('gulp-terser');
const sass = require('gulp-sass');
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
Enter fullscreen mode Exit fullscreen mode

Add Paths

Add below object of paths into gulpfile.js.
We are using this object because its the most convenient way to handle paths. It adds maintainability to our code.

const paths = {
  html: {
    src: ['./src/**/*.html'],
    dest: './dist/',
  },
  images: {
    src: ['./src/content/images/**/*'],
    dest: './dist/content/images/',
  },
  styles: {
    src: ['./src/scss/**/*.scss'],
    dest: './dist/css/',
  },
  scripts: {
    src: ['./src/js/**/*.js'],
    dest: './dist/js/',
  },
  cachebust: {
    src: ['./dist/**/*.html'],
    dest: './dist/',
  },
};
Enter fullscreen mode Exit fullscreen mode

Selectors

Pattern Match
*.scss Matches any file ending with .scss from current directory
*/.scss Matches any file ending with .scss from current directory and any child directory
*.+(scss|sass) Matches any file ending with .scss or .sass from current directory

Folder Structure

We will keep all our files in src directory and then using Gulp tasks we will create optimized files in dist directory.
Above paths object is created based on this folder structure.

Folder Structure

Create Tasks

Gulp performs actions on files in pipeline. Pipeline / .pipe() operator is nothing but chained processes running synchronously.
Files are not affected until all processes are completed.
Gulp .src() and .dest() operators are used to read and write files.

Below is the basic syntax for creating a Gulp task.

function task_name() {
  return src('source-location') // Select files from given location
    .pipe(gulpPlugin()) // Send files through a gulp plugin
    .pipe(dest('destination-location')); // Write modified files at given location
}
Enter fullscreen mode Exit fullscreen mode

Copy Files

Copy HTML files from src to dest location using below function.
We can add more function like this in case we want to copy some other files such as fonts or media files.
For that, All we need to do is add paths in the paths object.
Then create new function just as below and update src and dest paths.

function copyHtml() {
  return src(paths.html.src).pipe(dest(paths.html.dest));
}
Enter fullscreen mode Exit fullscreen mode

Optimize Images

Optimize all types of images(.png, .jpeg, .gif, .svg).
If any error occurs during performing the task, We will be logging that to the console.

function optimizeImages() {
  return src(paths.images.src)
    .pipe(imagemin().on('error', (error) => console.log(error)))
    .pipe(dest(paths.images.dest));
}
Enter fullscreen mode Exit fullscreen mode

Compile Styles

Compile sass/scss and convert them into optimized css.
Keep all the tasks that needs to be done on styles between sourcemaps.init() and sourcemaps.write('.'), Otherwise sourcemaps won't be generated properly.

We are using cssnano to optimize our styles that's why we are renaming files with .min suffix (Indicating minified).

function compileStyles() {
  return src(paths.styles.src)
    .pipe(sourcemaps.init())
    .pipe(sass().on('error', sass.logError))
    .pipe(postcss([autoprefixer(), cssnano()]))
    .pipe(rename({ suffix: '.min' }))
    .pipe(sourcemaps.write('.'))
    .pipe(dest(paths.styles.dest));
}
Enter fullscreen mode Exit fullscreen mode

Minify Scripts

Optimize scripts using below function.

function minifyScripts() {
  return src(paths.scripts.src)
    .pipe(sourcemaps.init())
    .pipe(terser().on('error', (error) => console.log(error)))
    .pipe(rename({ suffix: '.min' }))
    .pipe(sourcemaps.write('.'))
    .pipe(dest(paths.scripts.dest));
}
Enter fullscreen mode Exit fullscreen mode

Cache Busting

Browser cache files which we link in our .html files and next time use cached files whenever request is made for the same .html page.
This sometimes creates issue like, Browser still uses old cached files even if we have done changes in the files. This is known as browser caching issue.

Cache busting solves the browser caching issue by using a unique file version identifier to tell the browser that a new version of the file is available. Therefore the browser doesn't retrieve the old file from cache but rather makes a request to the origin server for the new file.

To make this work we will add cache_bust parameter (with any number) to all style and script urls in our .html files.

<html>
  <head>
    <link rel="stylesheet" href="/dist/css/style.min.css?cache_bust=123" />
  </head>
  <body>
    <script src="/dist/js/script.min.js?cache_bust=123"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Now using below function we will replace these cache_bust parameter value with unique number. So that these urls will be unique, Browser will know that url is changed and instead of using already cached file it will request for the file from the server.

function cacheBust() {
  return src(paths.cachebust.src)
    .pipe(replace(/cache_bust=\d+/g, 'cache_bust=' + new Date().getTime()))
    .pipe(dest(paths.cachebust.dest));
}
Enter fullscreen mode Exit fullscreen mode

Watch Files

Watch for file modification at specific paths and run respective tasks accordingly.

watch('path-to-watch',[tasks-to-perform]);
Enter fullscreen mode Exit fullscreen mode

We can perform as many tasks as we want using series() and parallel() operators.
series() will execute tasks synchronously and parallel() will execute tasks asynchrously.

function watcher() {
  watch(paths.html.src, series(copyHtml, cacheBust));
  watch(paths.images.src, optimizeImages);
  watch(paths.styles.src, parallel(compileStyles, cacheBust));
  watch(paths.scripts.src, parallel(minifyScripts, cacheBust));
}
Enter fullscreen mode Exit fullscreen mode

Export Tasks

Export created tasks so that they will be accessible to execute from command line.

exports.copyHtml = copyHtml;
exports.optimizeImages = optimizeImages;
exports.compileStyles = compileStyles;
exports.minifyScripts = minifyScripts;
exports.cacheBust = cacheBust;
exports.watcher = watcher;
Enter fullscreen mode Exit fullscreen mode

Create one default task which will execute all the tasks that we want and later starts watching files for modification.

exports.default = series(
  parallel(copyHtml, optimizeImages, compileStyles, minifyScripts),
  cacheBust,
  watcher
);
Enter fullscreen mode Exit fullscreen mode

Run Tasks

Run any specific task using below command.

gulp task_name
Enter fullscreen mode Exit fullscreen mode

Run default task using below command.

gulp
Enter fullscreen mode Exit fullscreen mode

To stop watcher task, which continuously watch for file modifications, press ctrl + c in command line and answer asked question as Y.

^CTerminate batch job (Y/N)? Y
Enter fullscreen mode Exit fullscreen mode

My gulpfile.js

Check out my gulpfile.js for reference.

Top comments (0)