Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added directory support #32

Merged
merged 2 commits into from Jan 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions .vs/config/applicationhost.config
Expand Up @@ -163,15 +163,15 @@
</site>
<site name="TestWebProject" id="2">
<application path="/" applicationPool="Clr4IntegratedAppPool">
<virtualDirectory path="/" physicalPath="C:\Users\rcooray\Source\Repos\EmbeddedResourceVirtualPathProvider2\TestWebProject" />
<virtualDirectory path="/" physicalPath="C:\Users\thorgeiro\Source\Repos\EmbeddedResourceVirtualPathProvider\TestWebProject" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:56338:localhost" />
</bindings>
</site>
<site name="NugetTestWebProject" id="3">
<application path="/" applicationPool="Clr4IntegratedAppPool">
<virtualDirectory path="/" physicalPath="C:\Users\rcooray\Source\Repos\EmbeddedResourceVirtualPathProvider2\NugetTestWebProject" />
<virtualDirectory path="/" physicalPath="C:\Users\thorgeiro\Source\Repos\EmbeddedResourceVirtualPathProvider\NugetTestWebProject" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:62200:localhost" />
Expand Down
14 changes: 14 additions & 0 deletions EmbeddedResourceVirtualPathProvider/EmbeddedResourcePath.cs
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EmbeddedResourceVirtualPathProvider
{
public class EmbeddedResourcePath
{
public string Directory { get; set; }

public string Filename { get; set; }
}
}
@@ -0,0 +1,45 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Hosting;

namespace EmbeddedResourceVirtualPathProvider
{
class EmbeddedResourceVirtualDirectory : VirtualDirectory
{
Dictionary<string, List<EmbeddedResource>> _resources;
Func<EmbeddedResource, EmbeddedResourceCacheControl> _cacheControl;

public EmbeddedResourceVirtualDirectory(string virtualPath, Dictionary<string, List<EmbeddedResource>> resources, Func<EmbeddedResource, EmbeddedResourceCacheControl> cacheControl) : base(virtualPath)
{
_resources = resources;
_cacheControl = cacheControl;
}

public override IEnumerable Children
{
get
{
return Files;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this return Files.Contat(Directories)? A more general question - does this PR work with nested directories (I don't think you explicitly mentioned it, but I notice the test folder is a single directory)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it should, but I did not implement sub-directories, so I left it out.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are subdirectories unnecessary for ASP.NET bundling, or will embedded resource bundling only work with single subdirectories?

Sorry for dumb questions but I don't use Bundles that much, and if there's any unexpected behaviour I'll need to document it to reduce the number of people raising Github Issues.

}

public override IEnumerable Directories
{
get
{
return new List<VirtualDirectory>();
}
}

public override IEnumerable Files
{
get
{
return _resources.Select(resource => new EmbeddedResourceVirtualFile(System.IO.Path.Combine(this.VirtualPath, resource.Key), resource.Value.FirstOrDefault(), _cacheControl(resource.Value.FirstOrDefault())));
}
}
}
}
Expand Up @@ -25,5 +25,6 @@ public override Stream Open()
}
return embedded.GetStream();
}

}
}
Expand Up @@ -43,6 +43,8 @@
<ItemGroup>
<Compile Include="EmbeddedResource.cs" />
<Compile Include="EmbeddedResourceCacheControl.cs" />
<Compile Include="EmbeddedResourcePath.cs" />
<Compile Include="EmbeddedResourceVirtualDirectory.cs" />
<Compile Include="EmbeddedResourceVirtualFile.cs" />
<Compile Include="Logger.cs" />
<Compile Include="Vpp.cs" />
Expand Down
118 changes: 96 additions & 22 deletions EmbeddedResourceVirtualPathProvider/Vpp.cs
Expand Up @@ -6,35 +6,73 @@
using System.Web.Caching;
using System.Web.Hosting;
using System.Linq;
using System.Text.RegularExpressions;

