Advanced Basics: Using Task Runner in Visual Studio

If the world is running on JavaScript, why not automate your client-side tasks through Visual Studio's Task Runner?

Written by Jonathan "JD" Danylko • Last Updated: • Develop •

Line-up of runners starting a race.

With JavaScript as currently the primary language for developers, it made sense when Microsoft released Visual Studio 2019 (or more recently Visual Studio 2022) to include client-side tools to simplify the JavaScript development process.

One of those tools was a Task Runner. 

When the Task Runner was introduced, developers of the C# community never even knew what it was or how it worked (including me).

Now, since I've dug into it, it proves it's another great tool in the DevOps toolbelt.

What is a Task Runner

A Task Runner is a tool to automate repetitive client-side tasks.

The tool runs off of a configuration file (either Gulp or Grunt) to execute on any number of static assets in a directory.

For this post, we'll focus on using Gulp with a gulpfile.js.

Setting up the Task Runner

In Visual Studio 2019 (or 2022), confirm you are running the latest Node version.

  1. Download the latest version of Node and install it normally.
  2. In Visual Studio, go to Tools, Options.
  3. Expand the Projects and Solutions, Web Package Management, then External Web Tools.
  4. Confirm $(PATH) is at the top of your "location of external tools"

If you installed NodeJs properly, it will be in your system path. The $(PATH) will point to that when a build process occurs.

Next, since a Task Runner uses Node for it's engine, we need a populated package.json file to hold the installed packages used in our Task Runner.

For starters, let's install our Gulp package.

  1. Open the Package Manager Console (View / Windows / Package Manager Console)
  2. Change into the directory containing the root of your solution.
  3. Type: npm install -g gulp-cli
  4. Then type: npm install --save-dev gulp

Examine the package.json file in the directory. It should contain a gulp entry:

"gulp": "^4.0.2"

Once we have our package installed, we can use it in our test.

Testing Gulp

One of the best ways to test it out is to create a simple task. Think of it like a "Hello World" for our Task Runner.

  1. Create a gulpfile.js in the root of your project with the following contents.

    var gulp = require('gulp');

    gulp
    .task('testTask', function (done) {
        console.log('Hello World! We finished a task!');
        done();
    });
  2. Save the file.
  3. Open your Task Runner Explorer.
  4. Right-click on your testTask and run it. 

You should see it executed to the right in the output window.

Troubleshooting

One of the issues I kept running into with the Task Runner was it kept saying "No Task Runner Configurations were found."

Screenshot of an unloaded configuration.

If you see this and you have a valid configuration file, click on the refresh button on the left and it should reappear.

The trick is if you make any changes to the gulpfile.js, it will unload from the Task Runner.

Click the refresh button to gain it back. NOTE: This has been fixed in Visual Studio 2022.

If you don't have a valid configuration file, check your Output Window (View / Output Window) and set your "Show Output From:" to Task Runner Output.

This will tell you where the error resides.  

Structure of a gulpfile.js

While every gulpfile is considered a "snowflake," the one structured pattern I see the most contains the following:

  • Packages defined at the top
  • Tasks (processing and cleanup)
  • Global default and cleanup tasks

The packages at the top are the modules installed through your "npm installs" 

For every automated concept in a gulpfile.js, there should be two tasks: a processing task and a cleanup task.

The processing task is meant to achieve what we want automated.

The cleanup task is meant to delete the created or processed files.

All of the examples below will contain this type of structure so you can reuse these tasks in your own projects.

Creating Tasks

For most tasks, you don't want to get confused by the various paths for each files which is why I create a source and destination object for my assets.

Now, the most used tasks for Task Runners are bundling and minifying. These two tasks are what makes websites fast.

I've always said speed is what keeps your audience on your site. If you create code small enough and relevant to a particular page, shove them into small chunks, and shrink down to their minimal size, you'll create websites that outperform 95% of your competition.

With that said, we'll focus on JavaScript and CSS.

Bundling/Minifying CSS

Whether it's SASS or LESS, bundling CSS is a necessity when building fast websites.

If you have a solid way of organizing your CSS, using Task Runners to bundle up stylesheets is an easy task. In our example, we'll be using SASS.

var path = require('path'),
    gulp = require('gulp'),
    rename = require('gulp-rename'),
    gp_clean = require('gulp-clean'),
    gp_sass = require('gulp-sass');

var
 basePath = path.resolve(__dirname, "wwwroot");

var
 srcPaths = {
   sass: [
        path.resolve(basePath, 'scss/blog-page.scss'),
        path.resolve(basePath, 'scss/users-page.scss'),
        path.resolve(basePath, 'scss/login-page.scss')
    ]
};

var
 destPaths = {
    css: path.resolve(basePath, 'css')
};

/* SASS/CSS */

gulp.task('sass_clean', function () {
    return gulp.src(destPaths.css + "*.*", { read: false })
        .pipe(gp_clean({ force: true }));
});

gulp
.task('sass', function() {
    return gulp.src(srcPaths.sass)
        .pipe(gp_sass({outputStyle: 'compressed'}))
        .pipe(rename({
            suffix: '.min'
        }))
        .pipe(gulp.dest(destPaths.css));
});

/* Defaults */

gulp.task('cleanup', gulp.series(['sass_clean']));

gulp
.task('default', gulp.series(['sass']));

You want your tasks to be as quick as possible.

Notice at the bottom. I have a global 'default' and 'cleanup' where you can have an array of tasks to cleanup and process your static files.

The gp_sass package eliminates two tasks with one method. It converts our stylesheets into plain CSS and compresses ("bundles") them into one. The last step adds a suffix of '.min' to the end of the file.

Minifying JavaScript

Ahh, the 400-pound gorilla in the room.

With JavaScript, it's only a matter of selecting the right libraries to perform the bundling and minifying of the project's scripts.

Again, if you have a method of organizing your scripts into a concise folder structure, the process will flow easier.

var path = require('path'),
    gulp = require('gulp'),
    gp_clean = require('gulp-clean'),
    gp_minify = require("gulp-minify");

var
 basePath = path.resolve(__dirname, "wwwroot");
var
 srcPaths = {     js: [         path.resolve(basePath, 'src/common/validation.js'),         path.resolve(basePath, 'src/common/treeTable.js')     ] };
var
 destPaths = {     js: path.resolve(basePath, 'js') };
/* JavaScript */
gulp.task('js', function () {     return gulp.src(srcPaths.js)         .pipe(gp_minify({ noSource: true }))         .on('error',             function (err) {                 console.error('Error!', err.message);             })         .pipe(gulp.dest(destPaths.js)); });
gulp
.task('js_clean', function () {     return gulp.src(path.resolve(destPaths.js, '**/*.js'), { read: false })         .pipe(gp_clean({ force: true })); });
/* Defaults */
gulp.task('cleanup', gulp.series(['js_clean']));
gulp
.task('default', gulp.series(['js']));

This gives you a simple way of minifying your JavaScript and placing it into a run-time folder.

Conclusion

In today's post, we took a simple tool and automated tasks to make our life a little easier.

While bundling and minifying are the basics when using a Task Runner, there are a ton of other tasks available including minifying HTML or optimizing your images to make them smaller and load faster.

Always be mindful when automating your tasks to find the right balance between fast tasks and useful client-side tasks.

If Visual Studio Code is your editor of choice, refer to this post on Microsoft's site.

Do you have a favorite Gulp or Grunt task? How many tasks run through your Task Runner? Post your comments below and let's discuss. 

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