DotNetCurry Logo

Using Grunt, Gulp and Bower in Visual Studio 2013 and 2015

Posted by: Ravi Kiran , on 3/4/2015, in Category Visual Studio, VSTS & TFS
Views: 117898
Abstract: Using Grunt, Gulp and Bower to build modern web apps in Visual Studio 2013 and 2015

Though Visual Studio started as an IDE for building applications based on Microsoft Technologies, lately the IDE is expanding its domain by adding capabilities to build apps based on other technologies too, including many open source technologies. Projects like Node.js Tools for Visual Studio (NTVS) and Python Tools for Visual Studio (PTVS) prove the openness the IDE is adopting these days.

 

Front-end web development went through a number of changes in the recent past. One primary change especially when it comes to developing on the Microsoft stack has been to take advantage of the rich ecosystem of tools created by the community. At times, it feels like front-end web development was always incomplete without tools like Bower, Grunt and Gulp - some JavaScript based task runners and package manager. It would be hard to find an open source front-end project on GitHub that doesn’t use these tools. So any rich web development environment not taking advantage of this already existing rich ecosystem, may sound a bit dated. Being a fan of Visual Studio for years, I was expecting these features to be supported by the IDE. Thankfully, the IDE got a couple of extensions recently that fills this gap for us.

This article is published from the DotNetCurry .NET Magazine – A Free High Quality Digital Magazine for .NET professionals published once every two months. Subscribe to this eMagazine for Free and get access to hundreds of free .NET tutorials from experts

In this article, we will see how these tools can be used to build modern web apps on Visual Studio. This articles uses Visual Studio 2013 and the latest Preview/CTP5 version of Visual Studio 2015.

Installing the Visual Studio Extensions

If you are a user of Visual Studio for a long time, you would be using Web Essentials too. Web Essentials is an extension from Microsoft created by Mads Kristensen, that adds a lot of features to make web development better on Visual Studio. In Visual Studio 2015, Web Essentials adds the features of adding Grunt and Gulp to Visual Studio. For VS 2013, we have a plugin called Grunt Launcher, written by a community member to add tooling support for Grunt and Gulp.

Before you move forward, make sure that you have these extensions installed. They can be installed either directly from Visual Studio menu (Tools > Extensions and Updates), or by downloading the vsix files from Visual Studio Extensions Gallery.

  • For VS 2013: Grunt Launcher
  • For VS 2015: Web Essentials

You need to restart Visual Studio in order to get these extensions working.

An Introduction to Bower, Grunt and Gulp

The tools that I have been mentioning till now, namely Bower, Grunt and Gulp, work on top of Node.js. You should have Node.js already installed on your system. The three tools we are going to work on can be installed using NPM (Node Package Manager). Following are the commands and a brief note on these tools:

bower: Bower is a package manager used to manage front-end packages in a project. It can be used to fetch packages from any Git repository. By default, it uses GitHub as the source of repositories. Bower has to be installed globally using the following command:

npm install bower -g

grunt: Grunt is a task runner. It can be used to perform a series of operations like concatenation, minification, copy, clear, check for quality using JS Hint, start an express server and similar tasks. Like bower, grunt also needs to be installed globally. The list of tasks have to be configured on a special file named Gruntfile.js. This name is a convention and shouldn’t be changed. The configuration has to be defined using an object literal. Grunt has to be installed globally using the following command:

npm install grunt –g

gulp: Gulp is an alternative to Grunt. Unlike Grunt, tasks for gulp have to be defined using pipes and streams. The tasks have to be defined in a file named Gulpfile.js; like Grunt, this file has to be named by convention. Gulp has to be installed globally as well using the following command:

npm install gulp -g

Note: To use Grunt and Gulp in VS 2015 Preview/CTP and in VS 2013, you need to have them installed globally. Final release of VS 2015 will use the packages installed in the project and won’t need the global packages.

Now that you got some idea on the tools to be used, let’s use them in an application.

About the Demo Application

We will not be building the application from scratch. Instead, I will add grunt and gulp support to an existing application. The demo application is a Movie List application (If you read my article on Node.js tools on Visual Studio, I am using the same demo here). It is a simple movie list application that has the following features:

  • Shows a list of movies
  • Adding a new movie
  • Marking a movie as released
  • Marking a movie as watched

If you want to follow along this article, download the sample code of this article and open the MovieList – 2013 or, MovieList – 2015 on either VS 2013 or VS 2015 respectively. These folders contain all of the required code, but don’t have NPM packages, Bower packages and Grunt or Gulp added to them. VS 2013 project is created from an empty ASP.NET template and the VS 2015 project is created using ASP.NET MVC 5 starter template. The reason for choosing two different templates is to show that Grunt and Gulp can be used with any kind of application.

