Implement $ref resolution for JSON components (needs server restart)

Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-16 18:06:51 +00:00
parent 22c1c36da6
commit 395104cd8b
2 changed files with 33 additions and 11 deletions

View File

@@ -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 {

View File

@@ -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<string, JsonValue> = {},
ComponentRegistry: Record<string, React.ComponentType<Record<string, unknown>>> = FAKEMUI_REGISTRY
ComponentRegistry: Record<string, React.ComponentType<Record<string, unknown>>> = 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(
</div>
)
}
return renderTemplate(template, context, ComponentRegistry)
return renderTemplate(template, context, ComponentRegistry, componentRegistry)
} catch (error) {
return (
<div style={{ padding: '1rem', border: '1px solid red', borderRadius: '0.25rem' }}>
@@ -67,7 +74,8 @@ export function renderJSONComponent(
function renderTemplate(
node: JsonValue,
context: RenderContext,
ComponentRegistry: Record<string, React.ComponentType<Record<string, unknown>>>
ComponentRegistry: Record<string, React.ComponentType<Record<string, unknown>>>,
componentRegistry?: Map<string, JSONComponent>
): 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<string, JsonValue>
// 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 (
<div style={{ padding: '0.5rem', border: '1px dashed orange', borderRadius: '0.25rem' }}>
<strong>Warning:</strong> Component reference "${nodeObj.$ref}" not found
</div>
)
}
}
// 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 (
<React.Fragment key={index}>
{renderTemplate(child, context, ComponentRegistry)}
{renderTemplate(child, context, ComponentRegistry, componentRegistry)}
</React.Fragment>
)
})
} else {
children = renderTemplate(nodeChildren, context, ComponentRegistry)
children = renderTemplate(nodeChildren, context, ComponentRegistry, componentRegistry)
}
}
@@ -189,12 +211,12 @@ function renderTemplate(
}
return (
<React.Fragment key={index}>
{renderTemplate(child, context, ComponentRegistry)}
{renderTemplate(child, context, ComponentRegistry, componentRegistry)}
</React.Fragment>
)
})
} else {
children = renderTemplate(nodeChildren, context, ComponentRegistry)
children = renderTemplate(nodeChildren, context, ComponentRegistry, componentRegistry)
}
}