Learning Gulp for Website Minification

Recently I developed a static website for a company, and as it gets closer to the release date, I try to do everything to satisfy Google PageSpeed Insights.
At first, as a C# developer, I thought about developing a small console application, which takes a folder and minifies all contents inside it. Then I remembered that there are task runners that do exactly that kind of thing. So this is a short summary of what I learned.

Setting up Gulp

Before starting with Gulp, you need npm.

A short Introduction to npm

npm is not a standalone package manager, it comes bundled with node. To install node, just head over to the download page, get the installer for your operating system, and install it. Nothing new or complicated here.

Now, make sure that the npm folder is added to the Path environment variable. If it is not, you’ll get a nice error message when trying to execute it:

npm : The term 'npm' is not recognized as the name of a cmdlet, function, script file, or operable program. Check
the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ npm
+ ~~~~
    + CategoryInfo          : ObjectNotFound: (npm:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

At this point, you can start creating a new project. Projects using npm are identified by a package.json file, which defines the name of the project, description, author, many other things, and most importantly, its dependencies.
You can either create it by hand, utilizing the package.json documentation, or let npm help you by using npm init. It asks you some questions and creates a package.json file based on your answers.

Now you can install dependencies. The first thing you need is Gulp (obviously, since this whole blogpost is about Gulp). Installing it is done with the npm install command.

npm install gulp --save-dev

--save-dev is a flag that indicates that the dependency is only used for development. With that flag npm install enters the dependency into the devDependencies part of the package.json file.
There is another flag that is frequently used, --save. With that flag the dependency will appear in the dependencies section.
Since Gulp and all the other packages used in this blogpost are all used during development, I’ll always use --save-dev.
A typical production dependency would be jQuery.

This completes the very short introduction to npm. If you’d like to gain in-depth knowledge, just head over to their documentation.

Starting with Gulp

Now, after that little detour, we can start using Gulp.

It looks for gulpfile.js per default, in which the tasks are defined. Create one if you haven’t already done so.

Since this is Javascript, you have to require Gulp to actually use it.

var gulp = require("gulp");

Visual Studio 2017 even provides intellisense for all installed dependencies, which is really really awesome!

Intellisense

Executing Gulp works with the gulp command. Simple. But if you do this right now, you’ll still get the error that the command cannot be found:

gulp : The term 'gulp' is not recognized as the name of a cmdlet, function, script file, or operable program. Check
the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ gulp
+ ~~~~
    + CategoryInfo          : ObjectNotFound: (gulp:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

You are missing a package, gulp-cli. It is installed using

npm install gulp-cli --global 

Installing this package globally is a good idea, so you don’t have to remember installing it for every project that uses Gulp.

If you run gulp now, you should get a message like this:

Using gulpfile ~\Path\To\Project
Task 'default' is not in your gulpfile
Please check the documentation for proper gulpfile formatting

Creating Tasks

Tasks are created with the function gulp.task, which takes the task name and a function to execute the task.
The first task we’ll create is the default task. It has special meaning, which will be covered later, in Executing Tasks.

gulp.task("default", function () {

});

A Javascript Minification Task

Its nice that we created the default task, but it doesn’t bring any value, since it does nothing. The next task will be for Javascript minification.

The first step is to select a Gulp plugin that can minify Javascript code. Googling for it gives multiple possible plugins for almost any usecase I could come up with. Selecting one can be difficult, because they have no obvious differences for a beginner. I resorted to just using the package with the most downloads.
For the usecase at hand it was gulp-uglify.

To create the task, first you have to require the package:

var uglify = require("gulp-uglify");

Then you can use it in the actual task:

gulp.task("minify-scripts", function () {
    return gulp.src("js/*.js")
        .pipe(uglify())
        .pipe(gulp.dest("public/js"));
});

The pattern is always the same. First, you get the files with gulp.src("selector"), then you pipe them through functions provided by required packages, and at the end you pipe them to a destination folder to write the changed files to disk.

For CSS minification I use gulp-clean-css, and for HTML minification gulp-htmlmin. The whole gulpfile, with Javascript, CSS and HTML minification looks like this:

var gulp = require("gulp");
var uglify = require("gulp-uglify");
var cleanCSS = require("gulp-clean-css");
var htmlmin = require("gulp-htmlmin");

gulp.task("default", function () {

});

gulp.task("minify-scripts", function () {
    return gulp.src("js/*.js")
        .pipe(uglify())
        .pipe(gulp.dest("public/js"));
});

gulp.task("minify-css", function () {
    return gulp.src("css/*.css")
        .pipe(cleanCSS({}))
        .pipe(gulp.dest("public/css"));
});

gulp.task("minify-html", function () {
    return gulp.src("*.html")
        .pipe(htmlmin({ collapseWhitespace: true }))
        .pipe(gulp.dest("public"));
});

Executing Tasks

Now that the tasks are defined, they need to be executed.
Normally, tasks are executed with

gulp <taskname>

But the default task has special meaning, it will also be executed if no other task name is given.

gulp

Executing multiple Tasks at once

Executing gulp minify-scripts, gulp minify-css and gulp minify-html every time before deployment quickly becomes annoying. Especially if there are even more tasks specified.

With Gulp it is possible to specify dependencies for a task, which are other tasks that need to complete before the current task starts executing. We can use this behavior to run all our tasks at once.

gulp.task("ready-for-deployment", ["minify-scripts", "minify-css", "minify-html"], function(){

});

Running gulp ready-for-deployment now executes minify-scripts, minify-css and minify-html. This makes our life a lot easier.
It is even possible to simplify this combined task further by removing the function. Since it is empty, it is not needed.

gulp.task("ready-for-deployment", ["minify-scripts", "minify-css", "minify-html"]);

Finishing Touches

If you just want to minify your resources with Gulp, you’re done.
But since I did this for a static website, there are a few more things I want to do.

First, I could deploy the website’s folder as it is. The files just wouldn’t be minified. I’d like to keep that, and just create a minified version of the website in a folder named public. To do that, I use the tasks defined above, and add a few more that simply copy the remaining needed files to the public folder. These include images, php files, icons, fonts and so on. There is not much more going on than what is already described above, so I’ll just show you the whole gulpfile:


var gulp = require("gulp");
var uglify = require("gulp-uglify");
var cleanCSS = require("gulp-clean-css");
var htmlmin = require("gulp-htmlmin");

gulp.task("default", function () {

});

gulp.task("ready-for-deployment", ["minify-scripts", "minify-css", "minify-html", "copy-php", "copy-fonts", "copy-images", "copy-icons", "copy-htaccess"]);

gulp.task("minify-scripts", function () {
    return gulp.src("js/*.js")
        .pipe(uglify())
        .pipe(gulp.dest(targetScriptsFolder));
});

gulp.task("minify-css", function () {
    return gulp.src("css/*.css")
        .pipe(cleanCSS({}))
        .pipe(gulp.dest("public/css"));
});

gulp.task("minify-html", function () {
    return gulp.src("*.html")
        .pipe(htmlmin({ collapseWhitespace: true }))
        .pipe(gulp.dest("public"));
});

gulp.task("copy-php", function () {
    return gulp.src("*.php")
        .pipe(gulp.dest("public"));
});

gulp.task("copy-fonts", function () {
    return gulp.src("fonts/*.*")
        .pipe(gulp.dest("public/fonts"));
});

gulp.task("copy-images", function () {
    return gulp.src("images/*.{jpg,png,ico}")
        .pipe(gulp.dest("public/images"));
});

gulp.task("copy-icons", function () {
    return gulp.src("icons/*.svg")
        .pipe(gulp.dest("public/icons"));
});

gulp.task("copy-htaccess", function () {
    return gulp.src(".htaccess")
        .pipe(gulp.dest("public"));
});

Using this little Gulp script reduces the size of the website by 35.7 KB. This doesn’t seem like much, but it is a reduction of 34%!

I hope this post helped you, and as always, don’t hesitate to comment if you have improvement suggestions or questions.