Skip to content

Instantly share code, notes, and snippets.

@brendankowitz
Last active August 29, 2015 14:01
Show Gist options
  • Save brendankowitz/f31b64100c6c159d2d55 to your computer and use it in GitHub Desktop.
Save brendankowitz/f31b64100c6c159d2d55 to your computer and use it in GitHub Desktop.
Register directory based script bundles in an mvc project

When visiting the url:

  ~/MyArea/Home/Index

The code will search for bundles in the following locations:

  ~/
  ~/MyArea
  ~/MyArea/Home
  ~/MyArea/Home/Index

Plus include subfolders of the following conventions into the parent bundle:

  _MyBundleSubFolder
  _MyBundleSubFolder\WithAnotherSubFolder
  Shared
  Shared\AnyFolderUnderShared

Which allows you to further separate your files, while having them include at the appropriate level:

  ~/MyArea/Shared/Scripts/MyArea.js

That example will be included in the ~/MyArea/scripts.js bundle. This also allows you to include scripts that are required for partial views at the relevant level.

As another example scripts or styles that are required by all actions in the controller could be include like so:

  ~/MyArea/Home/_Scripts
  ~/MyArea/Home/_Styles

And scripts / styles only required by one action can also be separated by making a folder with the same name:

  ~/MyArea/Home/Index
  ~/MyArea/Home/Index
<html>
<head>
@Html.RenderPageStyles()
</head>
<body>
<!-- Page content -->
@Html.RenderPageScripts()
</body>
</html>
var applicationPath = app.Server.MapPath("~/");
var contentBundleBasePath = app.Server.MapPath("~/Content/bundles");
bundles.CreateRecursiveBundles(
contentBundleBasePath,
applicationPath,
contentBundleBasePath);
bundles.CreateRecursiveBundles(app.Server.MapPath("~/Views"), applicationPath, applicationPath);
bundles.CreateRecursiveBundles(app.Server.MapPath("~/Areas"), applicationPath, applicationPath);
//Register directory based script bundles in an mvc project
using System;
using System.IO;
using System.Linq;
using System.Web.Optimization;
using ThirdDrawer.Extensions.CollectionExtensionMethods;
namespace Mvc.Extensions
{
public static class BundleExtensions
{
public static void CreateRecursiveBundles(this BundleCollection bundleCollection,
string folderStartPath,
string applicationPath,
string bundleRootPath)
{
//Virtual path to the file on the server
var currentVirtualPath = string.Format("~/{0}", folderStartPath.Replace(applicationPath, string.Empty)).AsWebPath();
//The path to the bundle we want to make
var currentEmulatedPath = string.Format("~/{0}", folderStartPath.Replace(bundleRootPath, string.Empty)).AsWebPath();
RegisterResourcesInPath<ScriptBundle>(bundleCollection,
Path.Combine(currentEmulatedPath, "js").AsWebPath(),
folderStartPath,
currentVirtualPath, "js");
RegisterResourcesInPath<LessBundle>(bundleCollection,
Path.Combine(currentEmulatedPath, "styles").AsWebPath(),
folderStartPath,
currentVirtualPath, "less");
foreach (var childDir in Directory.GetDirectories(folderStartPath))
CreateRecursiveBundles(bundleCollection, childDir, applicationPath, bundleRootPath);
}
private static void RegisterResourcesInPath<T>(BundleCollection bundles,
string bundlePath,
string dir,
string currentVirtualPath,
string resourceType) where T : Bundle
{
var bundle = bundles.GetBundleFor(bundlePath) ??
(T)Activator.CreateInstance(typeof(T), bundlePath);
var searchPattern = string.Format("*.{0}", resourceType);
bundle.IncludeDirectory(currentVirtualPath, searchPattern);
var hasChildDirFiles = false;
//include partials in parent bundle
Directory.GetDirectories(dir)
.Select(childDir => new DirectoryInfo(childDir))
.Where(x => x.Name.StartsWith("_") || string.Equals("shared", x.Name, StringComparison.InvariantCultureIgnoreCase))
.Do(x => {
bundle.IncludeDirectory(string.Concat(currentVirtualPath, (string) "/", (string) x.Name),
searchPattern, true);
if(Directory.GetFiles(Path.Combine(dir, x.Name), searchPattern, SearchOption.AllDirectories).Any())
hasChildDirFiles = true;
}).Done();
if (Directory.GetFiles(dir, searchPattern).Any() || hasChildDirFiles)
bundles.Add(bundle);
}
internal static string AsWebPath(this string path)
{
if (string.IsNullOrEmpty(path)) return path;
return path.Replace('\\', '/').Replace("//", "/");
}
}
}
//Render the bundles into the cshtml page
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using ThirdDrawer.Extensions.CollectionExtensionMethods;
namespace Mvc.Extensions
{
public static class PageBundleExtensions
{
public static IHtmlString RenderPageStyles(this HtmlHelper helper)
{
var bundles = new List<IHtmlString>();
GetBundlePaths(helper)
.Select(x => string.Concat(x, "/styles"))
.Where(HasBundle)
.Do(x => bundles.Add(Styles.Render(x)))
.Done();
return new HtmlString(string.Concat(bundles));
}
public static IHtmlString RenderPageScripts(this HtmlHelper helper)
{
var bundles = new List<IHtmlString>();
GetBundlePaths(helper)
.Select(x => string.Concat(x, "/js"))
.Where(HasBundle)
.Do(x => bundles.Add(Scripts.Render(x)))
.Done();
return new HtmlString(string.Concat(bundles));
}
private static IEnumerable<string> GetBundlePaths(HtmlHelper html, string baseVirtualPath = "~")
{
var razorView = html.ViewContext.View as RazorView;
if (razorView == null) yield break;
var view = razorView.ViewPath
.Replace("~/", string.Empty)
.Replace(".cshtml", string.Empty);
//site level items
yield return baseVirtualPath;
var path = view.Split('/');
for (var i = 0; i < path.Length; i++)
{
var currentPath = string.Join("/", path.Take(i + 1));
yield return string.Format("{0}/{1}", baseVirtualPath, currentPath);
}
}
static bool HasBundle(string path)
{
return BundleTable.Bundles.GetBundleFor(path) != null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment