Optimize your CSS files with Gulp.js – Part 2

In a previous post I showed how to use Gulp.js to concatenate and minify many css files into one. Now we want to satisfy other two requirements of our workflow: cache bust and automation.

imgres

Cache bust

We know that the browser will cache our all.css file, and this is a good thing. Nevertheless we want to be sure that when we modify it, the browser will reload it from scratch. This is important! Before doing this, I found myself in bad situations: some weeks after a bug fix in my css, users (and clients) continued to report me the same bug (they were accessing an old css file).

The most secure fix to this is to change the name of the css file every time we modify it, adding some hash to it.

But it would be a chore to do this manually at each change. So we are going to automate this with gulp.

To achieve this we need the plugin cachebust. The command to install it is the same as the other plugins we already installed:

(if you have a file not found error about a package.json file, type npm init in the shell to create it. This is a file used by npm to track the  modules actually installed in your app)

Open the gulpfile.js and add at the top the following lines to load the corresponding helper:

Before creating the new task (the bust task) we do a little modification to the css_build task:

The added line (.pipe(cachebust.resources()) renames the resulting file (all.css) according to its MD5 checksum.

Before continue, create a folder templates in the root of our project and copy inside it the file index.html (this step will be clear in a moment).

Now create a new task:

With the second parameter of the task ([‘css_build’]) we say that this task depends on the task css_build (css_build must be executed before css_bust). The square brackets are needed because the dependance is set as an array of tasks.

This task reads the output of the previous task (in our case the all.css file renamed with MD5 checksum, that is something like all.b18a574b.css) and create a new file that is a copy of templates/index.html with all the occurrences of the name of the file (the string all.js) replaced with the new name name (all.b18a574b.css). The result file is written in the destination path (./). The generated name changes every time the content of the file changes (it depends on the MD5 checksum).

From now on every time we type the command:

our css files are concatenated, minified, a random string  is added to the result file name and the reference in our index.html file is correctly updated.

Remember also that, from now on, the file index.html to work on is that inside templates, not that in the root folder (this is replaced automatically by the task). Obviously you can choose to organize your path as you prefer, but remember to change the corresponding gulp path parameters.

Automation

We are lazy, and we don’t want to launch the command gulp css_bust each time we change the css.

To automate the operation, we add another type of gulp task, a watch task. It tells gulp to “watch” our files and to launch a task (css_build) if one of them is modified:

Now open a terminal and launch the task

this command does not return automatically from the shell and starts watching the files launching the task every time it is needed, resulting in something like the following:

terminal_gulp_watch

Cleaning the room

As you can have noticed, each time a new file all.css is generated, the old files remain there, and they are unused.

So we should have a way to remove the old files each time a new one is generated.

To achieve this install the following modules:

  • rimraf (to delete files and folder)
  • through2 (to create streams of commands)
  • path (file’s paths utils)

And declare variables to use them in the gulp file:

The following function deletes all the files passed as input to it:

It can be a little intimidating but, practically, all it does is pass all the files (in our case ./css/all.*.css) as an input to the rimraf module.

To test it create another task named css:

This task launches all the previous tasks (it depends on css_bust) and remove all the files passed to it.

What is that {read: false} option? It tells gulp not to read the content of the files (all.*.css) because we don’t need them, this makes the task a lot faster!

But this alone is not useful because it removes all the files, comprised the last file created, the one we want to use. How to fix this?

We need a filter that removes from the list of files passed to the cleaner (./css/all.*.css) the most recent one.

I found a module just for this (gulp-rev-outdated) but after installing it I couldn’t make it work. After some research, I modified the function (myRevOutdated) and used it instead of the original.

I will not enter the details of it, but after some research, I found the problem is in their regular expression filter of the input files. I modified it from:

to

I encourage you to study that function and let me know if I’m missing something (you will find myRevOutdated in the github demo project, feel free to reuse it if you need).

Now the complete css_clean task is:

It removes all files except the most recent one.

We can finally rename this task simply css because it handles all the tasks needed to satisfy our original requirements.

And this is the task that watch has to launch when needed (before was css_bust):

Conclusions

Gulp is a great tool to automate our css optimization, and it can be used also for our js files and … for many other things (there is a module for almost everything!).

Another useful tool of this genre is Grunt (launch a google search with “Gulp vs Grunt”). Try it too and let me know if you like more or less than Gulp.

As usual, in the github demo project of this post you will find all the source code discussed.

Valerio Ferrucci

Valerio Ferrucci (valfer) develops software on Apple Macintosh since the 1990s until today for MacOS, OSX and, since some years, iOS. He is also a Web (PHP/MySQL/JS/CSS) and Android Developer.

More Posts - Website

Follow Me:
TwitterFacebookLinkedIn

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

Leave a Reply

Your email address will not be published. Required fields are marked *