UNPKG

@memberjunction/react-runtime

Version:

Platform-agnostic React component runtime for MemberJunction. Provides core compilation, registry, and execution capabilities for React components in any JavaScript environment.

369 lines 16.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.countComponentsInHierarchy = exports.flattenComponentHierarchy = exports.validateComponentSpec = exports.registerComponentHierarchy = exports.ComponentHierarchyRegistrar = void 0; const core_1 = require("@memberjunction/core"); class ComponentHierarchyRegistrar { constructor(compiler, registry, runtimeContext) { this.compiler = compiler; this.registry = registry; this.runtimeContext = runtimeContext; } async fetchExternalComponent(spec, contextUser) { try { const provider = core_1.Metadata?.Provider; if (!provider || !provider.ExecuteGQL) { console.warn('⚠️ [ComponentHierarchyRegistrar] No GraphQL provider available for external registry fetch'); return null; } const { GraphQLComponentRegistryClient } = await Promise.resolve().then(() => __importStar(require('@memberjunction/graphql-dataprovider'))); const graphQLClient = new GraphQLComponentRegistryClient(provider); const fullSpec = await graphQLClient.GetRegistryComponent({ registryName: spec.registry, namespace: spec.namespace || 'Global', name: spec.name, version: spec.version || 'latest' }); if (fullSpec && fullSpec.code) { if (!(0, core_1.GetProductionStatus)()) { (0, core_1.LogStatus)(`✅ [ComponentHierarchyRegistrar] Fetched external component ${spec.name} with code (${fullSpec.code.length} chars)`); } return fullSpec; } else { console.warn(`⚠️ [ComponentHierarchyRegistrar] Failed to fetch external component ${spec.name} or no code`); return null; } } catch (error) { console.error(`❌ [ComponentHierarchyRegistrar] Error fetching external component ${spec.name}:`, error); return null; } } async registerHierarchy(rootSpec, options) { let resolvedRootSpec = rootSpec; if (rootSpec.location === 'registry' && rootSpec.registry && !rootSpec.code) { if (!(0, core_1.GetProductionStatus)()) { (0, core_1.LogStatus)(`🌐 [ComponentHierarchyRegistrar] Fetching external registry component: ${rootSpec.registry}/${rootSpec.name}`); } resolvedRootSpec = await this.fetchExternalComponent(rootSpec, options.contextUser) || rootSpec; } const { styles, namespace = 'Global', version = 'v1', continueOnError = true, allowOverride = true } = options; const registeredComponents = []; const errors = []; const warnings = []; if (!(0, core_1.GetProductionStatus)()) { (0, core_1.LogStatus)('🌳 ComponentHierarchyRegistrar.registerHierarchy:', undefined, { rootComponent: resolvedRootSpec.name, hasLibraries: !!(resolvedRootSpec.libraries && resolvedRootSpec.libraries.length > 0), libraryCount: resolvedRootSpec.libraries?.length || 0 }); } const compiledMap = new Map(); const specMap = new Map(); const allLoadedLibraries = new Map(); const compileOnly = async (spec) => { if (!spec.code) return { success: true }; try { const validLibraries = spec.libraries?.filter(lib => { if (!lib || typeof lib !== 'object') return false; if (!lib.name || lib.name === 'unknown' || lib.name === 'null' || lib.name === 'undefined') return false; if (!lib.globalVariable || lib.globalVariable === 'undefined' || lib.globalVariable === 'null') return false; return true; }); const compileOptions = { componentName: spec.name, componentCode: spec.code, styles, libraries: validLibraries, dependencies: spec.dependencies, allLibraries: options.allLibraries }; const result = await this.compiler.compile(compileOptions); if (result.success && result.component) { compiledMap.set(spec.name, result.component); specMap.set(spec.name, spec); if (result.loadedLibraries) { result.loadedLibraries.forEach((value, key) => { if (!allLoadedLibraries.has(key)) { allLoadedLibraries.set(key, value); if (!(0, core_1.GetProductionStatus)()) { (0, core_1.LogStatus)(`📚 [registerHierarchy] Added library ${key} to accumulated libraries`); } } }); } return { success: true }; } else { return { success: false, error: { componentName: spec.name, error: result.error?.message || 'Unknown compilation error', phase: 'compilation' } }; } } catch (error) { return { success: false, error: { componentName: spec.name, error: error instanceof Error ? error.message : String(error), phase: 'compilation' } }; } }; const compileQueue = [resolvedRootSpec]; const visited = new Set(); while (compileQueue.length > 0) { let spec = compileQueue.shift(); if (visited.has(spec.name)) continue; visited.add(spec.name); if (spec.location === 'registry' && spec.registry && !spec.code) { const fetched = await this.fetchExternalComponent(spec, options.contextUser); if (fetched) { spec = fetched; } else { console.warn(`⚠️ [ComponentHierarchyRegistrar] Could not fetch external component ${spec.name}, skipping`); continue; } } const result = await compileOnly(spec); if (!result.success) { errors.push(result.error); if (!continueOnError) { return { success: false, registeredComponents, errors, warnings, resolvedSpec: resolvedRootSpec }; } } if (spec.dependencies) { compileQueue.push(...spec.dependencies); } } if (allLoadedLibraries.size > 0) { if (!this.runtimeContext.libraries) { this.runtimeContext.libraries = {}; } allLoadedLibraries.forEach((value, key) => { this.runtimeContext.libraries[key] = value; if (!(0, core_1.GetProductionStatus)()) { (0, core_1.LogStatus)(`✅ [registerHierarchy] Added ${key} to runtime context libraries`); } }); } for (const [name, compiled] of compiledMap) { const spec = specMap.get(name); const components = {}; for (const [depName, depCompiled] of compiledMap) { const depObject = depCompiled.factory(this.runtimeContext, styles); components[depName] = depObject.component; } const componentObject = compiled.factory(this.runtimeContext, styles, components); this.registry.register(spec.name, componentObject, spec.namespace || namespace, version); registeredComponents.push(spec.name); } return { success: errors.length === 0, registeredComponents, errors, warnings, resolvedSpec: resolvedRootSpec }; } async registerSingleComponent(spec, options) { const { styles, namespace = 'Global', version = 'v1', allowOverride = true } = options; try { if (!spec.code) { return { success: true, error: undefined }; } const existingComponent = this.registry.get(spec.name, namespace, version); if (existingComponent && !allowOverride) { return { success: false, error: { componentName: spec.name, error: `Component already registered in ${namespace}/${version}`, phase: 'registration' } }; } const validLibraries = spec.libraries?.filter(lib => { if (!lib || typeof lib !== 'object') return false; if (!lib.name || lib.name === 'unknown' || lib.name === 'null' || lib.name === 'undefined') return false; if (!lib.globalVariable || lib.globalVariable === 'undefined' || lib.globalVariable === 'null') return false; return true; }); if (!(0, core_1.GetProductionStatus)()) { (0, core_1.LogStatus)(`🔧 Compiling component ${spec.name} with libraries:`, undefined, { originalCount: spec.libraries?.length || 0, filteredCount: validLibraries?.length || 0, libraries: validLibraries?.map(l => l.name) || [] }); } const compileOptions = { componentName: spec.name, componentCode: spec.code, styles, libraries: validLibraries, dependencies: spec.dependencies, allLibraries: options.allLibraries }; const compilationResult = await this.compiler.compile(compileOptions); if (!compilationResult.success) { return { success: false, error: { componentName: spec.name, error: compilationResult.error?.message || 'Unknown compilation error', phase: 'compilation' } }; } if (compilationResult.loadedLibraries && compilationResult.loadedLibraries.size > 0) { if (!this.runtimeContext.libraries) { this.runtimeContext.libraries = {}; } compilationResult.loadedLibraries.forEach((value, key) => { this.runtimeContext.libraries[key] = value; if (!(0, core_1.GetProductionStatus)()) { (0, core_1.LogStatus)(`✅ [registerSingleComponent] Added ${key} to runtime context libraries`); } }); } if (!(0, core_1.GetProductionStatus)()) { (0, core_1.LogStatus)(`🏭 Calling factory for ${spec.name} with runtime context:`, undefined, { hasReact: !!this.runtimeContext.React, hasReactDOM: !!this.runtimeContext.ReactDOM, libraryCount: Object.keys(this.runtimeContext.libraries || {}).length }); } const componentObject = compilationResult.component.factory(this.runtimeContext, styles); this.registry.register(spec.name, componentObject, spec.namespace || namespace, version); return { success: true }; } catch (error) { return { success: false, error: { componentName: spec.name, error: error instanceof Error ? error.message : String(error), phase: 'registration' } }; } } async registerChildComponents(children, options, registeredComponents, errors, warnings) { for (const child of children) { const childResult = await this.registerSingleComponent(child, { styles: options.styles, namespace: options.namespace, version: options.version, allowOverride: options.allowOverride, allLibraries: options.allLibraries }); if (childResult.success) { if (child.code) { registeredComponents.push(child.name); } } else { errors.push(childResult.error); if (!options.continueOnError) { return; } } const nestedChildren = child.dependencies || []; if (nestedChildren.length > 0) { await this.registerChildComponents(nestedChildren, options, registeredComponents, errors, warnings); } } } } exports.ComponentHierarchyRegistrar = ComponentHierarchyRegistrar; async function registerComponentHierarchy(rootSpec, compiler, registry, runtimeContext, options) { const registrar = new ComponentHierarchyRegistrar(compiler, registry, runtimeContext); return registrar.registerHierarchy(rootSpec, options); } exports.registerComponentHierarchy = registerComponentHierarchy; function validateComponentSpec(spec) { const errors = []; if (!spec.name) { errors.push('Component specification must have a name'); } if (spec.code) { if (typeof spec.code !== 'string') { errors.push(`Component code for ${spec.name} must be a string`); } if (spec.code.trim().length === 0) { errors.push(`Component code for ${spec.name} cannot be empty`); } } const children = spec.dependencies || []; children.forEach((child, index) => { const childErrors = validateComponentSpec(child); childErrors.forEach(error => { errors.push(`Child ${index} (${child.name || 'unnamed'}): ${error}`); }); }); return errors; } exports.validateComponentSpec = validateComponentSpec; function flattenComponentHierarchy(rootSpec) { const components = [rootSpec]; const children = rootSpec.dependencies || []; children.forEach(child => { components.push(...flattenComponentHierarchy(child)); }); return components; } exports.flattenComponentHierarchy = flattenComponentHierarchy; function countComponentsInHierarchy(rootSpec, includeEmpty = false) { let count = 0; if (includeEmpty || rootSpec.code) { count = 1; } const children = rootSpec.dependencies || []; children.forEach(child => { count += countComponentsInHierarchy(child, includeEmpty); }); return count; } exports.countComponentsInHierarchy = countComponentsInHierarchy; //# sourceMappingURL=component-hierarchy.js.map