@memberjunction/react-runtime 2.94.0 → 2.95.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/CHANGELOG.md +12 -0
  3. package/dist/compiler/component-compiler.d.ts +1 -0
  4. package/dist/compiler/component-compiler.d.ts.map +1 -1
  5. package/dist/compiler/component-compiler.js +67 -24
  6. package/dist/compiler/component-compiler.js.map +1 -1
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +2 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/registry/component-registry-service.d.ts +2 -0
  12. package/dist/registry/component-registry-service.d.ts.map +1 -1
  13. package/dist/registry/component-registry-service.js +11 -0
  14. package/dist/registry/component-registry-service.js.map +1 -1
  15. package/dist/registry/component-registry.d.ts +2 -0
  16. package/dist/registry/component-registry.d.ts.map +1 -1
  17. package/dist/registry/component-registry.js +17 -0
  18. package/dist/registry/component-registry.js.map +1 -1
  19. package/dist/registry/component-resolver.d.ts.map +1 -1
  20. package/dist/registry/component-resolver.js +60 -5
  21. package/dist/registry/component-resolver.js.map +1 -1
  22. package/dist/runtime/component-hierarchy.d.ts.map +1 -1
  23. package/dist/runtime/component-hierarchy.js +73 -17
  24. package/dist/runtime/component-hierarchy.js.map +1 -1
  25. package/dist/runtime.umd.js +1 -1
  26. package/dist/types/index.d.ts +1 -1
  27. package/dist/types/index.d.ts.map +1 -1
  28. package/dist/types/index.js.map +1 -1
  29. package/dist/utilities/library-dependency-resolver.d.ts.map +1 -1
  30. package/dist/utilities/library-dependency-resolver.js +17 -8
  31. package/dist/utilities/library-dependency-resolver.js.map +1 -1
  32. package/dist/utilities/library-loader.d.ts.map +1 -1
  33. package/dist/utilities/library-loader.js +23 -2
  34. package/dist/utilities/library-loader.js.map +1 -1
  35. package/package.json +5 -5
  36. package/src/compiler/component-compiler.ts +73 -25
  37. package/src/index.ts +2 -1
  38. package/src/registry/component-registry-service.ts +21 -0
  39. package/src/registry/component-registry.ts +28 -0
  40. package/src/registry/component-resolver.ts +82 -9
  41. package/src/runtime/component-hierarchy.ts +97 -31
  42. package/src/types/index.ts +1 -1
  43. package/src/utilities/library-dependency-resolver.ts +21 -11
  44. package/src/utilities/library-loader.ts +24 -2