Both these projects contain Entity Framework code first classes and ASP.NET Web API or MVC 6 controllers to serve data. On the front-end, they have just one view built using Bootstrap and Angular. This view fetches data from the API and binds it to the view. You won’t be able to run the application unless you have the required scripts and styles at their target places.

In rest of the article, we will add the following Grunt or Gulp tasks to this application:

  • Install and Copy bower components
  • Run Karma tests on Chrome
  • Concatenate JavaScript files
  • Copy concatenated JavaScript file and CSS file to a target folder
  • Clear contents of a folder
     

Using Grunt and Bower in Visual Studio

Once you open the project inside Visual Studio, check the files and folders to get familiar with the code and the way it is organized. The project was created using ASP.NET Empty project template and then I added some other files to it. As you see, the project doesn’t have any existing Node and bower dependencies. Let’s add them now.

Add a new file to the project and name it package.json. This file will contain the list of Node.js dependencies required by the project. Add the following content to the file:

{
    "name": "Movie-list",
    "preferGlobal": true,
    "version": "0.1.0",
    "author": "Ravi Kiran",
    "description": "A sample movie list application",
    "bin": {
        "package-name": "./bin/package-name"
    },
    "dependencies": {
    },
    "analyze": false,
    "devDependencies": {
        "grun": "^0.4.5",
        "grunt-bowercopy": "^1.2.0",
        "karma": "^0.12.31",
        "karma-jasmine": "^0.3.5",
        "grunt-karma": "^0.10.1",
        "grunt-contrib-copy": "^0.7.0",
        "grunt-contrib-concat": "^0.5.0",
        "grunt-contrib-clean": "^0.6.0"
    },
    "license": "MIT",
    "engines": {
        "node": ">=0.6"
    }
}

If you type-in the dependencies, the IDE shows a list of suggestions based on the text entered. It fetches this information from the global NPM registry. It also shows suggestions for versions. Figure-1 shows a screenshot of the intellisense:

npm-intellisense

Figure 1: npm intellisense

To install these packages, you can either run the following command in a command prompt:

npm install

Or, you can use menu options in Visual Studio. The options are different in VS 2013 and VS 2015. In VS 2013, this option is available on right clicking the package.json file, as shown in Figure-2:

npm-install-vs-2013

Figure 2: NPM packages in Visual Studio 2013

In VS 2015, this option is available through context menu on NPM node under Dependencies node. Figure -3 shows it:

npm-restore-package

Figure 3: NPM packages in Visual Studio 2015

Now add another file to the project and name it bower.json. It should have following content:

{
  "name": "MovieList",
  "version": "0.1.0",
  "authors": [
    "Ravi Kiran"
  ],
  "description": "A sample movie list app",
  "license": "MIT",
  "private": true,
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "angular": "~1.3.11",
    "bootstrap": "3.3.2"
  }
}

Even in this file, you will get intellisense when you type-in the dependencies.

bower-intellisense

Figure 4: Dependencies Intellisense in bower.json

 

VS 2013 doesn’t have a menu to install Bower components, but VS 2015 has it. In VS 2015, you can install the Bower packages using Restore Packages option in the context menu in Bower option. I prefer installing the Bower packages using Grunt instead. Right click on the application and choose Add > New Item. From the menu, choose Gruntfile.js if available, otherwise add a new JavaScript file and name it Gruntfile.js. Add the following content to this file:

module.exports = function (grunt) {
    'use strict';
    grunt.initConfig({
        // read in the project settings from the package.json file into the pkg property
        pkg: grunt.file.readJSON('package.json')
    });
 
    //Add all plugins that your project needs here
    grunt.loadNpmTasks('grunt-bowercopy');
    grunt.loadNpmTasks('grunt-karma');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-clean');
};

Here we are loading all available grunt tasks and storing the contents of the package.json file in an object for further use.

To install Bower components using Grunt, we need to use the grunt-bowercopy task referred above. At times, we may not want to have all Bower dependencies in the default folder. In such cases, we can move these files to a place of our choice by setting paths for each of the dependency. Following snippet shows the Grunt configuration of this task, it has to be added as a property to the object passed to grunt.initConfig method in the snippet above:

bowercopy: {
    options: {
        runBower: true,
        destPrefix: 'public/libs'
    },
    libs: {
        files: {
            'angular': 'angular',
            'jquery': 'jquery/dist',
            'bootstrap': 'bootstrap/dist/css'
        }
    }
}

