88import type { RuntimeAdapter , ComponentSpec , DebugContextValue } from '../runtime/types' ;
99import type { FormAdapter } from '../forms/types' ;
1010import type { MiddlewareFn , MiddlewareContext } from '../middleware/types' ;
11- import { getComponentSpec } from '../registries/component-registry' ;
1211import { getRendererForType } from '../registries/renderer-registry' ;
1312import { applyMiddlewares } from '../middleware/types' ;
1413import { processValue } from '../expressions/template-processor' ;
1514import { createDefaultResolver } from '../expressions/variable-resolver' ;
16- import { FormSchema } from '../schema/schema-types' ;
1715
1816/**
1917 * Resolution result - successful component resolution
2018 */
2119export interface ResolutionSuccess {
22- renderSpec : ComponentSpec ;
23- componentToRender : ComponentSpec ;
20+ componentSpec : ComponentSpec ;
2421 rendererFn : ReturnType < typeof getRendererForType > ;
2522}
2623
@@ -48,26 +45,33 @@ export interface FactorySetupResult {
4845 * Resolve component spec from schema
4946 */
5047export function resolveSpec (
51- schema : FormSchema ,
48+ schema : any ,
5249 componentKey : string ,
5350 components : Record < string , ComponentSpec > ,
51+ customComponents ?: Record < string , ComponentSpec > ,
5452 localRenderers ?: Partial < Record < string , any > > ,
5553 debugEnabled ?: boolean
5654) : ResolutionResult {
5755 const componentName = schema [ 'x-component' ] || componentKey ;
58- // components already has provider components merged in the factory
59- // Pass components as globalComponents (includes provider components) and undefined as localComponents
60- const renderSpec = getComponentSpec ( componentName , components , undefined , debugEnabled ) ;
61-
62- if ( ! renderSpec ) {
56+
57+ const isCustomComponent = schema [ 'x-custom' ] === true ;
58+ let componentSpec = null ;
59+
60+ if ( isCustomComponent && customComponents ) {
61+ componentSpec = customComponents [ componentKey ] ;
62+ } else {
63+ componentSpec = components [ componentName ] ;
64+ }
65+
66+ if ( ! componentSpec ) {
6367 if ( debugEnabled ) {
6468 console . warn ( `Component not found: ${ componentName } ` ) ;
6569 }
6670 // Return error element (framework adapter will handle)
6771 return null ;
6872 }
6973
70- const componentType = renderSpec . type || 'field' ;
74+ const componentType = componentSpec . type || 'field' ;
7175 const rendererFn = getRendererForType (
7276 componentType ,
7377 undefined ,
@@ -76,8 +80,7 @@ export function resolveSpec(
7680 ) ;
7781
7882 return {
79- renderSpec,
80- componentToRender : renderSpec ,
83+ componentSpec,
8184 rendererFn,
8285 } ;
8386}
@@ -97,31 +100,31 @@ export function createRendererOrchestrator(
97100 namePath : string [ ] = [ ] ,
98101 isDirectRootProperty : boolean = false
99102 ) : any {
100- const {
103+ const {
101104 components,
102105 customComponents,
103- renderers : localRenderers ,
104- externalContext,
105- state,
106- middlewares,
106+ renderers : localRenderers ,
107+ externalContext,
108+ state,
109+ middlewares,
107110 onSubmit,
108111 debug,
109112 formAdapter
110113 } = getFactorySetup ( ) ;
111-
114+
112115 // Process schema with template expressions BEFORE extracting props
113116 // This ensures that $formValues.* and $externalContext.* are replaced
114117 // in any property of the schema (x-ui, x-content, x-component-props, etc.)
115118 const resolver = createDefaultResolver ( {
116119 externalContext,
117120 formValues : state ,
118121 } ) ;
119-
122+
120123 const processedSchema = processValue ( schema , resolver , {
121124 externalContext,
122125 formValues : state ,
123126 } ) as any ;
124-
127+
125128 // Check visibility via x-ui.visible
126129 // If visible === false, don't render this component (and its children)
127130 // By default, visible is true
@@ -130,113 +133,36 @@ export function createRendererOrchestrator(
130133 return null ;
131134 }
132135
133- // Check for x-custom flag - if true, use custom component instead
134- const isCustomComponent = processedSchema [ 'x-custom' ] === true ;
135-
136- if ( isCustomComponent && customComponents ) {
137- // Look up custom component by the property key name
138- const customSpec = customComponents [ componentKey ] ;
139-
140- if ( customSpec ) {
141- // Get renderer for the custom component type
142- const customRendererFn = getRendererForType (
143- customSpec . type || 'field' ,
144- undefined ,
145- localRenderers as any ,
146- debug ?. isEnabled
147- ) ;
148-
149- // Build props for custom component
150- const customProps = {
151- ...customSpec . defaultProps ,
152- // Injected for E2E: identifies component key in DOM
153- 'data-test-id' : `${ componentKey } ` ,
154- // Pass the original schema so custom component can access x-component-props, etc.
155- schema : processedSchema ,
156- // Pass the component key
157- componentKey,
158- // Pass the name path for form binding
159- name : parentProps . name ? `${ parentProps . name } .${ componentKey } ` : componentKey ,
160- // Pass external context
161- externalContext,
162- // Pass x-component-props if present
163- ...( processedSchema [ 'x-component-props' ] || { } ) ,
164- } ;
165-
166- // Render children if schema has properties
167- const customChildren : any [ ] = [ ] ;
168- if ( processedSchema . properties && typeof processedSchema . properties === 'object' ) {
169- const childParentProps = {
170- ...customProps ,
171- name : customProps . name ,
172- } ;
173-
174- const sortedEntries = Object . entries ( processedSchema . properties ) . sort (
175- ( [ , a ] , [ , b ] ) => {
176- const orderA = ( a as any ) ?. [ 'x-ui' ] ?. order ?? Infinity ;
177- const orderB = ( b as any ) ?. [ 'x-ui' ] ?. order ?? Infinity ;
178- return orderA - orderB ;
179- }
180- ) ;
181-
182- for ( const [ key , childSchema ] of sortedEntries ) {
183- const childResult = render ( key , childSchema as any , childParentProps , namePath , false ) ;
184- if ( childResult !== null && childResult !== undefined ) {
185- customChildren . push ( childResult ) ;
186- }
187- }
188- }
189-
190- // Render the custom component
191- return customRendererFn ( customSpec , customProps , runtime , customChildren . length > 0 ? customChildren : undefined ) ;
192- } else {
193- // Custom component not found - log warning and fall back to normal rendering
194- if ( debug ?. isEnabled ) {
195- console . warn ( `Custom component not found for key: ${ componentKey } . Falling back to standard component.` ) ;
196- }
197- }
198- }
199-
200136 // Parse schema (now using processed schema)
201137 const { 'x-component-props' : componentProps = { } } = processedSchema ;
202138
203- // const componentSpec = getComponentSpec(componentKey, components, undefined, debug?.isEnabled);
204-
205- // if(!componentSpec) {
206- // if (debug?.isEnabled) {
207- // console.warn(`Component not found: ${componentKey}`);
208- // }
209- // // Return error element (framework adapter will handle)
210- // return null;
211- // }
212-
213139 // Resolve component and renderer
214140 // Use processedSchema for x-component resolution (in case x-component has templates)
215141 // components already has provider components merged in the factory
216142 const resolution = resolveSpec (
217- processedSchema ,
218- componentKey ,
219- components ,
220- localRenderers ,
143+ processedSchema ,
144+ componentKey ,
145+ components ,
146+ customComponents ,
147+ localRenderers ,
221148 debug ?. isEnabled
222149 ) ;
223-
150+
224151 // Check if resolution failed
225152 if ( ! resolution || resolution === null ) {
226153 // Return error component (framework adapter will provide)
227154 return null ;
228155 }
229156
230157 // Extract successful resolution
231- const { renderSpec , componentToRender , rendererFn } = resolution as ResolutionSuccess ;
158+ const { componentSpec , rendererFn } = resolution as ResolutionSuccess ;
232159
233160 // Construct name path for nested fields
234161 // ONLY components with type: 'field' are included in the name path
235162 // EXCEPTION: componentKeys that are direct properties of root schema (isDirectRootProperty)
236163 // All other components (containers, FormContainer, content, etc.) are ignored
237- const componentType = renderSpec . type || 'field' ;
238- const isFieldComponent = componentType === 'field' ;
239-
164+ const isFieldComponent = componentSpec . type === 'field' ;
165+
240166 // If field OR direct root property: add componentKey to name path
241167 // If not field and not root property: keep parentProps.name (don't add this component's key)
242168 const shouldIncludeInPath = isFieldComponent || isDirectRootProperty ;
@@ -246,10 +172,11 @@ export function createRendererOrchestrator(
246172
247173 // Props Processing (using processedSchema)
248174 const baseProps = {
249- ...renderSpec . defaultProps ,
175+ ...componentSpec . defaultProps ,
250176 ...parentProps ,
251177 // Injected for E2E: identifies component key in DOM
252178 'data-test-id' : `${ componentKey } ` ,
179+ schema,
253180 // Add name prop ONLY for field components
254181 ...( isFieldComponent && currentName ? { name : currentName } : { } ) ,
255182 ...( Object . keys ( componentProps ) . length > 0 ? { 'x-component-props' : componentProps } : { } ) ,
@@ -271,7 +198,7 @@ export function createRendererOrchestrator(
271198 debug,
272199 formAdapter,
273200 } ;
274-
201+
275202 const mergedProps = applyMiddlewares ( baseProps , processedSchema , middlewares , middlewareContext ) ;
276203
277204 // Render children if schema has properties (use processedSchema)
@@ -292,7 +219,7 @@ export function createRendererOrchestrator(
292219 return orderA - orderB ;
293220 }
294221 ) ;
295-
222+
296223 for ( const [ key , childSchema ] of sortedEntries ) {
297224 // If this component is the root (FormContainer) and has no name,
298225 // then its direct children are root properties and should be included in name path
@@ -305,7 +232,7 @@ export function createRendererOrchestrator(
305232 }
306233
307234 // Final Rendering using renderer function with children
308- return rendererFn ( componentToRender , mergedProps , runtime , children . length > 0 ? children : undefined ) ;
235+ return rendererFn ( componentSpec , mergedProps , runtime , children . length > 0 ? children : undefined ) ;
309236 } ;
310237}
311238
0 commit comments