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.

476 lines 21.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LibraryLoader = void 0; const standard_libraries_1 = require("./standard-libraries"); const core_libraries_1 = require("./core-libraries"); const resource_manager_1 = require("./resource-manager"); const library_dependency_resolver_1 = require("./library-dependency-resolver"); const LIBRARY_LOADER_COMPONENT_ID = 'mj-react-runtime-library-loader-singleton'; class LibraryLoader { static async loadAllLibraries(config, additionalLibraries, options) { if (config) { standard_libraries_1.StandardLibraryManager.setConfiguration(config); } if (additionalLibraries && additionalLibraries.length > 0) { const currentConfig = standard_libraries_1.StandardLibraryManager.getConfiguration(); const mergedConfig = { libraries: [...currentConfig.libraries, ...additionalLibraries], metadata: { ...currentConfig.metadata, lastUpdated: new Date().toISOString() } }; standard_libraries_1.StandardLibraryManager.setConfiguration(mergedConfig); } return this.loadLibrariesFromConfig(undefined, options?.debug); } static async loadLibrariesFromConfig(options, debug) { const coreLibraries = (0, core_libraries_1.getCoreRuntimeLibraries)(debug); const corePromises = coreLibraries.map(lib => this.loadScript(lib.cdnUrl, lib.globalVariable, debug)); const coreResults = await Promise.all(corePromises); const React = coreResults.find((_, i) => coreLibraries[i].globalVariable === 'React'); const ReactDOM = coreResults.find((_, i) => coreLibraries[i].globalVariable === 'ReactDOM'); const Babel = coreResults.find((_, i) => coreLibraries[i].globalVariable === 'Babel'); if (typeof window !== 'undefined') { if (React && !window.React) { window.React = React; console.log('✓ Exposed React as window.React for UMD compatibility'); } if (ReactDOM && !window.ReactDOM) { window.ReactDOM = ReactDOM; console.log('✓ Exposed ReactDOM as window.ReactDOM for UMD compatibility'); } if (!window.PropTypes) { window.PropTypes = {}; console.log('✓ Exposed empty PropTypes as window.PropTypes for UMD compatibility'); } } const config = standard_libraries_1.StandardLibraryManager.getConfiguration(); const enabledLibraries = standard_libraries_1.StandardLibraryManager.getEnabledLibraries(); let pluginLibraries = enabledLibraries.filter(lib => !(0, core_libraries_1.isCoreRuntimeLibrary)(lib.id)); if (options) { if (options.categories) { pluginLibraries = pluginLibraries.filter(lib => options.categories.includes(lib.category)); } if (options.excludeRuntimeOnly) { pluginLibraries = pluginLibraries.filter(lib => !lib.isRuntimeOnly); } } pluginLibraries.forEach(lib => { if (lib.cdnCssUrl) { this.loadCSS(lib.cdnCssUrl); } }); const pluginPromises = pluginLibraries.map(lib => this.loadScript(lib.cdnUrl, lib.globalVariable, debug)); const pluginResults = await Promise.all(pluginPromises); const libraries = {}; pluginLibraries.forEach((lib, index) => { libraries[lib.globalVariable] = pluginResults[index]; }); return { React: React || window.React, ReactDOM: ReactDOM || window.ReactDOM, Babel: Babel || window.Babel, libraries }; } static async loadLibraries(options) { const { loadCore = true, loadUI = true, loadCSS = true, customLibraries = [] } = options; const categoriesToLoad = ['runtime']; if (loadCore) { categoriesToLoad.push('utility', 'charting'); } if (loadUI) { categoriesToLoad.push('ui'); } const result = await this.loadLibrariesFromConfig({ categories: categoriesToLoad }); if (customLibraries.length > 0) { const customPromises = customLibraries.map(({ url, globalName }) => this.loadScript(url, globalName)); const customResults = await Promise.all(customPromises); customLibraries.forEach(({ globalName }, index) => { result.libraries[globalName] = customResults[index]; }); } return result; } static async loadScript(url, globalName, debug = false) { const existing = this.loadedResources.get(url); if (existing) { if (debug) { console.log(`✅ Library '${globalName}' already loaded (cached)`); } return existing.promise; } const promise = new Promise((resolve, reject) => { const existingGlobal = window[globalName]; if (existingGlobal) { if (debug) { console.log(`✅ Library '${globalName}' already available globally`); } resolve(existingGlobal); return; } const existingScript = document.querySelector(`script[src="${url}"]`); if (existingScript) { this.waitForScriptLoad(existingScript, globalName, resolve, reject); return; } const script = document.createElement('script'); script.src = url; script.async = true; script.crossOrigin = 'anonymous'; const cleanup = () => { script.removeEventListener('load', onLoad); script.removeEventListener('error', onError); }; const onLoad = async () => { cleanup(); if (LibraryLoader.enableProgressiveDelay) { try { const global = await LibraryLoader.waitForGlobalVariable(globalName, url, debug); resolve(global); } catch (error) { reject(error); } } else { const global = window[globalName]; if (global) { if (debug) { console.log(`✅ Library '${globalName}' loaded successfully from ${url}`); } resolve(global); } else { const timeoutId = resource_manager_1.resourceManager.setTimeout(LIBRARY_LOADER_COMPONENT_ID, () => { const delayedGlobal = window[globalName]; if (delayedGlobal) { if (debug) { console.log(`✅ Library '${globalName}' loaded successfully (delayed initialization)`); } resolve(delayedGlobal); } else { reject(new Error(`${globalName} not found after script load`)); } }, 100, { url, globalName }); } } }; const onError = () => { cleanup(); reject(new Error(`Failed to load script: ${url}`)); }; script.addEventListener('load', onLoad); script.addEventListener('error', onError); if (debug) { console.log(`📦 Loading library '${globalName}' from ${url}...`); } document.head.appendChild(script); resource_manager_1.resourceManager.registerDOMElement(LIBRARY_LOADER_COMPONENT_ID, script); }); this.loadedResources.set(url, { element: document.querySelector(`script[src="${url}"]`), promise }); return promise; } static isLibraryReady(globalVariable) { if (!globalVariable) { return false; } if (typeof globalVariable === 'function') { return true; } if (typeof globalVariable === 'object') { const keys = Object.keys(globalVariable); return keys.length > 0 || Object.getOwnPropertyNames(globalVariable).length > 1 || globalVariable.constructor !== Object; } return true; } static async waitForGlobalVariable(globalName, url, debug = false) { const delays = [0, 100, 200, 300, 400]; const maxAttempts = delays.length; let totalDelay = 0; for (let attempt = 0; attempt < maxAttempts; attempt++) { if (attempt > 0) { const delay = delays[attempt]; if (debug) { console.log(`⏳ Waiting ${delay}ms for ${globalName} to initialize (attempt ${attempt + 1}/${maxAttempts})...`); } await new Promise(resolve => { resource_manager_1.resourceManager.setTimeout(LIBRARY_LOADER_COMPONENT_ID, () => resolve(undefined), delay, { globalName, attempt }); }); totalDelay += delay; } const global = window[globalName]; if (global) { const isReady = this.isLibraryReady(global); if (isReady) { if (debug) { if (totalDelay > 0) { console.log(`✅ ${globalName} ready after ${totalDelay}ms delay`); } else { console.log(`✅ Library '${globalName}' loaded successfully from ${url}`); } } return global; } else if (debug && attempt < maxAttempts - 1) { console.log(`🔄 ${globalName} exists but not fully initialized, will retry...`); } } } const finalGlobal = window[globalName]; if (finalGlobal) { console.warn(`⚠️ ${globalName} loaded but may not be fully initialized after ${totalDelay}ms`); return finalGlobal; } throw new Error(`${globalName} not found after script load and ${totalDelay}ms delay`); } static loadCSS(url) { if (this.loadedResources.has(url)) { return; } const existingLink = document.querySelector(`link[href="${url}"]`); if (existingLink) { return; } const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = url; document.head.appendChild(link); resource_manager_1.resourceManager.registerDOMElement(LIBRARY_LOADER_COMPONENT_ID, link); this.loadedResources.set(url, { element: link, promise: Promise.resolve() }); } static waitForScriptLoad(script, globalName, resolve, reject) { const checkGlobal = () => { const global = window[globalName]; if (global) { resolve(global); } else { resource_manager_1.resourceManager.setTimeout(LIBRARY_LOADER_COMPONENT_ID, () => { const delayedGlobal = window[globalName]; if (delayedGlobal) { resolve(delayedGlobal); } else { reject(new Error(`${globalName} not found after script load`)); } }, 100, { context: 'waitForScriptLoad', globalName }); } }; if (script.complete || script.readyState === 'complete') { checkGlobal(); return; } const loadHandler = () => { checkGlobal(); }; resource_manager_1.resourceManager.addEventListener(LIBRARY_LOADER_COMPONENT_ID, script, 'load', loadHandler, { once: true }); } static getLoadedResources() { return this.loadedResources; } static clearCache() { this.loadedResources.forEach((resource, url) => { if (resource.element && resource.element.parentNode) { resource.element.parentNode.removeChild(resource.element); } }); this.loadedResources.clear(); this.loadedLibraryStates.clear(); resource_manager_1.resourceManager.cleanupComponent(LIBRARY_LOADER_COMPONENT_ID); } static async loadLibraryWithDependencies(libraryName, allLibraries, requestedBy = 'user', options) { const debug = options?.debug || false; if (debug) { console.log(`📚 Loading library '${libraryName}' with dependencies`); } const existingState = this.loadedLibraryStates.get(libraryName); if (existingState) { if (debug) { console.log(`✅ Library '${libraryName}' already loaded (version: ${existingState.version})`); } if (!existingState.requestedBy.includes(requestedBy)) { existingState.requestedBy.push(requestedBy); } return window[existingState.globalVariable]; } const loadOrderResult = this.dependencyResolver.getLoadOrder([libraryName], allLibraries, options); if (!loadOrderResult.success) { const errors = loadOrderResult.errors?.join(', ') || 'Unknown error'; throw new Error(`Failed to resolve dependencies for '${libraryName}': ${errors}`); } if (loadOrderResult.warnings && debug) { console.warn(`⚠️ Warnings for '${libraryName}':`, loadOrderResult.warnings); } const loadOrder = loadOrderResult.order || []; if (debug) { console.log(`📋 Load order for '${libraryName}':`, loadOrder.map(lib => `${lib.Name}@${lib.Version}`)); } for (const library of loadOrder) { if (this.loadedLibraryStates.has(library.Name)) { if (debug) { console.log(`⏭️ Skipping '${library.Name}' (already loaded)`); } continue; } if (library.Status) { if (library.Status === 'Disabled') { console.error(`🚫 ERROR: Library '${library.Name}' is DISABLED and should not be used`); } else if (library.Status === 'Deprecated') { console.warn(`⚠️ WARNING: Library '${library.Name}' is DEPRECATED. Consider using an alternative.`); } } if (debug) { console.log(`📥 Loading '${library.Name}@${library.Version}'`); } if (!library.CDNUrl || !library.GlobalVariable) { throw new Error(`Library '${library.Name}' missing CDN URL or global variable`); } if (library.CDNCssUrl) { const cssUrls = library.CDNCssUrl.split(',').map(url => url.trim()); for (const cssUrl of cssUrls) { if (cssUrl) { this.loadCSS(cssUrl); } } } const loadedGlobal = await this.loadScript(library.CDNUrl, library.GlobalVariable, debug); const dependencies = Array.from(this.dependencyResolver.getDirectDependencies(library).keys()); this.loadedLibraryStates.set(library.Name, { name: library.Name, version: library.Version || 'unknown', globalVariable: library.GlobalVariable, loadedAt: new Date(), requestedBy: library.Name === libraryName ? [requestedBy] : [], dependencies }); if (debug) { console.log(`✅ Loaded '${library.Name}@${library.Version}'`); } } const targetLibrary = loadOrder.find(lib => lib.Name === libraryName); if (!targetLibrary || !targetLibrary.GlobalVariable) { throw new Error(`Failed to load library '${libraryName}'`); } return window[targetLibrary.GlobalVariable]; } static async loadLibrariesWithDependencies(libraryNames, allLibraries, requestedBy = 'user', options) { const debug = options?.debug || false; const result = new Map(); if (debug) { console.log(`📚 Loading libraries with dependencies:`, libraryNames); console.log(` 📦 Total available libraries: ${allLibraries.length}`); } const loadOrderResult = this.dependencyResolver.getLoadOrder(libraryNames, allLibraries, options); if (!loadOrderResult.success) { const errors = loadOrderResult.errors?.join(', ') || 'Unknown error'; throw new Error(`Failed to resolve dependencies: ${errors}`); } if (debug) { console.log(` 📊 Dependency resolution result:`, { success: loadOrderResult.success, errors: loadOrderResult.errors || [], warnings: loadOrderResult.warnings || [] }); if (loadOrderResult.order) { console.log(` 🔄 Resolved dependencies for each library:`); loadOrderResult.order.forEach(lib => { const deps = this.dependencyResolver.parseDependencies(lib.Dependencies); if (deps.size > 0) { console.log(` • ${lib.Name}@${lib.Version} requires:`, Array.from(deps.entries())); } else { console.log(` • ${lib.Name}@${lib.Version} (no dependencies)`); } }); } } if (loadOrderResult.warnings && debug) { console.warn(` ⚠️ Warnings:`, loadOrderResult.warnings); } const loadOrder = loadOrderResult.order || []; if (debug) { console.log(` 📋 Final load order:`, loadOrder.map(lib => `${lib.Name}@${lib.Version}`)); } for (const library of loadOrder) { if (this.loadedLibraryStates.has(library.Name)) { if (debug) { console.log(`⏭️ Skipping '${library.Name}' (already loaded)`); } const state = this.loadedLibraryStates.get(library.Name); if (libraryNames.includes(library.Name)) { result.set(library.Name, window[state.globalVariable]); } continue; } if (library.Status) { if (library.Status === 'Disabled') { console.error(`🚫 ERROR: Library '${library.Name}' is DISABLED and should not be used`); } else if (library.Status === 'Deprecated') { console.warn(`⚠️ WARNING: Library '${library.Name}' is DEPRECATED. Consider using an alternative.`); } } if (debug) { console.log(`📥 Loading '${library.Name}@${library.Version}'`); } if (!library.CDNUrl || !library.GlobalVariable) { throw new Error(`Library '${library.Name}' missing CDN URL or global variable`); } if (library.CDNCssUrl) { const cssUrls = library.CDNCssUrl.split(',').map(url => url.trim()); for (const cssUrl of cssUrls) { if (cssUrl) { this.loadCSS(cssUrl); } } } const loadedGlobal = await this.loadScript(library.CDNUrl, library.GlobalVariable, debug); const dependencies = Array.from(this.dependencyResolver.getDirectDependencies(library).keys()); this.loadedLibraryStates.set(library.Name, { name: library.Name, version: library.Version || 'unknown', globalVariable: library.GlobalVariable, loadedAt: new Date(), requestedBy: libraryNames.includes(library.Name) ? [requestedBy] : [], dependencies }); if (libraryNames.includes(library.Name)) { result.set(library.Name, loadedGlobal); } if (debug) { console.log(`✅ Loaded '${library.Name}@${library.Version}'`); } } return result; } static getLoadedLibraryStates() { return new Map(this.loadedLibraryStates); } static isLibraryLoaded(libraryName) { return this.loadedLibraryStates.has(libraryName); } static getLoadedLibraryVersion(libraryName) { return this.loadedLibraryStates.get(libraryName)?.version; } } exports.LibraryLoader = LibraryLoader; LibraryLoader.loadedResources = new Map(); LibraryLoader.loadedLibraryStates = new Map(); LibraryLoader.dependencyResolver = new library_dependency_resolver_1.LibraryDependencyResolver({ debug: false }); LibraryLoader.enableProgressiveDelay = false; //# sourceMappingURL=library-loader.js.map