We at Axelerant believe in developer productivity, and thus, tooling and automation are an integral part of our workflow. Over time, we have brought in and built several build and deployment tools as part of our development processes. Gulp is one such tool we use to automate our front-end build processes.

Gulp is one of many task-runners out there. However, it focuses on speed and simplicity. It harnesses the awesome power of Node stream, which make it exponentially faster than it’s counterparts. We have set up a baseline gulpfile.js which runs around half a dozen tasks like Compiling Sass, uglifying and minifying scripts, optimizing images, watching for changes and Livereload support. Running all these commands individually becomes cumbersome after an interval, and with Gulp you just have to run one command, and it takes care of all the tasks for you.

Setup

To get started, you will need to have Node.js installed on your systems. You can either install it using the executable file available on the Nodes.js Downloads Page or compile the binaries if you are feeling excited. Once you have gone through the installation process, you will have Node.js and NPM available at your disposal. To check if everything has gone as expected run the following commands in your terminal window, and you should get the version number of Node and NPM.
node --version
npm --version

Node & NPM Version - Axelerant

Node & NPM Version

We can now go ahead and install Gulp on our system.
sudo -H npm install --global gulp
gulp --version

Now that we have all the dependencies installed we will drop in the Gulpfile.js and package.json in our Drupal project. package.json is where all the required modules are defined. Here we have added all the Gulp plugins that we are using for the build setup.

Gulp Plugin Install - Axelerant

Plugin Install

Gulpfile.js

var config       = require('./config.json');
var gulp         = require('gulp');
var sass         = require('gulp-ruby-sass');
var autoprefixer = require('gulp-autoprefixer');
var csscomb      = require('gulp-csscomb');
var minify       = require('gulp-minify-css');
var rename       = require('gulp-rename');
var concat       = require('gulp-concat');
var jshint       = require('gulp-jshint');
var uglify       = require('gulp-uglify');
var imagemin     = require('gulp-imagemin');
var clean        = require('gulp-clean');
var pngcrush     = require('imagemin-pngcrush');
var newer        = require('gulp-newer');
var plumber      = require('gulp-plumber');
var browserSync  = require('browser-sync');
var notify       = require('gulp-notify');var onError = function (err) { console.log(err); notify({ message: 'Error!' }); };

gulp.task('styles', function() {
		return gulp.src(config.devPath.sass) .pipe(plumber({ errorHandler: onError })) .pipe(sass({ style: 'expanded', compass: true })) .pipe(autoprefixer('last 2 version')) .pipe(csscomb()) .pipe(gulp.dest(config.buildPath.styles)) .pipe(minify()) .pipe(rename({suffix: ".min"})) .pipe(gulp.dest(config.buildPath.styles)) .pipe(browserSync.reload({stream:true, once: true})) // .pipe(livereload()) .pipe(notify({ message: 'Styles task complete' }));
		});

gulp.task('scripts', function() {
		return gulp.src(config.devPath.scripts) .pipe(plumber({ errorHandler: onError })) .pipe(jshint('./.jshintrc')) .pipe(jshint.reporter('jshint-stylish')) .pipe(jshint.reporter('fail')) .pipe(concat('index.js')) .pipe(gulp.dest(config.buildPath.scripts)) .pipe(rename({suffix: ".min"})) .pipe(uglify()) .pipe(gulp.dest(config.buildPath.scripts)) .pipe(browserSync.reload({stream:true, once: true})) .pipe(notify({ message: 'Scripts task complete' }));
		});

gulp.task('images', function () {
		return gulp.src(config.devPath.images) .pipe(plumber({ errorHandler: onError })) .pipe(imagemin({ optimizationLevel: 7, progressive: true, interlaced: true })) .pipe(gulp.dest(config.buildPath.images)) .pipe(notify({ message: 'Images task complete' }));
		});

gulp.task('clean', function() {
		return gulp.src([config.buildPath.styles, config.buildPath.scripts, config.buildPath.images], {read: false})
		.pipe(clean());
		});

gulp.task('default', ['clean'], function() {
		gulp.start('styles', 'scripts', 'images');
		});

gulp.task('browser-sync', function() {
		browserSync.init(null, { proxy: "127.0.0.1" });
		});
gulp.task('watch', ['browser-sync'], function() {
		gulp.watch(config.devPath.scripts, ['scripts']);
		gulp.watch(config.devPath.sass, ['styles']);
		});

Package.json

