Taritsyn / BundleTransformer

Bundle Transformer - a modular extension for System.Web.Optimization (also known as the Microsoft ASP.NET Web Optimization Framework).
Apache License 2.0
130 stars 19 forks source link

File not found exception when using css-files from embedded resources in class library #85

Open Elimi81 opened 2 months ago

Elimi81 commented 2 months ago

Hi!

I'm using Asp.Net Mvc 5 / .net framework.

Testing the library and was unable to get it working in my scenario so I debugged the code a bit. I have a class library that has the css-files as embedded resources and then I use VirtualPathProvider to serve them. This all works with the build in asp.net web optimization but I would like to use this library for modern css-minification. The problem is it does not work as intended with the VirtualPathProvider.

The problem seems to be related to the library removing the tilde in front of the virtual path of the css-file. I include the files like this "~/Content/layout/Common/css/global.css" but when I'm debugging the tilde is gone when in fact if is was there there would be no problem as you can see from my screenshot. See the watches I added at left bottom. The file is found if FileExists is called with tilde and not if it is called without it: image

So any insight? Why is the tilde removed, is that a bug in this scenario?

Taritsyn commented 2 months ago

Hello, Esa!

… but I would like to use this library for modern css-minification.

What modules of the Bundle Transformer do you use?

I have a class library that has the css-files as embedded resources and then I use VirtualPathProvider to serve them.

I would like to look at your VirtualPathProvider. Сorrect implementation of such a provider should process virtual paths both with and without a tilde.

Why is the tilde removed, is that a bug in this scenario?

Tilde is not removed, just the application-relative path is converted to an absolute virtual path. This is a feature of the Bundle Transformer's pipeline implementation.

Elimi81 commented 2 months ago

I'm only using Nuglify to minimize css and nothing more.

Hmm I think you are right and the error resides in our implementation of the VirtualPathProvider since I was able to get things working with minor adjustments. Have to debug some more but thank you for the nudge in the right direction!

Taritsyn commented 2 months ago

Hello, Esa!

I'm only using Nuglify to minimize css and nothing more.

Microsoft ASP.NET Web Optimization Framework is an extensible library and such simple functionality can be implemented without a Bundle Transformer. It is enough to write just two new classes:

NUglifyCssMinify.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.Optimization;

using NUglify;
using NUglify.Css;

namespace NUglifyExtensions
{
    /// <summary>
    /// Bundle transformation that performs NUglify CSS minification.
    /// </summary>
    public class NUglifyCssMinify : IBundleTransform
    {
        // Not public since in the future we could add instance data to the transforms
        internal static readonly NUglifyCssMinify Instance = new NUglifyCssMinify();

        internal static string CssContentType = "text/css";

        /// <summary>
        /// Minifies the supplied CSS bundle and sets the Http content-type header to 'text/css'
        /// </summary>
        /// <param name="context">The <see cref="BundleContext"/> object that contains state for both the framework configuration and the HTTP request.</param>
        /// <param name="response">A <see cref="BundleResponse"/> object containing the bundle contents.</param>
        public virtual void Process(BundleContext context, BundleResponse response)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }

            // Don't minify in Instrumentation mode
            if (!context.EnableInstrumentation)
            {
                UglifyResult minifiedResult = Uglify.Css(response.Content, new CssSettings() { CommentMode = CssComment.None });
                if (minifiedResult.HasErrors)
                {
                    GenerateErrorResponse(response, minifiedResult.Errors);
                }
                else
                {
                    response.Content = minifiedResult.Code;
                }
            }

            response.ContentType = CssContentType;
        }

        private static void GenerateErrorResponse(BundleResponse bundle, List<UglifyError> errors)
        {
            StringBuilder errorResponse = new StringBuilder();
            errorResponse.Append("/* ");
            errorResponse.Append("Minification failed. Returning unminified contents.").Append("\r\n");
            foreach (object error in errors)
            {
                errorResponse.Append(error.ToString()).Append("\r\n");
            }
            errorResponse.Append(" */\r\n");
            errorResponse.Append(bundle.Content);
            bundle.Content = errorResponse.ToString();
        }
    }
}

NUglifyStyleBundle.cs

using System.Web.Optimization;

namespace TespAspNetMvc4
{
    /// <summary>
    /// Bundle designed specifically for processing cascading stylesheets (CSS)
    /// </summary>
    public class NUglifyStyleBundle : Bundle
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="NUglifyStyleBundle"/> class.
        /// </summary>
        /// <param name="virtualPath">The virtual path used to reference the <see cref="NUglifyStyleBundle"/> from within a view or Web page.</param>
        public NUglifyStyleBundle(string virtualPath)
            : base(virtualPath, new NUglifyCssMinify())
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="NUglifyStyleBundle"/> class.
        /// </summary>
        /// <param name="virtualPath">The virtual path used to reference the <see cref="NUglifyStyleBundle"/> from within a view or Web page.</param>
        /// <param name="cdnPath">An alternate url for the bundle when it is stored in a content delivery network.</param>
        public NUglifyStyleBundle(string virtualPath, string cdnPath)
            : base(virtualPath, cdnPath, new NUglifyCssMinify())
        {
        }
    }
}

And then use them instead of the built-in classes: CssMinify and StyleBundle.

Elimi81 commented 2 months ago

You are a lifesaver! Such a simple solution, my implementation was way too compicated.

Can't thank you enough! Much appreciated.

Taritsyn commented 2 months ago

Hello, Esa!

This code is based on the source codes of the CssMinify, JsMinify and StyleBundle classes of the Microsoft ASP.NET Web Optimization Framework.