From 1459b9be260d4ee621f35d3d2635059eaaec742a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 05:37:08 +0000 Subject: [PATCH 1/4] Initial plan From 522002f93d30a2d08d40e67caa5610fb3d0e5cbb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 05:44:34 +0000 Subject: [PATCH 2/4] Refactor ComponentRenderer to use reflection for cascading parameter detection Co-authored-by: justinyoo <1538528+justinyoo@users.noreply.github.com> --- .../Renderers/ComponentRenderer.cs | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/ScissorHands.Web/Renderers/ComponentRenderer.cs b/src/ScissorHands.Web/Renderers/ComponentRenderer.cs index 9485785..5868592 100644 --- a/src/ScissorHands.Web/Renderers/ComponentRenderer.cs +++ b/src/ScissorHands.Web/Renderers/ComponentRenderer.cs @@ -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; /// @@ -15,6 +19,35 @@ 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> _cascadingParameterNames = new(() => + { + var parameterNames = new HashSet(StringComparer.Ordinal); + + // Get all view base classes from the ScissorHands.Theme assembly + var viewBaseTypes = new[] + { + typeof(PageViewBase), + typeof(PostViewBase), + typeof(IndexViewBase), + typeof(NotFoundViewBase) + }; + + foreach (var type in viewBaseTypes) + { + // Find all properties with CascadingParameter attribute + var cascadingProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(p => p.GetCustomAttribute() != null); + + foreach (var property in cascadingProperties) + { + parameterNames.Add(property.Name); + } + } + + return parameterNames; + }); + /// public async Task RenderAsync(Type layoutType, IDictionary parameters, CancellationToken cancellationToken = default) where TComponent : IComponent @@ -32,18 +65,9 @@ public async Task RenderAsync(Type layoutType, IDictionary(0); var seq = 1; - var cascadingKeys = new HashSet(StringComparer.Ordinal) - { - "Documents", - "Document", - "Plugins", - "Theme", - "Site" - }; - foreach (var kvp in parameters) { - if (cascadingKeys.Contains(kvp.Key)) + if (_cascadingParameterNames.Value.Contains(kvp.Key)) { continue; } From 22a8134cb2f42fa4a5ec3d3def23bcb6eb634901 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 05:48:00 +0000 Subject: [PATCH 3/4] Improve reflection to auto-discover cascading parameters from entire Theme assembly Co-authored-by: justinyoo <1538528+justinyoo@users.noreply.github.com> --- .../Renderers/ComponentRenderer.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/ScissorHands.Web/Renderers/ComponentRenderer.cs b/src/ScissorHands.Web/Renderers/ComponentRenderer.cs index 5868592..47ab841 100644 --- a/src/ScissorHands.Web/Renderers/ComponentRenderer.cs +++ b/src/ScissorHands.Web/Renderers/ComponentRenderer.cs @@ -24,19 +24,14 @@ public sealed class ComponentRenderer(IServiceScopeFactory scopeFactory, ILogger { var parameterNames = new HashSet(StringComparer.Ordinal); - // Get all view base classes from the ScissorHands.Theme assembly - var viewBaseTypes = new[] - { - typeof(PageViewBase), - typeof(PostViewBase), - typeof(IndexViewBase), - typeof(NotFoundViewBase) - }; + // Discover all types in the ScissorHands.Theme assembly that have cascading parameters + var themeAssembly = typeof(PageViewBase).Assembly; + var allTypes = themeAssembly.GetTypes(); - foreach (var type in viewBaseTypes) + foreach (var type in allTypes) { // Find all properties with CascadingParameter attribute - var cascadingProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) + var cascadingProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) .Where(p => p.GetCustomAttribute() != null); foreach (var property in cascadingProperties) From 60a1defc249fb5d7dfc7691fc102747d2c875b24 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 05:50:22 +0000 Subject: [PATCH 4/4] Add error handling and use GetExportedTypes for safer reflection Co-authored-by: justinyoo <1538528+justinyoo@users.noreply.github.com> --- .../Renderers/ComponentRenderer.cs | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/ScissorHands.Web/Renderers/ComponentRenderer.cs b/src/ScissorHands.Web/Renderers/ComponentRenderer.cs index 47ab841..d129188 100644 --- a/src/ScissorHands.Web/Renderers/ComponentRenderer.cs +++ b/src/ScissorHands.Web/Renderers/ComponentRenderer.cs @@ -26,19 +26,28 @@ public sealed class ComponentRenderer(IServiceScopeFactory scopeFactory, ILogger // Discover all types in the ScissorHands.Theme assembly that have cascading parameters var themeAssembly = typeof(PageViewBase).Assembly; - var allTypes = themeAssembly.GetTypes(); - - foreach (var type in allTypes) + + try { - // Find all properties with CascadingParameter attribute - var cascadingProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) - .Where(p => p.GetCustomAttribute() != null); + var allTypes = themeAssembly.GetExportedTypes(); - foreach (var property in cascadingProperties) + foreach (var type in allTypes) { - parameterNames.Add(property.Name); + // Find all properties with CascadingParameter attribute + var cascadingProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) + .Where(p => p.GetCustomAttribute() 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; });