Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"private": true,
"scripts": {
"build": "turbo build",
"dev": "turbo dev",
"showcases:dev": "turbo --filter showcases dev",
"test": "turbo test --filter='./packages/*'",
"test:e2e": "playwright test --config=tests/playwright.config.ts",
"test:e2e:ui": "playwright test --ui --config=tests/playwright.config.ts",
Expand Down
54 changes: 54 additions & 0 deletions packages/core/src/defaults/register-default-renderers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ComponentSpec, ComponentType, RuntimeAdapter } from "../runtime/types";

/**
* Renderer function - wraps component rendering with additional logic
*/
export type RendererFn = (
componentSpec: ComponentSpec,
props: Record<string, any>,
runtime: RuntimeAdapter,
children?: any[]
) => any;

/**
* Default renderers - pass props as is
*/
export const defaultRenderers: Record<ComponentType, RendererFn> = {
field: (spec, props, runtime, children) => {
const propsWithChildren = children && children.length > 0
? { ...props, children }
: props;
return runtime.create(spec, propsWithChildren);
},
'field-wrapper': (spec, props, runtime, children) => {
const propsWithChildren = children && children.length > 0
? { ...props, children }
: props;
return runtime.create(spec, propsWithChildren);
},
'container': (spec, props, runtime, children) => {
return runtime.create(spec, { ...props, children });
},
content: (spec, props, runtime, children) => {
const propsWithChildren = children && children.length > 0
? { ...props, children }
: props;
return runtime.create(spec, propsWithChildren);
},
addon: (spec, props, runtime) => {
return runtime.create(spec, props);
},
'menu-item': (spec, props, runtime, children) => {
const propsWithChildren = children && children.length > 0
? { ...props, children }
: props;
return runtime.create(spec, propsWithChildren);
},
'menu-container': (spec, props, runtime, children) => {
const propsWithChildren = children && children.length > 0
? { ...props, children }
: props;
return runtime.create(spec, propsWithChildren);
},

}
7 changes: 4 additions & 3 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ export * from './runtime/types';
export * from './forms/types';

// Registry system
export * from './registry/component-registry';
export * from './registry/renderer-registry';
export * from './registries/component-registry';
export * from './registries/renderer-registry';

export * from './defaults/register-default-renderers'

// Orchestrator
export * from './orchestrator/renderer-orchestrator';
Expand All @@ -34,7 +36,6 @@ export * from './provider';

// Utils
export * from './utils/jexl-config';
export * from './utils/sanitize-props';

