Visual Studio 2015 Front-end Tooling - Gulp and Grunt

In our second post, we present two new JavaScript tools introduced in Visual Studio 2015: Grunt and Gulp. Today, we show you what they are and how to use them for client-side tasks.

Written by Jonathan "JD" Danylko • Last Updated: • Develop •
A runner getting ready to race.

Visual Studio Front-End Tooling Series

In our last post, we focused on optimizing our CSS using LESS and SASS. Now, let's look at automating these tasks.

With the announcement of when ASP.NET Core 1.0 arrives on June 27th through Scott Hunter's keynote presentation, .NET is moving towards a more cross-platform framework and a different way of developing web applications. A lot of concepts will change in the web world.

This will include a different way of minifying, bundling, and image optimization just to name a few.

Grunt and Gulp are such tools to help out with these tasks.

If you build ASP.NET MVC sites, you may notice something different when you create a new ASP.NET MVC Core 1.0 project.

Where's my bundles?!?!

If you've created a new project lately using ASP.NET MVC Core 1.0, you'll notice that your bundleconfig.cs is missing, isn't it?

That's because task-runners are the next evolution in web development. These tools help you to create that CSS and JavaScript bundle at design-time.

Yes, it looks like we need to learn a new tool.

What is a task runner?

A task runner is a tool used to automate a series of front-end tasks to save you time by using either Grunt or Gulp. Both are popular and have their differences.

Visual Studio 2015 already has a task-runner included and works with either Grunt or Gulp.

Since both are JavaScript based, they require NodeJs, which is also part of Visual Studio 2015.

But what's the difference between the two?

The Difference Between Gulp and Grunt

Gulp and Grunt are JavaScript-based build toolkits for client-side tasks.

Gulp uses Node Streams in memory where Grunt uses a file-based approach. Gulp's Node Streams are similar to pipes in Unix. It accepts data as input and pushes it as output.

Grunt uses a file-based approach and saves the progress in .tmp files.

One downside I noticed with Grunt is that when you change one file, all tasks run again, not just one that was changed...ALL of them.

Gulp places all of their tasks into a file called gulpfile.js where Grunt places their tasks into the gruntfile.js.

Grunt focuses on configuration where Gulp focuses on code.

Grunt is a bit more limited because it was built with a collection of presets for minifying, bundling, and optimization of code where Gulp enforces nothing and allows the micro-tasks to be more community-driven in connecting other tasks in the pipeline.

The good news is that both are included in Visual Studio 2015. If you create a new ASP.NET Core 1.0 project, you'll notice the package.json contains a devDependencies. This is where you place either Grunt or Gulp dependencies.

When creating a new project, Microsoft went with Gulp as the default build toolkit.

Moving forward, we'll focus on using Gulp in your projects.

Understanding Gulp Files

If you look under the Dependencies folder in your ASP.NET MVC Core structure, you'll notice that the folder called NPM has a list of Gulp plug-ins. These plug-ins are defined in the package.json.

Project structure with dependencies

You won't find it in your project unless you "Show All Files".

Another way to view it is to right-click on the NPM folder and click "Open package.json"

package.json

{
  "name": "asp.net",
  "version": "0.0.0",
  "private": true,
  "devDependencies": {
    "gulp": "3.8.11",
    "gulp-concat": "2.5.2",
    "gulp-cssmin": "0.1.7",
    "gulp-uglify": "1.2.0",
    "rimraf": "2.2.8"
  }
}

You can remove or add any Gulp package. When you save the json file, your npm directory will attempt to add or remove the packages.

Each package is explained below:

  • gulp - Core gulp
  • gulp-concat - package used to concatenate files together
  • gulp-cssmin - minify your CSS files
  • gulp-uglify - Minimize your JS files
  • rimraf - A deep deletion module for Node (think of the Unix command "remove" with -rf settings...<code>rm -rf</code>)

But how do you know the names of the packages you want in your automated workflow?

There are a number of recipes around the web. A couple of sites include:

A Gulp Example

With all of the fundamentals out of the way, let's pretend we didn't have the Web Compiler from last post and want to write a Gulp task for SASS.

First, we need npm to load a gulp package for SASS processing. The name of the package is called "gulp-sass".

So our new package.json file will look like this (with the change in bold):

package.json

{
  "name": "asp.net",
  "version": "0.0.0",
  "private": true,
  "devDependencies": {
    "gulp": "3.8.11",
    "gulp-concat": "2.5.2",
    "gulp-cssmin": "0.1.7",
    "gulp-sass": "2.3.1",
    "gulp-uglify": "1.2.0",
    "rimraf": "2.2.8"
  }
}

