Jekyll and Make: Optimizing Images
After about two years of using this little trick I’m finally confident enough to say that it works the way I want. One of the big hassles of a static site generator is the content pipeline for things like images. A CMS like Drupal or Wordpress handles a lot of things like resizing and thumbnailing where Jekyll needs to provide its own method. This is one that I put together using Make along with a few other open source tools.
The first order of business is to get the absolute path to the current directory
since we will be using it to route our processed image files. Additionally, Jekyll
only takes files in the assets/
directory into account when building the site,
so we will be storing our raw images in _images/
and then processing them into
assets/
where Jekyll will pick them up during the build.
CURRENT_DIR = $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
JEKYLL_IMAGE_SRC_DIR = $(CURRENT_DIR)/_images
JEKYLL_IMAGE_DEST_DIR = $(CURRENT_DIR)/assets/images
Next is creating a list of targets in assets/
for our Makefile out of every
image file we find in the _images/
directory. The implementation below only
takes into account a single file extension but the find
call could be expanded
as needed to take into account, for example, multiple JPEG file endings.
JEKYLL_OPTIMIZED_JPGS = $(subst $(JEKYLL_IMAGE_SRC_DIR),$(JEKYLL_IMAGE_DEST_DIR),$(shell find $(JEKYLL_IMAGE_SRC_DIR) -type f -name '*.jpg'))
JEKYLL_OPTIMIZED_PNGS = $(subst $(JEKYLL_IMAGE_SRC_DIR),$(JEKYLL_IMAGE_DEST_DIR),$(shell find $(JEKYLL_IMAGE_SRC_DIR) -type f -name '*.png'))
JEKYLL_OPTIMIZED_SVGS = $(subst $(JEKYLL_IMAGE_SRC_DIR),$(JEKYLL_IMAGE_DEST_DIR),$(shell find $(JEKYLL_IMAGE_SRC_DIR) -type f -name '*.png'))
JEKYLL_OPTIMIZED_GIFS = $(subst $(JEKYLL_IMAGE_SRC_DIR),$(JEKYLL_IMAGE_DEST_DIR),$(shell find $(JEKYLL_IMAGE_SRC_DIR) -type f -name '*.gif'))
Next we want to define any option variables we will need for our different commands. This is a good place to decide how optimized you want your JPEGs or if you want to maintain Exif tags (you probably don’t).
JPEGOPTIM_OPTIONS = --max=65 --all-progressive --strip-all
Last before the real magic we create our targets which we will call from the CLI to actually optimize our images. They depend on our lists of optimized images or in the case of the catch-all every image optimizer we have implemented.
jpg-optimize: $(JEKYLL_OPTIMIZED_JPGS)
png-optimize: $(JEKYLL_OPTIMIZED_PNGS)
svg-optimize: $(JEKYLL_OPTIMIZED_SVGS)
gif-optimize: $(JEKYLL_OPTIMIZED_GIFS)
image-optimize: jpg-optimize png-optimize svg-optimize gif-optimize
Finally, we will use jpegoptim
, optipng
, rsync
, and the ImageMagick suite
to convert and optimize our images. I wasn’t able to find any good GIF or SVG
optimizers so currently they are just a straight rsync
copy to the output
directory.
$(JEKYLL_IMAGE_DEST_DIR)/%.jpg: $(JEKYLL_IMAGE_SRC_DIR)/%.jpg
mkdir -p $(dir $@)
jpegoptim $(JPEGOPTIM_OPTIONS) --dest=$(dir $@) $^
convert $@ -resize '100x100^' -gravity center -crop 100x100+0+0 +repage $(dir $@)thumbnail-$(notdir $@)
rsync -a --ignore-existing $^ $@
$(JEKYLL_IMAGE_DEST_DIR)/%.png: $(JEKYLL_IMAGE_SRC_DIR)/%.png
mkdir -p $(dir $@)
optipng -out $@ $^
convert $@ -resize '100x100^' -gravity center -crop 100x100+0+0 +repage $(dir $@)thumbnail-$(notdir $@)
rsync -a --ignore-existing $^ $@
$(JEKYLL_IMAGE_DEST_DIR)/%.svg: $(JEKYLL_IMAGE_SRC_DIR)/%.svg
mkdir -p $(dir $@)
rsync -a --ignore-existing $^ $@
$(JEKYLL_IMAGE_DEST_DIR)/%.gif: $(JEKYLL_IMAGE_SRC_DIR)/%.gif
mkdir -p $(dir $@)
rsync -a --ignore-existing $^ $@
As you can see we are using generic rules to map files from the input to the
output directory as well as maintain any directory structure from the input.
After the images are optimized we also go ahead and create a 100x100px thumbnail
image for all of our JPEG and PNG images in the assets/
directory, great for
implementing a lightweight gallery!