{
	"name": "Fulp",
		"version": "0.0.1",
		"description": "Build tasks",
		"main": "index.js",
		"author": "Bassam Ismail",
		"license": "MIT",
		"devDependencies": {
			"browser-sync": "^0.9.1",
			"gulp": "^3.7.0",
			"gulp-autoprefixer": "0.0.7",
			"gulp-clean": "^0.3.0",
			"gulp-concat": "^2.2.0",
			"gulp-csscomb": "^0.1.0",
			"gulp-imagemin": "^0.6.0",
			"gulp-jshint": "^1.6.2",
			"gulp-minify-css": "^0.3.4",
			"gulp-newer": "^0.3.0",
			"gulp-notify": "^1.3.1",
			"gulp-plumber": "^0.6.2",
			"gulp-rename": "^1.2.0",
			"gulp-ruby-sass": "^0.5.0",
			"gulp-uglify": "^0.3.0",
			"imagemin-pngcrush": "^0.1.0"
		}
}

Our Gulpfile.js essentially has four tasks, each serving a specific purpose. We will go over each one-by-one.

Styles

We start by compiling all the SCSS files in the sass folder defined in the config.json(which makes it easier to update the directory structure). The compiled files are then passed to the Autoprefixer plugin which using the Can I Use API prefixes the CSS properties. Once the compiled CSS file is prefixed, they are then sorted using the CSSComb plugin based on the given csscomb.json file. After this, the file is saved in the build/css directory and a minified version using minify-css and rename plugin is generated in the same directory.

Scripts

The Scripts task takes care of all the *.js files in the scripts directory. It starts linting all the javascript files using JSHint. We can add a local per project .jshintrc files or else it will use the global defaults. Once all the javascript files are checked for errors, they are concatenated into a single file and stored in the build/js directory. After this, the concatenated file is uglified and minified so as to save some memory and transfer bandwidth.

Images

The images task looks for the pictures in the images directory and compresses them using tiny-png and image-optim. Jpeg files are progressively saved which keeps the quality intact, and the file size is reduced by a large margin. This is especially useful when developing for retina displays. Once the images task is complete, it stores the files in the build/images directory and in the terminal you can see the compression output.

Clean

Clean is the simplest of the four tasks. It cleans the build directory and helps you clear the deprecated assets in your build directory.

Browser-Sync

Okay, I lied. There is a fifth task! Browser-sync is similar to Livereload. However, it helps you test your site on multiple devices over a shared network. To get this working, you will have to use the Drupal Module for Browser-Sync. Just download and enable this module and you are good to go. Every time you make changes in your sass or js files your browser will automatically reload on all the connected devices. Browser-sync in the gulpfile.js is setup to work with the server running on 127.0.0.1, but you can change this as per your requirements.

Watch

If you run gulp watch, it will start watching your sass and js files for changes and when a change is detected it will run the styles and scripts tasks.

Default

The default function that you can run by just typing gulp in your terminal will clean the build directory, run the scripts, styles and images tasks and exit. We mostly use this before pushing the code to remote repository. You can go a step ahead and add this command to your pre-commit hook.

This is just the tip of the iceberg. We can also configure Gulp to run Drush as per our needs. This saves us a lot of time tending to the work at hand rather than fighting these process.

Want Axelerant to grow your team? Learn More

  • henzel

    Hello! Can you add archive with all used files like config.json?

    • skippednote

      Hey Henzel,
      Thank you for reaching out. You can pull down the files with latest updates from https://github.com/axelerant/gulp-baseline.

      Feel free to comment back on this thread if you have further questions.

      • henzel

        Thanks for help.

        Sorry for my english :)

        After “npm install” have error: “Invalid name: “Project Build Setup””.
        We need just rename project.

        Second error: “graceful-fs version 3 and before will fail on newer node releases. Please update to graceful-fs@^4.0.0 as soon as possible.”
        I used this command “npm install graceful-fs” – it’s work.

        Third error – “Task ‘default’ is not in your gulpfile”.
        I don’t know, how I can resolve this problem. Can you help me?

        And when I use “BSURL=myurl gulp watch” – browsersync doesn’t work. Edit my sass/style.scss file, save – doesn’t generate css and doesn’t reload browser

  • bparticle

    Hi, this is very interesting. I copied and edited the files to one of my projects to see how it works but I get an error with installing the dependencies. npm install says npm WARN ENOENT ENOENT: no such file or directory, open ‘/absolute-path-to-project/package.json’. Any ideas how to fix this?