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.
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.
❗ 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:
- How do I prevent Drupal raising a segmentation fault when using a Node.js theming workflow?
- Drupal Drush Segmentation Fault 11 Error: Avoiding the Rabbit Hole (This one’s a good read)
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 <1461498+huijing@users.noreply.github.com>",
"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.