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!
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.