Gulp is a powerful toolkit to help web developers save time by performing automated tasks on a per-project basis. It's often used to do front end tasks like:
- Spinning up a NodeJS server or automating deployment of files
- With BrowserSync you can refresh a localhost page when saving changes to a file
- Using pre-processors like Sass or LESS
- Optimizing and concatenating assets like CSS, JavaScript
- Expand HTML by including template engines like Handlebars
This is by no means all it does, but using Gulp to help with some basic tasks can really help speed up your daily development work-flow. Let's take a look at a minimal gulpfile.js file and setup some basic tasks that you would typically create when starting a new project.
Getting Started with Gulp.js
Step 1 - Install Node.js
The first thing to getting Gulp.js setup is to make sure you have Node.js installed with NPM (Node Package Manager). Click the link and follow the install instructions. Node is a JavaScript runtime that manages dependencies and does some heavy lifting for us while using Gulp.JS and other similar tools.
Step 2 - Verify Node Install
With Node installed now, you can type:
node -v
into your terminal/console and you should see the latest version of Node.js returned:
v6.3.1
Once you've installed Node.js, you can make sure you've got the very most recent version of npm using npm itself:
sudo npm install npm -g
On Windows, you can drop the “sudo” but you should run it as administrator. Running this update will give you the most recent stable version of npm, also supported by npm Inc. The -g on the end tells npm to install npm "globally" allowing you to use npm from a terminal / console anywhere on your machine (not just inside of a specific project folder).
Step 3 - Install Gulp.js
With npm ready to go, simply type:
npm install gulp-cli -g
npm install gulp -D
Now you have Gulp on your machine, installed globally, ready to go!
Adding Gulp to a new (or existing) Project
Now that Gulp.js is installed, you're ready to start automating some work-flow! Let's setup a package.json file for Node and a gulpfile.js for Gulp - both inside the root folder of your project.
Why do I need a package.json file?
NPM requires a package.json file to manage some basic info about your project and to also use as a place to save any dependancies you might need for your project (Twitter Bootstrap, or maybe jQuery, for example). By adding and saving dependancies, you can quickly and easily load project required items, or keep them up to date with the latest changes by a simple terminal command. You can learn more about package.json here.
Create package.json
Thankfully, Node helps you in the creation of this file. Navigate to the project folder inside your terminal / command prompt and type:
npm init
Node will ask you a bunch of questions, and then write a package.json for you. It attempts to make reasonable guesses about what you want things to be set to, and then writes a package.json file with the options you've selected.
If you already have a package.json file, it'll read that first, and default to the options in there. It is strictly additive, so it does not delete options from your package.json without a really good reason to do so.
Now with your package.json file generated you can start making that Gulp.js file!
Their are plenty of GulpJS articles online on how to make and formulate your gulp file. They do a great job and helped even me learn how to make and customize Gulp for my projects.
After playing around for a year or so I've come up with my boilerplate that I use for almost every front-end project.
Virtually(Creative) Default Gulp.js Boilerplate
I've pasted an example of one I personally use for a blank, full-scale single-page application. While it makes no assumptions about what you're going to use, I do assume a few things with this setup:
/app/ folder is your development folder and holds all the files of the SPA
- /app/ folder has a BrowserSync function to use during development and needs to be run to view.
/demo/ is created from /app/ and lets you test the build instructions
- /demo/ folder has a BrowserSync function to use during development and is auto-started after build
- Images are cached upon the first build, will only update if changes are made then after for faster builds
- Uses the awesome Rollbar Error reporting service to push errors to a Rollbar project, can be disabled.
- dependencies needed from Node are moved into /vendor/ folder
/dist/ folder is your PRODUCTION folder and is the production build of /app/
- /dist/ folder has a BrowserSync function to use during testing, it needs to be run to view.
- All JS/CSS files are concatenated into a single CSS and JS file called "main-bundle"
- Images are cached upon the first build, will only update if changes are made then after for faster builds
- Uses the Rollbar Error reporting service to push errors to a Rollbar project, can be disabled.
- dependencies needed from Node are moved into /vendor/ folder
/\*\*
\* Created by Alexander Romano on 02/05/2017.
\* Front-End DevOps - Automate using GulpJS
\*
\* A little dependant heavy, but not without purpose...
\*/
var gulp = require('gulp');
var less = require('gulp-less');
var syncDocs = require('browser-sync').create();
var browserSync = require('browser-sync').create();
var header = require('gulp-header');
var cleanCSS = require('gulp-clean-css');
var rename = require("gulp-rename");
var pkg = require('./package.json');
var merge = require('merge-stream');
var connect = require('gulp-connect');
var useref = require('gulp-useref');
var uglify = require('gulp-uglify');
var gulpIf = require('gulp-if');
var cssnano = require('gulp-cssnano');
var runSequence = require('run-sequence');
var imagemin = require('gulp-imagemin');
var cache = require('gulp-cache');
var del = require('del');
var sourcemaps = require('gulp-sourcemaps');
var rollbar = require('gulp-rollbar');
var rollbarToken = "###";
var rollbarVersion = "###";
var rollbarURL = "https://projectname.netlify.com";
var banner = \['/\*!\\n',
' \* Virtually(Creative) - <%= pkg.title %> v<%= pkg.version %> (<%= pkg.homepage %>)\\n',
' \* Copyright ' + (new Date()).getFullYear(), ' <%= pkg.author %>\\n',
' \* Licensed under <%= pkg.license.type %> (<%= pkg.license.url %>)\\n',
' \*/\\n',
''
\].join('');
/\*\*
\* BROWSER SYNC
\*/
gulp.task('browserSync:dev', function () {
browserSync.init({
server: {
baseDir: 'app'
},
});
});
gulp.task('browserSync:demo', function () {
browserSync.init({
server: {
baseDir: 'demo'
},
});
});
/\*\*
\* USEREF
\*/
gulp.task('useref:dev', function () {
return gulp.src('app/\*.html')
.pipe(useref())
.pipe(sourcemaps.init())
.pipe(sourcemaps.identityMap())
.pipe(gulpIf('app/js/\*.js', uglify()))
.pipe(rollbar({
accessToken: rollbarToken,
version: rollbarVersion,
sourceMappingURLPrefix: 'http://localhost:3000'
}))
.pipe(gulpIf('app/css/\*.css', cssnano()))
.pipe(gulp.dest('demo/'));
});
gulp.task('useref:prod', function () {
return gulp.src('app/\*.html')
.pipe(useref())
.pipe(sourcemaps.init())
.pipe(sourcemaps.identityMap())
.pipe(gulpIf('app/js/\*.js', uglify()))
.pipe(rollbar({
accessToken: rollbarToken,
version: rollbarVersion,
sourceMappingURLPrefix: rollbarURL
}))
.pipe(gulpIf('app/css/\*.css', cssnano()))
.pipe(gulp.dest('dist/'));
});
/\*\*
\* IMAGE OPTIMIZATION & CACHING
\*/
gulp.task('images:dev', function () {
return gulp.src('app/img/\*\*/\*.+(png|jpg|jpeg|gif|svg)')
.pipe(cache(imagemin({
interlaced: true
})))
.pipe(gulp.dest('demo/img'));
});
gulp.task('images:prod', function () {
return gulp.src('app/img/\*\*/\*.+(png|jpg|jpeg|gif|svg)')
.pipe(cache(imagemin({
interlaced: true
})))
.pipe(gulp.dest('dist/img'));
});
/\*\*
\* ASSET COPIES
\* FONTS & VIDEOS
\*/
gulp.task('fonts:dev', function () {
return gulp.src('app/fonts/\*\*/\*')
.pipe(gulp.dest('demo/fonts'));
});
gulp.task('fonts:prod', function () {
return gulp.src('app/fonts/\*\*/\*')
.pipe(gulp.dest('dist/fonts'));
});
gulp.task('vids:dev', function () {
return gulp.src('app/vids/\*\*/\*')
.pipe(gulp.dest('demo/vids'));
});
gulp.task('vids:prod', function () {
return gulp.src('app/vids/\*\*/\*')
.pipe(gulp.dest('dist/vids'));
});
/\*\*
\* DEPENDANTS (NPM / BOWER)
\*/
/\*\*
\* CLEAN (Delete)
\*/
gulp.task('clean:prod', function () {
return del.sync('dist');
});
gulp.task('clean:demo', function () {
return del.sync('demo');
});
/\*\*
\* MAIN BUILD TASKS
\*/
gulp.task('dev', \['browserSync:dev'\], function () {
gulp.watch('app/\*.html', browserSync.reload);
gulp.watch('app/js/\*\*/\*.js', browserSync.reload);
gulp.watch('app/css/\*\*/\*.css', browserSync.reload);
});
gulp.task('demo', \['browserSync:demo'\], function () {
gulp.watch('demo/\*.html', browserSync.reload);
gulp.watch('demo/js/\*\*/\*.js', browserSync.reload);
gulp.watch('demo/css/\*\*/\*.css', browserSync.reload);
});
gulp.task('demo:build', function (callback) {
runSequence('clean:demo',
\['useref:dev', 'fonts:dev', 'vids:dev', 'default'\], 'images:dev', 'demo',
callback
);
});
gulp.task('prod:build', function (callback) {
runSequence('clean:prod',
\['useref:prod', 'fonts:prod', 'vids:prod', 'default'\], 'images:prod',
callback
);
});
gulp.task('default', \['prod:build'\]);
gulp.task('build', function (callback) {
runSequence('prod:build', callback);
});
gulp.task('less', function () {
return gulp.src('app/less/\*.less')
.pipe(less())
.pipe(header(banner, {
pkg: pkg
}))
.pipe(gulp.dest('css'))
.pipe(browserSync.reload({
stream: true
}));
});
gulp.task('minify-css', function () {
var vCBP = gulp.src('app/css/\*.css')
.pipe(cleanCSS({
compatibility: 'ie8'
}))
.pipe(rename({
suffix: '.min'
}))
.pipe(gulp.dest('css'))
.pipe(browserSync.reload({
stream: true
}));
var vCVS = gulp.src('vendor/\*/css/\*.css')
.pipe(cleanCSS({
compatibility: 'ie8'
}))
.pipe(rename({
suffix: '.min'
}))
.pipe(gulp.dest('css'))
.pipe(browserSync.reload({
stream: true
}));
return merge(vCBP, vCVS);
});
gulp.task('minify-js', function () {
var vCBPjs = gulp.src('app/js/\*.js')
.pipe(uglify())
.pipe(header(banner, {
pkg: pkg
}))
.pipe(rename({
suffix: '.min'
}))
.pipe(gulp.dest('js'))
.pipe(browserSync.reload({
stream: true
}));
var vCVendorjs = gulp.src('vendor/\*/scripts/\*.js')
.pipe(uglify())
.pipe(header(banner, {
pkg: pkg
}))
.pipe(rename({
suffix: '.min'
}))
.pipe(gulp.dest('js'))
.pipe(browserSync.reload({
stream: true
}));
return merge(vCBPjs, vCVendorjs);
});
gulp.task('bootstrap', function () {
return gulp.src(\['node\_modules/bootstrap/dist/\*\*/\*', '!\*\*/npm.js', '!\*\*/bootstrap-theme.\*', '!\*\*/\*.map'\])
.pipe(gulp.dest('vendor/bootstrap'));
});
gulp.task('jquery', function () {
return gulp.src(\['node\_modules/jquery/dist/jquery.js', 'node\_modules/jquery/dist/jquery.min.js'\])
.pipe(gulp.dest('vendor/jquery'));
});
gulp.task('magnific-popup', function () {
return gulp.src(\['node\_modules/magnific-popup/dist/\*'\])
.pipe(gulp.dest('vendor/magnific-popup'));
});
gulp.task('scrollreveal', function () {
return gulp.src(\['node\_modules/scrollreveal/dist/\*.js'\])
.pipe(gulp.dest('vendor/scrollreveal'));
});
gulp.task('fontawesome', function () {
return gulp.src(\[
'node\_modules/font-awesome/\*\*',
'!node\_modules/font-awesome/\*\*/\*.map',
'!node\_modules/font-awesome/.npmignore',
'!node\_modules/font-awesome/\*.txt',
'!node\_modules/font-awesome/\*.md',
'!node\_modules/font-awesome/\*.json'
\])
.pipe(gulp.dest('vendor/font-awesome'));
});
gulp.task('copy', \['bootstrap', 'jquery', 'fontawesome', 'magnific-popup', 'scrollreveal'\]);
gulp.task('syncDocs', function () {
browserSync.init({
server: {
baseDir: 'docs/'
}
});
});
gulp.task('docs', \['syncDocs'\], function () {
gulp.watch('\*.html', syncDocs.reload);
gulp.watch('assets/\*.js', syncDocs.reload);
gulp.watch('assets/\*.css', syncDocs.reload);
});