ASP.NET Core Bundling and Minification Using Gulp
Tags: dotnet, aspnetcore, bundling, gulp
Published: 2021-08-29 09:39:11

In my previous post [A Step by Step Guide to Bundling and Minification in ASP.NET Core](https://www.ezzylearning.net/tutorial/a-step-by-step-guide-to-bundling-and-minification-in-asp-net-core), I gave you an overview of the different tools and strategies we can use to implement bundling and minification in ASP.NET Core. I also covered the Visual Studio extension called [Bundler & Minifier](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.BundlerMinifier). These basic tools and extensions are only useful for basic workflows and if your requirements are more complex or you need more than bundling and minification or you want to automate some of these tasks to make your life easier then you have to use task runners such as Gulp, Grunt or Webpack. In this tutorial, I will show you how to use Gulp in Visual Studio and how to use Gulp tasks to automate bundling and minification in Visual Studio.

[Download Source Code](https://github.com/ezzylearning/AspNetCoreBundlingWithGulpDemo)

## **What is Gulp?**

[Gulp](https://gulpjs.com/) is one of the most popular and cross-platform streaming task runners that provide you all the tools and plugins to automate slow and repetitive development routines and workflows and compose them into efficient build pipelines. At a high level, gulp reads files as streams and pipes these streams to different tasks. The main part of the development with gulp is creating tasks using JavaScript. These tasks can use thousands of npm packages to perform different tasks. There are also other popular task runners such as Grunt and Webpack and Visual Studio has native support for these task runners. In this tutorial, we will use the latest version of Gulp 4.0 that is rewritten from the ground-up and will allow us to compose tasks using new **series()** and **parallel()** methods. There are also many more advancements added in version 4.0 and you can read about all the new features [here](https://github.com/gulpjs/gulp).

![Understanding Gulp Tasks Pipeline](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jww47yvu5peso6wzqkdd.png)

The gulp tasks we needed to perform bundling and minification are as follows:

1. We need a gulp task that will delete existing bundled or minified files.
2. We need a gulp task that will read JavaScript files from a source folder and will bundle them together, minifies the bundled file, and finally save it to a target folder.
3. We need a gulp task that will read CSS files from a source folder and will bundle them together, minifies the bundled file, and finally save it to a target folder.

We will use the following plugins to perform the above tasks.

- [del](https://www.npmjs.com/package/del) – Deletes files and directories
- [gulp-concat](https://www.npmjs.com/package/gulp-concat) – Concatenates files
- [gulp-cssmin](https://www.npmjs.com/package/gulp-cssmin) – Minifies CSS files
- [gulp-uglify](https://www.npmjs.com/package/gulp-uglify) – Minifies JavaScript files
- [merge-streams](https://www.npmjs.com/package/merge-stream) – Merges multiple streams

There are thousands of plugins available to perform all types of routine tasks using gulp. You can search these plugins in [npm packages repository](https://www.npmjs.com). If you are not comfortable in using client-side packages and libraries, I will recommend you read my post [Working with Packages and Libraries in ASP.NET Core](https://www.ezzylearning.net/tutorial/working-with-packages-and-libraries-in-asp-net-core)

## **What is Gulpfile.js**?

So far, we have learned that we need to define gulp tasks using JavaScript and we have to use different plugins to perform different routine tasks now the last question is where we will define these tasks. This is where **Gulpfile.js** comes in. A Gulpfile.js or gulpfile.js is a file in your project directory that automatically loads when we run the **gulp** command. In this file, we often define tasks and use gulp APIs like src(), dest(), series(), parallel() etc. We will also create this file shortly to define our gulp tasks. You can read more about this file [here](https://gulpjs.com/docs/en/getting-started/javascript-and-gulpfiles/).

## **Converting bundleconfig.json to Gulp**

In my last tutorial [A Step by Step Guide to Bundling and Minification in ASP.NET Core](https://www.ezzylearning.net/tutorial/a-step-by-step-guide-to-bundling-and-minification-in-asp-net-core), we created a bundleconfig.json file for bundling and minification and we also installed a Visual Studio extension called **[BundlerMinifier](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.BundlerMinifier)**. We can reuse the same bundleconfig.json file from Gulp but you have to remove some extra options from the bundleconfig.json file. The following bundleconfig.json file is a trim down version we need in this tutorial. It is simply defining some CSS and JavaScript bundles with source and destination paths.
```
[
   {
      "outputFileName":"wwwroot/css/main.min.css",
      "inputFiles":[
         "wwwroot/lib/bootstrap/dist/css/bootstrap.css",
         "wwwroot/css/site.css"
      ]
   },
   {
      "outputFileName":"wwwroot/js/main.min.js",
      "inputFiles":[
         "wwwroot/lib/jquery/dist/jquery.js",
         "wwwroot/lib/bootstrap/dist/js/bootstrap.js",
         "wwwroot/js/site.js"
      ]
   }
]
```
The first step to start using Gulp in your ASP.NET Core project is to install Gulp and related npm modules and create a Gulpfile.js. We can do all this easily by using Bundler & Minifier "**Convert To Gulp**" option. Right-click on the bundleconfig.json file, choose Bundler & Minifier, and then choose "Convert To Gulp…" option from the menu as shown below.

![Convert to Gulp using Bundler and Minifier](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r7s3pzi1iac45udx5plu.png)

You will see the following confirmation message asking you about generating gulpfile.js and installing npm packages. You can press OK to proceed.

![Warning to Install Gulp File and NPM Packages](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j660qbk2lv41xunn029h.png)

After few minutes of downloading, you will see the following new items in your solution explorer.

1. A gulpfile.js
2. A package.json
3. Some npm packages are added in Dependencies
4. A node\_modules folder is added in which many node packages are downloaded and installed

![NPM Packages and Node Modules Installed in Project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xjxbsxmerathed8rov3a.png)

A package.json file is a JSON file that is normally available at the root of a project and it contains the metadata information related to the project. It is used to manage the project’s dependencies, scripts, versions, etc. If you will open the generated package.json file you will notice that it has project name, version, and some development related dependencies which are mainly npm packages we will use in gulpfile.js file shortly.
```
{
   "name":"AspNetCoreBundlingWithGulpDemo",
   "version":"1.0.0",
   "private":true,
   "devDependencies":{
      "del":"^6.0.0",
      "gulp":"^4.0.2",
      "gulp-concat":"^2.6.1",
      "gulp-cssmin":"^0.2.0",
      "gulp-uglify":"^3.0.2",
      "merge-stream":"^2.0.0"
   }
}
```
## **How to Define Tasks in Gulp**

If you will open gulpfile.js, you will notice that the Bundler & Minifier extension has already generated all the gulp tasks for you. The problem with this generated code is that it is not using Gulp 4.0 even though the package.json file is showing gulp version 4.0.2 in it. This means you have two options now.

1. You can downgrade your Gulp version in package.json and continue using the generated gulp tasks available in gulpfile.js file
2. You can use the latest Gulp version 4.0.2 and update gulpfile.js code to use the latest Gulp features.

I have chosen option 2 above as it's always a good option to use the latest version of any tool or framework.

It is now time to update gulpfile.js and learn more about gulp. First of all, we need to specify/import all the required npm packages. These packages can be loaded either by path or name and each of them will be assigned to a variable. This is similar to importing NuGet packages in C# files while working on the server-side.

We are loading the main ‘**gulp**’ package that contains all gulp related methods and features. Next, we are loading all npm packages I mentioned at the start of this tutorial. Finally, we are saving the reference of bundleconfig.json file in a local variable bundleconfig so that we can read input and output file paths from bundleconfig.json file.
```
var gulp = require("gulp"),
    concat = require("gulp-concat"),
    cssmin = require("gulp-cssmin"),
    uglify = require("gulp-uglify"),
    merge = require("merge-stream"),
    del = require("del"),
    bundleconfig = require("./bundleconfig.json");
```
Next, we have a regex variable that will keep both css and js files regex which are used in **getBundles** function to filter and fetch files matching with a specific regex pattern.
```
var regex = {
    css: /\\.css$/,
    js: /\\.js$/
};

function getBundles(regexPattern) {
    return bundleconfig.filter(function (bundle) {
        return regexPattern.test(bundle.outputFileName);
    });
}
```
### **Clean Task**

Next, we need to define the ‘**clean**’ gulp task. The clean task will remove the existing minified files so that they can be regenerated with the updated code or functionality. Notice that the gulp tasks are simply JavaScript functions. We create JavaScript functions and then registered them by using **gulp.task()** method as we did with our clean function below.
```
function clean() {
    var files = bundleconfig.map(function (bundle) {
        return bundle.outputFileName;
    });

    return del(files);
}
gulp.task(clean);
```
### **Minify JS Task**

To minify JavaScript files, I am creating minJs task. This task

1. Load all JavaScript files
2. Concatenates or bundle all files using ‘**concat**’ module
3. Minify the bundled file using ‘**ugligy**’ module
4. Place the resulting minified file at the root location
```
function minJs() {
    var tasks = getBundles(regex.js).map(function (bundle) {
        return gulp.src(bundle.inputFiles, { base: "." })
            .pipe(concat(bundle.outputFileName))
            .pipe(uglify())
            .pipe(gulp.dest("."));
    });
    return merge(tasks);
}
gulp.task(minJs);
```
### **Minify CSS Task**

To minify CSS files, I am creating minCss task. This task

1. Load all CSS files
2. Concatenates or bundle all files using ‘**concat**’ module
3. Minify the bundled file using '**cssmin**' module
4. Place the resulting minified file at the root location
```
function minCss() {
    var tasks = getBundles(regex.css).map(function (bundle) {
        return gulp.src(bundle.inputFiles, { base: "." })
            .pipe(concat(bundle.outputFileName))
            .pipe(cssmin())
            .pipe(gulp.dest("."));
    });
    return merge(tasks);
}
gulp.task(minCss);
```
In the end, I created a named task with the name ‘min’ and combine all task functions using gulp.series() method. The **gulp.series()** method executes multiple tasks one after another in a series. There is also another method called **gulp.parallel()** that can be used to execute multiple tasks simultaneously.
```
gulp.task("min", gulp.series(clean, minJs, minCss));
```
Following is the complete gulpfile.js file with all the tasks
```
"use strict";

var gulp = require("gulp"),
    concat = require("gulp-concat"),
    cssmin = require("gulp-cssmin"),
    uglify = require("gulp-uglify"),
    merge = require("merge-stream"),
    del = require("del"),
    bundleconfig = require("./bundleconfig.json");

var regex = {
    css: /\\.css$/,
    js: /\\.js$/
};

function getBundles(regexPattern) {
    return bundleconfig.filter(function (bundle) {
        return regexPattern.test(bundle.outputFileName);
    });
}

function clean() {
    var files = bundleconfig.map(function (bundle) {
        return bundle.outputFileName;
    });

    return del(files);
}
gulp.task(clean);

function minJs() {
    var tasks = getBundles(regex.js).map(function (bundle) {
        return gulp.src(bundle.inputFiles, { base: "." })
            .pipe(concat(bundle.outputFileName))
            .pipe(uglify())
            .pipe(gulp.dest("."));
    });
    return merge(tasks);
}
gulp.task(minJs);

function minCss() {
    var tasks = getBundles(regex.css).map(function (bundle) {
        return gulp.src(bundle.inputFiles, { base: "." })
            .pipe(concat(bundle.outputFileName))
            .pipe(cssmin())
            .pipe(gulp.dest("."));
    });
    return merge(tasks);
}
gulp.task(minCss);

gulp.task("min", gulp.series(clean, minJs, minCss));
```
## **Running Gulp Tasks in Visual Studio**

We are now ready to run our gulp tasks using Visual Studio. Open the Task Runner Explorer window and you will notice that it is reading Gulpfile.js and showing all gulp tasks. 

![Gulp File and Tasks in Task Runner Explorer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g85w1ywoqi5dts9lfztd.png)

You can run the ‘**min**’ task by right-clicking it and selecting "**Run**". The ‘min’ task will be shown on the right-hand side and you can also view the logs which display when a particular task is finished and how much time a particular task took to complete.

![Run Gulp Tasks in Visual Studio Task Runner Explorer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l10spw7t87d42y3ccy7k.png)

You will see that the **main.min.css** and **main.min.js** files will be generated in Solution Explorer. You can also run individual tasks to perform a specific function. For example, you can run the ‘clean’ task to delete the minified files.

![Minified Files Generated using Gulp Tasks](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rl9m7z9wdd3zd6gc0wb9.png)

## **Binding Gulp Tasks with Visual Studio Events**

Running gulp tasks from Task Runner Explorer every time you make some changes in JavaScript of CSS files during development seems a non-productive method. You can automate this process by binding these tasks with certain Visual Studio events. You can

1. Bind any task with any event
2. Bind one task with multiple events
3. Bind multiple tasks with a single event

We can bind Gulp.js tasks with the following Visual Studio events:

- Before Build
- After Build
- Clean Build
- Project Open

To bind a task with an event, right-click on a task in Task Runner Explorer and choose any event from the child menu of the “**Bindings**” menu as shown below:

![Binding Gulp Tasks with Visual Studio Events](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/98j9n2yt9exnbnhf3sk1.png)

We can also define this binding directly in the gulpfile.js using the following syntax. The following line states that the '**min**’ task should run when Visual Studio executes the **AfterBuild** event.
```
/// <binding AfterBuild='min' />
```
## **Summary**

In this tutorial, I have shown you how to write and integrate Gulp tasks in Visual Studio to manage the Bundling and Minification of JavaScript and CSS files automatically. You can create all sorts of tasks by using thousands of modules available in the online npm repository. I hope you have learned something new in this tutorial and you will start doing more and more automation to make yourself more productive as a developer.