Javascript, Tutorials

What is it?

Gulp is a frontend task manager. In the simplest of terms, it is a tool which saves you from performing mundane tasks, such as minifying, concatenating and linting your files. The time it saves you will be invaluable.

Gulp can be as complex or as simple as you need (or want) it to be. In this article we will cover installing gulp and the gulp command line interface so you can get up an running with a simple, but powerful gulp file in your local projects. 
 

Installing

Install Gulp globally

This is how we use gulp.

npm install --global gulp-cli

Initialise your project

npm init

Add Gulp to your dependencies

npm install --save-dev gulp

Create a new file called gulpfile.js

var gulp = require('gulp');

gulp.task('default', function() {
    // place code for your default task here
});

And that's it! You're ready to use gulp! 

What's Next?

Now we can start building up our gulpfile.js . We will be performing several tasks.

SCSS/CSS

  • Converting SCSS files to CSS
  • Concatenating all of our newly converted CSS files
  • Minifying our CSS

JS

  • Concatenating our JS files
  • Minifying our JS

Watching

  • Listen for any changes to the SCSS + JS files

File Structure

Before we get underway, let's make our file structure should look something like this:
|- - dist
|    |- - js
|    |- - css
|- - build
|    |- - css
|    |- - js
|    |- - scss
|- - index.html
|- - gulpfile.js

Plugins

Next, we will install some third party gulp plugins to help us out. 
The --save-dev option will add the gulp plugin to your dependency file for you.

npm install --save-dev gulp-util
npm install --save-dev gulp-concat
npm install --save-dev gulp-sass
npm install --save-dev gulp-cssnano
npm install --save-dev gulp-rename
npm install --save-dev gulp-notify 
npm install --save-dev gulp-uglify
npm install --save-dev del

Require

Add the require statements for the installed plugins in your gulpfile.js.

// Require gulp + plugins
var gulp    = require('gulp'),
    gutil   = require('gulp-util'),
    concat  = require('gulp-concat'),
    sass    = require('gulp-sass'),
    cssnano = require('gulp-cssnano'),
    rename  = require('gulp-rename'),
    notify  = require('gulp-notify'),
    uglify  = require('gulp-uglify'),
    del     = require('del');

Paths

In my gulpfile.js I have added my paths to an object for convenience, this will make it much easier to update the file if you ever decide to change folder names etc.

var paths = {
    dist:  './dist/',
    build: './build/'
}

Keeping It Clean

One thing to remember is, we will be updating the master CSS + JS in the dist folder. Every time we make a change to the files in the build folder we will need to empty the respective dist CSS or JS folder. These tasks will be executed before any other tasks.

// CLEAN ALL
// We call this function when we run the default gulp task, after only the `clean:js` or `clean:css` tasks are run
gulp.task('clean:all', function() {
    return del([
        paths.dist + 'js/**/*.
        paths.dist + 'css/**/*.css',
    ]);
});

// CLEAN JS
gulp.task('clean:js', function() {
    return del([
        paths.dist + 'js/**/*.js',
    ]);
});

// CLEAN CSS
gulp.task('clean:css', function() {
    return del([
        paths.dist + 'css/**/*.css',
    ]);
});

We can use del to empty a folder of its contents. This will allow us to have an empty folder to add the updated files with gulp.

SCCS/CSS

Next, we will write our SCSS and CSS tasks to modify and manipulate our source files to create a new file called dist.min.css

CSS
Every time we run this task we tell gulp to run `dev:scss` to make sure we pick up any scss files if there are any

