Roman Veselý Software Engineer from Slovakia. Somewhere between trees and bytes of code.

How to re-generate and refresh static website in Statie?

Make development with Statie a bit more convenient with a little use of gulp and Browsersync.

With a rising popularity of static site generators, we can find that there are many of them already in use. This blog runs on Statie, another such a tool written by Tomas Votruba, which differs from the others with Nette and its templating engine Latte used in the background.

If you are not aware what is this tool all about let’s take a look at a post series about it on Tomas’ blog firstly. It will lead you from the very beginning on how to run your own site upon Statie, so I won’t deal with this here.

In the end of the mentioned blog post, we can find a simple gulp task that runs generate command on every change in the source files. This is surely handy, but you still need to refresh your browser to be able to see your changes. With Browsersync we can make this step unnecessary.

Running an executable command

With vendor/bin/statie generate command we can generate our site content. In gulpfile.js we may have something like this:

var gulp = require('gulp');
var exec = require('child_process').exec;

gulp.task('default', function () {
  exec('vendor/bin/statie generate');
});

The problem is it doesn’t give you a message when the job is done, so you cannot tell when to refresh a browser to see new content. To get over this problem gulp-run package may be used.

Also, I recently found there is a new version of gulp coming with some handy features, namely: gulp.series and gulp.parallel which combine multiple functions together so tasks don’t have to have dependencies anymore. And we can use those features now!

Please, read a guide how to set up gulp 4 before you proceed.

Watching over changes

Get everything needed to start:

$ npm install --save-dev gulpjs/gulp.git#4.0 gulp-watch gulp-run browser-sync

Add some foundation to the gulpfile.js:

var gulp = require('gulp');
var run = require('gulp-run');
var browserSync = require('browser-sync').create();

function generate() {
  return run('vendor/bin/statie generate').exec();
}

function reload(done) {
  browserSync.reload();
  done();
}

Such simple functions, aren’t they? Simple, yet they do everything we need to have to set up a watcher function. Short notes on above code:

  • in generate() function return has to be used in order to use it in a gulp.series() properly
  • same applies for done() function used in reload() - repeated reloading would be not possible without it

Here comes the watcher function:

function watch() {
  browserSync.init({
    server: 'output',
  });

  gulp.watch('source/**/*', gulp.series(generate, reload));
}

With a use of gulp.series(generate, reload) we define that we want to reload the browser when new content generation finished, not before. The last step is to create a gulp task that will use previously defined functions:

gulp.task('default', gulp.parallel(generate, watch));

Now, when you run gulp, new content is generated by Statie along with local server running on localhost:3000 (default settings). Try to edit source files and save them. Browsersync should spot the changes that were generated into output folder and instantly refresh the browser.

We have just removed one step in our development process. Content edition is no pain anymore!

Here is the complete gulpfile.js.

var gulp = require('gulp');
var run = require('gulp-run');
var browserSync = require('browser-sync').create();

gulp.task('default', gulp.parallel(generate, watch));

function generate() {
  return run('vendor/bin/statie generate').exec();
}

function reload(done) {
  browserSync.reload();
  done();
}

function watch() {
  browserSync.init({
    server: 'output',
  });

  gulp.watch('source/**/*', gulp.series(generate, reload));
}

If you are interested also in the frontend development, take a look at how to implement a CSS preprocessor into this workflow.

Conclusion

With gulp 4 new features gulp.series and gulp.parallel is really easy to create a building workflow when one function depends on another.

Do you know about anything that can improve my approach or do you use something completely different? Let me know.