Using Gulp to automate & streamline your Drupal theme development

Steven Monetti
min read
March 7th, 2017

There was a time when you had to write in pure CSS. Thank God those days are over! Just in the past few years, the web development community has boomed with new and better ways to streamline your development process, from CSS compilers (SASS and LESS) to automation tools like Gulp and Grunt.

Let’s take a look at how we can use these tools in conjunction with Drupal.

Gulp

From the Gulp website itself, "gulp is a toolkit for automating painful or time-consuming tasks in your development workflow, so you can stop messing around and build something." What a great and accurate description! In this tutorial, I will cover the following topics:

  • Setting everything up
  • Converting SASS to CSS
  • Using BrowserSync for auto browser refresh
  • Automating by watching file changes
  • Creating an _autoimport.sass file which will automatically import all files in that folder

Setup

Since Gulp is a node module, before we can get started, we need to install Node.js (if you don’t already have it).

Node.js

Install Homebrew first by following these instructions

Then, run:

$ brew install node

Node.js uses a package manager called npm to download and install all the necessary modules. Those modules are defined in a file called package.json.

Create a package.json file in the root of your Drupal installation with the following content:

{
  "name": "drupal",
  "version": "1.0.0",
  "description": "Drupal",
  "main": "gulpfile.js",
  "author": "John Doe",
  "private": true,
  "devDependencies": {
    "browser-sync": "^2.18.8",
    "gulp": "^3.9.1",
    "gulp-autoprefixer": "^3.1.1",
    "gulp-each": "^0.5.0",
    "gulp-fs": "0.0.2",
    "gulp-sass": "^3.1.0",
    "gulp-sourcemaps": "^2.4.1",
    "gulp-watch": "^4.3.11"
  }
}

The, run the following to download all the modules required for your project:

$ npm update

Browesersync

Download the BrowserSync module from the Drupal website, install it and enable it. That module will disable Drupal from compiling the CSS to @import statements, which are currently not supported by BrowserSync, as well as injecting their JS code.

Gulp

You can verify that Gulp is installed by running:

$ gulp -v

Now it’s time for the fun stuff! Let’s start defining our Gulp tasks. Each task can be run individually by running gulp [task_name]. If you run gulp by itself, it will run the task called "default".

Create a file in the root of your Drupal installation called gulp.js. Change the config variable to match your paths. Make sure that all of your CSS lives under a directory called css in your theme (e.g. sites/all/themes/my_theme/css/application.sass).

var gulp = require('gulp');
var watch = require('gulp-watch');
var fs = require('fs');
var path = require('path');
var each = require('gulp-each');
var sass = require('gulp-sass');
var sourcemaps = require('gulp-sourcemaps');
var autoprefixer = require('gulp-autoprefixer');
var browserSync = require('browser-sync');

// Config
var config = {
  themes: {
    my_theme: {
      path: 'sites/all/themes/my_theme',
      filename: 'application.sass'
    }
  }
};


// Theme
gulp.task('sass:themes:my_theme', function() {
  return gulp.src(config.themes.ascend.path + '/css/' + config.themes.my_theme.filename)
    .pipe(sourcemaps.init())
    .pipe(sass({
      outputStyle: 'compressed'
    }).on('error', sass.logError))
    .pipe(autoprefixer('last 2 version'))
    .pipe(sourcemaps.write())
    .pipe(gulp.dest(config.themes.my_theme.path + '/css'))
    .pipe(browserSync.reload({
      stream: true
    }));
});

// Run The BrowserSync server
gulp.task('browser-sync', function() {
  browserSync.init();
});

// Watch files
gulp.task('watch', function() {
  // Watch theme SASS files
  gulp.watch(config.themes.ascend.path + '/**/*.{sass,scss}', ['sass:themes:my_theme']);
});

gulp.task('default', ['browser-sync', 'watch']);

To start the process, just type gulp in your terminal.

