Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 38 additions & 10 deletions src/ScissorHands.Web/Renderers/ComponentRenderer.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
using System.Reflection;

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

using ScissorHands.Theme;

namespace ScissorHands.Web.Renderers;

/// <summary>
Expand All @@ -15,6 +19,39 @@ public sealed class ComponentRenderer(IServiceScopeFactory scopeFactory, ILogger
private readonly IServiceScopeFactory _scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory));
private readonly ILoggerFactory _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));

// Lazy initialization of cascading parameter names discovered via reflection
private static readonly Lazy<HashSet<string>> _cascadingParameterNames = new(() =>
{
var parameterNames = new HashSet<string>(StringComparer.Ordinal);

// Discover all types in the ScissorHands.Theme assembly that have cascading parameters
var themeAssembly = typeof(PageViewBase).Assembly;

try
{
var allTypes = themeAssembly.GetExportedTypes();

foreach (var type in allTypes)
{
// Find all properties with CascadingParameter attribute
var cascadingProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
.Where(p => p.GetCustomAttribute<CascadingParameterAttribute>() is not null);

foreach (var property in cascadingProperties)
{
parameterNames.Add(property.Name);
}
}
}
catch (ReflectionTypeLoadException)
{
// If type loading fails, fall back to empty set
// This should not happen in normal operation, but provides safety
}

return parameterNames;
});

/// <inheritdoc />
public async Task<string> RenderAsync<TComponent>(Type layoutType, IDictionary<string, object?> parameters, CancellationToken cancellationToken = default)
where TComponent : IComponent
Expand All @@ -32,18 +69,9 @@ public async Task<string> RenderAsync<TComponent>(Type layoutType, IDictionary<s
builder.OpenComponent<TComponent>(0);
var seq = 1;

var cascadingKeys = new HashSet<string>(StringComparer.Ordinal)
{
"Documents",
"Document",
"Plugins",
"Theme",
"Site"
};

foreach (var kvp in parameters)
{
if (cascadingKeys.Contains(kvp.Key))
if (_cascadingParameterNames.Value.Contains(kvp.Key))
{
continue;
}
Expand Down