0

Is there a way to combine any number of already defined bundles into a new bundle that is created on the fly within a razor page?

For example, taking something like this on a razor page:

@section scripts
   @Scripts.Render("~/plugins/bundle1")
   @Scripts.Render("~/plugins/bundle2") 
   @Scripts.Render("~/plugins/bundle3") 
   @Scripts.Render("~/plugins/bundle4")
   @Scripts.Render("~/plugins/bundle5")
End Section

And doing something like this, obviously broken, code:

bundles.Add(New ScriptBundle("~/plugins/onTheFly").Include(
  "~/plugins/bundle1",
  "~/plugins/bundle2",
  "~/plugins/bundle3",
  "~/plugins/bundle4",
  "~/plugins/bundle5"
))
@section scripts
   @* only one script file for the browser to download *@
   @Scripts.Render("~/plugins/onTheFly")

As well as something similar for style bundles too.

Drew
  • 4,215
  • 3
  • 26
  • 40
  • see related post https://stackoverflow.com/questions/27553164/make-a-script-bundle-include-another-script-bundle –  Apr 16 '18 at 13:41
  • Thanks, that shows how to create another predefined bundle by combining other preexisting bundles, which is half of what I am trying to do. How do I create a new bundle from preexisting bundles on a razor page just before doing the @Scripts.Render call ? – Drew Apr 16 '18 at 13:50

1 Answers1

1

I found code at https://devblog.appriver.com/easy-cssjs-bundles-for-webforms-mvc-apps/ which I used as a starting point for the solution below.

This solution will allow you to add individual files into a new bundle, just like the class found at the link given above.

However, this solution also allows you to specify other bundles to add to this new bundle. When a bundle path is given this class will obtain the file path(s) that were defined in the bundle and include those files into the new bundle that is created.

When creating a new style bundle from other existing style bundles, this class will detect file paths that have the CssRewriteUrlTransform class specified and apply that transformation to that same file in the newly created bundle.

Usage:

I added this class at the end of the same file that I define the BundleConfig class that contains all of the various bundles we created:

Public Class BundleManager
#Region "Script Bundling"
    Public Overloads Shared Function ScriptBundle(newBundlePath As String, ParamArray bundleOrFilePaths As String()) As IHtmlString

        ' used when looping dictionaries below
        Dim pair As KeyValuePair(Of String, Boolean)

        ' Loops through all bundleOrFilePaths given and:
        ' --If the path is for a bundle then adds the filepaths in that bundle to the allFilePaths list
        ' --If the path is for a single file then adds that file path to the allFilePaths list
        ' The end result is a list of individual files to include in the new on-the-fly bundle
        Dim allFilePaths As New Dictionary(Of String, Boolean)
        For idx As Integer = 0 To bundleOrFilePaths.Length - 1
            Dim filepaths As Dictionary(Of String, Boolean) = GetFilepathsInBundle(bundleOrFilePaths(idx), False)
            For Each pair In filepaths
                If allFilePaths.ContainsKey(pair.Key) = False Then
                    allFilePaths.Add(pair.Key, pair.Value)
                End If
            Next
        Next

        Dim thisBundle = New ScriptBundle(newBundlePath)
        For Each pair In allFilePaths
            thisBundle.Include(pair.Key)
        Next
        BundleTable.Bundles.Add(thisBundle)
        Return Scripts.Render(newBundlePath)
    End Function
#End Region

#Region "Style Bundling"
    Public Overloads Shared Function StyleBundle(newBundlePath As String, ParamArray bundleOrFilePaths As String()) As IHtmlString

        ' used when looping dictionaries below
        Dim pair As KeyValuePair(Of String, Boolean)

        ' Loops through all bundleOrFilePaths given and:
        ' --If the path is for a bundle then adds the filepaths in that bundle to the allFilePaths list
        ' --If the path is for a single file then adds that file path to the allFilePaths list
        ' The end result is a list of individual files to include in the new on-the-fly bundle
        Dim allFilePaths As New Dictionary(Of String, Boolean)
        For idx As Integer = 0 To bundleOrFilePaths.Length - 1
            Dim filepaths As Dictionary(Of String, Boolean) = GetFilepathsInBundle(bundleOrFilePaths(idx), True)
            For Each pair In filepaths
                If allFilePaths.ContainsKey(pair.Key) = False Then
                    allFilePaths.Add(pair.Key, pair.Value)
                End If
            Next
        Next

        Dim thisBundle = New StyleBundle(newBundlePath)
        For Each pair In allFilePaths
            If pair.Value = True Then
                thisBundle.Include(pair.Key, New CssRewriteUrlTransform())
            Else
                thisBundle.Include(pair.Key)
            End If
        Next
        BundleTable.Bundles.Add(thisBundle)
        Return Styles.Render(newBundlePath)
    End Function
#End Region

    Public Shared Function GetFilepathsInBundle(ByVal bundleOrFilePath As String, Optional ByVal lookForCssRewriteUrlTransform As Boolean = False) As Dictionary(Of String, Boolean)
        Dim filepaths As New Dictionary(Of String, Boolean)

        Dim myBundle As System.Web.Optimization.Bundle
        myBundle = BundleTable.Bundles.GetBundleFor(bundleOrFilePath)
        If myBundle Is Nothing Then
            ' This isn't a bundle path so it's just a file path, add it to the filepaths list and return
            filepaths.Add(bundleOrFilePath, False)
            Return filepaths
        End If

        Dim currentContext As HttpContext = HttpContext.Current

        Dim httpCtxt As HttpContextWrapper
        httpCtxt = New HttpContextWrapper(currentContext)

        Dim myContext As BundleContext
        myContext = New BundleContext(httpCtxt, BundleTable.Bundles, bundleOrFilePath)

        Dim myIEnumerable As IEnumerable(Of BundleFile)
        myIEnumerable = myBundle.EnumerateFiles(myContext)

        For Each bundlefile In myIEnumerable.ToArray
            Dim useCssRewriteUrlTransform As Boolean = False
            If lookForCssRewriteUrlTransform = True AndAlso bundlefile.Transforms.Count > 0 Then
                useCssRewriteUrlTransform = True
            End If
            filepaths.Add(bundlefile.IncludedVirtualPath, useCssRewriteUrlTransform)
        Next

        Return filepaths

    End Function

End Class    

** In your razor page ** Create a single style and single script bundle for the browser to download by combining all the bundles and individual files that this particular razor page uses.

Note, the first parameter to StyleBundle/ScriptBundle is the path of the NEW bundle that will be created and used on-the-fly.

Place this where you want to include the styles on your page:

@BundleManager.StyleBundle("~/styles/onTheFly",
    "~/existing/style/bundle1",
    "~/existing/style/bundle2",
    "~/path/to/individual/file.css",    
    "~/existing/style/bundle3",
    "~/path/to/another/individual/file.css"    
)
' will result in something like:
' <link href="/styles/onthefly?v=tNdHeAW5fTG_oge_eQoH0xgkG9tvARsxlz864XQUBBk1" rel="stylesheet" />

Place this where you want to include the scripts on your page:

@BundleManager.ScriptBundle("~/scripts/onTheFly",
    "~/existing/script/bundle1",
    "~/existing/script/bundle2",
    "~/path/to/individual/file.js",
    "~/existing/script/bundle3",
    "~/path/to/another/individual/file.js"
)
' Will result in something like:
' <link href="/scripts/onthefly?v=tNdHeAW5fTG_oge_eQoH0xgkG9tvARsxlz864XQUBBk1" rel="stylesheet" />
Drew
  • 4,215
  • 3
  • 26
  • 40