Shazwazza / ClientDependency

DEPRECATED. A library for managing CSS & JavaScript dependencies and optimization in ASP.Net
139 stars 65 forks source link

Getting full path of rendered Client Dependency #140

Closed simoneldevig closed 6 years ago

simoneldevig commented 6 years ago

Hi,

I would like to add the Client Dependency path to a preload link, but I can't figure out how to get the path and is it even possible?

I would like to generate something like this: <link rel="preload" href="@pathToCDScript" as="script">

Any suggestions on how to accomplish this is welcome :-)

Shazwazza commented 6 years ago

Hi, yes this is possible but it's a convoluted/hacky process unfortunately. We do this in Umbraco to create a list of lazy loading composite URLs.

Just FYI my library called Smidge is the predecessor to this library and it has this functionality built in by default.

simoneldevig commented 6 years ago

Thanks for answer @Shazwazza!

Smidge looks awesome - so think we're gonna switch to that :-)

Shazwazza commented 6 years ago

Cool! It targets .NET Standard 2.0 but all docs are for ASP.NET Core. If you are building a .NET Framework site, there might be some boilerplate code to boot it all up which might be manual. I should defo update it so that it's also super easy to get running in a .NET Framework site too. I'll add an issue for that on that repo.

simoneldevig commented 6 years ago

Nice - but does that also mean that v3 supports .NET 4.5.2 or do we have to use v2 for that?

It is going to be used on a Umbraco project :-)

Shazwazza commented 6 years ago

v2 definitely doesn't support that, v3 is the only one that supports netstandard anything before that is only .NET Core.

I think i'll need to update the library to work nicely with .NET Framework TBH, it might be a struggle trying to make it work as-is without .NET Core.

simoneldevig commented 6 years ago

@Shazwazza - Alright.

You can just post on Twitter if you succeed with it :-)

creativesuspects commented 3 years ago

Hi @Shazwazza, I've added a custom renderer to my project and I've used a composer to initialize the custom renderer. I'm using Html.RequiresJs() and Html.RequiresCss() in my views to dynamically add the stylesheets and JavaScript files I need for a specific view. But I still can't seem to figure out how to apply the code you referred to to get the different paths that are rendered by CDF, so I can use these paths to add inside the views. Could you point me in the right direction?

namespace Application.Core
{
    /// <summary>
    /// A custom renderer that only outputs a dependency path instead of script tags - for use with the js loader with yepnope
    /// </summary>
    public class DependencyPathRenderer : StandardRenderer
    {
        public override string Name
        {
            get { return "Custom.DependencyPathRenderer"; }
        }

        /// <summary>
        /// Used to delimit each dependency so we can split later
        /// </summary>
        public const string Delimiter = "||||";

        /// <summary>
        /// Override because the StandardRenderer replaces & with &amp; but we don't want that so we'll reverse it
        /// </summary>
        /// <param name="allDependencies"></param>
        /// <param name="paths"></param>
        /// <param name="jsOutput"></param>
        /// <param name="cssOutput"></param>
        /// <param name="http"></param>
        public override void RegisterDependencies(List<IClientDependencyFile> allDependencies, HashSet<IClientDependencyPath> paths, out string jsOutput, out string cssOutput, HttpContextBase http)
        {
            base.RegisterDependencies(allDependencies, paths, out jsOutput, out cssOutput, http);

            jsOutput = jsOutput.Replace("&amp;", "&");
            cssOutput = cssOutput.Replace("&amp;", "&");
        }

        protected override string RenderSingleJsFile(string js, IDictionary<string, string> htmlAttributes)
        {
            return js + Delimiter;
        }

        protected override string RenderSingleCssFile(string css, IDictionary<string, string> htmlAttributes)
        {
            return css + Delimiter;
        }
    }
}

