My gulpfile (and why I still use Gulp in 2021)

Back to all posts
Snippets
23rd December 2020

In a world where Webpack, Parcel and Rollup are the prevalent ways of bundling JavaScript and carrying out all manner of other front end build tasks, it doesn't feel very on-trend to still be using, or talking about, Gulp.

But... I don't care! If I'm working on something like an SPA with React then sure, I'll be fully in Webpack world - but for most other applications (small to medium vanilla JS projects, or PHP-based CMS projects where I just want to import, transpile and uglify some assets and and point at them from my templates) I still absolutely love Gulp for its simplicity, speed and flexibility.

I also just can't get away from my strong distaste for how Webpack handles non-JS assets. Importing SCSS into a JavaScript file and then having it spit out CSS in a monolithic <style> block at the top of my page just feels nasty. I know there are different ways of handling this, but Gulp just feels a little closer to where I want to be out of the box.

So with all of that said, here's my current go-to gulpfile.js. This still uses Webpack to bundle JS, but I can handle the rest of the pipeline and any other assets in a way that makes more sense to my brain. During development, the only task I ever need to run is the default gulp which watches all of my SCSS and JS files, and does the following:

  • Compiles Sass, and then the sourcemaps, minifies and autoprefixes the resulting CSS
  • Bundles and transpiles modern (i.e. ES6 and above) JavaScript, and then sourcemaps and uglifies the output
  • Writes all of the above to the /dist/ directory, which I can then point to from my templates

The only other addition worth mentioning is gulp-mode, which allows me to pass in a "mode" flag (i.e. --development or --production) which I then use to modify a few tasks. Development mode is set by default, but when I'm ready to deploy to staging or live I can simply run gulp --production to create an optimised build (no sourcemaps, and webpack runs in "production" mode).

One common use for Gulp which I personally don't bother with is copying over arbitrary asset folders (fonts, images etc) from /src/ to /dist/. This has just never been a priority for me as I handle the optimisation of these kinds of assets manually and then just save the files straight into /dist/. This one just depends on your workflow, if you want to use an automated image optimiser, or simply prefer to work exclusively in your /src/ directory then this is something you may want to add.

// gulpfile.js

const gulp = require('gulp');
const uglify = require('gulp-uglify');
const cssnano = require('gulp-cssnano');
const autoprefixer = require('gulp-autoprefixer');
const sass = require('gulp-sass');
const sourcemaps = require('gulp-sourcemaps');
const webpack = require('webpack-stream');
const babel = require('gulp-babel');
const mode = require('gulp-mode')();


gulp.task('process-sass', () => {
    return gulp.src('src/scss/index.scss')
        .pipe(mode.development(sourcemaps.init()))
        .pipe(sass().on('error', sass.logError))
        .pipe(autoprefixer({
            overrideBrowserslist: ['> 1%']
        }))
        .pipe(cssnano())
        .pipe(mode.development(sourcemaps.write()))
        .pipe(gulp.dest('dist/css'));
});


gulp.task('process-js', () => {
    return gulp.src('src/js/index.js')
        .pipe(webpack({
            mode: mode.development() ? 'development' : 'production',
            watch: true,
            output: {
                filename: 'bundle.js'
            }
        }))
        .pipe(babel({ presets: ['@babel/env'] }))
        .pipe(mode.development(sourcemaps.init()))
        .pipe(uglify().on('error', (uglify) => {
            console.error(uglify.message);
            this.emit('end');
        }))
        .pipe(mode.development(sourcemaps.write()))
        .pipe(gulp.dest('dist/js'));
});


gulp.task('default', () => {

    gulp.watch(
        ['src/scss/*.scss','src/scss/*/*.scss'],
        { ignoreInitial: false },
        gulp.series('process-sass')
    );

    gulp.watch(
        ['src/js/*.js','src/js/*/*.js'],
        { ignoreInitial: false },
        gulp.series('process-js')
    );

});

And just for completeness, here's a package.json containing all of the required dev dependencies for the above gulpfile:

// package.json

{
  "name": "projectName",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "@babel/core": "^7.12.10",
    "@babel/preset-env": "^7.12.11",
    "gulp": "^4.0.2",
    "gulp-autoprefixer": "^7.0.1",
    "gulp-babel": "^8.0.0",
    "gulp-cssnano": "^2.1.3",
    "gulp-mode": "^1.0.2",
    "gulp-sass": "^4.1.0",
    "gulp-sourcemaps": "^3.0.0",
    "gulp-uglify": "^3.0.2",
    "webpack-stream": "^6.1.1"
  }
}

Comment on this post

I love to talk web. If you have any civil thoughts, corrections or comments on this post, feel free to tweet or DM @fraserboag or, if you prefer, email fraser.boag@gmail.com.

Tweet

Keep up to date

From time to time I post things which people seem to find fairly interesting, stick your email address in the box below and I'll make sure you hear about it!