WebForms can Gulp too – Using Node Tools with ASP.NET WebForms

Visual Studio 2015 brought a set of tools into the IDE from the Node environment that are great for web developers to use.  Many folks who build JavaScript applications or single-page-applications were eager to see the introduction of these tools to Visual Studio.  Can the tried and true ASP.NET Web Forms framework use Gulp, npm, bower, and the Task Runner Explorer?  In this blog post, we’ll take a look at how we can replace the ‘out of the box’ use of the Script Manager in a Web Forms app with these node-based tools.

Why Not Use ScriptBundles?

ScriptBundles and StyleBundles are a great tool that allow you to configure and deliver resources to your visitors in a compressed package to reduce or even eliminate extra requests to your web server.  These features allow you to define a bundle of resources in an xml-based config file or configuration classes like BundleConfig.cs  This approach, while easy to configure, requires the web service execute server-side code in order to configure and deliver these resources.  The alternative approach described below, completes this work one time during the build process and delivers static files every time to the requestor.  This approach will also prepare your assets for use on Cordova, WinJS, or ASP.NET Core where you don’t have the bundling capabilities.  Finally, because we are using package managers to manage our scripts and styles, we can get new versions with fixes and features installed easily with the help of these tools.

Introducing npm, gulp, and bower

Gulp Bower Npm logosThese three tools provide the building blocks for browser-side coding in our application.  Let’s take a quick look at each tool and what it does:

  • npm is the Node Package Manager.  This is the NuGet equivalent in the Node platform and can install other Node tools and components, including the other two listed below.
  • Bower is another package manager that delivers static content like CSS, Images, JavaScript, and Fonts.
  • Gulp is a task runner that can be used to ‘build’ your browser-side resources for delivery to your visitors.  It can be used to automate other tasks, but provides a great JavaScript-based framework and toolset to automate your repetitive tasks.

This walk-through focuses on the default ASP.NET 4.6 template and converting those resources to use this acquisition and build mechanism.

Step 1 – Configure npm

Nope.. Step 1 is not install NodeJS, because if you’re using Visual Studio 2015 its already installed for you in C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\Web Tools\External   We can move forward and start configuring the node package manager for our project.  In the Visual Studio Solution Explorer window, Add a New Item to the base folder of your project, and choose npm Configuration File.  You’ll probably notice that the other configuration files we’re going to use are in the list as well: Bower Configuration File and Gulp Configuration File.  Go ahead and add those to your project as well.

Starting with the package.json file, we can see that there isn’t a lot in the base file to start.  Thanks to the intellisense features built into Visual Studio, we’re going to get some help in building out the list of devDependencies at the bottom of the file.

initialPackageJson

Inside of the devDependencies block, lets enter a JSON name-value pair for the gulp package.  As you start typing “gulp”: the Visual Studio editor should begin showing some intellisense hints of the list of packages and versions available.  Choose the latest version of Gulp, and lets also add references for the gulp-concat, gulp-cssmin, and gulp-uglify packages.  My package.json file looks like this:

You can now right-click on the package.json file in your solution explorer and choose to ‘Restore Packages’ and Visual Studio will run the npm install command to acquire and make these packages available to you in a new “node_modules” folder.

Step 2 – Configuring Bower

Since we already added a bower.json template in the previous step, we work with that file to begin adding package references to match those that were installed by NuGet into our project.  There are two ways to do this: right-click bower.json and open the bower package manager UI or you can edit the bower.json file by hand.  In both scenarios, you need to add the bootstrap, jquery, modernizer, and respond packages to your configuration.  In my bower.json file, I added the matching version numbers of these packages so that they match the default version numbers in the ASP.NET template:

If you keyed in the package names like I did, you can perform a similar right-click action on the bower.json file to restore packages. This will create the bower_components folder in your project folder and fill it with the files from these packages.

Step 3 – Configure Bootstrap and My CSS with Gulp

Next, we need to write some JavaScript in the gulpfile.js file to dictate the tasks that need to be accomplished to build our client-side resources.  The initial gulpfile starts with a line to require(“gulp”) and we will need to require those other gulp-* libraries that were referenced by npm.    Those lines will look like the following:

