@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
JavaScript
;
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