Skip to content

Instantly share code, notes, and snippets.

@rupe120
Last active February 20, 2020 12:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rupe120/512a9eb837383963f80fd9ef4984eb15 to your computer and use it in GitHub Desktop.
Save rupe120/512a9eb837383963f80fd9ef4984eb15 to your computer and use it in GitHub Desktop.
@using MyApp
@using MyApp.Models
@using MyApp.HtmlHelpers;
@inject Microsoft.AspNetCore.Hosting.IHostingEnvironment HostingEnvironment
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
using Microsoft.AspNetCore.Html;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
namespace MyApp.HtmlHelpers
{
/// <summary>
/// Adapted from https://github.com/madskristensen/BundlerMinifier/wiki/Unbundling-scripts-for-debugging
///
/// Areas modified:
/// - Modified it to make it work with aspnetcore.
/// - Accept both scripts and styles.
/// - Read config from wwwroot
/// - Accept baseFolder since DI not suited for static methods
/// - Style nitpicks
/// </summary>
public class Bundler
{
/// <summary>
/// Unpacks the bundle (css/js) for debugging purposes. Makes the build faster by not bundling while debugging.
/// </summary>
/// <param name="baseFolder">The base folder for this application/</param>
/// <param name="bundlePath">The bundled file to unpack.</param>
/// <returns>Unpacked bundles</returns>
public static HtmlString Unpack(string baseFolder, string bundlePath)
{
var configFile = Path.Combine(baseFolder, "bundleconfig.json");
var bundle = GetBundle(configFile, bundlePath);
if (bundle == null)
return null;
// Clean up the bundle to remove the virtual folder that aspnetcore provides.
//var inputFiles = bundle.InputFiles.Select(file => file.Substring(VirtualFolder.Length));
var inputFiles = bundle.InputFiles;
var outputString = bundlePath.EndsWith(".js") ?
inputFiles.Select(inputFile => $"<script src='/{bundle.OutputFileName}/{inputFile}' type='text/javascript'></script>") :
inputFiles.Select(inputFile => $"<link rel='stylesheet' href='/{bundle.OutputFileName}/{inputFile}' />");
return new HtmlString(string.Join("\n", outputString));
}
/// <summary>
/// Unpacks the bundle (css/js) output file names, to create routes for debugging purposes
/// </summary>
/// <param name="baseFolder">The base folder for this application</param>
/// <returns></returns>
public static List<string> GetOutputFileNames(string baseFolder)
{
var configFile = Path.Combine(baseFolder, "bundleconfig.json");
var file = new FileInfo(configFile);
if (!file.Exists)
return null;
return JsonConvert.DeserializeObject<IEnumerable<Bundle>>(File.ReadAllText(configFile))
.Select(x => x.OutputFileName)
.ToList();
}
private static Bundle GetBundle(string configFile, string bundlePath)
{
var file = new FileInfo(configFile);
if (!file.Exists)
return null;
var bundles = JsonConvert.DeserializeObject<IEnumerable<Bundle>>(File.ReadAllText(configFile));
return (from b in bundles
where b.OutputFileName.EndsWith(bundlePath, StringComparison.InvariantCultureIgnoreCase)
select b).FirstOrDefault();
}
class Bundle
{
[JsonProperty("outputFileName")]
public string OutputFileName { get; set; }
[JsonProperty("inputFiles")]
public List<string> InputFiles { get; set; } = new List<string>();
}
}
}
<environment names="Development">
@Bundler.Unpack(HostingEnvironment.ContentRootPath, "/css/site.min.css")
</environment>
<environment names="Staging,Production">
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>
<environment names="Development">
@Bundler.Unpack(HostingEnvironment.ContentRootPath, "/js/site.min.js")
</environment>
<environment names="Staging,Production">
<script src="~/js/site.min.js" asp-append-version="true"></script>
</environment>
using MyApp.HtmlHelpers;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace MyApp.Extensions
{
public static class RouteBuilderExtensions
{
public static void MapBundledFileAccessForDebug(this IRouteBuilder routeBuilder, IHostingEnvironment env)
{
#if DEBUG
foreach(var outputfileName in Bundler.GetOutputFileNames(env.ContentRootPath))
{
routeBuilder.MapGet(outputfileName + "/{*filePath}", context =>
{
var filePath = context.GetRouteValue("filePath").ToString();
var extension = Path.GetExtension(filePath);
if (extension.Equals(".js", StringComparison.CurrentCultureIgnoreCase))
context.Response.ContentType = "application/javascript";
else if (extension.Equals(".css", StringComparison.CurrentCultureIgnoreCase))
context.Response.ContentType = "text/css";
else if (extension.Equals(".map", StringComparison.CurrentCultureIgnoreCase))
context.Response.ContentType = "application/json";
return context.Response.SendFileAsync(filePath);
});
}
#endif
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using MyApp.Extensions
namespace MyApp
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapBundledFileAccessForDebug(env);
});
}
}
}
@rupe120
Copy link
Author

rupe120 commented Jan 5, 2018

Make sure the route extension call routes.MapBundledFileAccessForDebug(); happens after your regular routes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment