Starter kit Part 3 - Tasks

This is third article from series about Starter kit. I use it for setting new up projects quickly and painlessly. This time, I want to describe individual tasks. Take a look also at article about folder structure or Gulp.

Tasks

Gulp tasks can be simple yet powerful. Just quick initial setup of any task and we got great functionality basically for free. Let me show you what I use in Starter kit.

Styles

When developing, I use task styles:dev: I enhanced the workflow with gulp-notify plugin. In case of error, macOS type notification will let me know.

// -------------------------------------
//   styles:dev
// -------------------------------------
//  Compile styles quickly without optimizations and with additional info
//
//  1 - Collects all Sass
//  2 - Add source maps
//  3 - Compiles to CSS
//  4 - Output to dist as `generatedByGulp.css`
//
gulp.task('styles:dev', function() {
    return gulp.src(config.paths.styles.src)
        .pipe(plumber())
        .pipe(cssGlobbing({ // 1
            extensions       : ['.scss'],
            autoReplaceBlock : {
                onOff             : true,
                globBlockBegin    : 'cssGlobbingBegin',
                globBlockEnd      : 'cssGlobbingEnd',
                globBlockContents : 'modules/*.scss'
            },
            scssImportPath: {
                leading_underscore: false,
                filename_extension: false
            }
        }))
        .pipe(sourcemaps.init()) // 2
        .pipe(sass(sassOptions).on('error', notify.onError(function (error) {
            return 'Problem file : ' + error.message;
        }))) // 3
        .pipe(sourcemaps.write())
        .pipe(rename(config.names.css))
        .pipe(gulp.dest(config.paths.styles.dest)); // 4
});

For production I have a task styles:prod. The difference is speed (it’s slower) and result (it is optimized). It is recommended to use this style before testing the site.

// -------------------------------------
//   styles:prod
// -------------------------------------
//  Compile styles with optimization
//
//  1 - Collects all Sass
//  2 - Compiles to CSS
//  3 - Add prefixes
//  4 - Rearange media query rules
//  5 - Minify
//  6 - Output to dist as `generatedByGulp.css`
//
gulp.task('styles:prod', function() {
    return gulp.src(config.paths.styles.src)
        .pipe(plumber())
        .pipe(cssGlobbing({ // 1
            extensions       : ['.scss'],
            autoReplaceBlock : {
                onOff             : true,
                globBlockBegin    : 'cssGlobbingBegin',
                globBlockEnd      : 'cssGlobbingEnd',
                globBlockContents : 'modules/*.scss'
            },
            scssImportPath: {
                leading_underscore: false,
                filename_extension: false
            }
        }))
        .pipe(sass().on('error', notify.onError(function (error) {
            return 'Problem file : ' + error.message;
        }))) // 2
        .pipe(postcss(postCssOpts)) // 3 + 4
        .pipe(cssnano({outputStyle: 'compressed'})) // 5
        .pipe(rename(config.names.css))
        .pipe(gulp.dest(config.paths.styles.dest)); // 6
});

Scripts

For scripts I also have different task for dev and prod environment.

scripts:dev is quicker and verbose:

// -------------------------------------
//   scripts:dev
// -------------------------------------
//  1 - concatenate own javascript files
//  2 - add source maps
//  3 - output GeneratedByGulp.js to dist folder
//
gulp.task('scripts:dev', function() {
    return gulp.src(config.paths.scripts.src)
        .pipe(plumber())
        .pipe(sourcemaps.init())
        .pipe(concat('will-be-renamed.js')) // 1
        .pipe(sourcemaps.write()) // 2
        .pipe(rename(config.names.js.app))
        .pipe(gulp.dest(config.paths.scripts.dest)); // 3
});

scripts:prod is slower and optimizes result.

// -------------------------------------
//   scripts:prod
// -------------------------------------
//  1 - concatenate own javascript files
//  2 - remove console.log and debugging statements
//  3 - minify it
//  4 - output GeneratedByGulp.js to dist folder
//
gulp.task('scripts:prod', function() {
    return gulp.src(config.paths.scripts.src)
        .pipe(plumber())
        .pipe(concat('will-be-renamed.js')) // 1
        .pipe(stripdebug()) // 2
        .pipe(uglify()) // 3
        .pipe(rename(config.names.js.app))
        .pipe(gulp.dest(config.paths.scripts.dest)); // 4
});

scripts:vendor is taks for third party libraries. Typically, you want to keep them separately, so you can control if/where/when to load them. This task only copies them to dist folder, but can be changed for concatenation easily.

// -------------------------------------
//   scripts-vendor:dev
// -------------------------------------
//  1 - copy vendor javascripts to dist folder
//  keeps files separated so we can decide where and when to load given scripts
//
gulp.task('scripts-vendor:dev', function() {
    return gulp.src(config.paths.scripts.vendor)
        .pipe(plumber())
        // .pipe(concat('will-be-renamed.js'))   // output them together
        // .pipe(rename(config.names.js.vendor)) // output them together
        .pipe(gulp.dest(config.paths.scripts.vendorDest));
});

scripts:vendor is the same, only with minification.

Lastly, I have a scripts:test, which checks the code with linter.

// -------------------------------------
//   scripts:test
// -------------------------------------
//  run through jshint linter
//
gulp.task('scripts:test', function() {
    console.log(config.paths.scripts.src);
    return gulp.src(config.paths.scripts.src)
        .pipe(plumber())
        .pipe(jshint())
        .pipe(jshint.reporter(stylish));
});

Default and production tasks

Default (= development) task looks like this:

// -------------------------------------
//   default task
// -------------------------------------
//  1 - Regenerate everything
//  2 - Fire up browserSync & watch for everything
//
gulp.task('default',
    gulp.series(
        gulp.parallel( // 1
            'styles:dev',
            'scripts:dev',
            'scripts-vendor:dev',
            'images',
            'svg',
            'markup'
        ),
        gulp.parallel('watch', 'serve') // 2
    )
);

Notice this is build for Gulp 4, which only supports the 2-parameter version of gulp.task. If you want to specify dependency, you need to use series function instead of a third parameter. For tasks that can run along each other, use parallel function. In our case I first tell gulp to regenerate styles, scripts, images and markup. Once these tasks are finished, gulp will spin up browser sync and will start watching for changes.

Production task is similar, but with few distinct changes.

// -------------------------------------
//   prod task
// -------------------------------------
//  1 - Clean dist folder
//  2 - Regenerate everything for production
//
gulp.task('prod',
    gulp.series(
        'clean', // 1
        gulp.parallel( // 2
            'styles:prod',
            'scripts:prod',
            'scripts-vendor:prod',
            'images',
            'images2webp',
            'svg',
            'markup'
        )
    )
);

Firstly, gulp deletes folder with generated assets, so it doesn’t swell up with outdated files. After completion it’ll call for production tasks (:prod), which we’ll investigate later and regenerate everything. That’s it. No serving or watching needed, in this case.

Summary

I didn’t write about each and every tasks. They are quite simple and documented in the code. However, I might skip something which is not clear. In such a case, let me now about it. I’ll be happy to explain.