Initial Gulp references

Next, start writing the gulp task called “min-css” with the following content:

The task is defined with the task method on the Gulp object, submitting a name for the task and an function that should be executed for this task. Each step of this task’s function is a call from the gulp object through a series of pipes.  In Gulp, content is piped from one step to the next using the pipe function.  The first steps on lines 14,15 grabs the contents of the Bootstrap fonts folder and copies it to the fonts folder in the project.  The second step on lines 17,18 does the same thing with the JavaScript files for bootstrap.  The final steps in lines 21-25 do some extra steps besides copying content. In this last step, the contents of the bootstrap.min.css file and the site.css file are to be processed.  These two files are marked up in the src method as an array of strings and are then piped into the concat command.  Concat will take the contents of the pipe and combine them into one file at the location submitted, in this case the Content/site.min.css file.  Those contents are then passed into the cssmin function which will optimized the content of the CSS files by eliminating spaces, tabs and comments.  This makes it smaller for transport and easier to download 1 file than multiple files.  The final method, gulp.dest indicates that the results of the cssmin operation should be written back to that site.min.css file. At this point, we can run this step in the Task Runner Explorer window in Visual Studio by right clicking on the ‘min-css’ task on the left side and choosing Run. This will run the task in the extra window pane of the Task Runner Explorer, and you should quickly have Bootstrap configured with a minimized CSS stylesheet at site.min.css Step 4 – JavaScript Configuration The JavaScript processing is a little more involved than the CSS, but reuses some of the same tasks we just learned about.

This time, we’re going to assemble a collection of these files into one JavaScript file.  For each of these five steps,  we’re going to move files into the Scripts folder.  For the jQuery, Modernizr, and Respond libraries it’s a straight copy into the Scripts folder.  With the MSAjaxBundle and WebFormsBundle, we’re going to take some steps before copying.  These two bundles are originally added to your project through NuGet packages and bring a significant number of JavaScript files to your project.  Let’s do something to simplify that.

Each of these steps for MSAjaxBundle and WebFormsBundle collects a group of JavaScript files by using a wildcard in the src function.  They then pipe those contents into the concat function to make one big JavaScript file named MSAjax.min.js or WebForms.min.js accordingly.  The contents of those files are then piped into the uglify function, a function that minifies JavaScript by eliminating spaces, comments, and reduces the name of variables in JavaScript to a single character.  Finally, those contents are saved back to disk in the same way that our CSS was with the gulp.dest method.

We can now run this js task in the Task Runner Explorer window and see it produce the five JavaScript files for our project:

TaskRunnerExplorer

 Step 5 – Update the Master page

In the Master page, you can remove all references to the bundles and replace them with direct references to the scripts and styles:

MasterPage Updated

You can also remove the configuration of bundles in the App_Start\BundleConfig.cs file to reduce some processing at application start up.

This is nice, but what happens when I add more to that site CSS file?  I need to re-run the min-css step, where ASP.NET would just recompile it each time.

Step 6 – Configure Gulp-Watch

You can configure gulp to watch files for an update and when that update occurs, trigger tasks to be processed.  This feature can be activated with the watch method on the gulp object.  I like to create a task called simply watch and have that task run in the background while I code.  Here is the code for my watch task in this project:

This method takes an array of file globs and an array of tasks to execute if it detects a change in any of the files referenced.  Now, when I change my site.css file, the minified version is regenerated very quickly.  The Task Runner Explorer makes this even easier to use, because I can bind my watch task to the ‘Project Open’ event so that the tasks runs whenever I open this project.  To enable this event binding, right-click on the ‘watch’ task in the Task Runner Explorer and choose “Bindings – Project Open”

Summary

This is just the beginning of a journey into the Node toolset for ASP.NET web forms developers.  There are many different ways to configure scripts and tasks to run against your project to automate many repetitive tasks.  What are some of your favorite packages and tips for managing JavaScript, CSS, and other resources in your project?  Let us know in the discussion area below.