Get actually used (overridden) stylesheets

I'm trying to further develop the Combinator module. The problem I'm currently facing is the following:

If a theme has a base theme, than it can override among others also its parent's style sheets. Now the work of resolving these overrides happens in src\Orchard\DisplayManagement\Descriptors\ResourceBindingStrategy\StylesheetBindingStrategy.cs in the Discover
method. For me it looks that there is no possible way for a module to get the list of the actual stylesheets. There is no way to override the Discover method (it's not virtual) by suppressing the Orchard dependency. There is no way to get the result out of
the Discover method since it's void and it only calls ResourceManager.WriteResource() with the resolved paths, which being a static method can't be overridden either.

I'm kind of stuck here. Any thoughts what could be done? It seems to me there is no way without modifying core classes.

Let's make a deal: I'll implement and submit a patch if you assist me with architectural questions :-).

I've thought along the lines of the following: StylesheetBindingStrategy should notify the ResourceManager. StylesheetBindingStrategy would keep a track of the actual stylesheets (making a list of resources) and then call a method on the IResourceManager
instance like _resourceManager.OverrideStylesheets(IList<ResourceDefinition> resources) where OverrideStylesheets() is virtual. This would overwrite the styles stored in the ResourceManager.

Help me understand the goal here? That StylesheetBindingStrat is discovering stylesheets and turning them into Shapes. Any template or module that is active can override the stylesheet by simply having one with the same file name, since it will also be turned
into a shape with the same name.

I'm not sure but it may be possible to enumerate all the shapes in the system and poke and prod them to find out what they are.

I started to experiment with the approach I described above. Now it won't work, as there is a problem getting an IResourceManager in StylesheetBindingStrategy's constructor (causes "No scope matching the expression 'value(Autofac.Builder.RegistrationBuilder`3+<>c__DisplayClass0[System.Object,Autofac.Builder.ConcreteReflectionActivatorData,Autofac.Builder.SingleRegistrationStyle]).lifetimeScopeTag.Equals(scope.Tag)'
is visible from the scope in which the instance was requested."). So it's not possible to notify the IResourceManager instance there (I guess it's already disposed there).

I've implemented a possible solution in the fork
OverriddenResources. This is far from being thoroughly tested, but it shows a basic approach: ResourceManager saves every resource written to the output (in the WriteResource method) in a static property named OverriddenResources. Now since this static
property lives as long the shell is alive, it eventually will keep all of the written resources.

Now it will be the task of the users (like my Combinator module) to process this data as they wish. I came to the conclusion that resources (namely stylesheets) should be named uniquely, despite being in different folders (like module folders), otherwise
they won't be written. This simplifies the task of keeping track of actually used resources, so the OverriddenResources property can be a dictionary where the key is the resource's file name. That said, e.g. if one would like to get the actually used
stylesheets for a page, one would do the following:

Get the included/required resources (from the IResourceManager instance)

For every resource present get the corresponding actually used, overridden resource (by file name) from OverriddenResources

It could lead to problems that naturally OverriddenResources is only populated fully after all the resources have been written. This means that most possibly all of the resources used on a page can only be retrieved on a subsequent page load, but I haven't
investigated this in detail.

Thanks for the reply! Of course constructive criticism is always warmly welcome!

I don't think we can impose such a rule of uniqueness. What's wrong with the full path?

Sorry, I've phrased my sentence not clear enough: this is the rule currently. I think the cause is that in ResourceManager's Require() the key used to identify a resource is it's name (i.e. the file name) and it's type. So now no resources of the same type
and with the same file name can be used concurrently. As I've looked at Require() for this only now, the implementation for keeping track of overridden resources should really be the same

I don't like the name "OverridenResources" as at this point they have not been overridden and this may be used for something else for all you know.

Sure, I haven't thought too long about this, so the name could be changed to something that is more meaningful to all of us.

What do you store? Is it just the name of the resources?

Stored are the ResourceDefinition objects that WriteResource() gets from StylesheetBindingStrategy's Discover(), their urls changed with the actual one.

What happens in the transition period when all resources have not been used?

If I understand correctly, the answer for this is also described below.

Can you describe the whole lifecycle of this thing, including the usage in your module?

The life cycle, including where my module would kick in. I've underlined the parts where the code I've shown changes something and marked with an arrow if the following sentences have to do with the Combinator module.

On a page view all the resources get included/required through the ResourceManager instance. The resources now stored are not necessarily the ones that will get used, as they could be overridden later.

Somehow with all the magic we get to StylesheetBindingStrategy's Discover(). Here the code which is responsible for get the overrides of stylesheets sits in a lambda expression, not yet evaluated. (Actually it is almost possible to get the actually used
stylesheet urls from the stylesheet links' displaycontext, but the key value - hit.fileVirtualPath - is not shared with the context.)

ResourceManager.BuildRequiredResources() is first called (from CoreShapes.WriteResources()), to build the stylesheets. -> Since Combinator suppresses ResourceManager, here its method is called, but now nothing can be done with overridden
styles since the actual stylesheets are not known yet (this resolving comes later). Therefore, the list of included stylesheets is returned, which we know, not necessarily contains the actually used stylesheets.

ResourceManager.WriteResource() (a static method!) gets called for every stylesheet. This is now where we can get the actually used stylesheet urls, the method gets them in the argument named url.
OverriddenStylesheets gets filled up one by one with the actually used stylesheets.

ResourceManager.BuildRequiredResources() is called for scripts. ->
This is where Combinator can get the list of the actually used stylesheets and process them as described in my Nov 2 post to make a list of them for the current page. Although it's late for this page view, but for subsequent page views with the same
set of stylesheets later the list of actually used stylesheet can be used. Since
OverriddenStylesheets gets filled incrementally with every new page view, if all the pages get viewed in the life time of the shell, it will contain all actually used resources. This is not an issues when determining
which ones to use on a specific page, when using the approach written in the post.

ResourceManager.WriteResource() gets called for script files.

ResourceManager.BuildRequiredResources() is called again for scripts. I guess this is because it gets called separately when detecting head and foot scripts, which is actually unnecessary, since as BuildRequiredResources() doesn't handle script locations,
it returns all the scripts both times.

ResourceManager.WriteResource() gets called again for script files (so these should be the foot scripts).

I've uploaded a new version. I figure out that I've actually forgotten to implement saving of the actually used stylesheets' urls. Now the essence of all the above is in these lines in ResourceManager.WriteResource():

OK then, thanks for the explanation. Personally, I don't see anything wrong with the core resource management gathering this data and making it available for modules like yours to use. What do others think?