@@ -172,68 +172,115 @@ export class ComponentCompiler {
172
172
  * @param dependencies - Optional child component dependencies
173
173
  * @returns Wrapped component code
174
174
  */
175
+ // Core libraries that are passed as parameters to createComponent and should not be destructured
176
+ private readonly CORE_LIBRARIES = new Set(['React', 'ReactDOM']);
177
+
175
178
  private wrapComponentCode(componentCode: string, componentName: string, libraries?: any[], dependencies?: Array<{ name: string }>): string {
176
179
  // Generate library declarations if libraries are provided
180
+ // Skip core libraries as they're passed as parameters to createComponent
177
181
  const libraryDeclarations = libraries && libraries.length > 0
178
182
  ? libraries
179
- .filter(lib => lib.globalVariable) // Only include libraries with globalVariable
183
+ .filter(lib => lib.globalVariable && !this.CORE_LIBRARIES.has(lib.globalVariable)) // Skip core libraries
180
184
  .map(lib => `const ${lib.globalVariable} = libraries['${lib.globalVariable}'];`)
181
185
  .join('\n ')
182
186
  : '';
183
-
187
+ const libraryLogChecks = libraries && libraries.length > 0
188
+ ? libraries
189
+ .filter(lib => lib.globalVariable && !this.CORE_LIBRARIES.has(lib.globalVariable)) // Skip core libraries
190
+ .map(lib => `\nif (!${lib.globalVariable}) { console.error('[React-Runtime-JS] Library "${lib.globalVariable}" is not defined'); } else { console.log('[React-Runtime-JS] Library "${lib.globalVariable}" is defined'); }`)
191
+ .join('\n ')
192
+ : '';
193
+
184
194
  // Generate component declarations if dependencies are provided
195
+ // Filter out the component being compiled to avoid naming conflicts
185
196
  const componentDeclarations = dependencies && dependencies.length > 0
186
197
  ? dependencies
187
- .map(dep => `const ${dep.name} = components['${dep.name}'];`)
198
+ .filter(dep => dep.name !== componentName) // Don't destructure the component being compiled itself
199
+ .map(dep => `const ${dep.name} = componentsOuter['${dep.name}'];`)
200
+ .join('\n ')
201
+ : '';
202
+ const componentLogChecks = dependencies && dependencies.length > 0
203
+ ? dependencies
204
+ .filter(dep => dep.name !== componentName) // Don't destructure the component being compiled itself
205
+ .map(dep => `if (!${dep.name}) { console.error('[React-Runtime-JS] Dependency "${dep.name}" is not defined'); } else { console.log('[React-Runtime-JS] Dependency "${dep.name}" is defined'); }`)
188
206
  .join('\n ')
189
207
  : '';
190
208
 
191
- return `
209
+ const wrappedCode = `
192
210
  function createComponent(
193
211
  React, ReactDOM,
194
212
  useState, useEffect, useCallback, useMemo, useRef, useContext, useReducer, useLayoutEffect,
195
213
  libraries, styles, console, components
196
214
  ) {
197
- // Parse the component code once to get the component definition
198
- ${libraryDeclarations ? libraryDeclarations + '\n ' : ''}${componentDeclarations ? componentDeclarations + '\n ' : ''}${componentCode}
215
+ // Code for ${componentName}
216
+ ${componentCode}
199
217
 
200
218
  // Ensure the component exists
201
219
  if (typeof ${componentName} === 'undefined') {
202
220
  throw new Error('Component "${componentName}" is not defined in the provided code');
203
221
  }
222
+ else {
223
+ console.log('[React-Runtime-JS] Component "${componentName}" is defined');
224
+ }
204
225
 
205
226
  // Store the component in a variable so we don't lose it
206
227
  const UserComponent = ${componentName};
228
+
229
+ // Check if the component is already a ComponentObject (has a .component property)
230
+ // If so, extract the actual React component
231
+ const ActualComponent = (typeof UserComponent === 'object' && UserComponent !== null && 'component' in UserComponent)
232
+ ? UserComponent.component
233
+ : UserComponent;
207
234
 
208
235
  // Debug logging to understand what we're getting
209
- console.log('Component ${componentName} type:', typeof UserComponent);
236
+ console.log('[React-Runtime-JS]Component ${componentName} type:', typeof UserComponent);
210
237
  if (typeof UserComponent === 'object' && UserComponent !== null) {
211
- console.log('Component ${componentName} keys:', Object.keys(UserComponent));
212
- console.log('Component ${componentName} has .component:', 'component' in UserComponent);
238
+ console.log('[React-Runtime-JS]Component ${componentName} keys:', Object.keys(UserComponent));
239
+ console.log('[React-Runtime-JS]Component ${componentName} has .component:', 'component' in UserComponent);
213
240
  if ('component' in UserComponent) {
214
- console.log('Component ${componentName}.component type:', typeof UserComponent.component);
241
+ console.log('[React-Runtime-JS]Component ${componentName}.component type:', typeof UserComponent.component);
215
242
  }
216
243
  }
217
244
 
218
- // Check if the component is already a ComponentObject (has a .component property)
219
- // If so, extract the actual React component
220
- const ActualComponent = (typeof UserComponent === 'object' && UserComponent !== null && 'component' in UserComponent)
221
- ? UserComponent.component
222
- : UserComponent;
223
-
224
245
  // Validate that we have a function (React component)
225
246
  if (typeof ActualComponent !== 'function') {
226
- console.error('Invalid component type for ${componentName}:', typeof ActualComponent);
227
- console.error('ActualComponent value:', ActualComponent);
228
- console.error('Original UserComponent value:', UserComponent);
229
- throw new Error('Component "${componentName}" must be a function (React component) or an object with a .component property that is a function. Got: ' + typeof ActualComponent);
247
+ console.error('[React-Runtime-JS] Invalid component type for ${componentName}:', typeof ActualComponent);
248
+ console.error('[React-Runtime-JS] ActualComponent value:', ActualComponent);
249
+ console.error('[React-Runtime-JS] Original UserComponent value:', UserComponent);
250
+ throw new Error('[React-Runtime-JS] Component "${componentName}" must be a function (React component) or an object with a .component property that is a function. Got: ' + typeof ActualComponent);
230
251
  }
252
+
253
+ let componentsOuter = null, utilitiesOuter = null;
254
+ const DestructureWrapperUserComponent = (props) => {
255
+ if (!componentsOuter) {
256
+ componentsOuter = props?.components || components;
257
+ }
258
+ if (!utilitiesOuter) {
259
+ utilitiesOuter = props?.utilities;
260
+ }
261
+ console.log('Props for ${componentName}:', props);
262
+ console.log('components for ${componentName}:', componentsOuter);
263
+ console.log('styles for ${componentName}:', styles);
264
+ console.log('utilities for ${componentName}:', utilitiesOuter);
265
+ console.log('libraries for ${componentName}:', libraries);
266
+ ${libraryDeclarations ? '// Destructure Libraries\n' + libraryDeclarations + '\n ' : ''}
267
+ ${componentDeclarations ? '// Destructure Dependencies\n' + componentDeclarations + '\n ' : ''}
268
+ ${libraryLogChecks}
269
+ ${componentLogChecks}
270
+
271
+ const newProps = {
272
+ ...props,
273
+ components: componentsOuter,
274
+ utilities: utilitiesOuter
275
+ }
276
+ return ActualComponent(newProps);
277
+ };
231
278
 
232
279
  // Create a fresh method registry for each factory call
233
280
  const methodRegistry = new Map();
234
281
 
235
282
  // Create a wrapper component that provides RegisterMethod in callbacks
236
- const ComponentWithMethodRegistry = React.forwardRef((props, ref) => {
283
+ const ComponentWithMethodRegistry = (props) => {
237
284
  // Register methods on mount
238
285
  React.useEffect(() => {
239
286
  // Clear previous methods
@@ -261,11 +308,11 @@ export class ComponentCompiler {
261
308
  }, [props.callbacks]);
262
309
 
263
310
  // Render the original component with enhanced callbacks
264
- return React.createElement(ActualComponent, {
311
+ return React.createElement(DestructureWrapperUserComponent, {
265
312
  ...props,
266
313
  callbacks: enhancedCallbacks
267
314
  });
268
- });
315
+ };
269
316
 
270
317
  ComponentWithMethodRegistry.displayName = '${componentName}WithMethods';
271
318
 
@@ -273,7 +320,6 @@ export class ComponentCompiler {
273
320
  return {
274
321
  component: ComponentWithMethodRegistry,
275
322
 
276
- // Legacy methods for backward compatibility
277
323
  print: function() {
278
324
  const printMethod = methodRegistry.get('print');
279
325
  if (printMethod) {
@@ -326,7 +372,7 @@ export class ComponentCompiler {
326
372
  if (method) {
327
373
  return method(...args);
328
374
  }
329
- console.warn(\`Method '\${methodName}' is not registered on component ${componentName}\`);
375
+ console.warn(\`[React-Runtime-JS] Method '\${methodName}' is not registered on component ${componentName}\`);
330
376
  return undefined;
331
377
  },
332
378
 
@@ -337,6 +383,8 @@ export class ComponentCompiler {
337
383
  };
338
384
  }
339
385
  `;
386
+
387
+ return wrappedCode;
340
388
  }
341
389
 
342
390
  /**
package/src/index.ts CHANGED
@@ -29,7 +29,8 @@ export { ComponentRegistry } from './registry';
29
29
  export {
30
30
  ComponentResolver,
31
31
  ComponentSpec,
32
- ResolvedComponents
32
+ ResolvedComponents,
33
+ ComponentRegistryService
33
34
  } from './registry';
34
35
 
35
36
  // Export runtime APIs
@@ -561,6 +561,27 @@ export class ComponentRegistryService {
561
561
  this.compiledComponentCache.clear();
562
562
  this.componentReferences.clear();
563
563
  }
564
+
565
+ /**
566
+ * Force clear all compiled components
567
+ * Used for Component Studio to ensure fresh loads
568
+ */
569
+ forceClearAll(): void {
570
+ this.compiledComponentCache.clear();
571
+ this.componentReferences.clear();
572
+ console.log('🧹 Component cache force cleared');
573
+ }
574
+
575
+ /**
576
+ * Reset the singleton instance
577
+ * Forces new instance creation on next access
578
+ */
579
+ static reset(): void {
580
+ if (ComponentRegistryService.instance) {
581
+ ComponentRegistryService.instance.forceClearAll();
582
+ ComponentRegistryService.instance = null;
583
+ }
584
+ }
564
585
 
565
586
  /**
566
587
  * Generate a cache key for a component
@@ -243,6 +243,34 @@ export class ComponentRegistry {
243
243
  this.registry.clear();
244
244
  }
245
245
 
246
+ /**
247
+ * Clear all components in a specific namespace
248
+ * @param namespace - Namespace to clear (default: 'Global')
249
+ * @returns Number of components removed
250
+ */
251
+ clearNamespace(namespace: string = 'Global'): number {
252
+ const toRemove: string[] = [];
253
+ for (const [key, entry] of this.registry) {
254
+ if (entry.metadata.namespace === namespace) {
255
+ toRemove.push(key);
256
+ }
257
+ }
258
+ for (const key of toRemove) {
259
+ this.registry.delete(key);
260
+ }
261
+ return toRemove.length;
262
+ }
263
+
264
+ /**
265
+ * Force clear all components and reset registry
266
+ * Used for development/testing scenarios
267
+ */
268
+ forceClear(): void {
269
+ this.stopCleanupTimer();
270
+ this.registry.clear();
271
+ console.log('🧹 Registry force cleared - all components removed');
272
+ }
273
+
246
274
  /**
247
275
  * Gets the current size of the registry
248
276
  * @returns Number of registered components
@@ -68,16 +68,27 @@ export class ComponentResolver {
68
68
  namespace: string = 'Global',
69
69
  contextUser?: UserInfo
70
70
  ): Promise<ResolvedComponents> {
71
+ console.log(`🚀 [ComponentResolver] Starting component resolution for: ${spec.name}`);
72
+ console.log(`📋 [ComponentResolver] Dependencies to resolve:`, (spec.dependencies || []).map(d => ({
73
+ name: d.name,
74
+ location: d.location,
75
+ namespace: d.namespace
76
+ })));
77
+
71
78
  const resolved: ResolvedComponents = {};
72
79
 
73
80
  // Initialize component engine if we have registry service
74
81
  if (this.registryService) {
82
+ console.log(`🔄 [ComponentResolver] Initializing component engine...`);
75
83
  await this.componentEngine.Config(false, contextUser);
84
+ console.log(`✅ [ComponentResolver] Component engine initialized with ${this.componentEngine.Components?.length || 0} components`);
76
85
  }
77
86
 
78
87
  // Resolve the component hierarchy
79
88
  await this.resolveComponentHierarchy(spec, resolved, namespace, new Set(), contextUser);
80
89
 
90
+ console.log(`📊 [ComponentResolver] Resolved components before unwrapping:`, Object.keys(resolved));
91
+
81
92
  // Unwrap component wrappers before returning
82
93
  // Components from the registry come as objects with component/print/refresh properties
83
94
  // We need to extract just the component function for use in child components
@@ -87,17 +98,29 @@ export class ComponentResolver {
87
98
  if (typeof value.component === 'function') {
88
99
  // This is a wrapped component - extract the actual React component function
89
100
  unwrapped[name] = value.component;
101
+ console.log(`✅ [ComponentResolver] Unwrapped component: ${name} (was object with .component)`);
90
102
  } else {
91
103
  // ComponentObject has a component property but it's not a function
92
- console.error(`Component ${name} has invalid component property:`, typeof value.component, value);
104
+ console.error(`❌ [ComponentResolver] Component ${name} has invalid component property:`, typeof value.component, value);
93
105
  unwrapped[name] = value; // Pass through the problematic value so we can see the error
94
106
  }
95
- } else {
96
- // This is already a plain component function or something else
107
+ } else if (typeof value === 'function') {
108
+ // Already a function - use as is
97
109
  unwrapped[name] = value;
110
+ console.log(`✅ [ComponentResolver] Component already a function: ${name}`);
111
+ } else {
112
+ // Something else - could be undefined or an error
113
+ console.warn(`⚠️ [ComponentResolver] Component ${name} is not a function or wrapped component:`, typeof value, value);
114
+ unwrapped[name] = value; // Pass through for debugging
98
115
  }
99
116
  }
100
117
 
118
+ console.log(`🎯 [ComponentResolver] Final resolved components:`, Object.keys(unwrapped).map(name => ({
119
+ name,
120
+ type: typeof unwrapped[name],
121
+ isUndefined: unwrapped[name] === undefined
122
+ })));
123
+
101
124
  return unwrapped;
102
125
  }
103
126
 
@@ -119,6 +142,12 @@ export class ComponentResolver {
119
142
  // Create a unique identifier for this component
120
143
  const componentId = `${spec.namespace || namespace}/${spec.name}@${spec.version || 'latest'}`;
121
144
 
145
+ // Check if already resolved (not just visited)
146
+ if (resolved[spec.name]) {
147
+ console.log(`⏭️ [ComponentResolver] Component already resolved: ${spec.name}`);
148
+ return;
149
+ }
150
+
122
151
  // Prevent circular dependencies
123
152
  if (visited.has(componentId)) {
124
153
  if (this.debug) {
@@ -128,17 +157,55 @@ export class ComponentResolver {
128
157
  }
129
158
  visited.add(componentId);
130
159
 
160
+ // *** CRITICAL: Process child components FIRST (depth-first, post-order) ***
161
+ console.log(`🔄 [ComponentResolver] Resolving dependencies for ${spec.name} BEFORE resolving itself`);
162
+ const children = spec.dependencies || [];
163
+ for (const child of children) {
164
+ console.log(` ↳ [ComponentResolver] Resolving dependency: ${child.name} for parent ${spec.name}`);
165
+ await this.resolveComponentHierarchy(child, resolved, namespace, visited, contextUser);
166
+ }
167
+ if (children.length > 0) {
168
+ console.log(`✅ [ComponentResolver] All ${children.length} dependencies resolved for ${spec.name}, now resolving itself`);
169
+ }
170
+
171
+ // NOW resolve the current component (it can access its dependencies)
131
172
  // Handle based on location
132
173
  if (spec.location === 'registry' && this.registryService) {
133
174
  // Registry component - need to load from database or external source
175
+ console.log(`🔍 [ComponentResolver] Looking for registry component: ${spec.name} in namespace: ${spec.namespace || namespace}`);
176
+
134
177
  try {
135
178
  // First, try to find the component in the metadata engine
179
+ const allComponents = this.componentEngine.Components || [];
180
+ console.log(`📊 [ComponentResolver] Total components in engine: ${allComponents.length}`);
181
+
182
+ // Log all matching names to see duplicates
183
+ const matchingNames = allComponents.filter((c: any) => c.Name === spec.name);
184
+ if (matchingNames.length > 0) {
185
+ console.log(`🔎 [ComponentResolver] Found ${matchingNames.length} components with name "${spec.name}":`,
186
+ matchingNames.map((c: any) => ({
187
+ ID: c.ID,
188
+ Name: c.Name,
189
+ Namespace: c.Namespace,
190
+ Version: c.Version,
191
+ Status: c.Status
192
+ }))
193
+ );
194
+ }
195
+
136
196
  const component = this.componentEngine.Components?.find(
137
197
  (c: any) => c.Name === spec.name &&
138
198
  c.Namespace === (spec.namespace || namespace)
139
199
  );
140
200
 
141
201
  if (component) {
202
+ console.log(`✅ [ComponentResolver] Found component in DB:`, {
203
+ ID: component.ID,
204
+ Name: component.Name,
205
+ Namespace: component.Namespace,
206
+ Version: component.Version
207
+ });
208
+
142
209
  // Get compiled component from registry service
143
210
  const compiledComponent = await this.registryService.getCompiledComponent(
144
211
  component.ID,
@@ -146,10 +213,13 @@ export class ComponentResolver {
146
213
  contextUser
147
214
  );
148
215
  resolved[spec.name] = compiledComponent;
216
+ console.log(`📦 [ComponentResolver] Successfully compiled and resolved: ${spec.name}, type: ${typeof compiledComponent}`);
217
+
149
218
  if (this.debug) {
150
219
  console.log(`📦 Resolved registry component: ${spec.name} from ${componentId}`);
151
220
  }
152
221
  } else {
222
+ console.error(`❌❌❌❌❌ [ComponentResolver] Registry component NOT found in database: ${spec.name} with namespace: ${spec.namespace || namespace} ❌❌❌❌`);
153
223
  if (this.debug) {
154
224
  console.warn(`Registry component not found in database: ${spec.name}`);
155
225
  }
@@ -163,34 +233,37 @@ export class ComponentResolver {
163
233
  // Embedded component - get from local registry
164
234
  // Use the component's specified namespace if it has one, otherwise use parent's namespace
165
235
  const componentNamespace = spec.namespace || namespace;
236
+ console.log(`🔍 [ComponentResolver] Looking for embedded component: ${spec.name} in namespace: ${componentNamespace}`);
237
+
166
238
  const component = this.registry.get(spec.name, componentNamespace);
167
239
  if (component) {
168
240
  resolved[spec.name] = component;
241
+ console.log(`✅ [ComponentResolver] Found embedded component: ${spec.name}, type: ${typeof component}`);
169
242
  if (this.debug) {
170
243
  console.log(`📄 Resolved embedded component: ${spec.name} from namespace ${componentNamespace}, type:`, typeof component);
171
244
  }
172
245
  } else {
173
246
  // If not found with specified namespace, try the parent namespace as fallback
247
+ console.log(`⚠️ [ComponentResolver] Not found in namespace ${componentNamespace}, trying fallback namespace: ${namespace}`);
174
248
  const fallbackComponent = this.registry.get(spec.name, namespace);
175
249
  if (fallbackComponent) {
176
250
  resolved[spec.name] = fallbackComponent;
251
+ console.log(`✅ [ComponentResolver] Found embedded component in fallback namespace: ${spec.name}, type: ${typeof fallbackComponent}`);
177
252
  if (this.debug) {
178
253
  console.log(`📄 Resolved embedded component: ${spec.name} from fallback namespace ${namespace}, type:`, typeof fallbackComponent);
179
254
  }
180
255
  } else {
181
256
  // Component not found - this might cause issues later
257
+ console.error(`❌ [ComponentResolver] Could not resolve embedded component: ${spec.name} in namespace ${componentNamespace} or ${namespace}`);
182
258
  console.warn(`⚠️ Could not resolve embedded component: ${spec.name} in namespace ${componentNamespace} or ${namespace}`);
183
259
  // Store undefined explicitly so we know it failed to resolve
184
260
  resolved[spec.name] = undefined;
185
261
  }
186
262
  }
187
263
  }
188
-
189
- // Process child components recursively
190
- const children = spec.dependencies || [];
191
- for (const child of children) {
192
- await this.resolveComponentHierarchy(child, resolved, namespace, visited, contextUser);
193
- }
264
+
265
+ // Child components have already been processed at the beginning of this method
266
+ // No need to process them again - we're using depth-first, post-order traversal
194
267
  }
195
268
 
196
269
  /**
@@ -7,7 +7,8 @@
7
7
  import {
8
8
  CompilationResult,
9
9
  CompileOptions,
10
- RuntimeContext
10
+ RuntimeContext,
11
+ CompiledComponent
11
12
  } from '../types';
12
13
  import { ComponentCompiler } from '../compiler';
13
14
  import { ComponentRegistry } from '../registry';
@@ -94,31 +95,97 @@ export class ComponentHierarchyRegistrar {
94
95
  const errors: ComponentRegistrationError[] = [];
95
96
  const warnings: string[] = [];
96
97
 
97
- // Register the root component
98
- const rootResult = await this.registerSingleComponent(
99
- rootSpec,
100
- { styles, namespace, version, allowOverride, allLibraries: options.allLibraries }
101
- );
102
-
103
- if (rootResult.success) {
104
- registeredComponents.push(rootSpec.name);
105
- } else {
106
- errors.push(rootResult.error!);
107
- if (!continueOnError) {
108
- return { success: false, registeredComponents, errors, warnings };
98
+ // PHASE 1: Compile all components first (but defer factory execution)
99
+ const compiledMap = new Map<string, CompiledComponent>();
100
+ const specMap = new Map<string, ComponentSpec>();
101
+
102
+ // Helper to compile a component without calling its factory
103
+ const compileOnly = async (spec: ComponentSpec): Promise<{ success: boolean; error?: ComponentRegistrationError }> => {
104
+ if (!spec.code) return { success: true };
105
+
106
+ try {
107
+ const compileOptions: CompileOptions = {
108
+ componentName: spec.name,
109
+ componentCode: spec.code,
110
+ styles,
111
+ libraries: spec.libraries,
112
+ dependencies: spec.dependencies,
113
+ allLibraries: options.allLibraries
114
+ };
115
+
116
+ const result = await this.compiler.compile(compileOptions);
117
+ if (result.success && result.component) {
118
+ compiledMap.set(spec.name, result.component);
119
+ specMap.set(spec.name, spec);
120
+ return { success: true };
121
+ } else {
122
+ return {
123
+ success: false,
124
+ error: {
125
+ componentName: spec.name,
126
+ error: result.error?.message || 'Unknown compilation error',
127
+ phase: 'compilation'
128
+ }
129
+ };
130
+ }
131
+ } catch (error) {
132
+ return {
133
+ success: false,
134
+ error: {
135
+ componentName: spec.name,
136
+ error: error instanceof Error ? error.message : String(error),
137
+ phase: 'compilation'
138
+ }
139
+ };
140
+ }
141
+ };
142
+
143
+ // Compile all components in hierarchy
144
+ const compileQueue = [rootSpec];
145
+ const visited = new Set<string>();
146
+
147
+ while (compileQueue.length > 0) {
148
+ const spec = compileQueue.shift()!;
149
+ if (visited.has(spec.name)) continue;
150
+ visited.add(spec.name);
151
+
152
+ const result = await compileOnly(spec);
153
+ if (!result.success) {
154
+ errors.push(result.error!);
155
+ if (!continueOnError) {
156
+ return { success: false, registeredComponents, errors, warnings };
157
+ }
158
+ }
159
+
160
+ if (spec.dependencies) {
161
+ compileQueue.push(...spec.dependencies);
109
162
  }
110
163
  }
111
-
112
- // Register child components recursively
113
- const childComponents = rootSpec.dependencies || [];
114
- if (childComponents.length > 0) {
115
- const childResult = await this.registerChildComponents(
116
- childComponents,
117
- { styles, namespace, version, continueOnError, allowOverride, allLibraries: options.allLibraries },
118
- registeredComponents,
119
- errors,
120
- warnings
164
+
165
+ // PHASE 2: Execute all factories with components available
166
+ for (const [name, compiled] of compiledMap) {
167
+ const spec = specMap.get(name)!;
168
+
169
+ // Build components object from all registered components
170
+ const components: Record<string, any> = {};
171
+ for (const [depName, depCompiled] of compiledMap) {
172
+ // Call factory to get ComponentObject, then extract React component
173
+ const depObject = depCompiled.factory(this.runtimeContext, styles);
174
+ components[depName] = depObject.component;
175
+ }
176
+
177
+ // Now call factory with components available
178
+ const componentObject = compiled.factory(this.runtimeContext, styles, components);
179
+
180
+ // Register in registry
181
+ this.registry.register(
182
+ spec.name,
183
+ componentObject,
184
+ spec.namespace || namespace,
185
+ version
121
186
  );
187
+
188
+ registeredComponents.push(spec.name);
122
189
  }
123
190
 
124
191
  return {
@@ -198,15 +265,14 @@ export class ComponentHierarchyRegistrar {
198
265
  }
199
266
 
200
267
  // Call the factory to create the ComponentObject
201
- const componentObject = compilationResult.component!.factory(this.runtimeContext, styles);
202
-
203
- // Debug logging to verify ComponentObject structure
204
- console.log(`📦 Registering ComponentObject for ${spec.name}:`, {
205
- hasComponent: 'component' in componentObject,
206
- componentType: typeof componentObject.component,
207
- hasPrint: 'print' in componentObject,
208
- hasRefresh: 'refresh' in componentObject
268
+ // IMPORTANT: We don't pass components here because child components may not be registered yet
269
+ // Components are resolved later when the component is actually rendered
270
+ console.log(`🏭 Calling factory for ${spec.name} with runtime context:`, {
271
+ hasReact: !!this.runtimeContext.React,
272
+ hasReactDOM: !!this.runtimeContext.ReactDOM,
273
+ libraryKeys: Object.keys(this.runtimeContext.libraries || {})
209
274
  });
275
+ const componentObject = compilationResult.component!.factory(this.runtimeContext, styles);
210
276
 
211
277
  // Register the full ComponentObject (not just the React component)
212
278
  this.registry.register(
@@ -13,7 +13,7 @@ import { ComponentLibraryDependency, ComponentStyles, ComponentObject } from '@m
13
13
  */
14
14
  export interface CompiledComponent {
15
15
  /** Factory function that creates a ComponentObject when called with context */
16
- factory: (context: RuntimeContext, styles?: ComponentStyles) => ComponentObject;
16
+ factory: (context: RuntimeContext, styles?: ComponentStyles, components?: Record<string, any>) => ComponentObject;
17
17
  /** Unique identifier for the component */
18
18
  id: string;
19
19
  /** Original component name */