namespace EmbeddedResourceVirtualPathProvider
{
public class Vpp : VirtualPathProvider, IEnumerable
{
readonly IDictionary<string, List<EmbeddedResource>> resources = new Dictionary<string, List<EmbeddedResource>>();
readonly Dictionary<string, Dictionary<string, List<EmbeddedResource>>> resources = new Dictionary<string, Dictionary<string, List<EmbeddedResource>>>();

public Vpp(params Assembly[] assemblies)
{
Array.ForEach(assemblies, a => Add(a));
Regex pathPattern = new Regex(@"^(?<directory>.*?)?\.(?<filename>[^.]*([\.-](?<version>[0-9]{1,5}\.[0-9]{1,5}\.[0-9]{1,5})(\.min)?)?\.[^.]*)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);

public Vpp(params Assembly[] assemblies)
{
UseResource = er => true;
UseLocalIfAvailable = resource => true;
CacheControl = er => null;
}
GetPath = resourcePath => DefaultPathFunction(resourcePath);

Array.ForEach(assemblies, a => Add(a));
}

public Func<EmbeddedResource, bool> UseResource { get; set; }
public Func<EmbeddedResource, bool> UseLocalIfAvailable { get; set; }
public Func<EmbeddedResource, EmbeddedResourceCacheControl> CacheControl { get; set; }
public IDictionary<string, List<EmbeddedResource>> Resources { get { return resources; } }
public Func<string, EmbeddedResourcePath> GetPath { get; set; }

public Dictionary<string, Dictionary<string, List<EmbeddedResource>>> Resources { get { return resources; } }

private EmbeddedResourcePath DefaultPathFunction(string resourcePath)
{
Match match = pathPattern.Match(resourcePath);
if (match.Success)
{
return new EmbeddedResourcePath()
{
Directory = match.Groups["directory"].Value,
Filename = match.Groups["filename"].Value
};
}

return null;
}

public void Add(Assembly assembly, string projectSourcePath = null)
{
var assemblyName = assembly.GetName().Name;
var assemblyName = assembly.GetName().Name;

foreach (var resourcePath in assembly.GetManifestResourceNames().Where(r => r.StartsWith(assemblyName)))
{
var key = resourcePath.ToUpperInvariant().Substring(assemblyName.Length).TrimStart('.');
if (!resources.ContainsKey(key))
resources[key] = new List<EmbeddedResource>();
resources[key].Insert(0, new EmbeddedResource(assembly, resourcePath, projectSourcePath));
EmbeddedResourcePath path = GetPath(resourcePath.Substring(assemblyName.Length + 1));
if (path != null)
{
Dictionary<string, List<EmbeddedResource>> directoryResources;
string directoryName = path.Directory.ToUpperInvariant();
string filename = path.Filename.ToUpperInvariant();

if (!resources.TryGetValue(directoryName, out directoryResources))
{
directoryResources = new Dictionary<string, List<EmbeddedResource>>();
resources.Add(directoryName, directoryResources);
}

if (!directoryResources.ContainsKey(filename))
{
directoryResources[filename] = new List<EmbeddedResource>();
}
directoryResources[filename].Insert(0, new EmbeddedResource(assembly, resourcePath, projectSourcePath));
}
}
}

Expand All @@ -43,7 +81,32 @@ public override bool FileExists(string virtualPath)
return (base.FileExists(virtualPath) || GetResourceFromVirtualPath(virtualPath) != null);
}

public override VirtualFile GetFile(string virtualPath)
public override VirtualDirectory GetDirectory(string virtualDir)
{
string key = virtualDir.Replace('/', '.').TrimStart('~', '.').TrimEnd('.').ToUpperInvariant();

Dictionary<string, List<EmbeddedResource>> directoryResources;

if (resources.TryGetValue(key, out directoryResources))
{
return new EmbeddedResourceVirtualDirectory(virtualDir, directoryResources, CacheControl);
}

return base.GetDirectory(virtualDir);
}

public override bool DirectoryExists(string virtualDir)
{
string key = virtualDir.Replace('/', '.').TrimStart('~', '.').TrimEnd('.').ToUpperInvariant();
if (resources.ContainsKey(key))
{
return true;
}

return base.DirectoryExists(virtualDir);
}

public override VirtualFile GetFile(string virtualPath)
{
//if (base.FileExists(virtualPath)) return base.GetFile(virtualPath);
var resource = GetResourceFromVirtualPath(virtualPath);
Expand All @@ -57,6 +120,7 @@ public override string CombineVirtualPaths(string basePath, string relativePath)
var combineVirtualPaths = base.CombineVirtualPaths(basePath, relativePath);
return combineVirtualPaths;
}

public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies)
{
var fileHash = base.GetFileHash(virtualPath, virtualPathDependencies);
Expand Down Expand Up @@ -117,16 +181,26 @@ public EmbeddedResource GetResourceFromVirtualPath(string virtualPath)

path = folder + path.Substring(index);
}
var cleanedPath = path.Replace('/', '.');
var key = (cleanedPath).ToUpperInvariant();
if (resources.ContainsKey(key))
{
var resource = resources[key].FirstOrDefault(UseResource);
if (resource != null && !ShouldUsePrevious(virtualPath, resource))
{
return resource;
}
}

Match pathMatch = pathPattern.Match(path.Replace('/', '.').ToUpperInvariant());
if (pathMatch.Success)
{
string directory = pathMatch.Groups["directory"].Value;
string filename = pathMatch.Groups["filename"].Value;

if (resources.ContainsKey(directory))
{
var directoryResources = resources[directory];
if (directoryResources.ContainsKey(filename))
{
var resource = directoryResources[filename].FirstOrDefault(UseResource);
if (resource != null && !ShouldUsePrevious(virtualPath, resource))
{
return resource;
}
}
}
}
return null;
}

