Around two years ago, I wrote a post called Drupal 101: Theming Drupal 7 with gulp, which covered some basics about Sass and gulp. I’m not going to repeat myself, so if you can read that article if you’re interested. This one is going to cover the delta for the gulpfile.js setup in Drupal 8.

gulp-ify your Drupal theme

If you’re just starting out with Drupal 8 theming, you can read my previous post on exactly that right here. I’m going to cover the gulp tasks that are relevant to my way of working, which is a whole lot less than what most other people do with gulp.

I only use gulp to compile Sass, handle ES6 and clear the cache when I update Twig templates. No minification because Drupal does that already.

Setting up the package.json file

This part is completely replicated from the Drupal 7 post. Screenshots are tedious, so just replace all instances of Drupal 7 in the screenshots with Drupal 8 in your mind, thanks.

Navigate to the root of your Theme folder and initiate a new node project.

npm init

This will trigger a series of prompts for the generation of a package.json file. This file will store all the information about the required node packages for your project.

npm init

Most of the prompts are pretty intuitive, and if you leave any of the fields blank, default values will be used. You can always change those values later. Set the entry point to gulpfile.js , and add information like the git repository if you wish.

npm init

Important: Preventing segmentation fault
To prevent triggering a segmentation fault when running Drush, we need to add a script to the package.json file that will remove all .info files from the node_modules folder. Each node package has it’s own .info file and it turns out that Drush thinks that they are all part of Drupal. Unfortunately, they are not in a format that Drush recognises and hence everything blows up badly. The .info files are not necessary for gulp to run properly so it’s safe to remove them.

If you had generated your package.json file by using npm init , locate the section called "scripts": , and replace the line:

"test": "echo \"Error: no test specified\" && exit 1"

with this line instead:

"postinstall": "find node_modules/ -name '*.info' -type f -delete"

Also, create a file called .npmrc in the root of your theme folder with the following contents:

unsafe-perm = true

References to this issue:

Plugins used

Here's the list of plug-ins needed and what they will be used for:

  • gulp - Still have to install gulp locally
  • gulp-sass - To compile Sass into CSS
  • gulp-autoprefixer - To add vendor-prefixes based on the latest specifications
  • browser-sync - To live-reload the browser
  • gulp-concat - To concatenate all your different Javascript files into one big one
  • gulp-babel - To write ES6 and transpile it so browsers can understand what you're writing
  • babel-preset-es2015 - Part of gulp-babel, but has to be installed as well

This is what the final package.json looks like:

{
  "name": "drupal-8-starter",
  "version": "1.0.0",
  "description": "gulp workflow for Drupal 8 theming",
  "main": "gulpfile.js",
  "devDependencies": {
    "babel-preset-es2015": "^6.24.1",
    "browser-sync": "^2.18.13",
    "gulp": "^3.9.1",
    "gulp-autoprefixer": "^4.0.0",
    "gulp-concat": "^2.6.1",
    "gulp-babel": "^6.1.2",
    "gulp-sass": "^3.1.0"
  },
  "scripts": {
    "postinstall": "find node_modules/ -name '*.info' -type f -delete"
  },
  "author": "huijing <[email protected]>",
  "license": "ISC"
}

gulpfile.js setup

The Drupal 7 post goes into a lot more detail on creating tasks and what each of the tasks does, and installing stuff, and if that’s what you need, head on over there. This is simply the updated gulpfile.js for Drupal 8.

var gulp        = require('gulp'),
    browserSync = require('browser-sync'),
    sass        = require('gulp-sass'),
    prefix      = require('gulp-autoprefixer'),
    concat      = require('gulp-concat'),
    babel       = require('gulp-babel'),
    cp          = require('child_process');

/**
 * Launch the Server
 */
 gulp.task('browser-sync', ['sass', 'scripts'], function() {
    browserSync.init({
      // Change as required, also remember to set in theme settings
      proxy: "HOSTNAME.dev",
      port: 3000
    });
});

/**
 * @task sass
 * Compile files from scss
 */
gulp.task('sass', function () {
  return gulp.src('_scss/styles.scss')
  .pipe(sass())
  .pipe(prefix(['last 3 versions', '> 1%', 'ie 8'], { cascade: true }))
  .pipe(gulp.dest('css'))
  .pipe(browserSync.reload({stream:true}))
});

/**
 * @task scripts
 * Compile files from js
 */
gulp.task('scripts', function() {
  return gulp.src(['_js/*.js', '_js/custom.js'])
  .pipe(babel({
    presets: ['es2015']
  }))
  .pipe(concat('scripts.js'))
  .pipe(gulp.dest('js'))
  .pipe(browserSync.reload({stream:true}))
});

/**
 * @task clearcache
 * Clear all caches
 */
gulp.task('clearcache', function(done) {
  return cp.spawn('drush', ['cache-rebuild'], {stdio: 'inherit'})
  .on('close', done);
});

/**
 * @task reload
 * Refresh the page after clearing cache
 */
gulp.task('reload', ['clearcache'], function () {
  browserSync.reload();
});

/**
 * @task watch
 * Watch scss files for changes & recompile
 * Clear cache when Drupal related files are changed
 */
gulp.task('watch', function () {
  gulp.watch(['_scss/*.scss', '_scss/**/*.scss'], ['sass']);
  gulp.watch(['_js/*.js'], ['scripts']);
  gulp.watch(['templates/*.html.twig', '**/*.yml'], ['reload']);
});

/**
 * Default task, running just `gulp` will 
 * compile Sass files, launch BrowserSync, watch files.
 */
gulp.task('default', ['browser-sync', 'watch']);

Wrap-up

Porting an existing project up a version number is always a bit of a chore, but it always feels good when it’s done. Dear future self, one day you’ll look back at this post and be thankful you wrote it, because you almost never remember anything unless you error out big time. You’re welcome.