Skip to navigation

How To Add HTML Minification To Hugo Deployment Process

Written by and published on

Hugo doesn’t currently support HTML minification, but it’s very simple to add it to the deployment process using an external tool. We’re going to use minify by Taco de Wolff, just because it’s written in Go and because it’s insanely fast.

Note

Even after writing this article I have opted not to use this tool yet, because it currently strips out white space from between pre tags if they contain other html elements. This is usually the case when there are code snippets that have syntax highlighting, like those created with Pygments. I have a lot of code snippets and it’s essential for their readability that indentation and alignment is preserved. If you don’t have preformatted text in your posts this tool will work fine. The issue with pre tags has been fixed.

Installing Go

If Go isn’t installed on your system, install it first.

$ cd ~
$ curl -O https://storage.googleapis.com/golang/go1.6.2.linux-amd64.tar.gz
$ tar -xvf go1.6.2.linux-amd64.tar.gz
$ sudo mv go /usr/local

Using your favorite editor, add the following to ~/.profile:

export PATH=$PATH:/usr/local/go/bin

Refresh your profile by running:

$ source ~/.profile

Verify that everything works:

$ go version

The above should print the version number of your Go installation.

Installing minify

Minify is available as a Go library and as a command line tool. We’re going to install the CLI.

First, add these lines to your ~/.profile. GOPATH is needed to install the packages and we’re adding the $GOPATH/bin so we can run the Go binaries without using the full path.

export GOPATH=~/go
export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin

Refresh your profile again:

$ source ~/.profile

Now, install the minify package (you need to have Git installed for this to work, by the way):

$ go get github.com/tdewolff/minify/cmd/minify

Once it’s installed you can minify the .html files on your site by running:

$ minify --recursive --verbose --match=\.*ml /path/to/dir

The above command goes recursively through /path/to/dir directory and minifies all .html and .xml files it finds (and others that match the pattern), giving verbose output while doing so. Since we haven’t specified output directory, it replaces the original HTML files with the minified ones.

Note

Do not use the –type=html switch if you have SVG files in the directory. If the type is set as html the minifier attempts to minify the SVG files too, but it currently doesn’t handle SVG files very well, meaning it mangles them. If you need to minify SVG files, consider using SVGO for the time being.

Making A Script For Deployment

Instead of typing all that each time, let’s make a very simple script for it.

The directory where the sources of my Hugo site are is hugo . public is the directory for compiled site. You should of course adjust those to reflect your directory structure and make the paths more specific if you place this script somewhere else.

Create a new file (deploy.sh in this example):

#!/bin/sh
# Destination directory is relative to the sources
# directory, so it's best to give full path
hugo -s hugo -d /absolute/path/to/public
minify -r -v --match=\.*ml public

Make the file executable so you can run it as a script:

$ chmod +x deploy.sh

After that you can run it by typing:

$ ./deploy.sh

Did it work? Yes? Great.

Now, let’s automate things a bit, shall we? I mean, wouldn’t it be better if all this happened automatically when files are changed?

Hands-free Deployment

Both Hugo and Minify support watching a directory and running when changes occur, but minify supports watching a directory only if it’s different from the output directory, which makes sense. Since Hugo isn’t able to run external scripts after compiling a site, the only choice is to use some other tool to watch for changes and run Hugo and minify separately.

If you’re using Linux you have many choices, but I have tested two: inotifywait and entr. I’m personally using entr, but I’ll show how to use inotifywait, too.

inotifywait

inotifywait is part of inotify-tools and it sits on top of inotify, which is a Linux kernel subsystem. inotify is therefore found on every Linux system, but inotify-tools needs to be installed separately.

$ sudo apt-get install inotify-tools

Then we create a script for it (I named mine stalk.sh):

#!/bin/sh
while inotifywait -r -e modify,attrib,close_write,move,create,delete hugo
do
  ./deploy.sh
done

Make this one executable, too:

$ chmod +x stalk.sh

You can then run it with ./stalk.sh. You can end the script execution with ctrl-c.

What happens here is that we tell inotifywait to watch the source directory recursively and wait for certain events to take place. When they do, our loop is triggered and the deploy.sh script is run, after which we again wait input from the inotifywait.

“Because the inotifywait command waits for something to happen in the directory, it’s much more efficient than continually polling the directory.” sayeth the wise people at Stack Overflow, so this should be a good solution resource-wise.

One of the disadvantages of inotifywait is that it can miss files that are modified while our script is in the do block, in which case the deploy script might miss them too.

entr

The Event Notify Test Runner is an actively developed “general purpose Unix utility intended to make rapid feedback and automated testing natural and completely ordinary”. It takes a list of files as input and when any of them change runs a given command. It can also monitor a directory for added and deleted files.

On Ubuntu you can install it with apt-get:

$ sudo apt-get install entr

Once entr is installed, create a file and type in the following:

#!/bin/sh
while :
do
  find hugo/ -type f | entr -d ./deploy.sh
done

Remember to make it executable like previously.

What we’re doing here is repeating the script endlessly. Each time we find all the files in the source directory and pipe them to entr, telling it to also watch the parent directory of each file for added and removed files (-d) and to execute deploy.sh when changes occur. Note: This script doesn’t notice added directories, only files. It also doesn’t notice if a file has been added to a previously empty directory.

Unlike inotifywait entr waits until the kernel stops sending events and acts only then, so it’s not as likely to miss a series of rapid changes. At least, that’s my understanding.

When I tested these scripts both worked without problems on my very small site. Your mileage may vary, of course.

For more options, see How to execute a command whenever a file changes? at Super User.

Comments

Commenting has been disabled until I get a proper spam protection working. =(

Juha

The issue at the top about pre tags has been fixed!

Great! Thank you.

PS: you have a lot of spammers…!

Yes, I know. Haven't had enough time to develop this commenting script to add any real anti-spam measures…

External Links

Back to beginning