Expand Down
1 change: 1 addition & 0 deletions TestResourceLibrary/Scripts/alert-1.12.34567.min.js
@@ -0,0 +1 @@
alert("Scripts/alert-1.12.34567.min.js - works with more complex path")
1 change: 0 additions & 1 deletion TestResourceLibrary/Scripts/alert.3.js

This file was deleted.

2 changes: 1 addition & 1 deletion TestResourceLibrary/TestResourceLibrary.csproj
Expand Up @@ -50,7 +50,7 @@
<EmbeddedResource Include="alert2.js" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Scripts\alert.3.js" />
<EmbeddedResource Include="Scripts\alert-1.12.34567.min.js" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="3rd3Party\3select2-3.4.5\select2.js" />
Expand Down
Expand Up @@ -13,12 +13,10 @@ public static void Start()
var assemblies = System.Web.Compilation.BuildManager.GetReferencedAssemblies()
.Cast<Assembly>()
.Where(a => a.GetName().Name.StartsWith("System") == false);
System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider(new EmbeddedResourceVirtualPathProvider.Vpp(assemblies.ToArray())
{
//you can do a specific assembly registration too. If you provide the assemly source path, it can read
//from the source file so you can change the content while the app is running without needing to rebuild
//{typeof(SomeAssembly.SomeClass).Assembly, @"..\SomeAssembly"}
});

var vpp = new EmbeddedResourceVirtualPathProvider.Vpp(assemblies.ToArray());

System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider(vpp);
}
}
}
2 changes: 1 addition & 1 deletion TestWebProject/Default.aspx
Expand Up @@ -7,7 +7,7 @@
<title></title>
<script src="/alert.js" type="text/javascript"></script>
<script src="/alert2.js" type="text/javascript"></script>
<script src="/Scripts/alert.3.js" type="text/javascript"></script>
<script src="/Scripts/alert-1.12.34567.min.js" type="text/javascript"></script>
<script src="/3rd3Party/3select2-3.4.5/select2.js" type="text/javascript"></script>
<script src="/3rd3Party/hammer.js-1.0.10/hammer.js" type="text/javascript"></script>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/3rd3Party/3select2-3.4.5/select2.js looks pretty tricky - does it work ok with the regex? (sorry I don't have access to VS to test this stuff out)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it works, Demo

</head>
Expand Down
1 change: 0 additions & 1 deletion TestWebProject/Default.aspx.cs
Expand Up @@ -11,7 +11,6 @@ public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{

}
}
}