Watching More Files With Dotnet Watch For Static Sites
Tags: dotnet
Published: 2020-01-22 19:34:51

---
title: Watching More Files With Dotnet Watch For Static Sites
published: false
date: 2020-01-22 00:00:00 UTC
tags: dotnet
canonical_url: https://khalidabuhakmeh.com/watching-more-files-with-dotnet-watch-for-static-sites
published: true
---

![Neon wall clock](https://images.unsplash.com/photo-1518717328608-59c0379cb82f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=960&q=80)

The marriage of .NET Core and static site generators is a beautiful thing. There are many great static site generator options out there like [`11ty`](https://www.11ty.dev/), [`Jekyll`](https://jekyllrb.com/), [`Hugo`](https://gohugo.io/), and many more. While you can accomplish amazing feats at build time, a dynamic backend can allow your application features that are just not possible at build time. The combination of .NET Core and the static site generator of your choice is a powerful one-two punch. In this post, I’ll show you how to set up an ASP.NET Core application hosting a static site generator, with the added ability to watch all files within your project.

<!--more-->
## Project and Static Site Setup

In this post, I’ll be using 11ty as my static site generator. This post could be adapted to any other static site framework. To get started with 11ty, check out the [documentation site](https://www.11ty.dev/). The first step is to create a new ASP.NET Core application. From a command line, let’s create a new app.

```console
$> mkdir staticz
$> cd staticz
$> dotnet new web

```

When we run this, we should have an empty ASP.NET Core applications.

The command `dotnet new web` will create an empty ASP.NET Core web application. Next, let’s run the commands to install `11ty`.

```console
$> npm init
$> npm install @11ty/eleventy --save-dev

```

To be able to run `11ty` from the `node_modules` folder, we need to add an NPM script in packages.json.

```json
{
  "name": "staticz",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "eleventy --input=. --output=wwwroot"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@11ty/eleventy": "^0.10.0"
  }
}

```

We are passing some default arguments to `11ty`. First, the directory to build, and secondly, the output directory of `wwwroot`. The output directory is the default content directory ASP.NET Core looks for in web projects. Before we run the script, let’s add a `index.html` file to the root of our project.

```html
<!DOCTYPE html>
<html>
  <head>
    <title>Hello, World!</title>
  </head>
  <body>
    <p>Hi</p>
  </body>
</html>

```

Let’s make sure our script works by running `npm run build` in the command line. You should see some build output, and a brand new `wwwroot` folder. Awesome! Let’s get to the ASP.NET Core part now.

## ASP.NET Core Configuration

The ASP.NET Core application has three parts: Add some static file middleware, modify our `csproj` file, and add a new build target. I’ve mentioned one of the approaches in a [previous blog post about kicking some butt with Parcel](/kick-ass-with-aspnet-core-vue-components-and-parcel).

Let’s first look at our changes to the `Startup` class. We need to register a few middleware components, specifically the `DefaultFile` middleware and the `StaticFile` middleware.

```c#
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace staticz
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();
            app.UseDefaultFiles();
            app.UseStaticFiles();
        }
    }
}

```

It is important that the `UseDefaultFiles` registration is called before the `UseStaticFiles` call. I also threw in the call to `UseHttpsRedirection` for good-practices reasons. The next step is to tell our application to run the `npm run build` command we have in `packages.json`. We do this by modifying our `.csproj` file.

```xml
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <Target Name="Static" BeforeTargets="Build">
    <Exec Command="npm run build" />
  </Target>

  <ItemGroup>
    <!-- extends watching group to include *.js files -->
    <Watch 
      Include=" **\*.cs;** \*.js; **\*.json;** \*.html;**.*.md"
      Exclude="node_modules\ **\*;wwwroot\** \*;" 
    />
</ItemGroup>

</Project>

```

There are two important elements to this project that you should take note of:

1. The build target of `Static`. It will run our NPM script.
2. The `Watch` element tells the `dotnet-watch` command to observe specific files. You can also exclude certain directories as well.

Cool! Now we have all the pieces, only one thing left to do.

### Dotnet Watch All The Things

In the command line, type the following command.

```console
$> dotnet watch run

```

This will use your current project and the configuration options we have in our project file. Here is my command output.

![dotnet watch run first time](https://khalidabuhakmeh.com/assets/images/generated/static-watch/dotnet-watch-run-1-800-969319.png)

Great! When we visit `https://localhost:5001`, we see the output of our `index.html` file.

![dotnet watch run web result](https://khalidabuhakmeh.com/assets/images/generated/static-watch/dotnet-localhost-1-753-343e17.png)

Now, let’s modify our `index.html` file from saying `Hi` to a hipper `Yo!`.

![dotnet watch run first time](https://khalidabuhakmeh.com/assets/images/generated/static-watch/dotnet-watch-run-2-800-2bd388.png)

As soon as the change occurs, our process is shut down and restarted. Since we modified the project to execute our NPM script, we also build our web assets. Going back to `https://localhost:5001` we see the results of our hard work.

![dotnet watch run web result](https://khalidabuhakmeh.com/assets/images/generated/static-watch/dotnet-localhost-2-658-c5a085.png)

Yes! Now we have a quick feedback loop that includes our ASP.NET Core work and our static site work. If you want to configure `dotnet-watch` more granularly, I recommend reading the [Microsoft Documentation](https://docs.microsoft.com/en-us/aspnet/core/tutorials/dotnet-watch?view=aspnetcore-3.1#customize-files-list-to-watch) site for more detailed information around configuring your project.

## Conclusion

We have many tools at our disposal, and I think the combination of static site generators and ASP.NET Core are a winning combination. If you work within a multi-disciplinary team, this approach also allows everyone to participate in building your web application. Copywriters can focus on writing content, and developers can focus on optimizing the application using C# and .NET. Remember, this approach can be modified to work with your favorite static site generators, so branch out and experiment. All in all, this is a winning recipe in my book.