From 395104cd8b1672c2e48dd1e8283f4a4166a4a193 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 18:06:51 +0000 Subject: [PATCH] Implement $ref resolution for JSON components (needs server restart) Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com> --- frontends/nextjs/src/app/page.tsx | 4 +- .../packages/json/render-json-component.tsx | 40 ++++++++++++++----- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/frontends/nextjs/src/app/page.tsx b/frontends/nextjs/src/app/page.tsx index 62844fa5f..742d86107 100644 --- a/frontends/nextjs/src/app/page.tsx +++ b/frontends/nextjs/src/app/page.tsx @@ -79,7 +79,7 @@ export default async function RootPage() { const component = pkg.components?.find(c => c.id === route.component || c.name === route.component) if (component !== undefined) { - return renderJSONComponent(component, {}, {}) + return renderJSONComponent(component, {}, {}, pkg.components) } } catch { // Package doesn't exist or can't be loaded, fall through @@ -116,7 +116,7 @@ export default async function RootPage() { ) ?? pkg.components[0] if (pageComponent !== undefined) { - return renderJSONComponent(pageComponent, {}, {}) + return renderJSONComponent(pageComponent, {}, {}, pkg.components) } } } catch { diff --git a/frontends/nextjs/src/lib/packages/json/render-json-component.tsx b/frontends/nextjs/src/lib/packages/json/render-json-component.tsx index 6ade3109a..f695bf52c 100644 --- a/frontends/nextjs/src/lib/packages/json/render-json-component.tsx +++ b/frontends/nextjs/src/lib/packages/json/render-json-component.tsx @@ -22,11 +22,13 @@ export interface RenderContext { * * By default, uses the FAKEMUI_REGISTRY to render components. * Pass a custom ComponentRegistry to override specific components. + * Pass allComponents to enable $ref resolution within the same package. */ export function renderJSONComponent( component: JSONComponent, props: Record = {}, - ComponentRegistry: Record>> = FAKEMUI_REGISTRY + ComponentRegistry: Record>> = FAKEMUI_REGISTRY, + allComponents?: JSONComponent[] ): React.ReactElement { if (component.render === undefined) { return ( @@ -36,6 +38,11 @@ export function renderJSONComponent( ) } + // Build component registry for $ref resolution + const componentRegistry = allComponents + ? new Map(allComponents.map(c => [c.id, c])) + : undefined + const context: RenderContext = { props, state: {}, @@ -50,7 +57,7 @@ export function renderJSONComponent( ) } - return renderTemplate(template, context, ComponentRegistry) + return renderTemplate(template, context, ComponentRegistry, componentRegistry) } catch (error) { return (
@@ -67,7 +74,8 @@ export function renderJSONComponent( function renderTemplate( node: JsonValue, context: RenderContext, - ComponentRegistry: Record>> + ComponentRegistry: Record>>, + componentRegistry?: Map ): React.ReactElement { if (node === null || typeof node !== 'object') { return <>{String(node)} @@ -81,6 +89,20 @@ function renderTemplate( // Now TypeScript knows it's a JsonObject (non-array object) const nodeObj = node as Record + // Handle $ref to other components in the same package + if (typeof nodeObj.$ref === 'string' && componentRegistry !== undefined) { + const referencedComponent = componentRegistry.get(nodeObj.$ref) + if (referencedComponent !== undefined && referencedComponent.render?.template !== undefined) { + return renderTemplate(referencedComponent.render.template, context, ComponentRegistry, componentRegistry) + } else { + return ( +
+ Warning: Component reference "${nodeObj.$ref}" not found +
+ ) + } + } + // Handle conditional rendering if (nodeObj.type === 'conditional') { const conditionValue = nodeObj.condition @@ -90,9 +112,9 @@ function renderTemplate( const condition = evaluateExpression(conditionValue, context) const conditionIsTrue = condition !== null && condition !== undefined && condition !== false && condition !== 0 && condition !== '' if (conditionIsTrue && nodeObj.then !== null && nodeObj.then !== undefined) { - return renderTemplate(nodeObj.then, context, ComponentRegistry) + return renderTemplate(nodeObj.then, context, ComponentRegistry, componentRegistry) } else if (!conditionIsTrue && nodeObj.else !== null && nodeObj.else !== undefined) { - return renderTemplate(nodeObj.else, context, ComponentRegistry) + return renderTemplate(nodeObj.else, context, ComponentRegistry, componentRegistry) } return <> } @@ -128,12 +150,12 @@ function renderTemplate( } return ( - {renderTemplate(child, context, ComponentRegistry)} + {renderTemplate(child, context, ComponentRegistry, componentRegistry)} ) }) } else { - children = renderTemplate(nodeChildren, context, ComponentRegistry) + children = renderTemplate(nodeChildren, context, ComponentRegistry, componentRegistry) } } @@ -189,12 +211,12 @@ function renderTemplate( } return ( - {renderTemplate(child, context, ComponentRegistry)} + {renderTemplate(child, context, ComponentRegistry, componentRegistry)} ) }) } else { - children = renderTemplate(nodeChildren, context, ComponentRegistry) + children = renderTemplate(nodeChildren, context, ComponentRegistry, componentRegistry) } }