You'll focus on being more productive instead of busy.

From this post you'll learn...

  • How to write and run Gulp tasks.
  • How to use Gulp to automate:
    • Compiling your SASS.
    • Minifying your CSS files.
    • Adding prefixes for major browsers such as -webkit- and -moz-.
    • Watching for file changes.
    • Adding @font-face rules to your CSS.
  • Using Node.js and npm to install Gulp plugins.
  • How to integrate Gulp tasks with Visual Studio's Task Runner.

What is Gulp?

Gulp is a task runner written in JavaScript.

Task runners are commonly used to run and automate a sequence of tasks.

Example of tasks include: minifying your JavaScript files, SASS compilation to CSS, and automatically refreshing your browser upon code changes.

You write a JavaScript file which contains your tasks. Then Gulp uses plugins to perform your development tasks. You can chain these plugins to perform more complex actions.

var gulp = require('gulp');
var less = require('gulp-less');
var minifyCSS = require('gulp-csso');

gulp.task('css', function(){
  return gulp.src('css/*.less')
    .pipe(less())
    .pipe(minifyCSS())
    .pipe(gulp.dest('build/css'))
});

gulp.task('default', 'css');

For us, this means little code and work is required to be able process and output our files in the format we want.

At the time of writing there are 3686 plugins available.

In addition, Gulp integrates well with Visual Studio's Task Runner Explorer.

Visual Studio Task Runner

Gulp or Grunt

You'll often find that it's not which is better, but what are your experiences and preferences.

Each task in Grunt accesses files separately to be able to process them. Gulp uses streams, meaning the files stay in memory until after we've finished modifying them.

Process and Autoprefix SASS
You'll find that Gulp is one of the most popular task runners available today.

Alternatives for Processing SASS?

Node Package Manager

As an alternative, developers use node package manager (npm) to run tasks. There are a large number of packages to allow you to do what you want. This can also be integrated with Visual Studio.

You write your scripts in your package.json file:

"name": "asp.net", 
"version": "1.0.0", 
"devDependencies": {
    "jshint": "latest"
},
"scripts": {
    "lint": "jshint *.js"
}

You use the npm run command to execute the script:

npm run lint

Webpack

You may notice module bundlers such as Webpack being used more often. Webpack is not a task runner, but it can be used to accomplish all of the tasks mentioned above: SASS, minify CSS and add vendor prefixes

Webpack has become one of the favourites and is highly used. But Webpack has a steeper learning curve compared to Gulp. It also requires applications to be written in a modular way, which is not always possible, especially if you're not familiar with the latest JavaScript frameworks.

Choose a tool that allows you to do the things you need to do easily and efficiently. Beyond the investment of time required in learning a new tool, your choice should allow your projects to be extensible, easy to maintain, and understood by other developers within your team.

Performing Tasks with Gulp

Install Node.js. Use the default installation settings because you also need to install node package manager.

Run the commands to ensure the installation was successful:

node -v
npm -v

Node package manager uses a file called package.json to store a list of all your locally installed node packages.

Make sure you are in the web projects root directory. All upcoming commands will be run from this directory.

You can run the following command to create your package.json file:

npm init --yes

This will generate a default package.json file, which you can edit through Visual Studio or any other text editor.

Or you can create one manually, which must contain at least the name and version properties:

{
  "name": "asp.net",
  "version": "1.0.0"
}

Installing Gulp

Run the following command:

npm install --save-dev gulp

After running the above command, a new folder called node_modules will be created.

The --save-dev option tells npm to add Gulp to our devDependencies list in our package.json file. devDependencies are modules which are required during development. Other examples of devDependencies include: JSHint, Grunt, Browsersync and TypeScript.

Libraries listed in both dependencies and devDependencies will be downloaded by running npm install.

By default, npm install will install all modules listed as dependencies in package.json.

With the --production flag (or when the NODE_ENV environment variable is set to production), npm will not install modules listed in devDependencies.

Installing SASS and Other Plugins

From the root of your project, run the following command:

npm install --save-dev gulp-sass gulp-rename gulp-postcss autoprefixer cssnano

Once these are installed you'll see them in your package.json file.

gulp-sass will take our SCSS files and convert them to CSS and gulp-rename will rename our files.

What is PostCSS?

We've introduced PostCSS. PostCSS allows you to modify your CSS using a variety of plugins. At the time of writing, PostCSS has more than 200 plugins. These plugins can be integrated with Gulp and injected into the pipe.

We will use autoprefixer to add vendor prefixes, such as -webkit- and -moz-. This means we don't have to write them ourselves. We will also use cssnano to minify our css.

Envato Tuts+ has some great articles on what PostCSS is and the benefits it can provide.

In a nutshell, PostCSS takes CSS and turns it into a form of data that JavaScript can manipulate. JavaScript-based plugins for PostCSS then perform said code manipulations.

Writing the Styles Task

We need to create a new JavaScript file called gulpfile.js and add it to the root of the web project:

Visual Studio Solution Explorer

We now begin writing our styles task:

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

var paths = {
    styles: './Styles/**/*.scss',
    css: './wwwroot/css/'
};

