In a recent project, we wanted our JavaScript to be built by webpack. We also wanted to remove the dependency to the Web.Optimization framework and solely use NPM and webpack to generate our files. This technique goes for assets in production because a rev-manifest.json is a pointer to hashed file names. This will bust the cache with a unique file name every time the asset change.

This article will only show you how to read rev-manifest and create a Html helper that prompts our the correct value for the built webpack file. Webpack configuration is a subject for another article.

Our rev-manifest.json will be made from webpack and will look something like this.

{
    "bundle.js": "bundle-0ff1eb070626596cf748.js",
    "someotherbundle.js": "someotherbundle-e4ca86d7972b0bf7d226.js"
}

We want to be able to render out the correct path to our razor view. Therefore, we’re going to create a html helper like this: @Html.RenderScript("bundle").

public static MvcHtmlString RenderScript(this HtmlHelper htmlHelper, string module)  
{
    return MvcHtmlString.Create($@"<script type=""text/javascript"" src=""{GetScriptPath(module)}""></script>");
}

After that, we need some way to read our rev-manifest.json and find the correct path. To make the job of parsing JSON easier, we’re using Newtonsoft.Json.
GetScriptPath will do some things. It will read a manifest file, parse it as JSON, look up our module and prompt the correct hashed value.

//This script doesn't account for paths, and assumes that every file is on root. Strings would be recommended to be configured some place rather than hard coded in this snippet. 

internal static string GetScriptPath(string module)  
{
    string manifestPath = HttpRuntime.AppDomainAppPath + "/rev-manifest.json";
    if (!File.Exists(manifestPath))
    {
        logger.Error($"Manifest file not found: {manifestPath}");
        return new Dictionary<string, string>();
    }
    var manifest = JsonConvert.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(manifestPath));
    string key = module + ".js";
    if (!manifest.ContainsKey(key))
    {
        logger.Error($"Script not found. Key: {key}");
        return null;
    }
    if (File.Exists(manifest[key])) 
        return path;
    logger.Error($"Script not found. File: {manifest[key]}");
    return null;
}

To further battle-proof this script for production we could cache up the manifest dictionary. If you’re using webpack-dev-server we could add some code to listen to the development server. This example also goes for css files built in gulp or a similar tool.