namespace Application.Core.Composing
{
    [RuntimeLevel(MinLevel = RuntimeLevel.Run)]
    public class ClientDependencyComposer : IUserComposer
    {
        public void Compose(Composition composition)
        {
            //Register a custom renderer
            var renderer = new DependencyPathRenderer();
            renderer.Initialize("Custom.DependencyPathRenderer", new NameValueCollection
            {
                { "compositeFileHandlerPath", ClientDependencySettings.Instance.CompositeFileHandlerPath }
            });
            ClientDependencySettings.Instance.MvcRendererCollection.Add(renderer);
        }
    }
}
Shazwazza commented 3 years ago

@creativesuspects Is there a reason you are using Html.RequiresJs() and Html.RequiresCss() in your views and then not just using RenderJsHere()/RenderCssHere() in your views? Typically this trick is for SPA applications where dependencies would be registered in c# (not in views). The only way this would work if you are doing Html.RequiresJs in a view, is to let all of your views render and then finally use that code to get the paths.

Shazwazza commented 3 years ago

@creativesuspects you can see here, this line would register the dependencies https://github.com/umbraco/Umbraco-CMS/blob/7ee510ed386495120666a78c61497f58ff05de8f/src/Umbraco.Web/UI/JavaScript/AssetInitialization.cs#L86 ... this code does not use views to do RequiresJs/RequiresCss since that requires your views to render.

creativesuspects commented 3 years ago

I assume you mean why I'm using Html.RequiresJs() and Html.RequiresCss() instead of using predefined bundles and only use RenderJsHere()/RenderCssHere() in the views? I'm specifying JS and CSS dependencies for each template separately. The master template contains a number of Html.RequiresJs() and Html.RequiresCss() for stuff that is needed for each template. But I try to minimize the number of JS and CSS dependencies I load for each page.

If I'd want to specify JS/CSS dependencies for each template separately could I add the dependencies in the controller? And then use this code https://github.com/umbraco/Umbraco-CMS/blob/7ee510ed386495120666a78c61497f58ff05de8f/src/Umbraco.Web/UI/JavaScript/AssetInitialization.cs#L86 to get the path of the rendered ClientDepency for that specific page?

Shazwazza commented 3 years ago

Unless you are building a SPA and dynamically loading the assets yourself, you should just be able to use RequiresJs/RequiresCss and RenderJsHere/RenderCssHere wherever you want it rendered and CDF will take care of deduplicating and rendering your js/css. If you want to dynamically render the URLs instead, then yes you would need to predefine all of the assets to load so that you know that they are registered by the time you call RegisterDependencies

creativesuspects commented 3 years ago

I'm not sure we're talking about the same thing. In case it wasn't clear: I'm using CDF on Umbraco 8 to bundle JS/CSS dependencies. I'm using the "MVC" approach (https://github.com/Shazwazza/ClientDependency/wiki#mvc) to add JS dependencies to each template. And I utilize the priority attribute to "inject" additional JS dependencies for a specific template.

So for example on the master template I'd use this:

Html.RequiresJs("~/scripts/sitewide-dependency1.js", 10)
    .RequiresJs("~/scripts/sitewide-dependency2.js", 10)
    .RequiresJs("~/scripts/sitewide-dependency3.js", 10)
    .RequiresJs("~/scripts/app.js", 20);

And on the homepage template I'd do something like this:

Html.RequiresJs("~/scripts/homepage-dependency1.js", 11)
    .RequiresJs("~/scripts/homepage-dependency2.js", 11);

@Html.RenderJsHere() would then render this out as follows (in my case just before the </body> closing tag):

<script src="/DependencyHandler.axd?s=L3NjcmlwdHMvZGlzdC9sYXp5c2l6ZXMubWluLmpzOw&amp;t=Javascript&amp;cdv=1869072828"  type="text/javascript">

This is a/the correct approach, right?

I would now like to be able to add this to the <head></head>:

<link rel="preload" href="/DependencyHandler.axd?s=L3NjcmlwdHMvZGlzdC9sYXp5c2l6ZXMubWluLmpzOw&amp;t=Javascript&amp;cdv=1869072828" as="script" />

But I don't understand how to do this when using the MVC approach I described above, or if it's even possible?