Big Gulp

Wed, 27 Dec 2017

I've spent most of my time building web apps with frameworks like React, Angular and Vue. Most of them come setup with nice tooling that automates half the tasks for you. It's great and a little bit magic but I'd never really thought about what it was doing.

When I decided to rebuild my personal site I didn't need a fancy framework with all it's tooling. It was going to be a static html and css site with a few lines of js at best. But as I started creating the site I decided to take it as an opportunity it learn a few new tools.

I started by implementing sass. It meant I could remove a lot of repitition in my css and make it a lot easier to read. However I didn't want to rebuild my css every time I made a change.

Enter Gulp

A refreshing Big Gulp drink

I started by grabbing the plugin gulp-sass this would let me setup a task to convert my sass into css. Paired with a gulp.watch and it would update automatically every time I made a change! Now I could focus on creating and not running a sass command every few minutes.

gulp.task("sass", function() {
  return gulp
    .src("src/sass/**/*.scss")
    .pipe(plumber()) //Plumber grabs errors to stop the gulp from crashing if I input some invalid sass
    .pipe(sass()) // This converts it to sass
    .pipe(minifyCSS()) // This minifies the output css
    .pipe(gulp.dest("dist/public/css")) // This puts the output css in my dist folder
    .pipe(connect.reload()); // This reloads my webserver
});

But this was just the tip of the iceberg for me! What else could I automate?

Cleaning my dist

gulp-clean If I made any big changes I wanted to make sure no files were getting left over in my dist folder

gulp.task("clean", function() {
  return gulp.src(["dist/*"], { read: false }).pipe(clean());
});

Webserver

gulp-connect I added a webserver with livereload so my changes would update on the page automatically without me having to refresh.

gulp.task("webserver", function() {
  connect.server({
    root: "dist",
    livereload: true
  });
});

Building HTML from templates

gulp-ejs I wanted to be able to deploy my site in both English and Japanese (did I mention I'm moving to Tokyo? 🎉) I used ejs for templates and a separate string file to store my English and Japanese strings. I was then able to build two different index.html based on the two languages and minify the output!

gulp.task("ejs-en", function() {
  var strings = require("./src/strings.js");
  var enStrings = Object.assign({}, strings);
  enStrings.lang = "en";
  return gulp
    .src("src/*.ejs")
    .pipe(ejs(enStrings, {}, { ext: ".html" }))
    .pipe(htmlmin({ collapseWhitespace: true }))
    .pipe(gulp.dest("dist"))
    .pipe(connect.reload());
});

gulp.task("ejs-jp", function() {
  var strings = require("./src/strings.js");
  var jpStrings = Object.assign({}, strings);
  jpStrings.lang = "jp";
  return gulp
    .src("src/*.ejs")
    .pipe(plumber())
    .pipe(ejs(jpStrings, {}, { ext: ".html" }))
    .pipe(htmlmin({ collapseWhitespace: true }))
    .pipe(gulp.dest("dist/jp"))
    .pipe(connect.reload());
});

Compressing images

gulp-imagemin gulp-webp Here I used two plugins. webp has amazing compression but isn't supported by all browsers. So I used webp and imagemin to get compressed images and crossbrowser functionality.

gulp.task("images", function() {
  return gulp
    .src("src/public/images/**/*.*")
    .pipe(
      imagemin([
        imagemin.gifsicle({ interlaced: true }),
        imagemin.jpegtran({ progressive: true }),
        imagemin.optipng({ optimizationLevel: 5 }),
        imagemin.svgo({
          plugins: [{ removeViewBox: true }, { cleanupIDs: false }]
        })
      ])
    )
    .pipe(gulp.dest("dist/public/images"))
    .pipe(connect.reload());
});

gulp.task("images-webp", function() {
  return gulp
    .src("src/public/images/**/*.*")
    .pipe(webp())
    .pipe(gulp.dest("dist/public/images"))
    .pipe(connect.reload());
});

Watch

I set it all to be watched. If any changes occur I can be confident my webserver will be up to date.

gulp.task("watch", ["webserver"], function() {
  gulp.watch("src/sass/**/*.scss", ["sass"]);
  gulp.watch("src/**/*.ejs", ["ejs-en", "ejs-jp"]);
  gulp.watch("src/**/strings.js", ["ejs-en", "ejs-jp"]);
  gulp.watch("src/public/images/**/*.*", ["images"]);
  gulp.watch("src/public/**/*.*", ["public"]);
});

Running tasks

Finally I setup two tasks. A default task that would build everything. And a dev task that would run a development server. I assigned these in my package.json for ease of use.

gulp.task("default", ["clean"], function() {
  gulp.start("ejs-en", "ejs-jp", "public", "images", "images-webp", "sass");
});

gulp.task("dev", ["default"], function() {
  gulp.start("watch");
});

If you're interested here's a full copy of my gulpfile

Are there any other gulp plugins I should be looking at? Let me know! @omgduke

Thanks for reading. Read another blog?