How did I know the version number? Use IntelliSense (CTRL-Space) to pick a version.

Save the file and you'll notice npm load the sass package.

Open your gulpfile.js and add a sass variable requiring the gulp-sass package (changes in bold).

var gulp = require("gulp"),
    rimraf = require("rimraf"),
    sass = require("gulp-sass"),
    concat = require("gulp-concat"),
    cssmin = require("gulp-cssmin"),
    uglify = require("gulp-uglify");

Next, we need to know where our SASS files are at and since we already have paths, we might as well add a new SASS path and destination path. I added a new directory called SCSS with a sample scss file in it to confirm it works.

var paths = {
    scss: webroot + "scss/**/*.scss",
    scssDest: webroot + "css",
    js: webroot + "js/**/*.js",
    minJs: webroot + "js/**/*.min.js",
    css: webroot + "css/**/*.css",
    minCss: webroot + "css/**/*.min.css",
    concatJsDest: webroot + "js/site.min.js",
    concatCssDest: webroot + "css/site.min.css"
};

Since we aren't specifying a filename and just a directory, Gulp will proceed to process all files with a .scss extension, dump the file into the destination directory, and overwrite the file.

Now we need the actual task to perform the SASS pre-processor.

gulp.task("sass", function() {
    return gulp.src(paths.scss)
        .pipe(sass())
        .pipe(gulp.dest(paths.scssDest));
});

To explain this a little better, we are creating a task called "sass" and it will grab all of the scss files and pipe them into the sass() function for conversion and finally dump them out to the CSS directory.

When you right-click on the "sass" task in your task-runner and Run, you will notice your new CSS is generated.

Here is our complete gulpfile.js.

gulpfile.js

/// <binding Clean='sass' />
"use strict";
var gulp = require("gulp"),
    rimraf = require("rimraf"),
    sass = require("gulp-sass"),
    concat = require("gulp-concat"),
    cssmin = require("gulp-cssmin"),
    uglify = require("gulp-uglify");
var webroot = "./wwwroot/";
var paths = {
    scss: webroot + "scss/**/*.scss",
    scssDest: webroot + "css",
    js: webroot + "js/**/*.js",
    minJs: webroot + "js/**/*.min.js",
    css: webroot + "css/**/*.css",
    minCss: webroot + "css/**/*.min.css",
    concatJsDest: webroot + "js/site.min.js",
    concatCssDest: webroot + "css/site.min.css"
};
gulp.task("sass", function() {
    return gulp.src(paths.scss)
        .pipe(sass())
        .pipe(gulp.dest(paths.scssDest));
});
gulp.task("clean:js", function (cb) {
    rimraf(paths.concatJsDest, cb);
});
gulp.task("clean:css", function (cb) {
    rimraf(paths.concatCssDest, cb);
});
gulp.task("clean", ["clean:js", "clean:css"]);
gulp.task("min:js", function () {
    return gulp.src([paths.js, "!" + paths.minJs], { base: "." })
        .pipe(concat(paths.concatJsDest))
        .pipe(uglify())
        .pipe(gulp.dest("."));
});
gulp.task("min:css", function () {
    return gulp.src([paths.css, "!" + paths.minCss])
        .pipe(concat(paths.concatCssDest))
        .pipe(cssmin())
        .pipe(gulp.dest("."));
});
gulp.task("min", ["min:js", "min:css"]);

Conclusion

Grunt and Gulp are great to automate your workflows, but each one has their own way of assisting with client-side tasks.

As mentioned, Grunt uses files and Gulp uses in-memory streams which, in my opinion, is a better way to create custom pipelines for automating your tasks.

Related Material

Visual Studio Front-End Tooling Series

ASP.NET 8 Best Practices on Amazon

ASP.NET 8 Best Practices by Jonathan Danylko


Reviewed as a "comprehensive guide" and a "roadmap to excellence" with over 120 Best Practices for ASP.NET Core 8, Jonathan's first book by Packt Publishing explores proven techniques for every phase of the SDLC.

Learn industry-standard concepts to improve your coding, debugging, and deployment of ASP.NET Core websites.

Order now on Amazon.com button

Picture of Jonathan "JD" Danylko

Jonathan "JD" Danylko is an author, web architect, and entrepreneur who's been programming for over 30 years. He's developed websites for small, medium, and Fortune 500 companies since 1996.

He currently works at Insight Enterprises as an Architect.

When asked what he likes to do in his spare time, he replies, "I like to write and I like to code. I also like to write about code."

comments powered by Disqus