Skip to content

Instantly share code, notes, and snippets.

@aaronwaldon
Last active March 24, 2023 14:25
Show Gist options
  • Save aaronwaldon/ef06dc31818ade807b4e33149b5c5340 to your computer and use it in GitHub Desktop.
Save aaronwaldon/ef06dc31818ade807b4e33149b5c5340 to your computer and use it in GitHub Desktop.
How to set up Gulp for Craft CMS. Includes SASS compilation and minification, JavaScript minification, livereloading, and browser sync.

How to set up Gulp with a Craft CMS project

I freaking love working with technologies like Gulp, and wanted to share how to get my current Craft front-end workflow set up. With a few tweaks, this can also be used with virtually any other sites (I've used it with Laravel, static sites, ExpressionEngine, etc).

Project Directory Structure

  • project root/
    • craft/
      • templates/
        • (your craft template files)
    • public/ (web root)
      • assets/
        • styles/
        • js/
        • images/
    • source/
      • js/
        • combined/ (these files will be collectively combined and minified and placed in the public/assets/js/ folder as scripts.js and scripts.min.js) libs/ (useful for things like jquery) * (various .js files) plugins/ (useful for things that depend on libs, like plugins) * (various .js files) site.js (this is where your hand-coded js will go)
        • individual/ (these files will be individually minified and combined and placed in the public/assets/js/ folder)
          • (various .js files)
      • scss/ (these files will be processed and places in the public/assets/styles folder)
        • (various .scss files)
        • styles.scss
    • gulpfile.js (download and include)
    • package.json
    • gulp.png (optional)

Installation

Install Node and Gulp (once per machine)

  • If Node is not yet installed on the machine, it will need to be installed

Install the node dependencies for the project (once per project, per machine)

  • If a package.json file already exists with list of all of the dependencies that Gulp will need for the project, then you can simply switch to the project root directory in the site terminal and run the command npm install. This only needs to happen once so that the dependencies can be downloaded for the project.
  • If a package.json file does not exist, switch to the project root directory for the site in a terminal. For this configuration, you can install gulp and the needed dependencies using the command npm install browser-sync gulp gulp-autoprefixer gulp-concat gulp-cssnano gulp-livereload gulp-newer gulp-notify gulp-plumber gulp-rename gulp-sass gulp-size gulp-sourcemaps gulp-uglify gulp-watch lazypipe node-sass --save-dev. You only need to do this once so that the dependencies can be downloaded for the project.

Add the gulp.js file (once per project)

Copy the gulp.js file (below) to the base of your site. The provided gulp.js file is set up to work with the following project directory structure.

  • Change 'Your Site Name' to the name of your site.
  • Change yoursite.dev to the domain name that browserSync will proxy.

Add .gitignore rules (once per project)

If using Git, be sure to add the .gitignore rules to your site's .gitignore file

Run Gulp (every time you want to use it)

  • To just compile the scripts and SCSS one time, simply run the command gulp.
  • To watch the templates, .scss, and .js files for changes, and to automatically compile and minify the relevant files, run the command gulp serve-lr. If the livereload browser extensions are installed and enabled, the browser will automatically refresh when any changes are made.
  • Additionally, you can run the command gulp serve-bs to run browser sync. It will also compile and minify the relevant files and reload the browser. Additionally, it will allow browsers to sync their scrolling and clicks. Also useful for testing the site on local network mobile devices.

Notification image (optional)

Feel free to use an icon for the project notifications. Simply place a png image named gulp.png file in the project root, and the script will use it in the notifications. You can optionally download and include this generic gulp image.

// -------------------- Configuration Settings --------------------
var config = {};
//basics
config.siteName = 'Your Site Name';
config.proxyDomain = 'yoursite.dev';
//source directory
config.src = 'source/';
//destinations
config.dest = 'public/assets/';
config.destJS = config.dest + 'js';
config.destCSS = config.dest + 'styles';
//globs
config.globs = {
scss : config.src + 'scss/**/*.scss',
js : {
individual : config.src + 'js/individual/**/*.js',
combined : [
config.src + 'js/combined/libs/*.js',
config.src + 'js/combined/plugins/*.js',
config.src + 'js/combined/pluginSubs/*.js',
config.src + 'js/combined/site/*.js',
config.src + 'js/combined/site.js'
]
},
watched : [
config.src + 'craft/templates/**/*',
config.destJS + '/**/*.min.js',
config.destCSS + '/**/*.min.css'
]
};
//browser sync
config.browserSync = {
files: config.globs.watched,
proxy: config.proxyDomain
};
// -------------------- Require Statements --------------------
var gulp = require('gulp'),
autoprefixer = require('gulp-autoprefixer'),
concat = require('gulp-concat'),
livereload = require('gulp-livereload'),
browserSync = require('browser-sync').create(),
newer = require('gulp-newer'),
notify = require('gulp-notify'),
plumber = require('gulp-plumber'),
rename = require('gulp-rename'),
sass = require('gulp-sass'),
size = require('gulp-size'),
uglify = require('gulp-uglify'),
watch = require('gulp-watch'),
path = require('path'),
cssnano = require('gulp-cssnano'),
sourcemaps = require('gulp-sourcemaps'),
lazypipe = require('lazypipe'),
fs = require('fs');
// -------------------- Notification Icon Detection --------------------
/**
* Checks to see if a file exists.
*
* @param filePath
* @returns {*}
*/
function fileExists(filePath)
{
try {
return fs.statSync(filePath).isFile();
} catch (err) {
return false;
}
}
var iconPath = path.join(__dirname, 'gulp.png');
var icon = fileExists( iconPath ) ? iconPath : null;
// -------------------- Plumber Error Handler --------------------
var plumberErrorHandler = function(err) {
console.log( 'plumber error! "' + err.message + '"' );
notify.onError({
title: config.siteName,
message: "Error: <%= err.message %>",
sound: 'Pop'
});
this.emit('end');
};
// -------------------- Processors --------------------
//individual scripts (not combined)
var jsIndividualScripts = lazypipe()
.pipe(plumber, {errorHandler: plumberErrorHandler})
.pipe(newer, { dest: config.destJS, ext: '.min.js' })
.pipe(gulp.dest, config.destJS)
.pipe(size, {showFiles: true})
.pipe(uglify)
.pipe(rename, { suffix: '.min' })
.pipe(gulp.dest, config.destJS)
.pipe(size, {showFiles: true});
//combined scripts
var jsCombinedScripts = lazypipe()
.pipe(plumber, {errorHandler: plumberErrorHandler})
.pipe(newer, config.dest + 'js/scripts.min.js')
.pipe(concat, 'scripts.js')
.pipe(gulp.dest, config.destJS)
.pipe(size, {showFiles: true})
.pipe(uglify)
.pipe(rename, { suffix: '.min' })
.pipe(gulp.dest, config.destJS)
.pipe(size, {showFiles: true});
//scss compiling
var scssProcessing = lazypipe()
.pipe(plumber, {errorHandler: plumberErrorHandler})
.pipe(sass, {outputStyle: ':compact'})
.pipe(autoprefixer, 'last 2 version')
.pipe(gulp.dest, config.destCSS)
.pipe(size, {showFiles: true})
.pipe(rename, { suffix: '.min' })
.pipe(sourcemaps.init)
.pipe(cssnano)
.pipe(sourcemaps.write, '.')
.pipe(gulp.dest, config.destCSS)
.pipe(size, {showFiles: true});
// -------------------- Tasks --------------------
//styles task
gulp.task('styles', function() {
if ( browserSync.active ) {
return gulp.src(config.globs.scss)
.pipe(scssProcessing())
.pipe(browserSync.reload({stream:true}));
}
return gulp.src(config.globs.scss).pipe(scssProcessing());
});
//scripts individual task
gulp.task('scripts-individual', function() {
return gulp.src(config.globs.js.individual).pipe(jsIndividualScripts());
});
//scripts combined task
gulp.task('scripts-combined', function() {
return gulp.src(config.globs.js.combined).pipe(jsCombinedScripts());
});
//watch task
gulp.task('live', function() {
//watch all .scss files
gulp.watch(config.globs.scss, ['styles']);
//watch each individual .js file
watch(config.globs.js.individual).pipe(jsIndividualScripts());
//watch all combined .js files
gulp.watch(config.globs.js.combined, ['scripts-combined']);
});
//default task - one time styles and scripts
gulp.task('default', ['styles', 'scripts-individual', 'scripts-combined']);
//start browser-sync server
gulp.task('serve-bs', ['live'], function() {
browserSync.init(config.browserSync)
});
//start livereload
gulp.task('serve-lr', ['live'], function() {
livereload.listen();
//watch for changes on transpired templates, css, and js files
gulp.watch(config.globs.watched, function(event) {
gulp.src(event.path)
.pipe(plumber({errorHandler: plumberErrorHandler}))
.pipe(livereload())
.pipe(notify({
title: config.siteName,
message: event.type + ': ' + event.path.replace(__dirname, '').replace(/\\/g, '/') + ' was reloaded'
//,sound: 'Pop'
, icon: icon
})
);
});
});
#node files
/node_modules/*
{
"private": true,
"devDependencies": {
"browser-sync": "^2.13.0",
"gulp": "^3.9.1",
"gulp-autoprefixer": "^3.1.0",
"gulp-concat": "^2.6.0",
"gulp-cssnano": "^2.1.2",
"gulp-livereload": "^3.8.1",
"gulp-newer": "^1.2.0",
"gulp-notify": "^2.2.0",
"gulp-plumber": "^1.1.0",
"gulp-rename": "^1.2.2",
"gulp-sass": "^2.3.2",
"gulp-size": "^2.1.0",
"gulp-sourcemaps": "^1.6.0",
"gulp-uglify": "^1.5.4",
"gulp-watch": "^4.3.9",
"lazypipe": "^1.0.1",
"node-sass": "^3.8.0"
}
}
@georgefeast
Copy link

Hi, thanks for this! One thing to note - in your directory structure guide above the scss directory should be inside the source directory in order for the gulp task to process the scss files

@aaronwaldon
Copy link
Author

@georgefeast You are correct. That was a typo. I just indented those list items. Thanks!

Copy link

ghost commented Feb 26, 2017

Thanks for sharing this -- much appreciated.

Would you mind also sharing how you use the different folders within the js/combined folder? In my particular case, I have a bunch of js files - some of which are unique to particular pages/templates, others are for the whole site and some I may or may not use in a particular project.

I'm wondering how to organize my source folder so that only those files I need are included (and only on the appropriate pages).

Thanks again.

All the best,

Moshe

@EarthlingDavey
Copy link

EarthlingDavey commented Aug 17, 2017

Hi, thanks for sharing your build tasks! This is an awesome start for a craft newbie. One tip for browserSync, it will only work if you call it directly after .pipe(gulp.dest, config.destCSS). So I've split scssProcessing into 2 parts... now browserSync is working, hope this helps others :)

//scss compiling
var scssProcessing = lazypipe()
  .pipe(plumber, { errorHandler: plumberErrorHandler })
  .pipe(sass, { 
    includePaths: config.sassPaths,
    outputStyle: ':compact' 
  })
  .pipe(autoprefixer, 'last 2 version')
  .pipe(gulp.dest, config.destCSS);
  
//css compiling
var cssProcessing = lazypipe()
  .pipe(plumber, { errorHandler: plumberErrorHandler })
  .pipe(size, { showFiles: true })
  .pipe(rename, { suffix: '.min' })
  .pipe(sourcemaps.init)
  .pipe(cssnano)
  .pipe(sourcemaps.write, '.')
  .pipe(gulp.dest, config.destCSS)
  .pipe(size, { showFiles: true });

// -------------------- Tasks --------------------
//styles task
gulp.task('styles', function () {
  if (browserSync.active) {
    return gulp.src(config.globs.scss)
      .pipe(scssProcessing())
      .pipe(browserSync.stream({ stream: true }))
      .pipe(cssProcessing());
  }
  return gulp.src(config.globs.scss).pipe(scssProcessing());
});

@antoniofelizardo
Copy link

antoniofelizardo commented Sep 14, 2017

Thanks for this! Just taking my first steps with Craft so have a question: I managed to get this working but only if I run it while I run MAMP and Sequel Pro for the db management (I'm on a Mac).

If i just run 'gulp serve-bs', the browser doesn't show anything, it's just stuck trying to open localhost:3000. So then if I run MAMP and open the website through it, it works and I can see the browser reloading as I make changes to the files. For now this is fine but I'm sure this is not the intended workflow. Would you please shed a light on the right set up? Thanks in advance.

@egivensjr
Copy link

Thank you so much for this! It saved me so much time in creating one from scratch. A+++

@MindSculpt
Copy link

MindSculpt commented Jan 14, 2018

This a great workflow, thanks for sharing. Line 30 of gulpfile.js has the wrong path to templates if you're using Craft 2. Since they are outside of the source folder, the Craft template watch path should just be 'craft/templates/**/*' here. Otherwise they aren't watched and changes don't live reload.

If you're using Craft 3, that same path should be 'templates/**/*'.

@coffeeneed
Copy link

@MindSculpt Thanks for the tip, but this doesn't work for me. Line 30 clearly states:

config.src + 'templates/**/*',

However, changes in craft\templates\index.html doesn't affect BrowserSync and browser is not automatically reloaded.

SCSS compiling and automatic reload does work. Any idea on what's going on?

My gulpfile.js:

// -------------------- Configuration Settings --------------------
var config = {};

//basics
config.siteName = 'Digis';
config.proxyDomain = 'http://localhost/craft/web/';

//source directory
config.src = 'source/';

//destinations
config.dest = 'public/assets/';
config.destJS = config.dest + 'js';
config.destCSS = config.dest + 'styles';

//globs
config.globs = {
  scss : config.src + 'scss/**/*.scss',
  js : {
    individual : config.src + 'js/individual/**/*.js',
    combined : [
      config.src + 'js/combined/libs/*.js',
      config.src + 'js/combined/plugins/*.js',
      config.src + 'js/combined/pluginSubs/*.js',
      config.src + 'js/combined/site/*.js',
      config.src + 'js/combined/site.js'
    ]
  },
  watched : [
    config.src + 'templates/**/*',
    config.destJS + '/**/*.min.js',
    config.destCSS + '/**/*.min.css'
  ]
};

//browser sync
config.browserSync = {
  files: config.globs.watched,
  proxy: config.proxyDomain
};

// -------------------- Require Statements --------------------
var gulp             = require('gulp'),
  autoprefixer     = require('gulp-autoprefixer'),
  concat           = require('gulp-concat'),
  livereload       = require('gulp-livereload'),
  browserSync      = require('browser-sync').create(),
  newer            = require('gulp-newer'),
  notify           = require('gulp-notify'),
  plumber          = require('gulp-plumber'),
  rename           = require('gulp-rename'),
  sass             = require('gulp-sass'),
  size             = require('gulp-size'),
  uglify           = require('gulp-uglify'),
  watch            = require('gulp-watch'),
  path             = require('path'),
  cssnano          = require('gulp-cssnano'),
  sourcemaps       = require('gulp-sourcemaps'),
  lazypipe         = require('lazypipe'),
  fs               = require('fs');

// -------------------- Notification Icon Detection --------------------
/**
 * Checks to see if a file exists.
 *
 * @param filePath
 * @returns {*}
 */
function fileExists(filePath)
{
  try {
    return fs.statSync(filePath).isFile();
  } catch (err) {
    return false;
  }
}

var iconPath = path.join(__dirname, 'gulp.png');
var icon = fileExists( iconPath ) ? iconPath : null;

// -------------------- Plumber Error Handler --------------------
var plumberErrorHandler = function(err) {
  console.log( 'plumber error! "' + err.message + '"' );
  notify.onError({
    title: config.siteName,
    message: "Error: <%= err.message %>",
    sound: 'Pop'
  });
  this.emit('end');
};

// -------------------- Processors --------------------
//individual scripts (not combined)
var jsIndividualScripts = lazypipe()
  .pipe(plumber, {errorHandler: plumberErrorHandler})
  .pipe(newer, { dest: config.destJS, ext: '.min.js' })
  .pipe(gulp.dest, config.destJS)
  .pipe(size, {showFiles: true})
  .pipe(uglify)
  .pipe(rename, { suffix: '.min' })
  .pipe(gulp.dest, config.destJS)
  .pipe(size, {showFiles: true});

//combined scripts
var jsCombinedScripts = lazypipe()
  .pipe(plumber, {errorHandler: plumberErrorHandler})
  .pipe(newer, config.dest + 'js/scripts.min.js')
  .pipe(concat, 'scripts.js')
  .pipe(gulp.dest, config.destJS)
  .pipe(size, {showFiles: true})
  .pipe(uglify)
  .pipe(rename, { suffix: '.min' })
  .pipe(gulp.dest, config.destJS)
  .pipe(size, {showFiles: true});

//scss compiling
var scssProcessing = lazypipe()
  .pipe(plumber, {errorHandler: plumberErrorHandler})
  .pipe(sass, {outputStyle: ':compact'})
  .pipe(autoprefixer, 'last 2 version')
  .pipe(gulp.dest, config.destCSS)
  .pipe(size, {showFiles: true})
  .pipe(rename, { suffix: '.min' })
  .pipe(sourcemaps.init)
  .pipe(cssnano)
  .pipe(sourcemaps.write, '.')
  .pipe(gulp.dest, config.destCSS)
  .pipe(size, {showFiles: true});

// -------------------- Tasks --------------------
//styles task
gulp.task('styles', function() {
  if ( browserSync.active ) {
    return gulp.src(config.globs.scss)
      .pipe(scssProcessing())
      .pipe(browserSync.reload({stream:true}));
  }
  return gulp.src(config.globs.scss).pipe(scssProcessing());
});

//scripts individual task
gulp.task('scripts-individual', function() {
  return gulp.src(config.globs.js.individual).pipe(jsIndividualScripts());
});

//scripts combined task
gulp.task('scripts-combined', function() {
  return gulp.src(config.globs.js.combined).pipe(jsCombinedScripts());
});

//watch task
gulp.task('live', function() {
  //watch all .scss files
  gulp.watch(config.globs.scss, ['styles']);

  //watch each individual .js file
  watch(config.globs.js.individual).pipe(jsIndividualScripts());

  //watch all combined .js files
  gulp.watch(config.globs.js.combined, ['scripts-combined']);
});

//default task - one time styles and scripts
gulp.task('default', ['styles', 'scripts-individual', 'scripts-combined']);

//start browser-sync server
gulp.task('serve-bs', ['live'], function() {
  browserSync.init(config.browserSync)
});

//start livereload
gulp.task('serve-lr', ['live'], function() {
  livereload.listen();

  //watch for changes on transpired templates, css, and js files
  gulp.watch(config.globs.watched, function(event) {
    gulp.src(event.path)
      .pipe(plumber({errorHandler: plumberErrorHandler}))
      .pipe(livereload())
      .pipe(notify({
          title: config.siteName,
          message: event.type + ': ' + event.path.replace(__dirname, '').replace(/\\/g, '/') + ' was reloaded'
          //,sound: 'Pop'
          , icon: icon
        })
      );
  });
});

@Michael-Reich
Copy link

I think its because of your Proxydomain. Just do http://localhost.dev

@danleecreates
Copy link

danleecreates commented Dec 14, 2018

I get the same template problem. SCSS and JS work fine and it even picks up that the templates has been changed in the notification but it doesn't reload the browser.

// -------------------- Configuration Settings --------------------
var config = {};

//basics
config.siteName = 'Franklin';
config.proxyDomain = 'https://franklin.test';

//source directory
config.src = 'src';

//destinations
config.dest = 'web';
config.templates = 'templates';
config.destJS = config.dest + '/assets/js';
config.destCSS = config.dest + '/assets/css';
config.destFonts = config.dest + '/assets/fonts';
config.destPWA = config.dest;

//globs
config.globs = {
	scss : config.src + '/scss/**/*.scss',
	js : {
		individual : config.src + '/js/individual/**/*.js',
		combined : [
			config.src + '/js/combined/libs/*.js',
			config.src + '/js/combined/plugins/*.js',
			config.src + '/js/combined/pluginSubs/*.js',
			config.src + '/js/combined/site/*.js',
			config.src + '/js/combined/site.js'
		]
	},
	fonts : config.src + '/fonts/**/*',
	pwa : config.src + '/pwa/**/*',
	watched : [
		config.templates + '/**/*',
		config.destJS + '/**/*.min.js',
		config.destCSS + '/**/*.min.css',
		config.destFonts + '/**/*',
		config.destPWA + '/**/*'
	]
};

//browser sync
config.browserSync = {
	files: config.globs.watched,
	proxy: config.proxyDomain
};

// -------------------- Require Statements --------------------
var gulp           = require('gulp'),
	autoprefixer     = require('gulp-autoprefixer'),
	concat           = require('gulp-concat'),
	livereload       = require('gulp-livereload'),
	browserSync      = require('browser-sync').create(),
	newer            = require('gulp-newer'),
	notify           = require('gulp-notify'),
	plumber          = require('gulp-plumber'),
	rename           = require('gulp-rename'),
	sass             = require('gulp-sass'),
	size             = require('gulp-size'),
	uglify           = require('gulp-uglify'),
	watch            = require('gulp-watch'),
	path             = require('path'),
	cssnano          = require('gulp-cssnano'),
	sourcemaps       = require('gulp-sourcemaps'),
	lazypipe         = require('lazypipe'),
	fs               = require('fs');

// -------------------- Notification Icon Detection --------------------
/**
 * Checks to see if a file exists.
 *
 * @param filePath
 * @returns {*}
 */
function fileExists(filePath)
{
	try {
		return fs.statSync(filePath).isFile();
	} catch (err) {
		return false;
	}
}

var iconPath = path.join(__dirname, 'gulp.png');
var icon = fileExists( iconPath ) ? iconPath : null;

// -------------------- Plumber Error Handler --------------------
var plumberErrorHandler = function(err) {
	console.log( 'plumber error! "' + err.message + '"' );
	notify.onError({
		title: config.siteName,
		message: "Dang! Error: <%= err.message %>",
		sound: 'Blow'
	});
	this.emit('end');
};

// -------------------- Processors --------------------
//individual scripts (not combined)
var jsIndividualScripts = lazypipe()
	.pipe(plumber, {errorHandler: plumberErrorHandler})
	.pipe(newer, { dest: config.destJS, ext: '.min.js' })
	.pipe(gulp.dest, config.destJS)
	.pipe(size, {showFiles: true})
	.pipe(uglify)
	.pipe(rename, { suffix: '.min' })
	.pipe(gulp.dest, config.destJS)
	.pipe(size, {showFiles: true});

//combined scripts
var jsCombinedScripts = lazypipe()
	.pipe(plumber, {errorHandler: plumberErrorHandler})
	.pipe(newer, config.dest + 'js/scripts.min.js')
	.pipe(concat, 'scripts.js')
	.pipe(gulp.dest, config.destJS)
	.pipe(size, {showFiles: true})
	.pipe(uglify)
	.pipe(rename, { suffix: '.min' })
	.pipe(gulp.dest, config.destJS)
	.pipe(size, {showFiles: true});

//scss compiling
var scssProcessing = lazypipe()
	.pipe(plumber, {errorHandler: plumberErrorHandler})
	.pipe(sass, {outputStyle: ':compact'})
	.pipe(autoprefixer, 'last 2 version')
	.pipe(gulp.dest, config.destCSS)
	.pipe(size, {showFiles: true})
	.pipe(rename, { suffix: '.min' })
	.pipe(sourcemaps.init)
	.pipe(cssnano)
	.pipe(sourcemaps.write, '.')
	.pipe(gulp.dest, config.destCSS)
	.pipe(size, {showFiles: true});

//fonts compiling
var fontsProcessing = lazypipe()
	.pipe(plumber, {errorHandler: plumberErrorHandler})
	.pipe(gulp.dest, config.destFonts);

//pwa compiling
var pwaProcessing = lazypipe()
	.pipe(plumber, { errorHandler: plumberErrorHandler })
	.pipe(gulp.dest, config.destPWA);


// -------------------- Tasks --------------------

//styles task
gulp.task('styles', function() {
	if ( browserSync.active ) {
		return gulp.src(config.globs.scss)
			.pipe(scssProcessing())
			.pipe(browserSync.reload({stream:true}));
	}
	return gulp.src(config.globs.scss).pipe(scssProcessing());
});

//scripts individual task
gulp.task('scripts-individual', function() {
	return gulp.src(config.globs.js.individual).pipe(jsIndividualScripts());
});

//scripts combined task
gulp.task('scripts-combined', function() {
	return gulp.src(config.globs.js.combined).pipe(jsCombinedScripts());
});

//fonts task
gulp.task('fonts', function() {
	return gulp.src(config.globs.fonts).pipe(fontsProcessing());
});

//pwa task
gulp.task('pwa', function () {
	return gulp.src(config.globs.pwa).pipe(pwaProcessing());
});

//watch task
gulp.task('live', function() {

	//watch all .scss files
	gulp.watch(config.globs.scss, ['styles']);

	//watch each individual .js file
	watch(config.globs.js.individual).pipe(jsIndividualScripts());

	//watch all combined .js files
	gulp.watch(config.globs.js.combined, ['scripts-combined']);

});

//default task - one time styles and scripts
gulp.task('default', ['styles', 'scripts-individual', 'scripts-combined', 'fonts', 'pwa']);

//start browser-sync server
gulp.task('serve-bs', ['live'], function() {
	browserSync.init(config.browserSync)
});

//start livereload
gulp.task('watch', ['live'], function() {
	livereload.listen();

	//watch for changes on transpired templates, css, and js files
	gulp.watch(config.globs.watched, function(event) {
		gulp.src(event.path)
			.pipe(plumber({errorHandler: plumberErrorHandler}))
			.pipe(livereload())
			.pipe(notify({
					title: config.siteName,
					message: event.type + ': ' + event.path.replace(__dirname, '').replace(/\\/g, '/') + ' was reloaded yo!',
					sound: 'Hero',
					icon: icon
				})
			);
	});
});

@pixeldeluxe
Copy link

Hi Aaron,
Thanks for your great script. We tried to convert your script to Gulp 4, but we encountered some errors.
Here I paste our code, but we get some errors.

Thanks in advance.

Cheers,
Martijn

The error:
iMac-van-Gebruiker:boilerplate-craft3 user$ gulp serve-lr [16:49:34] Using gulpfile /Applications/MAMP/htdocs/boilerplate-craft3/gulpfile.js [16:49:34] Starting 'serve-lr'... [16:49:34] Starting 'live'... [16:50:54] Starting 'build-styles'... [16:50:55] main.css 413.17 kB [16:50:59] main.min.css.map 550.92 kB [16:50:59] main.min.css 251.13 kB [16:50:59] all files 802.05 kB [16:50:59] Starting '<anonymous>'... [16:50:59] '<anonymous>' errored after 1.22 ms [16:50:59] Error: Invalid glob argument: undefined at Gulp.src (/Applications/MAMP/htdocs/boilerplate-craft3/node_modules/vinyl-fs/lib/src/index.js:20:11) at /Applications/MAMP/htdocs/boilerplate-craft3/gulpfile.js:159:15 at bound (domain.js:280:14) at runBound (domain.js:293:12) at asyncRunner (/Applications/MAMP/htdocs/boilerplate-craft3/node_modules/async-done/index.js:55:18) at _combinedTickCallback (internal/process/next_tick.js:73:7) at process._tickDomainCallback (internal/process/next_tick.js:128:9)

Our script:
`// -------------------- Configuration Settings --------------------
var config = {};

//basics
config.siteName = 'Boilerplate Craft3';
config.proxyDomain = 'boilerplate-craft3.local';

//source directory
config.src = 'src/';

//destinations
config.dest = 'public_html/public/';
config.destJS = config.dest + 'js';
config.destCSS = config.dest + 'css';

//globs
config.globs = {
scss : config.src + 'scss//*.scss',
js : {
main : config.src + "js/individual/main.js",
individual : config.src + 'js/individual/
/.js',
includes : config.src + 'js/includes/**/
.js'
},
watched : [
config.destJS + '//*.min.js',
config.destCSS + '/
/*.min.css'
]
};

//browser sync
config.browserSync = {
files: config.globs.watched,
proxy: config.proxyDomain,
open: false
};

// -------------------- Require Statements --------------------
var gulp = require('gulp'),
autoprefixer = require('gulp-autoprefixer'),
concat = require('gulp-concat'),
livereload = require('gulp-livereload'),
browserSync = require('browser-sync').create(),
newer = require('gulp-newer'),
include = require('gulp-include');
notify = require('gulp-notify'),
plumber = require('gulp-plumber'),
rename = require('gulp-rename'),
scss = require('gulp-sass'),
size = require('gulp-size'),
uglify = require('gulp-uglify'),
watch = require('gulp-watch'),
path = require('path'),
cssnano = require('gulp-cssnano'),
sourcemaps = require('gulp-sourcemaps'),
lazypipe = require('lazypipe'),
fs = require('fs'),
del = require("del");

// -------------------- Notification Icon Detection --------------------
/**

  • Checks to see if a file exists.
  • @param filePath
  • @returns {*}
    */
    function fileExists(filePath)
    {
    try {
    return fs.statSync(filePath).isFile();
    } catch (err) {
    return false;
    }
    }

var iconPath = path.join(__dirname, 'gulp.png');
var icon = fileExists( iconPath ) ? iconPath : null;

// -------------------- Plumber Error Handler --------------------
var plumberErrorHandler = function(err) {
console.log( 'plumber error! "' + err.message + '"' );
notify.onError({
title: config.siteName,
message: "Error: <%= err.message %>",
sound: 'Pop'
});
this.emit('end');
};

// -------------------- Processors --------------------
// Javascript compiling
var jsIndividualScripts = lazypipe()
.pipe(plumber, {errorHandler: plumberErrorHandler})
.pipe(include)
.pipe(newer, { dest: config.destJS, ext: '.min.js' })
.pipe(gulp.dest, config.destJS)
.pipe(size, {showFiles: true})
.pipe(uglify)
.pipe(rename, { suffix: '.min' })
.pipe(gulp.dest, config.destJS)
.pipe(size, {showFiles: true});

// Scss compiling
var scssProcessing = lazypipe()
.pipe(plumber, {errorHandler: plumberErrorHandler})
.pipe(scss, {outputStyle: ':compact'})
.pipe(autoprefixer, 'last 2 version')
.pipe(gulp.dest, config.destCSS)
.pipe(size, {showFiles: true})
.pipe(rename, { suffix: '.min' })
.pipe(sourcemaps.init)
.pipe(cssnano)
.pipe(sourcemaps.write, '.')
.pipe(gulp.dest, config.destCSS)
.pipe(size, {showFiles: true});

// -------------------- Tasks --------------------
// Build-styles task
gulp.task('build-styles', function() {
if (browserSync.active) {
return gulp.src(config.globs.scss)
.pipe(scssProcessing())
.pipe(browserSync.reload({stream:true}));
}

return gulp.src(config.globs.scss).pipe(scssProcessing());

});

// Build-js task
gulp.task('build-js', function() {
if (browserSync.active) {
return gulp.src(config.globs.scss)
.pipe(jsIndividualScripts())
.pipe(browserSync.reload({stream:true}));
}

return gulp.src(config.globs.js.main).pipe(jsIndividualScripts());

});

// Del js build files in public
gulp.task('del-js-build', function() {
return del([config.destJS + '/main.min.js', config.destJS + '/main.js']);
});

gulp.task('live', function() {
// Start livereload
livereload.listen();

// Watch all .scss files
gulp.watch(config.globs.scss, gulp.parallel(['build-styles']));

// Watch each individual .js file
watch(config.globs.js.individual).pipe(jsIndividualScripts());

// Watch included .js files and compile main.js on change
gulp.watch(config.globs.js.includes, gulp.parallel(['del-js-build', 'build-js']));

// Watch for changes on transpired css, and js files
gulp.watch(config.globs.watched, function(event) {
	return gulp.src(event.path)
		.pipe(plumber({errorHandler: plumberErrorHandler}))
		.pipe(livereload())
		.pipe(notify({
			title: config.siteName,
			message: event.type + ': ' + event.path.replace(__dirname, '').replace(/\\/g, '/') + ' was reloaded'
			,sound: 'Pop'
			, icon: icon
		})
	);
});

});

// Start browser-sync server
gulp.task('serve-bs', gulp.series(['live'], function() {
browserSync.init(config.browserSync);
}));

// Start livereload
gulp.task('serve-lr', gulp.series(['live']));`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment