import { JSDOM } from 'jsdom'; import axios from 'axios'; import vm from 'vm'; // Cache para los scripts y appParser let appParserCache = null; let windowCache = null; let scriptsCache = null; /** * Descarga y ejecuta los scripts remotos necesarios para appParser * Igual que en el frontend (src/main.js) */ async function loadRemoteParser() { if (appParserCache) { return { appParser: appParserCache, window: windowCache }; } const scripts = [ "https://cms.cocosolution.com/lib/plugins/builder_saas/js/lexer.js", "https://cms.cocosolution.com/lib/plugins/builder_saas/js/mixins/vuecomponents.js", "https://cms.cocosolution.com/lib/plugins/builder_saas/js/mixins/builderdata.js", "https://cms.cocosolution.com/lib/plugins/builder_saas/js/mixins/filters.js", "https://cms.cocosolution.com/lib/plugins/builder_saas/js/parseDocument.js", ]; // Crear un contexto jsdom const dom = new JSDOM('', { runScripts: "dangerously", resources: "usable" }); const window = dom.window; const document = window.document; // Mock de objetos necesarios que pueden no estar en jsdom if (!window.btoa) { window.btoa = (str) => Buffer.from(str).toString('base64'); } if (!window.atob) { window.atob = (str) => Buffer.from(str, 'base64').toString(); } // Asegurar que DOMParser esté disponible (jsdom lo tiene en window) // Pero también lo necesitamos como variable global para los scripts const DOMParser = window.DOMParser; // Mock de window.bus (puede que no sea necesario para el parseo) if (!window.bus) { window.bus = { $emit: () => {}, $on: () => {}, $off: () => {} }; } // Crear contexto VM con todas las referencias necesarias // Los scripts remotos esperan que window, document, DOMParser, etc. estén disponibles globalmente const context = vm.createContext({ window: window, document: document, DOMParser: DOMParser, // Añadir DOMParser como variable global console: console, Buffer: Buffer, setTimeout: setTimeout, setInterval: setInterval, clearTimeout: clearTimeout, clearInterval: clearInterval, // Añadir todas las propiedades globales necesarias ...global, // Asegurar que las referencias estén disponibles también como variables globales global: global, process: process }); // Descargar y ejecutar cada script for (const scriptUrl of scripts) { try { console.log(`Descargando script: ${scriptUrl}`); const response = await axios.get(scriptUrl, { timeout: 10000 // 10 segundos de timeout }); const scriptContent = response.data; // Ejecutar el script en el contexto VM // Los scripts pueden usar 'window', 'document', etc. directamente vm.runInContext(scriptContent, context); } catch (error) { console.error(`Error cargando script ${scriptUrl}:`, error.message); // Si falla un script crítico, lanzar error if (scriptUrl.includes('parseDocument.js')) { throw new Error(`Error crítico cargando parseDocument.js: ${error.message}`); } // Continuar con los demás scripts para los no críticos } } // Verificar que appParser esté disponible if (!window.appParser) { throw new Error('appParser no se cargó correctamente desde los scripts remotos'); } appParserCache = window.appParser; windowCache = window; scriptsCache = scripts; return { appParser: appParserCache, window: windowCache }; } /** * Obtiene appParser, cargándolo si es necesario */ export async function getAppParser() { const { appParser } = await loadRemoteParser(); return appParser; } /** * Wrapper para parseComponents usando appParser remoto * Usa tipo 2 (Twig) explícitamente * Firma real: parseComponents(code, prefixVar, type = 0) */ export async function parseComponents(html, moduleIds = [], listTables = [], prefixVar = "", skipBuilderData = false) { const { appParser, window } = await loadRemoteParser(); // Setear las variables globales en el window real donde se cargó appParser window.allModules = moduleIds; window.tables = listTables; // La firma real es: parseComponents(code, prefixVar, type = 0) // Pasamos 2 (número) para Twig explícitamente return appParser.parseComponents(html, prefixVar, 2); } /** * Wrapper para generateBuilderVars usando appParser remoto * Usa tipo 2 (Twig) explícitamente, igual que en Api.js * Nota: El código remoto falla si previousSchema es null, así que pasamos {} en su lugar */ export async function generateBuilderVars(code, previousSchema = null) { const appParser = await getAppParser(); // Pasar 2 para Twig (igual que en Api.js: parseInt(type) donde type="2") // El código remoto falla si previousSchema es null, así que usamos {} en su lugar const safePreviousSchema = previousSchema || {}; return appParser.generateBuilderVars(code, 2, safePreviousSchema); } /** * Limpia la caché (útil para forzar recarga) */ export function clearCache() { appParserCache = null; scriptsCache = null; }