So, Before writing out optimiser functions , we need to install a couple of caching plugins.
bash $ npm install --save-dev gulp-cached
bash $ npm install --save-dev gulp-remember
You might wonder why two caches eh!. gulp-cached , passes only modified or new content down the pipeline to other plugins. So, Since we want files without change to be used for concatenating into a single file per asset( css | js ) as well, we need gulp-remember in addition to gulp-cached
First we use gulp-cached to build a list of files that have changed
Second , we need gulp-remember to keep a track of all files that are passed through by that list in memory.
First Run : No files are cached , so gulp-cached will pass them all to gulp-remember
Subsequent runs : Only modified or new files are piped down by gulp-cached. Since the content of the current file has changed , gulp-remember updates its cache.
Cool , Let us write our first optimizer
// Goal
/*
1. cache existing files on the first run
2. each file ,
a. Is autoprefixed with cross browser compatible prefixes for any css property ( justify-content for e.g)
b. Is concatenated into app.css
3. app.css is minified
4. On subsequent runs , the above process is implemented on file modifications/additions only
*/
/*
*@src - input a glob pattern - a string eg 'images/**/*' or '**/*.css' or, an array eg ['glob1','glob2']
*/
var optimizeStyles = function(src) {
return $.src(src).
pipe($$.cached('styles')).
pipe($$.autoprefixer({
browsers: ['last 2 versions']
})).
pipe($$.remember('auto-prefixed-stylesheets')).
pipe($$.concat('app.css')).
pipe($.dest('build/css')).
pipe($$.cleanCss()).
pipe($$.rename({
suffix: '.min'
})).
pipe($.dest('build/css'))
}
$ - > gulp
$$ - > gulp-load-plugins
$.src - > builds file streams matching the glob passed as src
$.dest - > saves the manipulated file in the path specified
// Goal
/*
1. cache existing files on the first run
2. each file ,
a. Is linted with jshint
b. Is concatenated into app.js
3. app.js is minified
4. On subsequent runs , the above process is implemented on file modifications/additions only
*/
/*
*@src - input a glob pattern - a string eg 'js/**/*' or '**/*.js' or, an array eg ['glob1','glob2']
*/
var optimizeScripts = function(src) {
return $.src(src).
pipe($$.cached('scripts')).
pipe($$.jshint()).
pipe($$.jshint.reporter('default')).
pipe($$.jshint.reporter('fail')).
pipe($$.remember('linted-scripts')).
pipe($$.concat('app.js')).
pipe($.dest('build/js')).
pipe($$.uglify()).
pipe($$.rename({
suffix: '.min'
})).
pipe($.dest('build/js'))
}
$ - > gulp
$$ - > gulp-load-plugins
$.src - > builds file streams matching the glob passed as src
$.dest - > saves the manipulated file in the path specified
Let us now move on to image processing. So, the aim , is to have an array of sizes for each image you are going to serve.
Why?
Well , to understand why we need a battery of images with a range of widths , we need to ponder over the fact , that there are probably zillions of devices with varied resolutions. We need an image to scale without much pixelation. At the same time , we need to improve page load times , by downloading just the one image , which fits the width it is contained by , and is also with the smallest possible dimension , to do so. There are scholarly blogs like the one Eric Portis wrote , which highlights the ineffectiveness of just media queries and serves as a comprehensive guide to understanding concepts like srcsets and sizes.
You can refer to Eric Portis' epic write up here
Now, Our function , needs to take a glob , and a width as inputs, and do its magic and push the file each run generates , to a destination and minify the responsified image.
We have installed two image compression plugins in our first example
Since these plugins DO NOT start with a "gulp-" prefixed, we need to manually load them onto our gulpfile.
SO Let us require them manually , after the gulp-load-plugins declaration at the top of our gulpfile.
like so:
var compressJpg = require('imagemin-jpeg-recompress');
var pngquant = require('imagemin-pngquant');
It is worthwhile to note , that gulp-responsive comes with the sharp image processor , which is better than imagemagick BY FAAAAR!. Sharp is what is used by gulp-responsive , to crop your images to desired widths.
you might look at gulp-responsive-configuration-options,for a comprehensive list of configuration params. I have only used
- width - to crop our images to a width w, passed as a parameter
- rename - to add a suffix to the image name, so that it remains unique
in my configuration function below. so our function , will crop the image to the width passed as input , for all matching images deciphered via the glob input. then, each image is compressed using jpeg-recompress or pngquant and saved inside build/images.
With that in mind, our function would be like so:
/*
@generateResponsiveImages
*@Description:takes in a src of globs, to stream matching image files , a width,
*to resize the matching image to, and a dest to write the resized and minified files to
*@src - input a glob pattern - a string eg 'images/** /*' or 'images/*' or, an array
eg ['glob1','glob2']
*@return returns a stream
*/
var generateResponsiveImages = function(src, width, dest) {
//declare a default destination
if (!dest)
dest = 'build/images';
//case 1: src glob - images/**/*
// the base is the directory immediately preceeding the glob - images/ in this case
//case 2: images/fixed/flourish.png : the base here is images/fixed/ - we are overriding
// that by setting base to images.This is done so that, the path beginning after images/
// - is the path under our destination - without us setting the base, dest would be,
//build/images/flourish.png.But with us setting the base, the destination would be
// build/images/fixed/flourish.png
return $.src(src, {
base: 'images'
})
//generate resized images according to the width passed
.pipe($$.responsive({
//match all pngs within the src stream
'**/*.png': [{
width: width,
rename: {
suffix: '-' + width
},
withoutEnlargement: false,
}],
//match all jpgs within the src stream
'**/*.jpg': [{
width: width,
rename: {
suffix: '-' + width
},
progressive: true,
withoutEnlargement: false,
}]
}, {
errorOnEnlargement: false,
errorOnUnusedConfig: false,
errorOnUnusedImage: false
}))
//once the file is resized to width, minify it using the plugins available per format
.pipe($$.if('*.jpg', compressJpg({
min: 30,
max: 90,
target: 0.5
})()))
//use file based cache gulp-cache and it will minify only changed or new files
//if it is not a new file and if the contents havent changed, the file is served from cache
.pipe($$.cache($$.imagemin({
verbose: true
})))
//write to destination - dest + path from base
.pipe($.dest(dest));
}
$ - > gulp
$$ - > gulp-load-plugins
$.src - > builds file streams matching the glob passed as src
$.dest - > saves the manipulated file in the path specified
*
*@minifyHtml
*Description:takes in a glob src, and minifies all '.html' files matched by the glob
*@src - input a glob pattern - a string eg '/**/*.html /*' or '*.html' or, an array eg ['glob1','glob2']
*@dest=file.base means, the modified html file will be in the same directory as the src file being minified
*often means, the process is just a modification on the existing src file
*@return returns a stream
*/
var minifyHtml = function(src) {
return $.src(src)
.pipe($$.minifyHtml())
.pipe($.dest(function(file) {
//file is provided to a dest callback -
// Refer here https://github.com/gulpjs/gulp/blob/master/docs/API.md#path
return file.base;
}));
}