// Validation
export * from './validation';
Expand Down
14 changes: 12 additions & 2 deletions packages/core/src/orchestrator/renderer-orchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import type { RuntimeAdapter, ComponentSpec, DebugContextValue } from '../runtime/types';
import type { FormAdapter } from '../forms/types';
import type { MiddlewareFn, MiddlewareContext } from '../middleware/types';
import { getComponentSpec } from '../registry/component-registry';
import { getRendererForType } from '../registry/renderer-registry';
import { getComponentSpec } from '../registries/component-registry';
import { getRendererForType } from '../registries/renderer-registry';
import { applyMiddlewares } from '../middleware/types';
import { processValue } from '../expressions/template-processor';
import { createDefaultResolver } from '../expressions/variable-resolver';
Expand Down Expand Up @@ -200,6 +200,16 @@ export function createRendererOrchestrator(
// Parse schema (now using processed schema)
const { 'x-component-props': componentProps = {} } = processedSchema;

// const componentSpec = getComponentSpec(componentKey, components, undefined, debug?.isEnabled);

// if(!componentSpec) {
// if (debug?.isEnabled) {
// console.warn(`Component not found: ${componentKey}`);
// }
// // Return error element (framework adapter will handle)
// return null;
// }

// Resolve component and renderer
// Use processedSchema for x-component resolution (in case x-component has templates)
// components already has provider components merged in the factory
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/provider/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { describe, it, expect } from 'vitest';
import { mergeProviderConfigs, resolveProviderConfig } from './provider';
import type { ProviderConfig } from './types';
import type { FormSchema } from '../schema/schema-types';
import { createComponentSpec } from '../registry/component-registry';
import { createComponentSpec } from '../registries/component-registry';

describe('Provider Utilities', () => {
describe('mergeProviderConfigs', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/provider/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import type { ProviderConfig } from './types';
import { defaultDebugConfig } from './types';
import { getRendererRegistry } from '../registry/renderer-registry';
import { getRendererRegistry } from '../registries/renderer-registry';

/**
* Merge two provider configurations hierarchically
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/provider/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import type { ComponentSpec, ComponentType, DebugConfig } from '../runtime/types';
import type { FormSchema } from '../schema/schema-types';
import type { MiddlewareFn } from '../middleware/types';
import type { RendererFn } from '../registry/renderer-registry';
import { RendererFn } from '../defaults/register-default-renderers';

/**
* Provider configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export const defaultTypeProps: Record<ComponentType, Record<string, any>> = {
field: { fullWidth: true },
'field-wrapper': {},
'container': {},
'FormContainer': {},
content: {},
addon: {},
'menu-item': {},
Expand Down Expand Up @@ -54,21 +53,6 @@ export function getFactoryDefaultComponents(): Record<string, ComponentSpec> {
return factoryDefaultComponents;
}

/**
* Registry overrides (global registrations)
*/
const registryOverrides = new Map<string, Partial<ComponentSpec>>();

/**
* Register a component override globally
*/
export function registerComponent(name: string, config: Partial<ComponentSpec>): void {
registryOverrides.set(name, {
...registryOverrides.get(name),
...config,
});
}

/**
* Get unified component registry
*
Expand Down Expand Up @@ -101,16 +85,6 @@ export function getComponentRegistry(
});
}

// Apply registry overrides
for (const [name, override] of Array.from(registryOverrides.entries())) {
const existing = merged[name];
merged[name] = {
...existing,
...override,
id: override.id || existing?.id || name,
} as ComponentSpec;
}

return merged;
}

Expand Down
79 changes: 79 additions & 0 deletions packages/core/src/registries/renderer-registry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Renderer Registry
*
* Framework-agnostic renderer registration system.
* Renderers are functions that wrap components with additional rendering logic.
*/

import { defaultRenderers, RendererFn } from '../defaults/register-default-renderers';
import type { ComponentType } from '../runtime/types';


/**
* Get unified renderer registry with hierarchical merging
*
* Priority order: local > global > default
*/
export function getRendererRegistry(
globalRenderers?: Partial<Record<ComponentType, RendererFn>>,
localRenderers?: Partial<Record<ComponentType, RendererFn>>
): Record<ComponentType, RendererFn> {
// Start with built-in renderers
let merged = { ...defaultRenderers };

// Apply global renderers (from provider)
if (globalRenderers) {
Object.keys(globalRenderers).forEach(type => {
const renderer = globalRenderers[type as ComponentType];
if (renderer) {
merged[type as ComponentType] = renderer;
}
});
}

// Apply local renderers (maximum priority)
if (localRenderers) {
Object.keys(localRenderers).forEach(type => {
const renderer = localRenderers[type as ComponentType];
if (renderer) {
merged[type as ComponentType] = renderer;
}
});
}

return merged;
}

/**
* Get effective renderer for a component type
* Includes debug logging
*/
export function getRendererForType(
type: ComponentType,
globalRenderers?: Partial<Record<ComponentType, RendererFn>>,
localRenderers?: Partial<Record<ComponentType, RendererFn>>,
debugEnabled?: boolean
): RendererFn {
// Local renderers have maximum priority
if (localRenderers?.[type]) {
if (debugEnabled) {
console.log(`Renderer resolved from local: ${type}`);
}
return localRenderers[type]!;
}

// Global renderers second priority
if (globalRenderers?.[type]) {
if (debugEnabled) {
console.log(`Renderer resolved from global: ${type}`);
}
return globalRenderers[type]!;
}

// Default renderer last
if (debugEnabled) {
console.log(`Renderer resolved from default: ${type}`);
}
return defaultRenderers[type];
}

Loading