gulp.task('dev:css', gulp.series('dev:scss', function() {
    return gulp.src([paths.build + 'css/**/*.css')   // Tell gulp what folder we want to look inside
        .pipe(concat('build.css'))                   // Tell gulp to concatenate every file into a new one called `build.css`
      .pipe(cssnano())                               // Minify our `build.css` file
      .pipe(rename({suffix: '.min'}))                // Rename the file to signify it is minified
      .pipe(gulp.dest(paths.dist + 'css'))           // Tell gulp we are saving this new file in dist/css
      .pipe(notify({message: 'CSS task complete'})); // Send a notification when we are done
}));

Let's look at that dev:scss task we mentioned earlier. This task does what is essentially the same as the dev:css task, but with a couple of differences. Firstly it compiles the SCSS into valid CSS. Secondly, it will place these new CSS files in the build/css folder for the dev:css task to perform its functions on. 

// SCSS
gulp.task('dev:scss', function() {
    return gulp.src(paths.build + 'scss/**/*.scss')      // Tell gulp what folder we want to look inside
        .pipe(concat('build.scss'))                       // Tell gulp to concatenate every file into a new one called `build.scss`
      .pipe(sass())                                   // Compile SCSS into CSS
      .pipe(gulp.dest(paths.build + 'css'))           // Tell gulp we are saving this new file in build/css
      .pipe(notify({message: 'SCSS task complete'})); // Send a notification when we are done
});


This completes our core SCSS/CSS functionality. 

JS

Now we can move on the JS part of the gulpfile.js. Again, we will be doing largely the same thing as before. 
 

// JS
gulp.task('dev:js', function() {
    return gulp.src(paths.build + 'js/**/*.js')     // Tell gulp what folder we want to look inside
        .pipe(concat('build.js'))                       // Tell gulp to concatenate every file into a new one called `build.js`
      .pipe(uglify())                               // Minify our `build.js` file
      .pipe(rename({suffix: '.min'}))               // Rename the file to signify it is minified
      .pipe(gulp.dest(paths.dest + 'js')            // Tell gulp we are saving this new file in dist/js
      .pipe(notify({message: 'JS task complete'})); // Send a notification when we are done
});


Watching

Now, for this to be even more useful is to update the dist files overtime the build files are updated. We can do this by creating watch tasks. These watch task will essentially listen for any changes in the folders we specify and will execute a function when these conditions are met. 

// WATCH
gulp.task('watch:scss', function(done) {
    gulp.watch( 
        paths.src + 'scss/**/*.scss',
      gulp.series('clean:css', 'dev:css')
  );
  done();
});

gulp.task('watch:css', function(done) {
  gulp.watch([
      '!' + paths.src + 'css/**/build.css',
      paths.src + 'css/**/*.css'
  ],
      gulp.series('clean:css', 'dev:css')
  );
  done();
});

gulp.task('watch:js', function(done) {
  gulp.watch(
      paths.src + 'js/**/*.js',
      gulp.series('clean:js', 'dev:js')
  );
  done();
});

So, the first argument for the watch function is the path to the folder we want to watch, and secondly, we call gulp.series() to execute our chosen tasks. 

A quick note about the gulp.series() and gulp.parallel functions. gulp.series() will execute the tasks/functions you pass to it sequentially, while gulp.parallel will execute its functions in parallel. These functions allow you to apply fine control to how your gulpfile.js behaves in any situation.

Default Task

Now we can update our default task.

// We want to run all of the watch tasks in parallel
var all = gulp.parallel('watch:scss', 'watch:css', 'watch:js');

// The clean and dev tasks need to ordered sequentially to work
// Finally we pass in our parallel tasks at the end
gulp.task('default', gulp.series('clean:all', 'dev:css', 'dev:js', all));

Running

Now we can use gulp and test our gulpfile.js, everything has been attached to to the default task which means all we need to do is write gulp.

gulp

Voila! As simple as that.

Of course, you can rename the default task if you want. For example:

gulp.task('awesome', gulp.series('clean:all', 'dev:css', 'dev:js', all));

And then we write

gulp awesome

 

All together

Stitching everything together we end up with a gulpfile.js looking something like this.

var gulp    = require('gulp'),
    gutil   = require('gulp-util'),
    concat  = require('gulp-concat'),
    sass    = require('gulp-sass'),
    cssnano = require('gulp-cssnano'),
    rename  = require('gulp-rename'),
    notify  = require('gulp-notify'),
    uglify  = require('gulp-uglify'),
    del     = require('del');

var paths = {
  dist: './dist/',
  build: './build/'
}

// CLEAN
gulp.task('clean:all', function() {
  return del([
    paths.dist + 'js/**/*.js',
    paths.dist + 'css/**/*.css',
  ]);
});

gulp.task('clean:js', function() {
  return del([
    paths.dist + 'js/**/*.js',
  ]);
});

gulp.task('clean:css', function() {
  return del([
    paths.dist + 'css/**/*.css',
  ]);
});

// SCSS
gulp.task('dev:scss', function() {
  return gulp.src(paths.build + 'scss/**/*.scss')
    .pipe(concat('build.scss'))
    .pipe(sass())
    .pipe(gulp.dest(paths.dist + 'css'))
    .pipe(notify({message: 'SCSS task complete'}));
});

// CSS
gulp.task('dev:css', gulp.series('dev:scss', function() {
  return gulp.src(paths.build + 'css/**/*.css')
    .pipe(concat('build.css'))
    .pipe(cssnano())
    .pipe(rename({suffix: '.min'}))
    .pipe(gulp.dest(paths.dist + 'css'))
    .pipe(notify({message: 'CSS task complete'}));
}));

// JS
gulp.task('dev:js', function() {
  return gulp.src(paths.build + 'js/**/*.js')
    .pipe(concat('build.js'))
    .pipe(uglify())
    .pipe(rename({suffix: '.min'}))
    .pipe(gulp.dest(paths.dist + 'js'))
    .pipe(notify({message: 'JS task complete'}));
});

// WATCH
gulp.task('watch:scss', function(done) {
  gulp.watch(
    paths.build + 'scss/**/*.scss',
    gulp.series('clean:css', 'dev:css')
  );
  done();
});

gulp.task('watch:css', function(done) {
  gulp.watch([
    '!' + paths.build + 'css/**/build.css',
    paths.build + 'css/**/*.css'
  ],
    gulp.series('clean:css', 'dev:css')
  );
  done();
});

gulp.task('watch:js', function(done) {
  gulp.watch(
    paths.build + 'js/**/*.js',
    gulp.series('clean:js', 'dev:js')
  );
  done();
});

//DEFAULT
var all = gulp.parallel('watch:scss', 'watch:css', 'watch:js');
gulp.task('default', gulp.series('clean:all', 'dev:css', 'dev:js', all));

Example

This is an example of what our final project might look like after we have executed the default gulp task.

|- - build
|    |- - js
|    |    `- - build.min.js
|    |- - css
|    |    `- - build.min.css
|- - dist
|    |- - css
|    |- - js
|    |    |- - scripts.js
|    |    `- - animate.js
|    |- - scss
|    |    |- - colors.scss
|    |    `- - styles.scss
|- - index.html
|- - gulpfile.js

Final thoughts
 

Gulp can feel daunting at first, but once it is broken down it is much easier to digest. Play around with this file and experiment with different plugins.

For more information on gulp visit the docs