How to Automate ASP.NET Custom Controls with the Gulp JavaScript Task Runner

Here I am, writing a module for my next Pluralsight course and I ran into an interesting problem.  When I write a custom ASP.NET control, I now have lots of JavaScript and CSS libraries that I want to ship with it.  With the introduction of the Task Runner Explorer in Visual Studio, I decided to try my hand at writing a Gulp script that will combine and compress these static resources inside of my class library project.

Introducing Gulp for the ASP.NET Developer

Gulp is another task runner for use with NodeJS, similar to Grunt.  Unlike Grunt, Gulp runs based on a JavaScript file of instructions and can be run in an asynchronous manner.  This leads to Gulp being a bit easier to read as a series of functions that will run faster than the Grunt JSON configuration file.

To get started with Gulp and Visual Studio:

1. Install NodeJS from www.nodejs.org

2. Install Gulp with the NPM package manager as follows:

npm install gulp -g

This will install Gulp globally and make the gulp command-line executable available.

3. Create a package.json file in the root of your project.  This can be a file that contains just a pair of curly braces.

4. Configure gulp locally to the project by running the following script on the command line at the project root folder:

npm install gulp --save-dev

5. Install a handful of gulp plugins to manage the concatenation, renaming, minifying and notification processes:

npm install gulp-minify-css gulp-concat gulp-uglify gulp-notify gulp-rename --save-dev

With those command issued, we’re ready to build our gulpfile.js to contain the instructions for how to automate working with our static resources.

Create a file called gulpfile.js on the root of the project.  This file should be excluded from the project.  Begin by defining a series of variables using gulp’s built-in require commands:

var gulp = require('gulp'),
  concat = require('gulp-concat'),
  minifycss = require('gulp-minify-css'),
  rename = require('gulp-rename'),
  notify = require('gulp-notify'),
  uglify = require('gulp-uglify');

Next, we can define a simple gulp task to minify the bootstrap.css file that is in our project:

gulp.task('css', function() {

  return gulp.src('css/bootstrap.css')
    .pipe(rename('mycontrols.min.css'))
    .pipe(minifycss())
    .pipe(gulp.dest('css'))
    .pipe(notify({message: 'Styles minified'}));

});

The name of the task is “css” and it will trigger a function.  The function returns the result of operations on the css/bootstrap.css file.  The file is piped from one operation to the next with the pipe method.  First, the file is renamed to mycontrols,min.css so that the file matches the “mycontrols” project name.  Next, the contents of the CSS file are minified with the minifycss command and then piped to the css folder using the gulp.dest method.  Finally, a notification is triggered with the notify method and the message text specified.

The JavaScript processing is similar:

gulp.task('js', function() {
  
  return gulp.src(['scripts/jquery-1.10.2.js','scripts/respond.js','scripts/bootstrap.js'])
    .pipe(concat('mycontrols.js'))
    .pipe(gulp.dest('scripts'))
    .pipe(rename({suffix: '.min'}))
    .pipe(uglify())
    .pipe(gulp.dest('scripts'))
    .pipe(notify({message: 'Scripts merged and minified'}));

});

This time, I am passing an array of files in to be processed.  The collection are concatenated together with the concat method into the mycontrols.js file and written into the scripts folder with another gulp.dest method.  The filename is renamed with a suffix of .min and sent into the uglify processor for minification.  The results of the uglification are written back to the scripts folder and a notification is triggered with an appropriate message.

Finally, I wrote a default task to call the JS and CSS tasks.  I called this task default:

gulp.task('default', ['js','css'], function() {});

The second argument in this method call is the collection of dependent tasks that should be called before the function is executed.  In this way, the js and css tasks are called asynchronously.

Automating Gulp with Task Runner Explorer

With the Task Runner Explorer extension in Visual Studio, I can connect any and all of these tasks to various steps in the build process.  In my case, I right-clicked on the ‘default’ task and chose to bind it to the ‘Before Build’ run time.

Now, every time I build my MyControls project, the gulp default task will run and deliver appropriate CSS and JS files for my controls to use.

 

Custom Control Project Considerations

Importantly, for this project I marked my gulpfile, package.json, and all of my original CSS and JS files as  Build Action None and Do not copy to output directory.  I did mark the new mycontrols.min.js and mycontrols.min.css as Embedded Resources so that they would be delivered inside of the DLL I will distribute with my controls.

Summary

The new Task Runner Explorer combined with the NodeJS command line interface and Gulp have made automating packaging assets for my custom controls much easier than previously.  Now, visitors to my applications will download one script file and one css file for my controls.  This will provide a faster and easier experience for my visitors and my developers that use my custom ASP.NET controls