Bundle your assets dynamically in runtime in Sitecore

Good people! Blazor is the true path, please take a look at it and be amazed πŸ™‚ It is also official that Microsoft will release a server-side Blazor(called razor pages) next year together with .NET Core 3. The server-side Blazor will be using signalR, see it as a SPA on stereoids. And while you are at it, why not try out my little SitecoreBlazor project at github – SitecoreBlazor. Guess what? It’s among the samples list in Blazor.net. Microsoft and all, it’s indeed a great honor πŸ™‚ – https://blazor.net/community.html

Anyway I will take a short break from the lovely Blazor. This post will be about bundling, yes I know nothing new here. In fact it’s pretty old stuff but I found out some very neat stuff you guys can do with the good old bundler in Asp.Net MVC. Let me show you.

If you want to bundle(not using webpack, we want to do it in good old backend style) in Sitecore, you can do it in global.asax or using pipelines. Doing stuff in global.asax is a BIG NO NO for me, you should of course use pipelines.

So normally you will do something like this. Create a pipeline and add the files you want to bundle.

namespace Sandbox.Foundation.Assets.Pipelines.Initialize
{
  using Sitecore;
  using Sitecore.Pipelines;
  using System.Web.Optimization;

  public class BundleAssets
  {
    [UsedImplicitly]
    public virtual void Process(PipelineArgs args)
    {
      RegisterBundles(BundleTable.Bundles);
    }

    private void RegisterBundles(BundleCollection bundles)
    {

      bundles.Add(new ScriptBundle("~/bundles/mybundle").Include(
                "~/Scripts/blala.js",
                "~/Scripts/blibli.js"
                ));

    }
  }
}

Here is the config patch, we also need to add “bundle” to the IgnoreUrlPrefixes.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">
  <sitecore>
    <pipelines>
      <initialize>
        <processor patch:before="*[@type='Sitecore.Mvc.Pipelines.Loader.InitializeGlobalFilters, Sitecore.Mvc']" type="Sandbox.Foundation.Assets.Pipelines.Initialize.BundleAssets, Sandbox.Foundation.Assets"/>
      </initialize>
    </pipelines>
    <settings>
      <setting name="IgnoreUrlPrefixes" value="/sitecore/default.aspx|/trace.axd|/webresource.axd|/sitecore/shell/Controls/Rich Text Editor/Telerik.Web.UI.DialogHandler.aspx|/sitecore/shell/applications/content manager/telerik.web.ui.dialoghandler.aspx|/sitecore/shell/Controls/Rich Text Editor/Telerik.Web.UI.SpellCheckHandler.axd|/Telerik.Web.UI.WebResource.axd|/sitecore/admin/upgrade/|/layouts/testing|/sitecore/service/xdb/disabled.aspx|/bundles"/>
    </settings>
  </sitecore>
</configuration>

Then in your Layout page, Default.cshtml. You will do something like this:


@Scripts.Render("~/bundles/mybundle")

</body>
</html>

And here is the result, when you do a view page source:


 <script src="/bundles/mybundle?v=hoiWfteIVzBD4TLAz53Kozq93ZTif-J-AigER2yQf2o1"></script>

</body>
</html>

Ok fine. But hey I want to add files dynamically in runtime, specially the ones that are fetched from the Assets project(in Habitat).

Today you will do this in the layout page, calling the

@RenderAssetsService.Current.RenderScript(ScriptLocation.Body)

And the end result will be like this:

<script src="/scripts/fromArendering.js"></script>
<script src="/scripts/fromApage.js"></script>

But I want the Assets project to update my bundle in runtime. Is it doable? It sure is πŸ™‚

First we need to register the bundle like we did above. But we will register an EMPTY bundle:

namespace Sandbox.Foundation.Assets.Pipelines.Initialize
{
  using Sitecore;
  using Sitecore.Pipelines;
  using System.Web.Optimization;

  public class BundleAssets
  {
    [UsedImplicitly]
    public virtual void Process(PipelineArgs args)
    {
      RegisterBundles(BundleTable.Bundles);
    }

    private void RegisterBundles(BundleCollection bundles)
    {
      bundles.Add(new ScriptBundle("~/bundles/emptyBundle"));
    }
  }
}

Next thing will be to add a new pipeline in the Assets project. We will put it together with the rest of the GetPageRendering pipelines.
This pipeline will update the empty bundle with “script files” which we get from the AssetRepository.

namespace Sandbox.Foundation.Assets.Pipelines.GetPageRendering
{

  using Sitecore;
  using Sitecore.Pipelines;
  using Sandbox.Foundation.Assets.Models;
  using Sandbox.Foundation.Assets.Repositories;
  using System.Linq;
  using System.Web.Optimization;

