OneConversorTemplate/OnlyOneAccessTemplate/wwwroot/js/module-loader.js
2025-06-08 18:00:23 -03:00

269 lines
8.4 KiB
JavaScript

// Dynamic Module Loader System
window.ModuleLoader = (function () {
const loadedScripts = new Set();
const loadedStyles = new Set();
const moduleInstances = new Map();
// Cache de recursos carregados
const resourceCache = new Map();
function log(message, ...args) {
console.log(`🔧 ModuleLoader: ${message}`, ...args);
}
function error(message, ...args) {
console.error(`❌ ModuleLoader: ${message}`, ...args);
}
async function loadScript(url, moduleBaseUrl) {
const fullUrl = resolveUrl(url, moduleBaseUrl);
if (loadedScripts.has(fullUrl)) {
log(`Script já carregado: ${fullUrl}`);
return true;
}
try {
log(`Carregando script: ${fullUrl}`);
const response = await fetch(fullUrl);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const scriptContent = await response.text();
// Criar script element e executar
const script = document.createElement('script');
script.textContent = scriptContent;
script.setAttribute('data-module-script', fullUrl);
document.head.appendChild(script);
loadedScripts.add(fullUrl);
log(`✅ Script carregado com sucesso: ${fullUrl}`);
return true;
} catch (err) {
error(`Falha ao carregar script ${fullUrl}:`, err);
return false;
}
}
async function loadStyle(url, moduleBaseUrl) {
const fullUrl = resolveUrl(url, moduleBaseUrl);
if (loadedStyles.has(fullUrl)) {
log(`CSS já carregado: ${fullUrl}`);
return true;
}
try {
log(`Carregando CSS: ${fullUrl}`);
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = fullUrl;
link.setAttribute('data-module-style', fullUrl);
return new Promise((resolve, reject) => {
link.onload = () => {
loadedStyles.add(fullUrl);
log(`✅ CSS carregado com sucesso: ${fullUrl}`);
resolve(true);
};
link.onerror = () => {
error(`Falha ao carregar CSS: ${fullUrl}`);
reject(false);
};
document.head.appendChild(link);
});
} catch (err) {
error(`Erro ao carregar CSS ${fullUrl}:`, err);
return false;
}
}
function resolveUrl(url, baseUrl) {
if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('//')) {
return url;
}
if (url.startsWith('/')) {
const base = new URL(baseUrl);
return `${base.protocol}//${base.host}${url}`;
}
return new URL(url, baseUrl).href;
}
function extractModuleMetadata(container) {
const metadataScript = container.querySelector('#module-metadata');
if (!metadataScript) {
log('Nenhum metadata encontrado no módulo');
return null;
}
try {
const metadata = JSON.parse(metadataScript.textContent);
log('Metadata extraído:', metadata);
return metadata;
} catch (err) {
error('Erro ao parsear metadata:', err);
return null;
}
}
function getModuleBaseUrl(moduleUrl) {
try {
const url = new URL(moduleUrl, window.location.href);
const pathParts = url.pathname.split('/');
pathParts.pop(); // Remove o último segmento (nome do endpoint)
url.pathname = pathParts.join('/');
return url.href;
} catch (err) {
error('Erro ao extrair base URL:', err);
return window.location.origin;
}
}
async function initializeModule(containerId, moduleUrl, metadata) {
const moduleBaseUrl = getModuleBaseUrl(moduleUrl);
log(`Inicializando módulo em ${containerId} com base URL: ${moduleBaseUrl}`);
try {
// Carregar CSS se especificado
if (metadata.cssUrl) {
await loadStyle(metadata.cssUrl, moduleBaseUrl);
}
// Carregar JavaScript
if (metadata.jsUrl) {
const scriptLoaded = await loadScript(metadata.jsUrl, moduleBaseUrl);
if (!scriptLoaded) {
throw new Error('Falha ao carregar script principal');
}
}
// Aguardar um momento para o script ser executado
await new Promise(resolve => setTimeout(resolve, 100));
// Chamar função de inicialização
if (metadata.jsFunction) {
const functionPath = metadata.jsFunction.split('.');
let func = window;
for (const part of functionPath) {
func = func[part];
if (!func) {
throw new Error(`Função ${metadata.jsFunction} não encontrada`);
}
}
if (typeof func === 'function') {
log(`Chamando função de inicialização: ${metadata.jsFunction}`);
const result = func(containerId);
if (result) {
moduleInstances.set(containerId, {
metadata,
moduleUrl,
moduleBaseUrl,
initialized: true
});
log(`✅ Módulo ${metadata.moduleId} inicializado com sucesso`);
return true;
}
}
}
return false;
} catch (err) {
error(`Erro ao inicializar módulo:`, err);
return false;
}
}
async function loadModule(moduleId, containerId, moduleUrl) {
log(`Carregando módulo ${moduleId} em ${containerId} de ${moduleUrl}`);
try {
// Carregar HTML do módulo
const response = await fetch(moduleUrl);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const html = await response.text();
const container = document.getElementById(containerId);
if (!container) {
throw new Error(`Container ${containerId} não encontrado`);
}
// Inserir HTML
container.innerHTML = html;
// Extrair metadata
const metadata = extractModuleMetadata(container);
if (!metadata) {
log('⚠️ Módulo sem metadata, tentando funcionar sem JavaScript');
return true;
}
// Inicializar módulo
const success = await initializeModule(containerId, moduleUrl, metadata);
if (success) {
// Disparar evento
const event = new CustomEvent('moduleLoaded', {
detail: {
moduleId: metadata.moduleId,
containerId,
moduleUrl,
metadata
}
});
document.dispatchEvent(event);
return true;
}
return false;
} catch (err) {
error(`Erro ao carregar módulo ${moduleId}:`, err);
return false;
}
}
function getModuleInfo(containerId) {
return moduleInstances.get(containerId);
}
function unloadModule(containerId) {
const info = moduleInstances.get(containerId);
if (info) {
// Limpar container
const container = document.getElementById(containerId);
if (container) {
container.innerHTML = '';
}
moduleInstances.delete(containerId);
log(`Módulo removido: ${containerId}`);
}
}
// API pública
return {
loadModule,
getModuleInfo,
unloadModule,
version: '2.0.0'
};
})();
// Compatibilidade com sistema antigo
window.ModuleSystem = {
loadModule: window.ModuleLoader.loadModule,
version: '2.0.0-compat'
};