Gulp is an amazing addition to any front end developers repertoire. Here I will go through the Gulp build system I created on one of my recent projects where I used the static site generator Jekyll
I’ll presume you already know atleast the basics of Gulp. If not…youtube is your friend for the next hour or so. There are so many resources there and elsewhere online that comprehensively explain why Gulp is imperative for any web developer and how to get started with it
The Imports
As with any Gulp build, we need to load all the packages we’ve installed using npm. Here is a list of all the modules we require for this build, each hyperlinked to their respective installation page:
- gulp-browsersync
- gulp-minify-css
- gulp-sass
- gulp-autoprefixer
- gulp-imagemin
- gulp-concat
- gulp-uglify
- gulp-sourcemaps
- gulp-util
- gulp-plumber
To install these modules type in npm install --save browser-sync
for example. Inclusion of the --save
saves the module as a dependancy in your package.json file (which you must create beforehand). Succinctly put this just means by typing npm install
in your Terminal in the root directory of a new project which only contains the gulpfile.js and package.json files, npm will create a new node_module directory and install all the packages listed in your package.json file. Nice way to make your code reusable basically
Once you’ve installed all the node modules load them in your gulpfile.js file:
var gulp = require('gulp'), cp = require('child_process'), imagemin = require('gulp-imagemin'), browserSync = require('browser-sync').create(), concat = require('gulp-concat'), uglify = require('gulp-uglify'), sass = require('gulp-sass'), minify = require('gulp-minify-css'), sourcemaps = require('gulp-sourcemaps'), util = require('gulp-util'), plumber = require('gulp-plumber'), prefixer = require('gulp-autoprefixer');
Setup
Before we get started I find that the best way to work with file directories and other nitty gritty info is to include it in an Object or function at the top of your file. This way it’ll be easy to make a change that effects many areas of your code. I’ll create is config Object (general configuration info, eg. directory paths) and errorMssg function (what to do upon errors)
var config = { cssDir: 'assets/css/', jsDir: 'assets/js/', bowerDir: 'assets/js/components/', imgDir: 'assets/img/', production: !!util.env.production, notProduction: !util.env.production }; var errorMssg = function (err) { util.beep(3); util.log(util.colors.red(err)); };
Most are just paths leading to css, javascript or image directories. bowerDir is where all your bower installations are held (if you’re using bower). Note that if you include a .bowerrc file in same directory as your bower.json file, you can specify where bower should save all the packages you acquired with bower install
. The production and notProduction properties serve as functions for the gulp-util module. This is to differentiate when to carry out certain actions depending on whether we add --production
after typing gulp
in the terminal. I’ll explain more about this later on. errorMssg is a function which is called when an error occurs. It just lets out 3 beeps, and logs the error message (in red) into the terminal.
BrowserSync task
gulp.task('browser-sync', ['styles', 'js', 'jekyll-build'], function(){ browserSync.init({ server: { baseDir: '_site' } }); });
This is where we set the directory where the Browsersync server should run from and also what it should do upon reloading; run the ‘styles’, ‘js’ and ‘jekyll-build’ tasks in like order. So now we write these tasks:
Jekyll task
Lets begin with building the Jekyll task. Node.js comes bundled with a module child_process which can run other programs for you. So we will use it to run a Jekyll gem to generate our site in the _site folder (I assume you know all about Jekyll. Its very simple, read their documentation here)
gulp.task('jekyll-build', function(done){ return cp.spawn('jekyll', ['build'], {stdio: 'inherit'}) .on('close', done); }); gulp.task('jekyll-rebuild', ['jekyll-build'], function () { browserSync.reload(); });
The ‘jekyll-build’ simply uses the jekyll gem to run jekyll build
. ‘jekyll-rebuild’ firstly runs the ‘jekyll-build’ task, then runs the function which reloads all Browsersync windows
Styles task
gulp.task('styles', function(){ gulp.src('assets/css/main.scss') .pipe(config.notProduction ? sourcemaps.init() : util.noop()) .pipe(plumber({ errorHandler: errorMssg }) .pipe(sass({ includePaths: ['scss'] })) .pipe(prefixer({ browsers: ['last 2 versions'], cascade: false })) .pipe(config.production ? minify() : util.noop()) .pipe(config.notProduction ? sourcemaps.write('.') : util.noop()) .pipe(gulp.dest('_site/' + config.cssDir)) .pipe(browserSync.stream()) .pipe(gulp.dest('assets/css')); });
We want gulp to build our styles differently when for production (ready to push into server) and when developing. Firstly we don’t want to show our sourcemap files in production (optional). Sourcemaps keeps track of where all the imports we used in our main.scss file are so that the browser can specify where exactly a bit of css code came from. Without this the browser would just point to the long main.css. This is especially handy when fixing bugs. So if we haven’t included the –production suffix we will run sourcemaps.init(), if we have we will do nothing, util.noop(). Next we run gulp-plumber, that instead of gulp seizing to run upon an error, it just runs the errorMssg function as Browsersync waits for you to amend your errors before automatically reloading the webpage. Finaly we run the gulp-sass, autoprefixor and minify (if –production is included) modules. One important thing to note with our Jekyll setup; it is important to save the final .css file twice. Firstly save it into the _site directory before running browserSync.stream() as this is the directory we’ve set our Browsersync server to run from, then save it into your ‘local’ assets folder to keep a copy for jekyll to use when it next builds your project
Javascript task
gulp.task('js', function(){ return gulp.src([config.bowerDir+'jquery/dist/jquery.min.js', config.jsDir + 'contact.js']) .pipe(config.notProduction ? sourcemaps.init() : util.noop()) .pipe(plumber({ errorHandler: errorMssg })) .pipe(concat('main.js')) .pipe(config.production ? uglify() : util.noop()) .pipe(config.notProduction ? sourcemaps.write('.') : util.noop()) .pipe(gulp.dest('_site/' + config.jsDir)) .pipe(browserSync.stream()) .pipe(gulp.dest('assets/js')); });
I personally prefer bundling all my javascript files (even libraries from bower) into one minified file. More calls to the server (with numerous script tags) will mean your webpage loads slower. The less the better. So for the source I gather all the different javascript files I use from my bower directory and javascript directory, then if for production, I minify and save the file (twice again like the main.css file)
Images task
gulp.task('min-images', function(){ gulp.src([config.imgDir+'*.jpg', config.imgDir+'*.jpg']) .pipe(imagemin({ progressive: true })) .pipe(gulp.dest(config.imgDir)); });
The images task runs gulp-imagemin. This just minifies your images (jpegs, pngs…add as required) automatically upon saving them into the images directory
Watch task
gulp.task('watch', function(){ gulp.watch(config.jsDir + '*.js', ['js']); gulp.watch(config.cssDir + '**/*.scss', ['styles']); gulp.watch(config.imgDir + '*.', ['min-images']); gulp.watch(['_includes/*.html', 'index.html'], ['jekyll-rebuild']); });
Here we tell gulp of all the directories it should constantly check for changes and the corresponding action it should carry out when changes are detected.
Lastly we set the default gulp action. This is mandatory for all gulp builds as it designates the action that should be carried out when simply gulp
is typed into the console
gulp.task('default', ['browser-sync', 'watch']);
So when gulp is typed into the console we allow the Browsersync server to run (after it has run the tasks ‘styles’, ‘js’ and ‘jekyll-build’), and we watch for any changes to update our webpage.
And thats it. Hopefully this blog post has been of help to you. I have put a project based on this gulp build on GitHub for you to use as a starting point for your next project if you’re using Jekyll. Clone the project (git clone https://github.com/shadrech/Gulp-Jekyll-Build.git
) and as long as you have npm and gulp installed globally on your machine, all you have to do is type npm install
in home directory containing the gulpfile.js, and you’ll be good to go!