Kentico 12: Design Patterns Part 5 - Front-End Dependency Management
Tags: kentico, dotnet, javascript, aspnet
Published: 2019-06-17 13:45:47

---
title: Kentico 12: Design Patterns Part 5 - Front-End Dependency Management
published: true
description: How to setup front-end dependency management in a Kentico 12 MVC project, the issues with the default MVC approach, and an elegant solution using client-side development tools.
tags: kentico, dotnet, javascript, aspnet
series: Kentico 12 - Design Patterns
---

![](https://thepracticaldev.s3.amazonaws.com/i/f990qahf699vx8vcohri.jpg)
<figcaption>Photo by <a href="https://unsplash.com/@chuttersnap">chuttersnap</a> on <a href="https://unsplash.com">Unsplash</a></figcaption>

Since Kentico CMS 12 was released, and [ASP.NET MVC became the recommended framework](https://devnet.kentico.com/documentation/kentico-12-mvc) for building web sites and applications based on Kentico, we have new ways of accomplishing many of our development goals.

As .NET developers, we have traditionally managed our library dependencies through NuGet packages.

What are the ways that we can manage our front-end dependencies? What are the pros and cons of the available options? 🤔

In this post I discuss the two main options I see available to developers building a Kentico 12 MVC site, and describe why I think one of them is clearly better than the other.

## Using `System.Web.Optimization`

When creating a new Kentico 12 MVC project we are given several configuration classes in the `App_Start` folder. One of these is found in `BundleConfig.cs`.

This `BundleConfig` class adds `ScriptBundle` and `StyleBundle` instances to the `BundleCollection` provided by `BundleTable.Bundles`.

```csharp
private static void RegisterJqueryBundle(BundleCollection bundles)
{
    var bundle = new ScriptBundle("~/bundles/jquery")
    {
        CdnPath = "https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.3.1.min.js",
        CdnFallbackExpression = "window.jQuery"
    };

    bundle.Include("~/Scripts/jquery-{version}.js");

    bundles.Add(bundle);
}

```

These bundles can then be referenced in Views, often in the `_Layout.cshtml`, by the identifiers used to register them.

```html
<body>
  <!-- begin content -->
  <div class="container">
    @RenderBody()
  </div>
  <!-- end content -->

  @Scripts.Render("~/bundles/jquery")
</body>
```

All of these types can be found in the `System.Web.Optimization` namespace and you can find the source code on [GitHub](https://github.com/aspnet/AspNetWebOptimization/tree/master/src/System.Web.Optimization). 🤓

> You can read more about how to use `BundleCollection` for bundling and minification in the [Microsoft Docs](https://docs.microsoft.com/en-us/aspnet/mvc/overview/performance/bundling-and-minification#using-bundling-and-minification-with-aspnet-mvc).

The primary goal of `System.Web.Optimization` and `BundleTable.Bundles` in ASP.NET is to give developers an easy way of bundling and minifying sets of JavaScript and CSS files.

These framework features, provided for us out of the box, "just work". 😀

However, these tools were created back when managing client-side dependencies was difficult, the community hadn't yet established consistency or best practices, and the dependencies being managed were much simpler.

## The Problems With `System.Web.Optimization`

All of this bundling technology has been re-vamped for ASP.NET Core as a new tool integrated into Visual Studio called [LibMan](https://devblogs.microsoft.com/aspnet/library-manager-client-side-content-manager-for-web-apps/).

There is a helpful explanation provided in the description of LibMan that puts it (and ASP.NET's "bundling" approach) into perspective, given all the tools available for building modern web applications:

> If you’re happily using npm/yarn/(or something else), we encourage you to continue doing so. LibMan was not developed as a replacement for these tools. 

The docs also mentioned that LibMan is for simple use-cases and requires no additional tools:

> If your project does not require additional tools (like Node, npm, Gulp, Grunt, WebPack, etc) and you simply want to acquire a couple of files, then LibMan might be for you.

Due to the way ASP.NET tries to simplify client-side dependency management, it leads to some design patterns that I don't agree with:

- 👎🏼 Treating client-side dependencies as a small bucket of scripts and styles
- 👎🏼 Managing library versions by manually downloading files from the internet
- 👎🏼 Committing libraries to source control and including them in the ASP.NET project (usually under a `\Scripts` or `\Styles` folder)
- 👎🏼 Not [tree-shaking](https://en.wikipedia.org/wiki/Tree_shaking) client-side dependencies
- 👎🏼 Not using modern CSS tooling ([Sass](https://sass-lang.com/), [PostCSS](https://postcss.org/), [stylelint](https://stylelint.io/))
- 👎🏼 Not using modern JavaScript features (transpiling, ES Modules for dependency management, ES2015+ language enhancements)

The world of 2019 web development is very different from 2009 when ASP.NET MVC first came out - let's embrace the world we live in! 😉

> There are [some tools](https://www.codeproject.com/articles/842961/introducing-dynamic-bundles-for-asp-net-mvc) that enhance ASP.NET's bundling functionality, providing dependency management, and more of a feature-folder based approach, but they don't deal with all the issues I mention above.

## Using Client-Side Tools

So, what will we use instead of `System.Web.Optimization`?

I believe that we should be using modern client-side development tools to manage our client-side dependencies.

- ✅ npm for package and version management
- ✅ Sass for creating our stylesheets
- ✅ Webpack, GulpJs, ParcelJs, or a SPA CLI for bundling & minification
- ✅ VS Code for the best editor + tooling experience

### Requirements

We will need the following tools installed to have the best client-side development experience:

- [nodejs](https://nodejs.org/en/download/)
- [VS Code](https://code.visualstudio.com/)

> The reasons for installing and using VS Code will be clearer in my next post

### Removing `System.Web.Optimization`

First, we will need to delete all the existing bundling code. 🔫🤠💣

Delete `App_Start\BundleConfig.cs` and the reference to it in `Global.asax.cs`.

Next, delete the calls to `@Scripts.Render()` and `@Styles.Render()` in `Shared\_Layout.cshtml`.

We will also delete the `\Scripts` and `\Styles` directories as all of our client-side libraries will be managed by `npm` and our CSS files will be generated from our Sass files.

### Using npm

First, open the terminal and navigate to the MVC project directory.

Assuming you installed VS Code, you should be able to open your current folder in Code by typing the following command:

```bash
code .
```

Next, initialize the project with the `npm` CLI and accept all the defaults (you can change them later):

```bash
npm init -y
```

Now, start installing the packages for the libraries you would like to use! In this example we'll install `jquery`:

```bash
npm install jquery
```

> We want to ensure that the libraries installed from npm are not committed to source control. npm stores all of its packages in a `\node_modules` folder, which should be added to our `.gitignore` file.

### Creating Client-Side Code

To use `jQuery` in our application we need to write some modern JavaScript and use it. 😎

Create a `\src` folder, which is where we will keep the entry points to our client-side source files.

> In the next post you will see how we take a "feature folder" based approach to client-side development.

The first file we will create, `\src\styles.scss`, will be the entry point for all of our [Sass](https://sass-lang.com/) code. Add the following (not very amazing) content:

```scss
// Yup, we're using Kentico's theme!
$background-color: #f14b00;

body {
    background-color: $background-color;
}
```
Now, create `\src\app.js` with the following content:

```javascript
/*
 * We use this non-standard import 
 * to ensure our Sass is part of the build process
 */
import './styles.scss'; 

import $ from 'jquery';

const PIE = '🍰';

$(() => console.log(`Document loaded! It's easy as ${PIE}`));
```

> To learn more about what `import`, `const`, `() =>` and `${}` means, there are many wonderful free resources online like [Exploring ES6 by Dr. Axel Rauschmayer](https://exploringjs.com/es6/index.html), [Learn ES2015 from Babel](https://babeljs.io/docs/en/learn/), and [Learn ES6 on Egghead](https://egghead.io/courses/learn-es6-ecmascript-2015)

### ParcelJs

If we use a tool like [ParcelJs](https://parceljs.org/getting_started.html) for building and bundling, we can get running very quickly, but with limitations on how far we can customize our build pipeline for client-side dependencies.

> ParcelJs is a great tool to start with and we will explore other options in my next post.

To use it, we will need to install ParcelJs as a development dependency (using the `-D` option):

```bash
npm i parcel-bundler -D
```

We will also need to define commands we will run with npm that use ParcelJs, so replace the `scripts` block in your `package.json` with the following:

```json
  "scripts": {
    "start": "parcel watch src/app.js",
    "dev": "parcel build src/app.js --no-minify",
    "prod": "parcel build src/app.js"
  },
```

When we run `npm start` at the command line we can see that our JavaScript and Sass is transpiled, with sourcemaps to help with debugging in browser developer tools, into a `\dist` directory. 👍

ParcelJs will continue to watch for changes to the source files and produce new output automatically anytime we save those changes. 😏

> To stop this "watch" mode type `ctrl+c`

Running `npm run dev` will create the same files as `npm start` but the command will exit once compilation is completed.

If we run `npm run prod`, we will produce a "production" ready version of our code.

> With any client-side build process we will end up with "compiled"/"transpiled" output, which we **do not** want to commit to source control. These files or folders should be added to our `.gitignore` file.

### Using Client-Side Build Ouptut

To use this build output we need to add references to it in our `Shared\_Layout.cshtml`.

Where we were previously referencing the `jquery` and CSS bundles we can now reference the output of the ParcelJs build:

```html
<head>
  <!-- various meta -->
  <link href="/dist/app.css" rel="stylesheet" />
</head>
<body>
  <!-- body content -->
  <script src="/dist/app.js"></script>
</body>
```

### End-To-End Build Coordination

To ensure our client side assets get created when we build our ASP.NET project in Visual Studio we can use MSBuild configuration in our MVC project's `.csproj` file.

We need it to perform the following steps:

- ✅ Install npm packages
- ✅ Run the correct npm command based on the build (Debug/Release)
- ✅ Finish with the normal .NET build

> There is a clever solution on [StackOverflow](https://stackoverflow.com/a/48020233/939634) that uses file modification dates to ensure we don't install packages if we already have everything installed. 🤯

The following MSBuild XML added to our `.csproj` will serve our purposes:

```xml
<PropertyGroup>
    <!-- File with mtime of last successful npm install -->
    <NpmInstallStampFile>node_modules/.install-stamp</NpmInstallStampFile>
</PropertyGroup>
<ItemGroup>
    <JSFile Include="src\**\*.js" />
    <SCSSFile Include="src\**\*.scss" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <NpmCommand>npm run dev</NpmCommand>
    <NpmOutput>dist\app.js</NpmOutput>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' != 'Debug|AnyCPU' ">
    <NpmCommand>npm run prod</NpmCommand>
    <NpmOutput>dist\app.js</NpmOutput>
</PropertyGroup>
<Target Name="NpmInstall" 
    BeforeTargets="NpmBuildClientAssets" 
    Inputs="package.json"
    Outputs="$(NpmInstallStampFile)">
    <Exec Command="npm install" />
    <Touch Files="$(NpmInstallStampFile)" AlwaysCreate="true" />
</Target>
<Target Name="NpmBuildClientAssets"
    BeforeTargets="BeforeBuild" 
    Inputs="@(JSFile);@(SCSSFile)"
    Outputs="$(NpmOutput)">
    <Exec Command="$(NpmCommand)" />
</Target>
```

> One convenience of having your project open in VS Code is that it's a lot easier to edit the `.csproj` file 😁

Now when we build our project in Visual Studio we are guaranteed to have the client-side build assets in the `\dist` directory before the site ever starts running. 👏🏽

## So What Did We Accomplish?

Before we look to where we can go from here, let's remember where are are!

We realized that while the classes ASP.NET provides to us in `System.Web.Optimization` had great APIs and tooling when they first came out, the web, and front-end development, has changed significantly. 🤔

There are some software development patterns we would like to avoid, like committing libraries to source control, that this older approach encourages. 😞

Using client-side tools for client-side development actually works pretty well! 😄

We can also integrate the client-side development process into our .NET development process to have a great end-to-end solution. 💪

## What's Next?

Now that we've set up the foundational pieces we can start to explore all the wonderful front-end tools and libraries that can improve our development experience.

In my next post I'm going to discuss those tools and libraries, how to integrate them into VS Code, and what a "best practices" setup might look like. 😮

---

If you are looking for additional Kentico content, checkout the Kentico tag here on DEV:

{% tag kentico %}

or my [Kentico 12: Design Patterns](https://dev.to/search?q=Kentico%2012%20-%20Design%20Patterns) series.