gulp.task('styles', function () {
    var plugins = [
        autoprefixer(),
        cssnano()
    ];

    return gulp.src(paths.styles)
        .pipe(sass())

        .pipe(postcss(plugins))

        .pipe(rename(function (path) {
            path.basename = path.basename.toLowerCase() + '.min'
        }))

        .pipe(gulp.dest(paths.css));
});

We've including the modules we are going to use in our tasks.

We've named our task 'styles' and provided it with a function that will convert our SASS to CSS. The src path contains a globbing pattern that tells Gulp to take all files with a .scss extension inside of our Styles folder, and inside any of its subdirectories, and add them to the pipe ready to process.

We've used the default configuration from Autoprefixer.

Autoprefixer uses Browserslist, so you can specify the browsers you want to target in your project by queries like last 2 versions or > 5%.

If you want to override the defaults and instead specify the browsers you want to target, like the last 2 versions, you pass in an object with a browsers property:

var plugins = [
    // passing options to autoprefixer
    autoprefixer({ browsers: 'last 2 major versions' }),
    cssnano()
];

There are many options to choose from and a variety of ways to target specific browsers.

We rename our files to include the .min suffix.

We save our processed files to the /wwwroot/css/ directory.

Testing the Task

Running the below command from the root of our web project will run our styles task:

gulp styles

Processed SASS to CSS

The Watch Task

The 'watch' task looks for changes to files and then responds by executing a function:

gulp.task('watch', function () {
    gulp.watch(paths.styles, ['styles']);
});

The .watch() method is going to look for all files ending in a .scss inside the Styles folder or any of its subdirectories. The second parameter is the name of the task we want to run when those files change. We want to run a single task so we've used the syntax:

gulp.watch(paths.styles, ['styles']);

To run multiple tasks we add additional names in the array of task names:

gulp.watch(paths.styles, ['styles', 'styles2']);

Running the Watch Task

Run the following command to test your watch task:

gulp watch

Make changes to your .scss files and check the updates are visible in your .css files.

Gulp Watch CSS

You can stop the watch task by pressing the Ctrl + C keys.

We like to run our watch task each time we open our web project. We can do this with Visual Studio's Task Runner.

Right click on your gulpfile.js file and select Task Runner Explorer:

Visual Studio Gulpfile.js Right Click

Right-click your watch task and select Bindings - Project Open.

Visual Studio Task Runner Watch

The watch task will run automatically after you open the project.

Update Node Version in Visual Studio

If you're trying to run your watch task with Visual Studio's Task Explorer you may encounter the following error:

Output-Gulp-Error

You'll need to update a few things to be able to fix this.

First modify where Visual Studio looks for Node.js.

Select Tools – Options.

Expand Projects and Solutions, then expand Web Package Management and select External Web Tools.

Move the $(PATH) path above the $(VSINSTALLDIR)\Web\External path:

Visual_Studio_Options_External_Tools

Run the following commands to re-install gulp-sass:

npm uninstall --save-dev gulp-sass
npm install --save-dev gulp-sass

On the Task Runner Explorer, select the refresh icon and the Explorer will show your tasks.

Handling the Watch Task errors

When there is an error in your SASS code, Gulp will throw an error and the watch task will stop running. You will need to fix all errors and start the Gulp watch task again. The biggest problem is realizing there is an error and the task has stopped running. You'll continue writing your code and debugging your application, only to realize later that your changes are not shown on your web application.

We can change this by adding a Gulp plugin called gulp-plumber.

Install gulp-plumber:

npm install --save-dev gulp-plumber

After installation, we can add it to our styles task:

var gulp = require('gulp'),
    rename = require('gulp-rename'),
    sass = require('gulp-sass'),
    postcss = require('gulp-postcss'),
    cssnano = require('cssnano'),
    autoprefixer = require('autoprefixer'),
    plumber = require('gulp-plumber'); // include plumber
return gulp.src(paths.styles)
    .pipe(plumber()) // add plumber to pipe

    .pipe(sass())

    .pipe(postcss(plugins))

    .pipe(rename(function (path) {
        path.basename = path.basename.toLowerCase() + '.min'
    }))

    .pipe(gulp.dest(paths.css));

Using Google Fonts

The Post CSS plugin Font Magician will generate our font-face rules when we use Google Fonts.

Want to use Google Fonts? I'll add them for you. Want to use the visitor's local copy of a font? Done. Want to host your own fonts? Just tell me where they are, and I'll do the rest.

npm install postcss-font-magician --save-dev
var gulp = require('gulp'),
    rename = require('gulp-rename'),
    sass = require('gulp-sass'),
    postcss = require('gulp-postcss'),
    cssnano = require('cssnano'),
    autoprefixer = require('autoprefixer'),
    plumber = require('gulp-plumber'),
    fontmagician = require('postcss-font-magician'); // add postcss-font-magician

Because we'll be using Font Magician, we do not want cssnano to optimize font values:

gulp.task('styles', function () {
    var plugins = [
        autoprefixer({ browsers: 'last 2 major versions' }),
        cssnano({ minifyFontValues: false }), // add optimization option
        fontmagician() // add fontmagician
    ];

Font_Magician_Fonts