  public class UpdateBundle
  {
    [UsedImplicitly]
    public virtual void Process(PipelineArgs args)
    {
      UpdateBundles(BundleTable.Bundles);
    }

    private void UpdateBundles(BundleCollection bundles)
    {

      var myEmptyBundle = bundles.GetBundleFor("~/bundles/emptyBundle");

      if (myEmptyBundle == null)
        return;

      var listOfJavascripts = AssetRepository.Current.Items
        .Where(asset => asset.Type == AssetType.JavaScript && asset.ContentType == AssetContentType.File).ToList();

      myEmptyBundle.Include(listOfJavascripts.Select(a => $"~/{a.Content}").ToArray<string>());

    }
  }
  
}

First we will try to find “emptyBundle” bundle, then we will grab the Javascript/or css files from the AssetRepository and finally we will add them to the empty bundle.

That’s the magic people! We can update a bundle on the fly πŸ™‚

Here is the config patch for the pipeline(together with the rest of the other pipelines from the Assets project):

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">
  <sitecore>
    <pipelines>
      <initialize>
        <processor patch:before="*[@type='Sitecore.Mvc.Pipelines.Loader.InitializeGlobalFilters, Sitecore.Mvc']" type="Sandbox.Foundation.Assets.Pipelines.Initialize.BundleAssets, Sandbox.Foundation.Assets"/>
      </initialize>
      <mvc.getPageRendering>
        <processor patch:before="*[@type='Sitecore.Mvc.Pipelines.Response.GetPageRendering.GetLayoutRendering, Sitecore.Mvc']" type="Sandbox.Foundation.Assets.Pipelines.GetPageRendering.ClearAssets, Sandbox.Foundation.Assets"/>
       
        <processor patch:before="*[@type='Sitecore.Mvc.Pipelines.Response.GetPageRendering.GetLayoutRendering, Sitecore.Mvc']" type="Sandbox.Foundation.Assets.Pipelines.GetPageRendering.AddAssets, Sandbox.Foundation.Assets">
          <siteAssets hint="raw:AddAsset" />
        </processor>
        <processor patch:before="*[@type='Sitecore.Mvc.Pipelines.Response.GetPageRendering.GetLayoutRendering, Sitecore.Mvc']" type="Sandbox.Foundation.Assets.Pipelines.GetPageRendering.AddThemeAssets, Sandbox.Foundation.Assets"/>
        <processor patch:before="*[@type='Sitecore.Mvc.Pipelines.Response.GetPageRendering.GetLayoutRendering, Sitecore.Mvc']" type="Sandbox.Foundation.Assets.Pipelines.GetPageRendering.AddPageAssets, Sandbox.Foundation.Assets"/>
        <processor patch:before="*[@type='Sitecore.Mvc.Pipelines.Response.GetPageRendering.GetLayoutRendering, Sitecore.Mvc']" type="Sandbox.Foundation.Assets.Pipelines.GetPageRendering.AddRenderingAssets, Sandbox.Foundation.Assets"/>
        <processor patch:before="*[@type='Sitecore.Mvc.Pipelines.Response.GetPageRendering.GetLayoutRendering, Sitecore.Mvc']" type="Sandbox.Foundation.Assets.Pipelines.GetPageRendering.UpdateBundle, Sandbox.Foundation.Assets"/>

      </mvc.getPageRendering>
    </pipelines>
    <settings>
      <setting name="IgnoreUrlPrefixes" value="/sitecore/default.aspx|/trace.axd|/webresource.axd|/sitecore/shell/Controls/Rich Text Editor/Telerik.Web.UI.DialogHandler.aspx|/sitecore/shell/applications/content manager/telerik.web.ui.dialoghandler.aspx|/sitecore/shell/Controls/Rich Text Editor/Telerik.Web.UI.SpellCheckHandler.axd|/Telerik.Web.UI.WebResource.axd|/sitecore/admin/upgrade/|/layouts/testing|/sitecore/service/xdb/disabled.aspx|/bundles"/>
    </settings>
  </sitecore>
</configuration>

And in your layout page – Default.cshtml. You do this:


@Scripts.Render("~/bundles/myEmptyBundle")

</body>
</html>

The end result will be a bundle filled with your lovely javascript files πŸ™‚


 <script src="/bundles/myEmptyBundle?v=hoiWfteIVzBD4TLAz53Kozq93ZTif-J-AigER2yQf2o1"></script>

</body>
</html>

And finally… Blazor will rock your world. Start blazoring now or be forever overrun

That’s all for now folks πŸ™‚

Advertisements

One thought on “Bundle your assets dynamically in runtime in Sitecore

  1. However with the rise of http2 bundling and spriting is highly unnecessary. It would be nice though to retain the minification part of the bundler and the part that is spitting out tags with the crc of the files.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s