/** * Normaliza todos los componentes al patrño .ctx * Convierte: const XxxContext = createTerraContext(...) * A: class XxxState { static readonly ctx = createTerraContext(...) } */ import { readFileSync, writeFileSync, readdirSync, existsSync } from 'node:fs'; import { resolve, dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const terraDir = resolve(__dirname, '../src/uix/terra'); interface ContextDef { name: string; className: string; fullMatch: string; lineNumber: number; } function findContextDefinitions(content: string): ContextDef[] { const contexts: ContextDef[] = []; const lines = content.split('\n'); // Patrón para: const XxxContext = createTerraContext("...") const regex = /const\s+(\w+Context)\s*=\s*createTerraContext\s*<\s*(\w+)\s*\>\s*\(\s*["']([^"']+)["']\s*\)/g; let match; while ((match = regex.exec(content)) !== null) { const contextName = match[1]; const className = match[2]; const fullMatch = match[0]; // Encontrar número de línea const beforeMatch = content.substring(0, match.index); const lineNumber = beforeMatch.split('\n').length; contexts.push({ name: contextName, className, fullMatch, lineNumber }); } return contexts; } function findClassDeclaration( content: string, className: string ): { start: number; end: number } | null { const classRegex = new RegExp(`export\\s+class\\s+${className}\\s*\\{`, 'g'); const match = classRegex.exec(content); if (!match) return null; const start = match.index; const openBraces = [start]; let end = start + 1; let depth = 1; // Encontrar el cierre de la clase while (end < content.length && depth > 0) { if (content[end] === '{') depth++; else if (content[end] === '}') depth--; end++; } return { start, end }; } function convertToCtxPattern(content: string, contexts: ContextDef[]): string { let modified = content; // Ordenar por número de línea descendente (para modificar de atrás hacia adelante) contexts.sort((a, b) => b.lineNumber - a.lineNumber); for (const ctx of contexts) { console.log(` Procesando: ${ctx.name} -> ${ctx.className}.ctx`); // 1. Eliminar la definición del contexto externo modified = modified.replace(ctx.fullMatch, ''); // 2. Encontrar la clase y agregar static readonly ctx const classInfo = findClassDeclaration(modified, ctx.className); if (!classInfo) { console.log(` ⚠️ No se encontró la clase ${ctx.className}`); continue; } // Buscar el primer { de la clase const classStart = modified.indexOf('{', classInfo.start); if (classStart === -1 || classStart > classInfo.end) continue; // Insertar static readonly ctx después de la llave de apertura const indent = ' '; // Usar tabs como el resto del archivo const ctxProperty = `${indent}static readonly ctx = createTerraContext<${ctx.className}>('${ctx.className}');\n`; modified = modified.slice(0, classStart + 1) + '\n' + ctxProperty + modified.slice(classStart + 1); // 3. Reemplazar XxxContext.get() por XxxState.ctx.get() modified = modified.replace( new RegExp(`\\b${ctx.name}\\.get\\(\\)`, 'g'), `${ctx.className}.ctx.get()` ); // 4. Reemplazar XxxContext.set() por XxxState.ctx.set() modified = modified.replace( new RegExp(`\\b${ctx.name}\\.set\\(`, 'g'), `${ctx.className}.ctx.set(` ); // 5. Reemplazar XxxContext.getOr() por XxxState.ctx.getOr() modified = modified.replace( new RegExp(`\\b${ctx.name}\\.getOr\\(`, 'g'), `${ctx.className}.ctx.getOr(` ); } return modified; } function processFile(filePath: string): boolean { const content = readFileSync(filePath, 'utf-8'); const original = content; const contexts = findContextDefinitions(content); if (contexts.length === 0) return false; console.log(`📄 ${filePath}`); const modified = convertToCtxPattern(content, contexts); if (modified !== original) { writeFileSync(filePath, modified, 'utf-8'); console.log(` ✅ ${contexts.length} contexto(s) convertido(s)\n`); return true; } return false; } // Buscar todos los archivos .svelte.ts en terra/ function findAllTsFiles(dir: string): string[] { const files: string[] = []; const entries = readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = join(dir, entry.name); if (entry.isDirectory() && !entry.name.startsWith('.')) { files.push(...findAllTsFiles(fullPath)); } else if (entry.isFile() && entry.name.endsWith('.svelte.ts')) { files.push(fullPath); } } return files; } const files = findAllTsFiles(terraDir); let modified = 0; let skipped = 0; console.log('🔧 Normalizando al patrón .ctx...\n'); for (const file of files) { if (processFile(file)) { modified++; } else { skipped++; } } console.log(`\n📊 Resumen:`); console.log(` Modificados: ${modified}`); console.log(` Skippeados: ${skipped}`); console.log(` Total: ${modified + skipped}`);