Building this Blog with Jekyll and Gulp

Lately I’ve been experimenting with different JavaScript build systems - namely Grunt and gulp. After deciding that I like gulp more (this is not a Grunt vs. gulp post), I’ve been starting to use it in some of my projects, including this blog.

Behind the Curtain

atsh.me is a static site generated by Jekyll. It used Sass with Compass (now Less, more on that later) as a preprocessor for styles (not really necessary for a site of this size, but more fun). The site is deployed on GitHub using GitHub pages.

Why use a build system?

Although this site is very simple, doing work on it was unecessarily complex. Making any change required running jekyll serve --watch as well as compass watch, with another terminal open for Git. Because I’m usually in Windows, this experience is pretty awful (Windows terminals suck). If I could streamline this by combining the Jekyll and Compass commands, it would greatly reduce development friction. Additionally, using gulp would make it possible for me to use LiveReload, further simplifying my workflow.

Using Jekyll with Gulp

Jekyll is a Ruby gem, so we need to call out to it from Node using the child_process module. We can spawn a Jekyll build using the following gulp task:

gulp.task('jekyll', function() {
    jekyll = spawn('jekyll.bat', ['build', '--drafts', '--future']);

    jekyll.stdout.on('data', function (data) {
        console.log('jekyll:\t' + data); // works fine
    });
});

Notice we include the --drafts switch, as during development we probably want to see our draft posts. In production GitHub builds the site itself, so no need to worry about dev and production builds in this case.

Building Stylesheets

While making the switch to a gulp based build system, I decided to switch from Sass to Less for the CSS preprocessor. I’ve been using Less a lot more lately, and I wasn’t really using any of the Compass features beyond browser prefixes. Switching to Less makes the build process a lot simpler, as the Less compiler is written in JavaScript, whereas Compass is another Ruby gem.

var styles = combine(
    gulp.src('less/main.less'),
    less({"compress": true}),
    prefix(),
    gulp.dest('css/')
);

styles.on('error', handleErrors);

return styles;

Compiling Less with gulp is a pretty standard task with lots of documentation on the internet, so I won’t bother explaining this too much. This task compiles Less into uglified CSS, which is then prefixed with vendor prefixes using gulp- autoprefixr. The streams are combined using stream-combiner, allowing me to handle errors in the compilation pipeline without crashing gulp.

Compiling on File Change

Now I can compile my Jekyll site and the stylesheets, I need to create a task which will automatically compile my styles and site and refresh my browser whenever I save changes to any file I’m working on.

gulp.task('watch', function() {
    var server = livereload();

    var reload = function(file) {
        server.changed(file.path);
    };

    gulp.watch('less/**', ['styles']);
    gulp.watch(['css/**', '_layouts/**', '_includes/**', 'blog/**'], ['jekyll']);
    gulp.watch(['_site/**']).on('change', reload);
});

This task will watch all of the files in the Less folder for changes. Whenever a Less file is compiled, it will output a CSS file in the css/ folder, triggering the second watch. The second watch also watches for changes to the files which compose the site like the layout and posts. Any changes to these files will cause the Jekyll task to be run. The Jekyll task will build the site into the _site folder, which will trigger LiveReload to refresh the browser.

Serving the Built Site

Building the site is nice, but I still need to replace the functionality provided by jekyll serve. Thankfully this is very easy in Node.

gulp.task('serve', function() {
    var app = connect()
        .use(connect.logger('dev'))
        .use(connect.static(path.resolve('_site')));

    http.createServer(app).listen(4000);
});

This task starts up a static HTTP server which serves the built _site folder on port 4000.

To Conclude

Now whenever I go to work on this site, I simply need to open a terminal and type gulp to get a static server, a watch task, and LiveReload. Simplifying my workflow was super satisfying, even though it probably took longer to set this all up than the time I was wasting before. A bit of yak shaving never hurt anyone though.

Of course, the code for this blog is available on GitHub and if you want to see the full gulpfile, you can view it here.