Streamline Jekyll experience with browser-sync

In my post from 2015 on build process of my own jekyll-based blog, I'd implemented blog deploy script with Gulp. It featured with html/css/js checking and minifications. It was very usable for final deploying of blog to hosting, but not much convenient when I wrote the posts.

Here is an explanation how I'd integrated browser-sync into my process.

When I wrote the posts I used Sublime Text with following plugins:

Later I discovered Atom which supports markdown editing and preview out of the box.

This editors is convenient when you write posts but they are cannot show post inside my blog's markup. Also, markup editing with selected process was painful. I needed to rebuild blog on each post or style change manually with gulp build command.

I tolerate this process for two years and now I'd like to improve this experience. I bet on jekyll build --watch --incremental command in conjunction with browser-sync web-server and browser synchronization library.

jekyll build --watch

When you run jekyll build command, jekyll will delete all the contents of _site folder and will generate all site content again. There is --watch command line modifier which stands for "watch for changes and rebuild".

The challenge was to copy external bower components to _site/vendor folder. Jekyll deleted this folder each time I executed jekyll build or jekyll build --watch.

The solution is to tell jekyll which of folders he should not delete. It is possible with keep_files configuration option. It stands for "when clobbering the _site destination, keep the selected files". It is useful for files that are not generated by jekyll. The paths are relative to the _site destination.

I'd added following line to my _config.yml:

keep_files: [ "vendor" ]

Now jekyll will delete all content of _site except vendor folder.

browser-sync

I discovered browser-sync JavaScript library. It has built-in web server which includes into served HTML <body> tag a custom <script> with synchronization implementation. The idea of using this library is to watch changes with gulp and execute browserSync.reload() when files in _site changes (when jekyll rewrites changed posts or styles).

gulp integration

Here I specified four gulp tasks:

  • jekyll-watch task runs jekyll in incremental watch mode
  • copy_bower_components task was not changed since my post in 2015 and intended to copy external bower components into vendor folder of _site
  • webserver task
    • Runs browserSync webserver on port=4000
    • Watch all the changes in jekyll output (_site). When change event is fired, browserSync will reload changed pages using browserSync.reload().
    • Note reloadThrottle and reloadDebounce settings which defines time window to minimize reloading of pages when multiple changes occured almost simultaneously
    • Note setTimeout to defer watching of changes. I need this because jekyll-watch and webserver tasks working in parallel and jekyll needs some time to generate the site content.
  • serve task intended to run from command line and combines all necessary tasks to support build site, watching changes and reload pages in browser.
var gulp        = require('gulp');
var browserSync = require('browser-sync').create();
var shell       = require('gulp-shell');

gulp.task('jekyll-watch', shell.task(['jekyll build --watch --incremental']));

gulp.task('copy_bower_components', function() {
    gulp.src([
        'bower_components/bootstrap/dist/css/bootstrap.min.css',
        'bower_components/bootstrap/dist/css/bootstrap-theme.min.css',
        'bower_components/fontawesome/css/font-awesome.min.css',
        'bower_components/normalize.css/normalize.css'
        ]).pipe(gulp.dest('./_site/vendor/css/'));
    gulp.src([
        'bower_components/bootstrap/dist/fonts/*',
        'bower_components/fontawesome/fonts/*',
        ]).pipe(gulp.dest('./_site/vendor/fonts/'));
    gulp.src([
        'bower_components/bootstrap/dist/js/bootstrap.min.js',
        'bower_components/jquery/dist/jquery.min.js',
        ]).pipe(gulp.dest('./_site/vendor/js/'));
    gulp.src([
        'bower_components/modernizr/modernizr.js',
        ]).pipe(uglify())
        .pipe(gulp.dest('./_site/vendor/js/'));
});

gulp.task('webserver', function () {
    browserSync.init({
        port: 4000,
        open: true,
        reloadThrottle: 500,
        reloadDebounce: 500,
        server: {
            baseDir: '_site/'
        }
    });
    setTimeout(function () {
        gulp.watch('_site/**/*.*').on('change', browserSync.reload);
    }, 5000);
})

gulp.task('serve', ['jekyll-watch', 'copy_bower_components', 'webserver'], function () {});

Yes, html/js/css is not minified with this gulp serve command. But it is not necessary for writing-time. I haven't changed my deploy script for production. See my previous post for details.

Conclusion

browser-sync changed the way I can write posts and modify markup and styles for my blog. Manual work is minimized and I can preview my blog posts almost interactively. Solution is so simple that I can't imagine how I lived before without this stuff :).

Happy blogging!

Comments