Using gulp to automate building of Jekyll-based site

comments

Mostly, I'm not frontend developer. But sometimes I need to deal with frontend development. Recently I moved my blog from WordPress to Jekyll platform (Jekyll is the static site generator).

Default layout for Jekyll has very limited functions and design, and I decided to make custom layouts for my blog.

My goals was to learn and use:

  • bootstrap and font-awesome
  • disqus comments
  • tags pages and tags cloud
  • month and year archives pages
  • sharing posts to social networks like facebook and twitter
  • google analytics
  • yandex.metrika
  • use best practices to work with assets
  • automate all the things I can

My goals completed for now. In this article I'll write how I use gulp to automate building jekyll-based blog. This post is mainly for memory. Hope it helps someone.

I won't give you instructions how to install gulp and its modules with npm here. This is not an introduction to gulp and you should already know how to install gulp and how to run gulp. I only give parts of my gulpfile.js here.

Selecting frameworks

In march I was on #дамп conference. I decided to visit one of reports to learn mainstream vision to frontend development. In summary:

  • mainstream task runner is gulp for now
  • you need to minify all the things: html, js, css, images
  • you need to have different images for different device-pixel-ratio now, and to minimize count of assets you should use svg

Report motivated me to use gulp instead of grunt, also I'll use bower to install third-party components.

Build jekyll site with gulp

First of all, we need to build jekyll site with simple jekyll build command. Note that jekyll cleans output _site folder before build.

var childProcess = require('child_process');
gulp.task('jekyll', function(cb) {
    var child = childProcess.exec('jekyll build', function(error, stdout, stderr) {
        cb(error);
    });
});

I use gulp task with callback here because other tasks will depend on this task and they must be started strongly after jekyll build completes. I use child_process.exec function to run build command and call cb callback when build command exits.

Minifying all the things

You may need to minify html, javascript, images and css before upload it to hosting.

HTML

You can use gulp-htmlmin npm module to minify HTML (plus javascript and css styles it contains) with options:

var htmlmin = require('gulp-htmlmin');
gulp.task('htmlmin', function() {
    return gulp.src('./_site/**/*.html')
        .pipe(htmlmin({
            collapseWhitespace: true,
            removeComments: true,
            conservativeCollapse: true,
            collapseBooleanAttributes: true,
            removeRedundantAttributes: true,
            removeEmptyAttributes: true,
            removeEmptyElements: true,
            lint: false,
            minifyJS: true,
            minifyCSS: true,
        }))
        .pipe(gulp.dest('./_site/'));
});

CSS

To prepare your css gulp-minify-css and gulp-autoprefixer npm modules are useful:

var minifycss = require('gulp-minify-css');
var autoprefixer = require('gulp-autoprefixer');
gulp.task('build-css', function() {
    return gulp.src('./_site/css/*.css')
        .pipe(minifycss())
        .pipe(autoprefixer({
            browsers: ['> 1%'],
            cascade: false,
        }))
        .pipe(gulp.dest('./_site/css'));
});

Update Note that gulp-minify-css is deprecated, you can use gulp-cleancss module instead.

JavaScript

Before minifying JavaScript (with gulp-uglify npm module) you can validate it's syntax with gulp-jsvalidate npm module:

var jsValidate = require('gulp-jsvalidate');
var uglify = require('gulp-uglify');
gulp.task('build-js', function() {
    return gulp.src('./_site/js/**/*.js')
        .pipe(jsValidate())
        .pipe(uglify())
        .pipe(gulp.dest('./site/js/'));
});

Images

You can use gulp-imagemin to optimize your images on production:

var imagemin = require('gulp-imagemin');
gulp.task('imagemin', function () {
    return gulp.src('./_site/images/*.png')
        .pipe(imagemin([imagemin.optipng({optimizationLevel: 5})]))
        .pipe(gulp.dest('./_site/images'));
});

Copy third-party components

I have exclude: [ 'bower_components', 'npm_modules' ] string in Jekyll config. It's because bower will install full packages of third-party modules and I need only part of them. I will copy necessary files to output folder with gulp to minimize size of used components:

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/'));
});

This snippet will put necessary files to vendor folder.

Put things together

Now we should run all minifying tasks after jekyll build command. I do it with build task which depends on jekyll:

gulp.task('build', [ 'jekyll' ], function() {
    gulp.run('copy_bower_components');
    gulp.run('build-css');
    gulp.run('htmlmin');
});

gulp.task('default', [ 'build' ], function() {});

I also made gulp build task as default, so you can simply execute gulp to run build.

Validation and static analysis

There is great gulp plugins:

var bootlint = require('gulp-bootlint');
var html5Lint = require('gulp-html5-lint');
var htmlhint = require("gulp-htmlhint");
gulp.task('check-html', function() {
    return gulp.src('./_site/**/*.html')
        .pipe(html5Lint())
        .pipe(htmlhint())
        .pipe(bootlint());
});

var scsslint = require('gulp-scss-lint');
gulp.task('check-scss', function() {
    gulp.src([ './_sass/**/*.scss' ])
        .pipe(scsslint())
});

gulp.task('check', [ 'check-html', 'check-scss' ], function() {});

Now with gulp check command we run all validation checks with our site.

Serve results

As I wrote, I have exclude: [ 'bower_components' ] in my jekyll config file. Therefore I cannot use jekyll serve command anymore (it imply clean output folder and run jekyll build before serving, vendor folder will be deleted). All that we need is to run web server application with content based on output folder. gulp has such one plugin called gulp-webserver:

var webserver = require('gulp-webserver');
gulp.task('serve', function() {
    gulp.src('./_site/')
        .pipe(webserver({
            host: "localhost",
            port: 4000,
            open: true,
        }));
});

gulp.task('run', [ 'default' ], function() {
    gulp.run('serve');
});

Note property open: true will automatically open site in browser once you run gulp serve command. If you want to build site and serve it in one command, use gulp run.

Conclusion

With gulp we have automated build of jekyll-based site that ready for production hosting.

  • with gulp command we build site and minify HTML, CSS and JavaScript.

  • with gulp check we run validation on the site.

  • with gulp serve we can observe our site in browser.

  • with gulp run we build and serve our site in one command.

You may find full gulpfile.js on github.

Enjoy!

Comments