Note: In VS 2015, modify the value of destPrefix to wwwroot/lib, as all static files are referred from wwwroot by default

This task will copy the libraries into the folder libs under public folder.

Though we create a number of files while writing code, we ship just one file containing all the code in a minified format to optimize the network calls by browsers. For the movie list application, I created 3 different JavaScript files. Let’s write Grunt tasks to concatenate these files. The files can be minified using grunt-contrib-uglify task, I am leaving it as an assignment to the reader. Alternatively you can also read Using Grunt.js to Merge and Minify JavaScript files in an ASP.NET MVC Application.

Let’s concatenate the files using the Grunt task grunt-contrib-concat. Following is the Grunt configuration for this task, it concatenates the three files and stores the resultant file in another folder.

concat: {
    options: {
        separator: ';'
    },
    dist: {
        src: ['app/moviesApp.js', 'app/moviesCRUDSvc.js', 'app/MoviesCtrl.js'],
        dest: 'app/combined/moviesCombined.js'
    }
}

It is a good practice to have all deliverables at one place to make the deployment easier. So let’s copy the combined JavaScript file and the CSS file to a single folder. It is done using the grunt-contrib-copy task. Following is the configuration:

copy: {
    main: {
        expand: true,
        flatten: true,
        filter: 'isFile',
        src: ['app/combined/*.js','styles/*.css'],
        dest: 'public/dist/'
    }
}

Note: In VS 2015, modify the value of destPrefix to wwwroot/dist

The option flatten: true is used to avoid creation of folder structure under the target folder.

After copying the combined JavaScript file, we can remove it from the place where it was created. Following is the clean task that does this:

clean: ["app/combined/"]

We need these tasks to run sequentially. So let’s combine them in the right order into one task. Following is the task:

grunt.registerTask('default', ['bowercopy:libs', 'concat:dist','copy:main', 'clean']);

Here, default is the alias task that runs a number of other tasks internally.

Finally, to run the unit tests using Karma, we need to add a task. Following is the task for it:

karma: {
    unit: {
        configFile: 'karma.conf.js'
    }
}

Now we are done with configuring the tasks. Let’s run the tasks and see how it behaves. To run the task, open Task Explorer in Visual Studio through View > Other Windows > Task Explorer.

As you see, the task explorer is smart enough to detect the Grunt file and list all tasks and alias tasks configured. Right click on the default task and click run. This will run all the tasks under default, which means, it will run bower, copy libraries, concatenate, and copy the files to the specified location. Figure-5 shows output of an instance of the execution:

grunt-default

Figure 5: Task Runner Explorer

Now run the application using F5 or Ctrl+F5. You will be able to see the page loading in the browser.

As we didn’t include karma in the default task, it didn’t run tests. We can run karma using the karma node in the Task Runner Explorer. Figure-6 shows an instance of running karma:

grunt-karma

Figure 6: Karma Unit tests

Using Gulp and Bower in Visual Studio

Gulp is an alternative to Grunt. It uses a different approach to solve the same problem. As already mentioned, Gulp uses node streams. Each task in Gulp produces a stream that can be piped into another task to continue execution. This approach is comparatively efficient than the approach used by Grunt, as it doesn’t need to store results physically to make it available to the next task.

Also, being programmers we prefer calling functions over writing configuration objects. In Gulp, we write chained function calls (as we do in LINQ).

Contents of the bower.json file would remain the same as in case of Grunt. Contents of package.json would change to include Gulp task packages. Following are contents of the package.json file:

{
    "name": "Movie-list",
    "preferGlobal": true,
    "version": "0.1.0",
    "author": "Ravi Kiran",
    "description": "A sample movie list application",
    "bin": {
        "package-name": "./bin/package-name"
    },
    "dependencies": {
    },
    "analyze": false,
    "devDependencies": {        
        "karma": "^0.12.31",
        "karma-jasmine": "^0.3.5",
        "gulp": "^3.8.10",
        "gulp-bower": "^0.0.10",
        "gulp-concat": "^2.4.3",
        "gulp-karma": "^0.0.4"
    },
    "license": "MIT",
    "engines": {
        "node": ">=0.6"
    }
}

Install these packages using the Visual Studio context menu options discussed in the previous section.

Add a new JavaScript file to the project and name it Gulpfile.js. This name is a convention, we can’t change the name. To start with, load all required NPM packages to this file:

var gulp = require('gulp');
var bower = require('gulp-bower');
var concat = require('gulp-concat');
var karma = require('gulp-karma');

Let’s install bower packages using the gulp-bower package. Following snippet creates a gulp task using this package to install all bower components inside bower_components folder:

gulp.task('bower', function () {
    return bower('./bower_components');
});

Now that we have the front-end libraries, let’s move them to a different folder. For this, we need to set a destination for each of them. Following task does it:

gulp.task('copyLibs', ['bower'], function () {
    gulp.src(['bower_components/angular/*.*'])
        .pipe(gulp.dest('public/libs/angular'));
 
    gulp.src(['bower_components/bootstrap/dist/css/*.*'])
        .pipe(gulp.dest('public/libs/bootstrap'));
 
    gulp.src(['bower_components/jquery/dist/*.*'])
        .pipe(gulp.dest('public/libs/jquery'));
});

Note: In VS 2015, modify values passed into dest as wwwroot/lib, as all static files are referred from wwwroot by default

As you see, this setup is quite different from the setup we did in grunt. It is also a bit tricky to think in the terms of Gulp if you are used to Grunt for a long time. In the above snippet, gulp.src is a task and its result is passed to gulp.dest using pipe. As mentioned earlier, Gulp works on streams. gulp.src produces a stream and this stream is used by gulp.dest.

The second parameter in the above snippet is a list of tasks that have to run before this task runs. So whenever we run the copyLibs task, it internally runs the bower task.

Let’s combine the script files into one file using the gulp-concat task. As we can combine multiple tasks using pipes, we can copy the concatenated files to a target folder in the same task. Following is the task:

gulp.task('concat', function () {
    return gulp.src(['app/moviesApp.js', 'app/moviesCRUDSvc.js', 'app/MoviesCtrl.js'])
    .pipe(concat('public/dist/moviesCombined.js'))
    .pipe(gulp.dest('.'));
});

The only independent task left is copying CSS file to the dist folder. This task is straight forward.

gulp.task('copyCss', function () {
    return gulp.src(['styles/*.css'])
        .pipe(gulp.dest('public/dist'));
});

Note: In VS 2015, modify values passed into dest as wwwroot/dist, as all static files are referred from wwwroot by default

Finally, we need to combine these tasks into one task. Following is the default task that combines all of these tasks:

gulp.task('default', ['copyLibs', 'copyCss', 'concat']);

To ensure correctness of the code, we should run unit tests too. Following task creates a task to run tests using karma:

gulp.task('karma', function () {
    return gulp.src(['tests/*.js']).pipe(karma({
        configFile: 'karma.conf.js',
        action: 'watch'
    }));
});

Task Runner Explorer in Visual Studio parses the Gulpfile.js file and lists all of the tasks configured as nodes using which we can directly run the tasks. Following figures 7 and 8 show instances of running the default task and the karma unit task respectively:

gulp-default

Figure 7: Running Default task

gulp-karma

Figure 8: Running Karma Unit task

Conclusion

As you saw, Visual Studio has first class support for working with the front-end task runners. If you have kept yourself away from these tools till now, now is the time to start using them to get your job done and have an experience with the tools that everyone else is using. You will love it!

Download the entire source code of this article (Github)

 

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+
Further Reading - Articles You May Like!
Author
Rabi Kiran (a.k.a. Ravi Kiran) is a developer working on Microsoft Technologies at Hyderabad. These days, he is spending his time on JavaScript frameworks like AngularJS, latest updates to JavaScript in ES6 and ES7, Web Components, Node.js and also on several Microsoft technologies including ASP.NET 5, SignalR and C#. He is an active blogger, an author at SitePoint and at DotNetCurry. He is rewarded with Microsoft MVP (Visual Studio and Dev Tools) and DZone MVB awards for his contribution to the community


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by ng-martin on Thursday, March 5, 2015 5:24 PM
HiRavi, thanks for your article. I tried to download the sample code but its not available.
MA.
Comment posted by ng-martin on Thursday, March 5, 2015 5:32 PM
HiRavi, thanks for your article. I tried to download the sample code but its not available.
MA.
Comment posted by Suprotim on Friday, March 6, 2015 5:58 AM
@ng-martin sorry about that. We fixed it. Try the sample again.
Comment posted by Trent on Wednesday, April 1, 2015 7:18 AM
What about during a TFS build. Do i need to run grunt with node to achieve the same?
Comment posted by Ravi on Wednesday, April 1, 2015 7:40 AM
Trent, Yes. You should have Node installed on the build server and your deployment process should have grunt scheduled in it.
Comment posted by David Paquette on Tuesday, May 5, 2015 6:41 PM
@Trent, I posted an article recently on integrating Gulp and Bower with hosted TFS builds:
http://www.davepaquette.com/archive/2015/04/08/integrating-gulp-and-bower-with-visual-studio-online-hosted-builds.aspx