Gulp - Page 2

gulp

// Development Tools - Gulp:

npm install gulp-newer gulp-imagemin --save-dev
npm install browser-sync --save-dev
npm install gulp-deporder gulp-concat gulp-strip-debug gulp-uglify --save-dev
npm install gulp-sass gulp-postcss postcss-assets autoprefixer css-mqpacker 
  cssnano --save-dev

On its own, Gulp does not do anything.  We must install Gulp plug-ins and write 
tasks which utilize those plug-ins to do something useful.  It is possible to 
write our own plug-ins, but there are a lot of plug-ins available, it is 
unlikely that we will ever need to write our own.  We can search the Gulp 
plug-ins directory ( http://gulpjs.com/plugins ).

Gulp provides three primary methods:

1. gulp.task: defines a new task with a name, optional array of dependencies, 
   and a function that provide concrete implementation for the task.

2. gulp.src: sets the folder where source files are located.  Its parameters are
   globs, and an optional options object.  It uses .pipe for chaining its output 
   to other plugins.

3. gulp.dest: sets the destination folder where the result files will be placed.

We can have any number of plug-in calls, set with pipe, between .src and .dest.

Gulp also offer another method: gulp.watch, which can monitor our source files 
and run an appropriate task whenever a file is changed. The method is passed a 
folder and a list of tasks to execute when a change occurs.   The gulp.watch 
method, like the gulp.task method, has two main forms, both return an 
EventEmitter that emits change events.  The first of which takes a glob, and 
optional object, and an array of tasks to execute.

var gulp = require('gulp');
gulp.task('task-name', function() {
  // Stuff here
});

gulp.task('hello', function() {
  console.log('Hello Zell');
});

We can run this task in the command line:

gulp hello

gulp.task('task-name', function () {
  return gulp.src('source-files') // Get source files with gulp.src
    .pipe(aGulpPlugin()) // Sends it through a gulp plugin
    .pipe(gulp.dest('destination')) // Outputs the file in the destination folder
});

gulp.task('taskA', function() {
  //do stuff
});

gulp.task('taskB', ['taskA'], function() {
  //do stuff after 'taskA' is done.
});

var gulp = require('gulp');
var newer = require('gulp-newer');
var imagemin = require('gulp-imagemin');
var htmlclean = require('gulp-htmlclean');
var concat = require('gulp-concat');
var deporder = require('gulp-deporder');
var stripdebug = require('gulp-strip-debug');
var uglify = require('gulp-uglify');
var sass = require('gulp-sass');
var postcss = require('gulp-postcss');
var assets = require('postcss-assets');
var autoprefixer = require('autoprefixer');
var mqpacker = require('css-mqpacker');
var cssnano = require('cssnano');

// development mode?
var devBuild = (process.env.NODE_ENV !== 'production');

var folder = {
    src: 'src/',
    build: 'build/'
  }
;

// image processing
gulp.task('images', function() {
  var out = folder.build + 'images/';
  return gulp.src(folder.src + 'images/**/*')
    .pipe(newer(out))
    .pipe(imagemin({ optimizationLevel: 5 }))
    .pipe(gulp.dest(out));
});

All tasks are syntactically similar. This code:

1. Creates a new task named images.

2. Defines a function with a return value which…

3. Defines an out folder where build files will be located.

4. Sets the Gulp src source folder. The /**/* ensures that images in sub-folders 
   are also processed.

5. Pipes all files to the gulp-newer module. Source files that are newer than 
   corresponding destination files are passed through. Everything else is 
   removed.

6. The remaining new or changed files are piped through gulp-imagemin which sets 
   an optional optimizationLevel argument.

7. The compressed images are output to the Gulp dest folder set by out.

// HTML processing
gulp.task('html', ['images'], function() {
  var
    out = folder.build + 'html/',
    page = gulp.src(folder.src + 'html/**/*')
      .pipe(newer(out));

  // minify production code
  if (!devBuild) {
    page = page.pipe(htmlclean());
  }

  return page.pipe(gulp.dest(out));
});

This reuses gulp-newer and introduces a couple of concepts:

1. The [images] argument states that our images task must be run before 
   processing the HTML (the HTML is likely to reference images). Any number of 
   dependent tasks can be listed in this array and all will complete before the 
   task function runs.

2. We only pipe the HTML through gulp-htmlclean if NODE_ENV is set to 
   production. Therefore, the HTML remains uncompressed during development 
   which may be useful for debugging.

// JavaScript processing
gulp.task('js', function() {

  var jsbuild = gulp.src(folder.src + 'js/**/*')
    .pipe(deporder())
    .pipe(concat('main.js'));

  if (!devBuild) {
    jsbuild = jsbuild
      .pipe(stripdebug())
      .pipe(uglify());
  }

  return jsbuild.pipe(gulp.dest(folder.build + 'js/'));

});

The task above does:

1. ensure dependencies are loaded first using the gulp-deporder plug-in. This 
   analyses comments at the top of each script to ensure correct ordering e.g. 
   // requires: defaults.js lib.js.

2. concatenate all script files into a single main.js file using gulp-concat

3. remove all console and debugging statements with gulp-strip-debug and 
   minimize code with gulp-uglify. This step will only occur when running in 
   production mode.

// CSS processing
gulp.task('css', ['images'], function() {

  var postCssOpts = [
  assets({ loadPaths: ['images/'] }),
  autoprefixer({ browsers: ['last 2 versions', '> 2%'] }),
  mqpacker
  ];

  if (!devBuild) {
    postCssOpts.push(cssnano);
  }

  return gulp.src(folder.src + 'scss/main.scss')
    .pipe(sass({
      outputStyle: 'nested',
      imagePath: 'images/',
      precision: 3,
      errLogToConsole: true
    }))
    .pipe(postcss(postCssOpts))
    .pipe(gulp.dest(folder.build + 'css/'));

});

The above task uses gulp-sass. This is a Gulp plug-in for node-sass which binds 
to the super-fast LibSass C/C++ port of the Sass engine (we won’t need to 
install Ruby). We will presume that our primary Sass file scss/main.scss is 
responsible for loading all partials.  The above task also utilize the fabulous 
PostCSS via the gulp-postcss plug-in.

Note that the images task is set as a dependency because the postcss-assets 
plug-in can reference images during the build process. In addition, most 
plug-ins can be passed arguments.

// run all tasks
gulp.task('run', ['html', 'css', 'js']);

Note that we omitted the images task because it is already set as a dependency 
for the html and css tasks.

// watch for changes
gulp.task('watch', function() {

  // image changes
  gulp.watch(folder.src + 'images/**/*', ['images']);

  // html changes
  gulp.watch(folder.src + 'html/**/*', ['html']);

  // javascript changes
  gulp.watch(folder.src + 'js/**/*', ['js']);

  // css changes
  gulp.watch(folder.src + 'scss/**/*', ['css']);

});

// default task
gulp.task('default', ['run', 'watch']);

Now run 'gulp' at the command line. Our images, HTML, CSS and JavaScript will 
all be processed then Gulp will remain active watching for updates and 
re-running tasks as necessary. Hit Ctrl/Cmd + C to abort monitoring and return 
to the command line.

One useful method in gulp-util is .noop() which passes data straight through 
without performing any action. This could be used for cleaner development / 
production processing code:

var gutil = require('gulp-util');

// HTML processing
gulp.task('html', ['images'], function() {
  var out = folder.src + 'html/**/*';

  return gulp.src(folder.src + 'html/**/*')
    .pipe(newer(out))
    .pipe(devBuild ? gutil.noop() : htmlclean())
    .pipe(gulp.dest(out));

});

Gulp can also call other Node.js modules – they don’t necessarily need to be 
plug-ins.  For example:

1. browser-sync: automatically reload assets or refresh your browser when 
   changes occur

2. del: delete files and folders (perhaps clean your build folder at the start 
   of every run).

var browserSync = require('browser-sync').create();

gulp.task('browserSync', function() {
  browserSync.init({
    server: {
      baseDir: 'app'
    },
  })
})

gulp.task('sass', function() {
  return gulp.src('app/scss/**/*.scss') // Gets all files ending with .scss in app/scss
    .pipe(sass())
    .pipe(gulp.dest('app/css'))
    .pipe(browserSync.reload({
      stream: true
    }))
});

gulp.task('watch', function(){
  gulp.watch('app/scss/**/*.scss', ['sass']); 
  // Other watchers
})

The first parameter for the gulp.watch method is the patterns that specifies the 
source files to watch for changes.  The second parameters for gulp.watch is an 
array of tasks to be run when Gulp detects that the source files were changed.

Notice that in the above 'sass' task, we invoke browserSync.reload({stream: true}).

It'll be cumbersome to open up two command line windows and run gulp browserSync 
and gulp watch separately, so let's get Gulp to run them together by telling the 
watch task that browserSync must be completed before watch is allowed to run.

We can do so by adding a second argument to the watch task. The syntax is:

gulp.task('watch', ['array of tasks to complete before watch'], function () {
  // ...
})

And in this case we're adding the browserSync task:

gulp.task('watch', ['browserSync'], function (){
  gulp.watch('app/scss/**/*.scss', ['sass']); 
  // Other watchers
})

With the above code, when we run 'gulp watch' from the command line, it 
automatically run the browserSynch task.  In the above code, we specified that 
the browserSync task is a dependency for the watch task.

gulp.task('watch', ['browserSync', 'sass'], function (){
  gulp.watch('app/scss/**/*.scss', ['sass']); 
  // Reloads the browser whenever HTML or JS files change
  gulp.watch('app/*.html', browserSync.reload); 
  gulp.watch('app/js/**/*.js', browserSync.reload); 
});

One problem developers face when optimizing CSS and JavaScript is that it's 
difficult to concatenate your scripts in the correct order.  Consider a 
scanario where we have to load JavaScript libraries in a specific order.  These 
scripts are located in different folders.  It'll be difficult to concatenate 
them with traditional plugins like gulp-concatenate, because it did not read 
the order of these libraries from our HTML files. The gulp-useref plug-in solves 
this problem.

Gulp-useref concatenates any number of CSS and JavaScript files into a single 
file by looking for a comment that starts with "<!--build:" and ends with 
"<!--endbuild-->". Its syntax is:

<!-- build:<type> <path> -->
... HTML Markup, list of script / link tags.
<!-- endbuild -->

<type> can either be js, css, or remove. It's best to set type to the type of 
file that you're trying to concatenate. If you set type to remove, Gulp will 
remove the entire build block without generating a file.

<path> refers to the target path of the generated file.

<!--build:js js/main.min.js -->
<script src="js/lib/a-library.js"></script>
<script src="js/lib/another-library.js"></script>
<script src="js/main.js"></script>
<!-- endbuild -->

npm install gulp-useref --save-dev
npm install gulp-uglify --save-dev 
npm install gulp-cssnano --save-dev
npm install gulp-imagemin --save-dev
npm install gulp-cache --save-dev
npm install del --save-dev
npm install run-sequence --save-dev

vvar uglify = require('gulp-uglify');
var gulpIf = require('gulp-if');
var cssnano = require('gulp-cssnano');
var imagemin = require('gulp-imagemin');
var cache = require('gulp-cache');
var del = require('del');
var runSequence = require('run-sequence');

gulp.task('useref', function(){
  return gulp.src('app/*.html')
    .pipe(useref())
    .pipe(gulpIf('*.js', uglify()))
    // Minifies only if it's a CSS file
    .pipe(gulpIf('*.css', cssnano()))
    .pipe(gulp.dest('dist'))
});

Gulp-useref automatically remove the script tags from our HTML file and replace 
them with a single script tag that represent the combined and minified file.

gulp.task('images', function(){
  return gulp.src('app/images/**/*.+(png|jpg|gif|svg)')
  .pipe(imagemin())
  .pipe(gulp.dest('dist/images'))
});

Since different file types can be optimized differently, you might want to add o
ptions to imagemin to customize how each file is optimized.  For example, we can 
create interlaced GIFs by setting the interlaced option key to true:

gulp.task('images', function(){
  return gulp.src('app/images/**/*.+(png|jpg|jpeg|gif|svg)')
  .pipe(imagemin({
      // Setting interlaced to true
      interlaced: true
    }))
  .pipe(gulp.dest('dist/images'))
});

gulp.task('images', function(){
  return gulp.src('app/images/**/*.+(png|jpg|jpeg|gif|svg)')
  // Caching images that ran through imagemin
  .pipe(cache(imagemin({
      interlaced: true
    })))
  .pipe(gulp.dest('dist/images'))
});

We can copy files with Gulp simply by specifying the gulp.src and gulp.dest 
without any plugins:

gulp.task('fonts', function() {
  return gulp.src('app/fonts/**/*')
  .pipe(gulp.dest('dist/fonts'))
})

gulp.task('clean:dist', function() {
  return del.sync('dist');
})

To clear the caches off our local system, you can create a separate task named 
'cache:clear':

gulp.task('cache:clear', function (callback) {
  return cache.clearAll(callback)
});

gulp.task('watch', ['browserSync', 'sass'], function (){
  // ... watchers
})

gulp.task('build', [`clean`, `sass`, `useref`, `images`, `fonts`], function (){
  console.log('Building files');
})

Unfortunately, we wouldn't be able to write the build task this way because 
Gulp activates all tasks in the second argument simultaneously.  There's a 
possibility that useref, images, or even fonts gets completed before clean does, 
which means the entire `dist` folder gets deleted.

To ensure that cleans get completed before the rest of the tasks, we need to 
use an extra plugin called Run Sequence.

gulp.task('task-name', function(callback) {
  runSequence('task-one', 'task-two', 'task-three', callback);
});

Run Sequence also allows you to run tasks simultaneously if you place them in 
an array:

gulp.task('task-name', function(callback) {
  runSequence('task-one', ['tasks','two','run','in','parallel'], 'task-three', 
    callback);
});

In this case, Gulp first runs task-one. When task-one is completed, Gulp runs 
every task in the second argument simultaneously. All tasks in this second 
argument must be completed before task-three is run.

gulp.task('build', function (callback) {
  runSequence('clean:dist', 
    ['sass', 'useref', 'images', 'fonts'],
    callback
  )
});

A task for copying files:

gulp.task('copyHtml', function() {
  // copy any html files in source/ to public/
  gulp.src('source/*.html').pipe(gulp.dest('public'));
});

npm install --save-dev gulp-jshint jshint-stylish

var gulp   = require('gulp');
var jshint = require('gulp-jshint');

// define the default task and add the watch task to it
gulp.task('default', ['watch']);

// configure the jshint task
gulp.task('jshint', function() {
  return gulp.src('source/javascript/**/*.js')
    .pipe(jshint())
    .pipe(jshint.reporter('jshint-stylish'));
});

// configure which files to watch and what tasks to use on file changes
gulp.task('watch', function() {
  gulp.watch('source/javascript/**/*.js', ['jshint']);
});

The gulp.watch method, like the gulp.task has two main forms, both return an 
EventEmitter that emits change events. The first of which takes a glob, an 
optional options object, and an array of tasks as it’s parameters:

gulp.watch('source/javascript/**/*.js', ['jshint']);

When any of the files matched by the glob change, run the tasks. In the above 
code block, when any files in the source/javascript subfolders that have an 
extension of .js change, then the task jshint will be run against those files.

The second form takes the glob, an optional options object, and an optional 
callback that will run when a change is picked up.

Using source-maps:

var gulp       = require('gulp'),
    jshint     = require('gulp-jshint'),
    sass       = require('gulp-sass'),
    sourcemaps = require('gulp-sourcemaps');

gulp.task('build-css', function() {
  return gulp.src('source/scss/**/*.scss')
    .pipe(sourcemaps.init())  // Process the original sources
      .pipe(sass())
    .pipe(sourcemaps.write()) // Add the map to modified source.
    .pipe(gulp.dest('public/assets/stylesheets'));
});

gulp.task('build-js', function() {
  return gulp.src('source/javascript/**/*.js')
    .pipe(sourcemaps.init())
      .pipe(concat('bundle.js'))
      //only uglify if gulp is ran with '--type production'
      .pipe(gutil.env.type === 'production' ? uglify() : gutil.noop()) 
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('public/assets/javascript'));
});

Gulp-sass uses LibSass to convert Sass into CSS. It's much quicker than 
Ruby-based methods. If you want, you can still use Ruby methods with Gulp by 
using gulp-ruby-sass or gulp-compass instead.

On Windows, we'll need to install python 2.7.x and Visual Studio Express 2013 
in order to compile libsass. Mac and Linux will use whatever gcc is available.  
An alternative is to use gulp-ruby-sass, which uses ruby and the sass gem 
instead.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License