Working with Gulp (2)

After attending TheAntiEvent, I realized that the story I wrote some months ago about Gulp is quite different from the nowadays reality: https://medium.com/@jontorrado/working-with-gulp-step-by-step-e0d5fe2d2f23. I won’t write again about how to install Gulp, but I will update some of the tasks explained there and I will add some new.

SCSS to CSS transformation

Not a lot of changes here:

var gulp = require('gulp'),
autoprefixer = require('autoprefixer'),,
cssnano = require('cssnano'),
postcss = require('gulp-postcss'),
rename = require('gulp-rename'),

sass = require('gulp-sass'),
sasslint = require('gulp-sass-lint');
var paths = {
js: contexts.assets + '/js',
sass: 'app/Resources/assets/scss',
buildCss: 'web/css',
buildJs: 'web/js'
};
var watch = {
js: [
paths.js + '/**/*.js'
],
sass: [
paths.sass + '/**/*.scss'
]
};
gulp.task('sass', function () {
var plugins = [
autoprefixer({browsers: ['last 1 version']}),
cssnano()
];

return gulp.src(paths.sass + '/app.scss')
.pipe(sass())
.pipe(postcss(plugins))
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest(paths.buildCss));
});
gulp.task('sass-lint', function () {
return gulp.src(paths.sass + '/**/*.scss')
.pipe(sassLint())
.pipe(sassLint.format())
.pipe(sassLint.failOnError());
gulp.task('watch', function () {
gulp.watch(watch.sass, { interval: 500 }, ['sass']);
});

Before explaining what’s new here in comparison with the last story, be sure to add the needed dependencies (listed on the top part of the code above) in your package.jsonfile. In the previous code, you can read the following tasks:

  • sass: takes the main app.scss file, calls sass(), then it minifies the result, adds a .min to the file name and moves it to the destination folder. The new elements: we are using postcss to add the vendor prefixes and to minify the resulting file. cssnano allows some configurations, such as cssnano({zindex: false}).
  • sass-lint: if you are new to the lining world, WELCOME. With this task you will be able to detect SCSS errors (now with Node instead of Ruby). You must add your linting rules in an sass-lint.yml file. Check the example file here.
  • watch: this will automatically call the sass task whenever a .scss file is modified. Just type gulp watch to start watching. The interval parameter makes it lighter (good for SSD disks).

JavaScript bundling and minifying

Nothing changed here, almost.

There was an error in the previous story when using livereload. Gulp sent as many page reloads as the quantity of JavaScript files it processes. So, the question was, how to send just 1 page reload after finishing the whole JavaScript task? Here you have the solution:

var gulp = require('gulp'),
concat = require('gulp-concat'),
eslint = require('gulp-eslint'),
uglify = require('gulp-uglify');
// Paths in the code above
var jsFiles = [
paths.js + '/**/*.js'
];
gulp.task('js', function () {
return gulp.src(jsFiles)
.pipe(concat('app.min.js'))
.pipe(uglify())
.pipe(gulp.dest(paths.buildJs));
});
gulp.task('eslint', function () {
return gulp.src(watch.js)
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failAfterError());
});
gulp.task('livereload-js', ['js'], function () {
livereload.reload();
});
gulp.task('watch', function () {
gulp.watch(watch.sass, { interval: 500 }, ['sass']);
gulp.watch(watch.js, { interval: 500 }, ['js', 'livereload-js']);
});

As you see, the new task livereload-js depends on js, so before launching it, it will wait for the js task to finish. Awesome!

Launching binary files

Maybe you want to use Gulp for every single task, even the binary files (or execute sh files too!). You can accomplish that goal with the following code:

var cp = require('child_process');
gulp.task('binary', function () {
return cp.execFile('bin/binary', ['--option=value'], function (err, stdout, stderr) {
console.log(stdout);
console.log(stderr);
});
});

Plumber

Plumber will replace your pipe method and catch the error launched that breaks the execution (for example, when you do something wrong and the watch method stops). Is just this simple:

// ... plumber = require('gulp-plumber')
gulp.task('sass', function () {
var plugins = [
autoprefixer({browsers: ['last 1 version']}),
cssnano()
];
return gulp.src(paths.sass + '/app.scss')
.pipe(plumber())
.pipe(sass())
.pipe(postcss(plugins))
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest(paths.buildCss));
});

Including other Gulpfile.js

If you are working in a big project with some separated submodules (for example, website and admin), you may want to have one gulpfile that includes the others. Here is how you can do that:

var gulp = require('gulp');
var chug = require('gulp-chug');
var argv = require('yargs').argv;
config = [
'— rootPath',
argv.rootPath || '../../../../web/assets/',
'— nodeModulesPath',
argv.nodeModulesPath || '../../../../node_modules/'
];
gulp.task('admin', function() {
gulp.src('submodule/path/to/Admin/Gulpfile.js', { read: false })
.pipe(chug({ args: config }))
;
});

Convert images to WebP

You should be using .webp images with the <picture> tag to allow Chrome users navigate much faster (and consume less bandwith!). You can automate the process of transforming images to webp.

var gulp = require('gulp'),
webp = require('gulp-webp');

gulp.task('default', function() {
gulp.src('path/to/images/**.jpg')
.pipe(webp())
.pipe(gulp.dest('dist'))
});

Other useful plugins

Here you have a curated list of useful gulp plugins you should read about carefully: https://gist.github.com/renarsvilnis/ab8581049a3efe4d03d8.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.