How to make bundles unminify and list individual files when using a cookieless static content server?
A

2

3

I have two VS projects, one for the main website and one for a "static content" website where all the css, js, images, and other static content will be stored and accessed via a cookieless domain.

So I have a BundleConfig.cs in my static site that creates all the bundles:

public class BundleConfig
{
    public static void RegisterBundles(BundleCollection bundles)
    {
        bundles.Add(new StyleBundle("~/bundles/styles").IncludeDirectory("~/app/styles", "*.css", true));
        bundles.Add(new ScriptBundle("~/bundles/scripts").IncludeDirectory("~/app/src", "*.js", true));
    }
}

And in the main site I have another BundleConfig.cs where I point the main site to the static content site like this:

public class BundleConfig
{
    public static void RegisterBundles(BundleCollection bundles)
    {
        var staticWebsite = ConfigurationManager.AppSettings["StaticWebsite"];
        var versionNumber = ConfigurationManager.AppSettings["VersionNumber"];

        Styles.DefaultTagFormat = string.Format("<link href='{0}{{0}}?v={1}' rel='stylesheet'/>", staticWebsite, versionNumber);
        Scripts.DefaultTagFormat = string.Format("<script src='{0}{{0}}?v={1}'></script>", staticWebsite, versionNumber);

    }
}

Now I can use @Styles.Render("~/bundles/styles") and @Scripts.Render("~/bundles/scripts") which render like this, just the way I want and it works great:

<link href='http://mycookielessdomain.com/bundles/styles?v=1.0.0.0' rel='stylesheet'/>
<script src='http://mycookielessdomain.com/bundles/scripts?v=1.0.0.0'></script>

The problem I have is that the content is always minified and bundled regardless of whether debug=true or not. Even if I use BundleTable.EnableOptimization = false in both BundleConfig.cs files, @Styles.Render() and @Scripts.Render() still only render one tag each and refer to content that is minified.

I understand that the main site would have no knowledge of the individual files that were bundled in the static content site, but my hope is that there is some way to manually specify these paths in the main site BundleConfig so that the Render() methods can list them individually when optimizations are off... if I can ever get them to turn off, that is.

Athamas answered 13/3, 2014 at 20:31 Comment(0)
A
4

So I was able to get this working by adding a custom VirtualPathProvider which makes the main project search within the static content project for the individual files. When in DEBUG mode, the files are listed individually. When in RELEASE mode, the minified bundles are referenced instead.

public class BundleConfig
{
    public static void RegisterBundles(BundleCollection bundles)
    {
        var staticWebsite = ConfigurationManager.AppSettings["StaticWebsite"];
        var versionNumber = ConfigurationManager.AppSettings["VersionNumber"];

        Styles.DefaultTagFormat = string.Format("<link href='{0}{{0}}?v={1}' rel='stylesheet'/>", staticWebsite, versionNumber);
        Scripts.DefaultTagFormat = string.Format("<script src='{0}{{0}}?v={1}'></script>", staticWebsite, versionNumber);

#if DEBUG
        // Includes files from the static content project so they can be listed individually if in DEBUG mode.
        BundleTable.VirtualPathProvider = new StaticContentVirtualPathProvider();
        bundles.Add(new StyleBundle("~/bundles/styles").IncludeDirectory("~/app/styles", "*.css", true));
        bundles.Add(new ScriptBundle("~/bundles/scripts").IncludeDirectory("~/app/src", "*.js", true));
#endif
    }
}

Here is my custom VirtualPathProvider:

public class StaticContentVirtualPathProvider : VirtualPathProvider
{
    // Modify this to be the relative path from your main project to your static content project.
    private const string StaticContentRelativePath = @"..\..\MyStaticContentProjectFolder";

    public static string GetStaticContentPath(string virtualPath, bool isDirectory = false)
    {
        virtualPath = virtualPath.Replace('/', '\\').Replace("~", "");

        if (isDirectory && !virtualPath.EndsWith("\\")) virtualPath += "\\";

        return HttpRuntime.AppDomainAppPath + StaticContentRelativePath + virtualPath;
    }

    public override bool FileExists(string virtualPath)
    {
        return File.Exists(GetStaticContentPath(virtualPath));
    }

    public override bool DirectoryExists(string virtualDir)
    {
        return Directory.Exists(GetStaticContentPath(virtualDir));
    }

    public override VirtualFile GetFile(string virtualPath)
    {
        return new StaticContentVirtualFile(virtualPath);
    }

    public override VirtualDirectory GetDirectory(string virtualDir)
    {
        return new StaticContentVirtualDirectory(virtualDir);
    }

    private class StaticContentVirtualFile : VirtualFile
    {
        public StaticContentVirtualFile(string virtualPath)
            : base(virtualPath)
        {
            this.virtualPath = virtualPath;
        }

        private readonly string virtualPath;

        public override Stream Open()
        {
            return new FileStream(StaticContentVirtualPathProvider.GetStaticContentPath(virtualPath), FileMode.Open);
        }
    }

    internal class StaticContentVirtualDirectory : VirtualDirectory
    {
        public StaticContentVirtualDirectory(string virtualPath)
            : base(virtualPath)
        {
        }

        public override IEnumerable Files
        {
            get
            {
                var filePaths = Directory.GetFiles(GetStaticContentPath(this.VirtualPath, true));
                var files = filePaths.Select(filePath => new StaticContentVirtualFile(this.VirtualPath + Path.GetFileName(filePath))).ToList();
                return files;
            }
        }

        public override IEnumerable Directories
        {
            get
            {
                var subDirectoryPaths = Directory.GetDirectories(GetStaticContentPath(this.VirtualPath, true));
                var dirs = subDirectoryPaths.Select(subDirectoryPath => new StaticContentVirtualDirectory(this.VirtualPath + Path.GetFileName(subDirectoryPath))).ToList();
                return dirs;
            }
        }

        public override IEnumerable Children { get { throw new NotImplementedException(); } }
    }
}
Athamas answered 17/3, 2014 at 20:18 Comment(0)
B
1

BundleTable.EnableOptimization = "false" isn't event coming in to play here, as your main site is referencing the "bundle", which is always going to be bundled and minified, regardless of debug status or EnableOptimization.

So @Styles.Render("~/bundles/styles") on the static site will render the individual files when BundleTable.EnableOptimization = "false", but if you navigate directly to /bundles/styles, you still get the minified bundle (what you are doing from your main site).

One option (perhaps your only) would be to configure the bundler on the static site to not minify the bundle when BundleTable.EnableOptimization = "false". This can be accomplished by writing a class that inherits from Bundle and uses it's own custom IBundleBuilder (you can even write the file name as a comment as you add each file into the bundle). Some example code to point you in the right direction can be found on GitHub.

Botany answered 17/3, 2014 at 14:20 Comment(1)
Thanks, that makes sense. I was able to get it to not minify by using new Bundle() instead of new ScriptBundle() when in debug mode, but I also wanted to have the files list out individually so I wouldn't have to rebuild the bundle every time I made a JS change. I posted an answer that allowed me to do this.Athamas

© 2022 - 2024 — McMap. All rights reserved.