This will start two tasks: browser-sync and watch. Your process will stay alive as it's looking for changes in any .sass or .scss files. Once a change is detected (you need to save) it will compile the SASS file, compress it, add browser prefixes (if missed, e.g. -webkit, -moz), append the sourcemaps, write the CSS file to disk, and finally send a signal to your browser with the changes.

Adding the autoloading feature

Have you ever wanted to just import all SASS files from a folder because order is not important to you? It would be awesome if we could do something like this:

@import "blocks/*"

Though SASS doesn't currently support that, as for most people, the order in which CSS is generated is really important. In our websites though, we generally style things with really specific IDs or classes; therefore, the order is not important. Fortunately, we've found a way around that using Gulp! We've created a task that looks for all files called _autoimport.sass and will automatically inject @import statements for all files in that folder.

gulp.task('sass:themes:my_theme:autoimport', function() {
  return gulp.src(config.themes.my_theme.path + '/css/**/' + config.autoimport_filename)
    .pipe(each(function(content, file, callback) {
      var folder = path.relative(file.cwd, file.path.replace('/' + config.autoimport_filename, ''));
      var autoimport = folder + '/' + config.autoimport_filename;

      fs.writeFile(autoimport, "// Do not modify this file directly. `gulp` automatically generates them.\n");
      gulp.src([folder + '/*.{sass,scss}', '!' + autoimport])
        .pipe(each(function(content, file, callback) {
          fs.appendFile(autoimport, '@import "' + file.relative + '"' + "\n");
          callback(null, content);
        }));

      callback(null, content);
    }));
});

Now we can add a dependency on our original task sass:themes:my_theme to this one.

Final gulp.js file

var gulp = require('gulp');
var watch = require('gulp-watch');
var fs = require('fs');
var path = require('path');
var each = require('gulp-each');
var sass = require('gulp-sass');
var sourcemaps = require('gulp-sourcemaps');
var autoprefixer = require('gulp-autoprefixer');
var browserSync = require('browser-sync');

// Config
var config = {
  themes: {
    my_theme: {
      path: 'sites/all/themes/my_theme',
      filename: 'application.sass'
    }
  },
  autoimport_filename: '_autoimport.sass'
};


gulp.task('sass:themes:my_theme', ['sass:themes:my_theme:autoimport'], function() {
  return gulp.src(config.themes.my_theme.path + '/css/' + config.themes.my_theme.filename)
    .pipe(sourcemaps.init())
    .pipe(sass({
      outputStyle: 'compressed'
    }).on('error', sass.logError))
    .pipe(autoprefixer('last 2 version'))
    .pipe(sourcemaps.write())
    .pipe(gulp.dest(config.themes.my_theme.path + '/css'))
    .pipe(browserSync.reload({
      stream: true
    }));
});


gulp.task('sass:themes:my_theme:autoimport', function() {
  return gulp.src(config.themes.my_theme.path + '/css/**/' + config.autoimport_filename)
    .pipe(each(function(content, file, callback) {
      var folder = path.relative(file.cwd, file.path.replace('/' + config.autoimport_filename, ''));
      var autoimport = folder + '/' + config.autoimport_filename;

      gulp.src([folder + "/*.{sass,scss}", "!" + autoimport])
        .pipe(each(function(content, file, callback) {
          fs.appendFile(autoimport, '@import "' + file.relative + '"' + "\n");
          callback(null, content);
        }));

      callback(null, content);
    }));
});


gulp.task('browser-sync', function() {
  browserSync.init();
});

gulp.task('watch', function() {
  gulp.watch([config.themes.my_theme.path + '/**/*.{sass,scss}', '!' + config.themes.my_theme.path + '/**/' + config.autoimport_filename], ['sass:themes:my_theme']);
});

gulp.task('default', ['browser-sync', 'watch']);

Run by just calling gulp in your terminal.

$ gulp

Comments