versacompiler 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +1 -1
  2. package/dist/compiler/compile.js +2520 -25
  3. package/dist/compiler/error-reporter.js +467 -38
  4. package/dist/compiler/linter.js +72 -1
  5. package/dist/compiler/minify.js +272 -1
  6. package/dist/compiler/minifyTemplate.js +230 -1
  7. package/dist/compiler/module-resolution-optimizer.js +844 -1
  8. package/dist/compiler/parser.js +336 -1
  9. package/dist/compiler/performance-monitor.js +204 -56
  10. package/dist/compiler/tailwindcss.js +39 -1
  11. package/dist/compiler/transform-optimizer.js +392 -1
  12. package/dist/compiler/transformTStoJS.js +16 -1
  13. package/dist/compiler/transforms.js +554 -1
  14. package/dist/compiler/typescript-compiler.js +172 -2
  15. package/dist/compiler/typescript-error-parser.js +281 -10
  16. package/dist/compiler/typescript-manager.js +304 -2
  17. package/dist/compiler/typescript-sync-validator.js +295 -31
  18. package/dist/compiler/typescript-worker-pool.js +936 -1
  19. package/dist/compiler/typescript-worker-thread.cjs +466 -22
  20. package/dist/compiler/typescript-worker.js +339 -1
  21. package/dist/compiler/vuejs.js +396 -37
  22. package/dist/hrm/VueHRM.js +359 -1
  23. package/dist/hrm/errorScreen.js +83 -1
  24. package/dist/hrm/getInstanciaVue.js +313 -1
  25. package/dist/hrm/initHRM.js +586 -1
  26. package/dist/main.js +353 -7
  27. package/dist/servicios/browserSync.js +589 -2
  28. package/dist/servicios/file-watcher.js +425 -4
  29. package/dist/servicios/logger.js +63 -3
  30. package/dist/servicios/readConfig.js +399 -53
  31. package/dist/utils/excluded-modules.js +37 -1
  32. package/dist/utils/module-resolver.js +466 -1
  33. package/dist/utils/promptUser.js +48 -2
  34. package/dist/utils/proxyValidator.js +68 -1
  35. package/dist/utils/resolve-bin.js +58 -1
  36. package/dist/utils/utils.js +21 -1
  37. package/dist/utils/vue-types-setup.js +435 -241
  38. package/dist/wrappers/eslint-node.js +1 -1
  39. package/dist/wrappers/oxlint-node.js +122 -1
  40. package/dist/wrappers/tailwind-node.js +94 -1
  41. package/package.json +106 -103
@@ -1,25 +1,2520 @@
1
- import{createHash as e}from"node:crypto";import{glob as t,mkdir as n,readFile as r,stat as i,unlink as a,writeFile as o}from"node:fs/promises";import s from"node:os";import c from"node:path";import l,{argv as u,cwd as d,env as f}from"node:process";import{logger as p,setProgressManagerGetter as m}from"../servicios/logger.js";import{promptUser as h}from"../utils/promptUser.js";import{showTimingForHumans as g}from"../utils/utils.js";m(()=>Z.getInstance());async function _(e,t,n){let r=new Promise((e,r)=>setTimeout(()=>r(Error(`Timeout en ${n} (${t}ms)`)),t));return Promise.race([e,r])}let v,y,b,x,S,C,w,T,E,D,O;class k{static instance;isInitialized=!1;loadedModules=new Set;modulePool=new Map;loadingPromises=new Map;usageStats=new Map;preloadQueue=new Set;backgroundLoader=null;preloadLock=null;MAX_POOL_MEMORY=100*1024*1024;MAX_POOL_SIZE=15;HOT_MODULES=[`chalk`,`parser`];currentContext=null;constructor(){this.startBackgroundPreloading()}static getInstance(){return k.instance||(k.instance=new k),k.instance}startBackgroundPreloading(){this.backgroundLoader=this.preloadCriticalModules()}async preloadCriticalModules(){try{let e=this.HOT_MODULES.map(e=>this.ensureModuleLoaded(e).catch(()=>{}));await Promise.allSettled(e)}catch{}}async preloadForContext(e,t=new Set){if(this.preloadLock){await this.preloadLock;return}this.preloadLock=this.doPreload(e,t);try{await this.preloadLock}finally{this.preloadLock=null}}async doPreload(e,t=new Set){this.currentContext=e,this.backgroundLoader&&await this.backgroundLoader;let n=[];e===`batch`||e===`watch`?n.push(`transforms`,`vue`,`typescript`,`module-resolution-optimizer`):(t.has(`.vue`)&&n.push(`vue`),(t.has(`.ts`)||t.has(`.vue`))&&n.push(`typescript`),this.loadedModules.has(`transforms`)||n.push(`transforms`));for(let e of[[`chalk`,`parser`],[`transforms`],[`vue`,`typescript`],[`module-resolution-optimizer`],[`minify`]]){let t=e.filter(e=>n.includes(e));t.length>0&&await Promise.allSettled(t.map(async e=>{try{await this.ensureModuleLoaded(e)}catch(t){if(f.VERBOSE===`true`){let n=t instanceof Error?t.message:String(t);console.warn(`[Verbose] Warning: No se pudo precargar módulo ${e}:`,n)}}}))}}async ensureModuleLoaded(e){if(this.modulePool.has(e))return this.updateUsageStats(e),this.modulePool.get(e);if(this.loadingPromises.has(e))return this.loadingPromises.get(e);let t=this.loadModuleInternal(e);this.loadingPromises.set(e,t);try{let n=await t;return this.modulePool.set(e,n),this.loadedModules.add(e),this.updateUsageStats(e),n}finally{this.loadingPromises.delete(e)}}updateUsageStats(e){let t=this.usageStats.get(e)||0;this.usageStats.set(e,t+1)}async loadModuleInternal(e){switch(e){case`chalk`:return this.loadChalk();case`parser`:return this.loadParser();case`transforms`:return this.loadTransforms();case`vue`:return this.loadVue();case`typescript`:return this.loadTypeScript();case`minify`:return this.loadMinify();case`tailwind`:return this.loadTailwind();case`linter`:return this.loadLinter();case`transform-optimizer`:return this.loadTransformOptimizer();case`module-resolution-optimizer`:return this.loadModuleResolutionOptimizer();default:throw Error(`Módulo desconocido: ${e}`)}}async loadChalk(){return v||(v=(await import(`chalk`)).default),v}async loadParser(){return S||(S=(await import(`./parser.js`)).getCodeFile),S}async loadTransforms(){return w||(w=(await import(`./transforms.js`)).estandarizaCode),w}async loadVue(){return E||(E=(await import(`./vuejs.js`)).preCompileVue),E}async loadTypeScript(){return T||(T=(await import(`./typescript-manager.js`)).preCompileTS),T}async loadMinify(){return x||(x=(await import(`./minify.js`)).minifyWithTemplates),x}async loadTailwind(){return C||(C=(await import(`./tailwindcss.js`)).generateTailwindCSS),C}async loadLinter(){if(!y||!b){let e=await import(`./linter.js`);y=e.ESLint,b=e.OxLint}return{ESLint:y,OxLint:b}}async loadTransformOptimizer(){return D||(D=(await import(`./transform-optimizer.js`)).TransformOptimizer.getInstance()),D}async loadModuleResolutionOptimizer(){return O||(O=(await import(`./module-resolution-optimizer.js`)).ModuleResolutionOptimizer.getInstance()),O}getPerformanceStats(){let e=Array.from(this.usageStats.entries()).sort((e,t)=>t[1]-e[1]).slice(0,5).map(([e])=>e);return{loadedModules:Array.from(this.loadedModules),usageStats:Object.fromEntries(this.usageStats),poolSize:this.modulePool.size,loadingInProgress:Array.from(this.loadingPromises.keys()),mostUsedModules:e}}estimateModuleSize(e){let t={"transform-optimizer":5*1024*1024,typescript:10*1024*1024,vue:8*1024*1024,"module-resolution-optimizer":3*1024*1024,transforms:2*1024*1024,minify:2*1024*1024,linter:1*1024*1024,parser:500*1024,chalk:100*1024,default:500*1024};return t[e]??t.default}getPoolMemoryUsage(){let e=0;for(let t of this.modulePool.keys())e+=this.estimateModuleSize(t);return e}cleanupUnusedModules(){let e=this.getPoolMemoryUsage(),t=this.modulePool.size;if(e>this.MAX_POOL_MEMORY||t>this.MAX_POOL_SIZE){let n=Array.from(this.usageStats.entries()).sort((e,t)=>e[1]-t[1]).filter(([e])=>!this.HOT_MODULES.includes(e)),r=this.MAX_POOL_MEMORY*.7,i=this.MAX_POOL_SIZE*.7;for(let[e]of n){this.modulePool.delete(e),this.loadedModules.delete(e),this.usageStats.delete(e);let t=this.getPoolMemoryUsage(),n=this.modulePool.size;if(t<=r&&n<=i)break}f.VERBOSE===`true`&&console.log(`[ModuleManager] Limpieza: ${t} → ${this.modulePool.size} módulos, ${Math.round(e/1024/1024)}MB → ${Math.round(this.getPoolMemoryUsage()/1024/1024)}MB`)}}reset(){this.isInitialized=!1,this.loadedModules.clear(),this.modulePool.clear(),this.loadingPromises.clear(),this.usageStats.clear(),this.preloadQueue.clear(),this.currentContext=null,this.backgroundLoader=null,this.startBackgroundPreloading()}}async function A(){return v||(v=(await import(`chalk`)).default),v}async function j(){if(!y||!b){let e=await import(`./linter.js`);y=e.ESLint,b=e.OxLint}return{ESLint:y,OxLint:b}}async function oe(){return x||(x=(await import(`./minify.js`)).minifyWithTemplates),x}async function M(){return S||(S=(await import(`./parser.js`)).getCodeFile),S}async function N(){return C||(C=(await import(`./tailwindcss.js`)).generateTailwindCSS),C}async function se(){return w||(w=(await import(`./transforms.js`)).estandarizaCode),w}async function ce(){return T||(T=(await import(`./typescript-manager.js`)).preCompileTS),T}async function le(){return E||(E=(await import(`./vuejs.js`)).preCompileVue),E}const P=[],F=[],I=[`NODE_ENV`,`isPROD`,`TAILWIND`,`ENABLE_LINTER`,`VERBOSE`,`typeCheck`,`PATH_ALIAS`,`tailwindcss`,`linter`,`tsconfigFile`];class L{cache=new Map;maxEntries=200;maxMemory=50*1024*1024;currentMemoryUsage=0;fileWatchers=new Map;dependencyGraph=new Map;reverseDependencyGraph=new Map;packageJsonPath=c.join(d(),`package.json`);nodeModulesPath=c.join(d(),`node_modules`);isWatchingDependencies=!1;async generateContentHash(t){try{let n=await r(t,`utf8`);return e(`sha256`).update(n).digest(`hex`)}catch{let n=`${t}-${Date.now()}`;return e(`sha256`).update(n).digest(`hex`)}}generateConfigHash(){try{let t={isPROD:f.isPROD||`false`,TAILWIND:f.TAILWIND||`false`,ENABLE_LINTER:f.ENABLE_LINTER||`false`,PATH_ALIAS:f.PATH_ALIAS||`{}`,tailwindcss:f.tailwindcss||`false`,linter:f.linter||`false`,tsconfigFile:f.tsconfigFile||`./tsconfig.json`},n=JSON.stringify(t,Object.keys(t).sort());return e(`sha256`).update(n).digest(`hex`).substring(0,12)}catch{return`no-config`}}generateEnvHash(){try{let t=I.map(e=>`${e}=${f[e]||``}`).join(`|`);return e(`sha256`).update(t).digest(`hex`).substring(0,12)}catch{return`no-env`}}async generateDependencyHash(){try{let t=e(`sha256`),n=await r(c.join(d(),`package.json`),`utf8`),a=JSON.parse(n),o={...a.dependencies,...a.devDependencies},s=JSON.stringify(o,Object.keys(o).sort());t.update(`package:${s}`);try{let e=await r(c.join(d(),`package-lock.json`),`utf8`),n=JSON.parse(e),i={};if(n.packages){for(let[e,t]of Object.entries(n.packages))if(e&&e!==``&&typeof t==`object`&&t){let n=e.replace(`node_modules/`,``);t.version&&(i[n]=t.version)}}t.update(`lock:${JSON.stringify(i,Object.keys(i).sort())}`)}catch{}try{let e=c.join(d(),`node_modules`),n=await i(e);t.update(`nmtime:${n.mtimeMs}`);let r=Object.keys(o).slice(0,10);for(let n of r)try{let r=await i(c.join(e,n));t.update(`${n}:${r.mtimeMs}`)}catch{t.update(`${n}:missing`)}}catch{t.update(`nmtime:none`)}return t.digest(`hex`).substring(0,16)}catch(t){return e(`sha256`).update(`error:${t instanceof Error?t.message:`unknown`}`).digest(`hex`).substring(0,16)}}async generateCacheKey(e){let t=await this.generateContentHash(e),n=this.generateConfigHash(),r=this.generateEnvHash(),i=await this.generateDependencyHash();return`${e}|${t.substring(0,12)}|${n}|${r}|${i}`}async isValid(e){let t=this.cache.get(e);if(!t)return!1;try{await i(t.outputPath);let n=await this.generateContentHash(e);if(t.contentHash!==n)return this.cache.delete(e),!1;let r=this.generateConfigHash();if(t.configHash!==r)return this.cache.delete(e),!1;let a=this.generateEnvHash();if(t.envHash!==a)return this.cache.delete(e),!1;let o=await this.generateDependencyHash();return t.dependencyHash!==o||(await i(e)).mtimeMs>t.mtime?(this.cache.delete(e),!1):(t.lastUsed=Date.now(),!0)}catch{return this.cache.delete(e),!1}}async set(e,t){try{let n=await i(e),r={contentHash:await this.generateContentHash(e),configHash:this.generateConfigHash(),envHash:this.generateEnvHash(),dependencyHash:await this.generateDependencyHash(),mtime:n.mtimeMs,outputPath:t,lastUsed:Date.now(),size:n.size};this.evictIfNeeded(r.size),this.cache.set(e,r),this.currentMemoryUsage+=r.size}catch(t){console.warn(`Warning: No se pudo cachear ${e}:`,t)}}evictIfNeeded(e){for(;this.cache.size>=this.maxEntries*.8;)this.evictLRU();for(;this.currentMemoryUsage+e>this.maxMemory*.8&&this.cache.size>0;)this.evictLRU();if(l.memoryUsage().heapUsed/(1024*1024)>200&&this.cache.size>50){let e=Math.min(this.cache.size-50,10);for(let t=0;t<e;t++)this.evictLRU()}}evictLRU(){let e=``,t=1/0;for(let[n,r]of this.cache)r.lastUsed<t&&(t=r.lastUsed,e=n);if(e){let t=this.cache.get(e);t&&(this.currentMemoryUsage-=t.size,this.cache.delete(e))}}cleanOldEntries(e=20){let t=0;for(let n=0;n<e&&this.cache.size>0;n++){let e=this.cache.size;if(this.evictLRU(),this.cache.size<e)t++;else break}return t}async load(e){try{if(f.cleanCache===`true`){this.cache.clear(),this.currentMemoryUsage=0;try{await a(e)}catch{}return}let t=await r(e,`utf-8`),n=JSON.parse(t);for(let[e,t]of Object.entries(n)){let n=t;n.contentHash&&n.outputPath&&n.mtime&&(this.cache.set(e,n),this.currentMemoryUsage+=n.size||0)}}catch{this.cache.clear(),this.currentMemoryUsage=0}}async save(e,t){try{await n(t,{recursive:!0});let r=Object.fromEntries(this.cache);await o(e,JSON.stringify(r,null,2))}catch(e){console.warn(`Warning: No se pudo guardar el cache:`,e)}}clear(){this.cache.clear(),this.currentMemoryUsage=0}getOutputPath(e){return this.cache.get(e)?.outputPath||``}getStats(){return{entries:this.cache.size,memoryUsage:this.currentMemoryUsage,hitRate:0}}async startDependencyWatching(){if(!this.isWatchingDependencies)try{let e=await import(`chokidar`);if(await this.fileExists(this.packageJsonPath)){let t=e.watch(this.packageJsonPath,{persistent:!1,ignoreInitial:!0});t.on(`change`,()=>{p.info(`📦 package.json modificado - invalidando cache de dependencias`),this.invalidateByDependencyChange()}),this.fileWatchers.set(`package.json`,t)}if(await this.fileExists(this.nodeModulesPath)){let t=e.watch(this.nodeModulesPath,{persistent:!1,ignoreInitial:!0,depth:1,ignored:/(^|[/\\])\../});t.on(`addDir`,e=>{p.info(`📦 Nueva dependencia instalada: ${e.split(/[/\\]/).pop()}`),this.invalidateByDependencyChange()}),t.on(`unlinkDir`,e=>{p.info(`📦 Dependencia eliminada: ${e.split(/[/\\]/).pop()}`),this.invalidateByDependencyChange()}),this.fileWatchers.set(`node_modules`,t)}this.isWatchingDependencies=!0,p.info(`🔍 Vigilancia de dependencias iniciada`)}catch(e){p.warn(`⚠️ No se pudo iniciar vigilancia de dependencias:`,e)}}async stopDependencyWatching(){for(let[e,t]of this.fileWatchers)try{await t.close(),p.info(`🛑 Vigilancia detenida: ${e}`)}catch(t){p.warn(`⚠️ Error cerrando watcher ${e}:`,t)}this.fileWatchers.clear(),this.isWatchingDependencies=!1}registerDependencies(e,t){let n=this.dependencyGraph.get(e);if(n)for(let t of n){let n=this.reverseDependencyGraph.get(t);n&&(n.delete(e),n.size===0&&this.reverseDependencyGraph.delete(t))}let r=new Set(t);this.dependencyGraph.set(e,r);for(let t of r)this.reverseDependencyGraph.has(t)||this.reverseDependencyGraph.set(t,new Set),this.reverseDependencyGraph.get(t).add(e)}invalidateByDependencyChange(){let e=0;for(let[t]of this.cache)this.cache.delete(t),e++;this.dependencyGraph.clear(),this.reverseDependencyGraph.clear(),this.currentMemoryUsage=0,p.info(`🗑️ Cache invalidado: ${e} archivos (cambio en dependencias)`)}invalidateCascade(e){let t=[],n=new Set([e]),r=[e];for(;r.length>0;){let e=r.shift(),t=this.reverseDependencyGraph.get(e);if(t)for(let e of t)n.has(e)||(n.add(e),r.push(e))}for(let e of n)if(this.cache.has(e)){let n=this.cache.get(e);this.currentMemoryUsage-=n.size,this.cache.delete(e),t.push(e)}return t.length>0&&p.info(`🔄 Invalidación cascada: ${t.length} archivos afectados por ${e}`),t}async fileExists(e){try{return await i(e),!0}catch{return!1}}getAdvancedStats(){return{entries:this.cache.size,memoryUsage:this.currentMemoryUsage,hitRate:0,dependencyNodes:this.dependencyGraph.size,watchingDependencies:this.isWatchingDependencies,activeWatchers:this.fileWatchers.size}}}const R=new L,z=c.join(c.resolve(f.PATH_PROY||d(),`compiler`),`.cache`),B=c.join(z,`versacompile-cache.json`);async function ue(){await R.load(B),(f.WATCH_MODE===`true`||u.includes(`--watch`)||u.includes(`-w`))&&await R.startDependencyWatching()}async function de(){await R.save(B,z)}function V(e,t,n,r=`error`,i,a){P.push({file:e,stage:t,message:n,severity:r,details:i,help:a,timestamp:Date.now()})}function H(e,t,n,r=[]){let i=F.find(t=>t.stage===e);i?(i.errors+=t,i.success+=n,i.files.push(...r)):F.push({stage:e,errors:t,success:n,files:[...r]})}async function U(e,t,n,r,i=!1){let a=e instanceof Error?e.message:e,o=e instanceof Error?e.stack:void 0;if(V(t,n,a,`error`,o),H(n,1,0,[t]),r===`individual`||r===`watch`){let e=await A(),r=c.basename(t),s=await J(n);if(i)p.error(e.red(`❌ Error en etapa ${s(n)} - ${r}:`)),p.error(e.red(a)),o&&(n===`typescript`||n===`vue`)&&o.split(`
2
- `).slice(0,5).forEach(t=>{t.trim()&&p.error(e.gray(` ${t.trim()}`))});else{let t=a.split(`
3
- `)[0];p.error(e.red(`❌ Error en ${s(n)}: ${r}`)),p.error(e.red(` ${t}`)),p.info(e.yellow(`💡 Usa --verbose para ver detalles completos`))}}}function W(e,t){H(t,0,1,[e])}function G(){P.length=0,F.length=0}async function K(e=!1,t){let n=await A();if(P.length===0&&F.length===0){p.info(n.green(`✅ No hay errores de compilación para mostrar.`)),t&&p.info(n.bold(`\n⏱️ TIEMPO TOTAL DE COMPILACIÓN: ${t}`));return}let r=`━`.repeat(40);if(p.info(``),p.info(n.bold.cyan(`📊 Resumen de Compilación`)),p.info(n.gray(r)),t&&(p.info(n.bold(`⏱️ Tiempo Total: ${n.green(t)}`)),p.info(``)),F.length>0){p.info(n.bold.blue(`🔧 Estadísticas por Etapa:`));for(let e of F){let t=e.success+e.errors,r=t>0?Math.round(e.success/t*100):0,i=_e(e.stage),a=e.errors===0?n.green:n.red,o=$(r,20);p.info(` ${i} ${n.bold(e.stage)}`),p.info(` ${a(`●`)} ${e.success}/${t} archivos ${a(`(${r}%)`)}`),p.info(` ${o}`),e.errors>0&&p.info(` ${n.red(`⚠`)} ${e.errors} ${e.errors===1?`error`:`errores`}`),p.info(``)}}if(P.length>0){p.info(n.red(`\n❌ Se encontraron ${P.length} errores:`));let t=new Map;P.forEach(e=>{t.has(e.file)||t.set(e.file,[]),t.get(e.file).push(e)});let r=1;for(let[i,a]of t){let t=c.basename(i),o=a.filter(e=>e.severity===`error`).length,s=a.filter(e=>e.severity===`warning`).length;p.info(n.cyan(`\n📄 ${r}. ${t}`)),p.info(n.gray(` Ruta: ${i}`)),p.info(n.yellow(` ${o} errores, ${s} advertencias`));for(let t of a){let r=t.severity===`error`?`❌`:`⚠️`,i=await J(t.stage);p.info(` ${r} [${i(t.stage)}] ${t.message}`),e&&t.details&&t.details.split(`
4
- `).slice(0,5).forEach(e=>{e.trim()&&p.info(n.gray(` ${e.trim()}`))}),t.help&&p.info(n.blue(` 💡 ${t.help}`))}r++}let i=P.filter(e=>e.severity===`error`).length,a=P.filter(e=>e.severity===`warning`).length,o=t.size,s=`═`.repeat(50);p.info(``),p.info(n.bold.cyan(s)),p.info(n.bold.cyan(` 📊 RESUMEN FINAL`)),p.info(n.bold.cyan(s)),p.info(``),p.info(n.bold(`🎯 Resultados:`)),p.info(` 📁 Archivos afectados: ${n.cyan.bold(o)}`),p.info(` ${i>0?n.red(`●`):n.green(`○`)} Errores: ${i>0?n.red.bold(i):n.green.bold(`0`)}`),p.info(` ${a>0?n.yellow(`●`):n.green(`○`)} Advertencias: ${a>0?n.yellow.bold(a):n.green.bold(`0`)}`),p.info(``),i>0?(p.info(n.red.bold(`🚨 COMPILACIÓN COMPLETADA CON ERRORES`)),p.info(n.red(` Por favor revisa y corrige los problemas anteriores.`))):a>0?(p.info(n.yellow.bold(`⚠️ COMPILACIÓN COMPLETADA CON ADVERTENCIAS`)),p.info(n.yellow(` Considera revisar las advertencias anteriores.`))):(p.info(n.green.bold(`✅ COMPILACIÓN EXITOSA`)),p.info(n.green(` ¡Todos los archivos se compilaron sin problemas!`))),p.info(``),p.info(n.bold.cyan(s))}else{let e=`═`.repeat(50);p.info(``),p.info(n.bold.green(e)),p.info(n.bold.green(` ✨ ÉXITO`)),p.info(n.bold.green(e)),p.info(``),p.info(n.green.bold(`🎉 COMPILACIÓN COMPLETADA EXITOSAMENTE`)),p.info(n.green(` ¡No se encontraron errores ni advertencias!`)),p.info(``),p.info(n.bold.green(e))}p.info(``)}async function q(e){let t=await A(),n=new Map;e.forEach(e=>{n.has(e.file)||n.set(e.file,[]),n.get(e.file).push(e)});let r=e.filter(e=>e.severity===`error`).length,i=e.filter(e=>e.severity===`warning`).length,a=n.size;p.info(t.bold.rgb(255,120,120)(`╭─────────────────────────────────────────────────────────────╮`)),p.info(t.bold.rgb(255,120,120)(`│ `)+t.bold.white(`🔍 LINTER REPORT`)+t.bold.rgb(255,120,120)(` │`)),p.info(t.bold.rgb(255,120,120)(`╰─────────────────────────────────────────────────────────────╯`));let o=r>0?t.red(`●`):t.green(`○`),s=i>0?t.yellow(`●`):t.green(`○`);if(p.info(``),p.info(t.bold(`📊 Summary:`)),p.info(` ${o} ${t.bold(r)} ${t.red(`errors`)}`),p.info(` ${s} ${t.bold(i)} ${t.yellow(`warnings`)}`),p.info(` 📁 ${t.bold(a)} ${t.cyan(`files`)}`),p.info(``),r===0&&i===0){p.info(t.green.bold(`✨ All checks passed! No issues found.`));return}let c=1;for(let[e,r]of n)await fe(e,r,c,a),c++,c<=a&&p.info(t.gray(`─`.repeat(80)));p.info(``),p.info(t.bold.rgb(255,120,120)(`╭─────────────────────────────────────────────────────────────╮`)),p.info(t.bold.rgb(255,120,120)(`│ `)+t.bold.white(`Found ${r+i} issues in ${a} files`)+` `.repeat(Math.max(0,52-`Found ${r+i} issues in ${a} files`.length))+t.bold.rgb(255,120,120)(` │`)),p.info(t.bold.rgb(255,120,120)(`╰─────────────────────────────────────────────────────────────╯`))}async function fe(e,t,n,r){let i=await A(),a=t.filter(e=>e.severity===`error`).length,o=t.filter(e=>e.severity===`warning`).length,s=a>0?i.red(`✕`):i.yellow(`⚠`),u=e.endsWith(`.vue`)?`🎨`:e.endsWith(`.ts`)?`📘`:e.endsWith(`.js`)?`📜`:`📄`;p.info(``),p.info(i.bold(`${s} ${u} ${i.cyan(c.relative(l.cwd(),e))}`)),p.info(i.gray(` ${a} errors, ${o} warnings`)),p.info(``);for(let n=0;n<t.length;n++){let r=t[n];await pe(r,e,n+1,t.length)}}async function pe(e,t,n,r){let i=await A(),a=await import(`node:fs/promises`),o=e.severity===`error`,s=o?i.red:i.yellow,l=o?`✕`:`⚠`,u=e.line||1,d=e.column||1,f=e.ruleId||e.from||`unknown`,m=` ${s(l)} ${i.bold(e.message)}`,h=`${i.gray(f)}`,g=`${i.blue(`${u}:${d}`)}`;p.info(m),p.info(` ${i.gray(`at`)} ${g} ${i.gray(`·`)} ${h}`);try{let e=c.resolve(t),n=(await a.readFile(e,`utf-8`)).split(`
5
- `),r=parseInt(u.toString())-1;if(r>=0&&r<n.length){p.info(``);let e=Math.max(0,r-1),t=Math.min(n.length-1,r+1),a=(t+1).toString().length;for(let o=e;o<=t;o++){let e=o+1,t=n[o]||``,c=e.toString().padStart(a,` `);if(o===r){p.info(` ${i.red(`>`)} ${i.gray(c)} ${i.gray(`│`)} ${t}`);let e=` `.repeat(Math.max(0,d-1))+s(`^`);p.info(` ${i.gray(` `)} ${i.gray(` `.repeat(a))} ${i.gray(`│`)} ${e}`)}else p.info(` ${i.gray(` `)} ${i.gray(c)} ${i.gray(`│`)} ${i.gray(t)}`)}}}catch{p.info(` ${i.gray(`│`)} ${i.gray(`(Unable to read file content)`)}`)}if(e.help){p.info(``);let t=e.help.replace(/^Regla \w+: /,``).trim();p.info(` ${i.blue(`💡`)} ${i.blue(`Help:`)} ${i.gray(t)}`)}n<r&&p.info(``)}async function J(e){let t=await A();switch(e){case`vue`:return t.green;case`typescript`:return t.blue;case`standardization`:return t.yellow;case`minification`:return t.red;case`tailwind`:return t.magenta;case`file-read`:return t.gray;default:return t.white}}export function normalizeRuta(e){if(c.isAbsolute(e))return c.normalize(e).replace(/\\/g,`/`);let t=c.normalize(e.startsWith(`.`)?e:`./`+e).replace(/\\/g,`/`);return t.startsWith(`./`)?t:`./${t}`}export function getOutputPath(e){let t=f.PATH_SOURCE??``,n=f.PATH_DIST??``;if(!t||!n)return e.replace(/\.(vue|ts)$/,`.js`);let r=c.normalize(e).replace(/\\/g,`/`),i=c.normalize(t).replace(/\\/g,`/`),a=c.normalize(n).replace(/\\/g,`/`),o;if(r.includes(i)){let e=r.substring(r.indexOf(i)+i.length).replace(/^[/\\]/,``);o=c.join(a,e).replace(/\\/g,`/`)}else{let e=c.basename(r);o=c.join(a,e).replace(/\\/g,`/`)}return o.includes(`vue`)||o.includes(`ts`)?o.replace(/\.(vue|ts)$/,`.js`):o}class Y{static instance;fileSystemCache=new Map;debounceTimers=new Map;DEBOUNCE_DELAY=100;static getInstance(){return Y.instance||(Y.instance=new Y),Y.instance}async compileForWatch(e,t){return new Promise(n=>{let r=this.debounceTimers.get(e);r&&clearTimeout(r);let a=setTimeout(async()=>{this.debounceTimers.delete(e);try{let r=await i(e),a=this.fileSystemCache.get(e);if(a&&a.mtime>=r.mtimeMs){n({success:!0,cached:!0});return}let{TypeScriptWorkerPool:o}=await import(`./typescript-worker-pool.js`);o.getInstance().setMode(`watch`);let s=await t(e);this.fileSystemCache.set(e,{mtime:r.mtimeMs}),n(s)}catch(e){n({success:!1,error:e})}},this.DEBOUNCE_DELAY);this.debounceTimers.set(e,a)})}cleanup(){this.debounceTimers.forEach(e=>clearTimeout(e)),this.debounceTimers.clear(),this.fileSystemCache.clear()}}async function me(e,t,r=`individual`){let i={};e=c.isAbsolute(e)?normalizeRuta(e):normalizeRuta(c.resolve(e));let a=k.getInstance(),s=Date.now(),l=c.extname(e);await a.ensureModuleLoaded(`parser`);let u=await(await M())(e),d=u.code,m=u.error;if(i.fileRead=Date.now()-s,m)throw await U(m instanceof Error?m:Error(String(m)),e,`file-read`,r,f.VERBOSE===`true`),Error(m instanceof Error?m.message:String(m));if(!d||d.trim().length===0||d===`undefined`||d===`null`)throw await U(Error(`El archivo está vacío o no se pudo leer.`),e,`file-read`,r,f.VERBOSE===`true`),Error(`El archivo está vacío o no se pudo leer.`);let h=f.VERBOSE===`true`,g;if(l===`.vue`){s=Date.now(),h&&p.info(v.green(`💚 Precompilando VUE: ${c.basename(e)}`)),await a.ensureModuleLoaded(`vue`);let t=await le();if(typeof t!=`function`)throw Error(`loadVue devolvió ${typeof t} en lugar de una función para archivo: ${e}`);if(g=await t(d,e,f.isPROD===`true`),i.vueCompile=Date.now()-s,g==null)throw Error(`preCompileVue devolvió ${g} para archivo: ${e}`);if(g.error)throw await U(g.error instanceof Error?g.error:Error(String(g.error)),e,`vue`,r,f.VERBOSE===`true`),Error(g.error instanceof Error?g.error.message:String(g.error));W(e,`vue`),d=g.data}if(!d||d.trim().length===0)throw await U(Error(`El código Vue compilado está vacío.`),e,`vue`,r,f.VERBOSE===`true`),Error(`El código Vue compilado está vacío.`);let _;if(l===`.ts`||g?.lang===`ts`){s=Date.now(),h&&p.info(v.blue(`🔄️ Precompilando TS: ${c.basename(e)}`)),await a.ensureModuleLoaded(`typescript`);let t=await ce();if(typeof t!=`function`)throw Error(`loadTypeScript devolvió ${typeof t} en lugar de una función para archivo: ${e}`);if(_=await t(d,e),i.tsCompile=Date.now()-s,_==null)throw Error(`preCompileTS devolvió ${_} para archivo: ${e}`);if(_.error)if(r===`all`)V(e,`typescript`,_.error instanceof Error?_.error.message:String(_.error),`error`);else throw await U(_.error,e,`typescript`,r,f.VERBOSE===`true`),Error(_.error instanceof Error?_.error.message:String(_.error));else W(e,`typescript`),d=_.data}if(!d||d.trim().length===0)throw await U(Error(`El código TypeScript compilado está vacío.`),e,`typescript`,r,f.VERBOSE===`true`),Error(`El código TypeScript compilado está vacío.`);h&&p.info(v.yellow(`💛 Estandarizando: ${c.basename(e)}`)),s=Date.now(),await a.ensureModuleLoaded(`transforms`);let y=await(await se())(d,e);if(i.standardization=Date.now()-s,y==null)throw Error(`estandarizaCode devolvió ${y} para archivo: ${e}`);if(y.error)throw await U(Error(y.error),e,`standardization`,r,f.VERBOSE===`true`),Error(y.error);if(W(e,`standardization`),d=y.code,!d||d.trim().length===0)throw await U(Error(`El código estandarizado está vacío.`),e,`standardization`,r,f.VERBOSE===`true`),Error(`El código estandarizado está vacío.`);if(f.isPROD===`true`){s=Date.now(),h&&p.info(v.red(`🤖 Minificando: ${c.basename(e)}`)),await a.ensureModuleLoaded(`minify`);let t=await(await oe())(d,e,!0);if(i.minification=Date.now()-s,t==null)throw Error(`minifyJS devolvió ${t} para archivo: ${e}`);if(t.error)throw await U(t.error instanceof Error?t.error:Error(String(t.error)),e,`minification`,r,f.VERBOSE===`true`),Error(t.error instanceof Error?t.error.message:String(t.error));W(e,`minification`),d=t.code}if(await n(c.dirname(t),{recursive:!0}),await o(t,d,`utf-8`),h){let t=Object.values(i).reduce((e,t)=>e+t,0);p.info(v.cyan(`⏱️ Timing para ${c.basename(e)}:`)),i.fileRead&&p.info(v.cyan(` 📖 Lectura: ${i.fileRead}ms`)),i.vueCompile&&p.info(v.cyan(` 💚 Vue: ${i.vueCompile}ms`)),i.tsCompile&&p.info(v.cyan(` 🔄️ TypeScript: ${i.tsCompile}ms`)),i.standardization&&p.info(v.cyan(` 💛 Estandarización: ${i.standardization}ms`)),i.minification&&p.info(v.cyan(` 🤖 Minificación: ${i.minification}ms`)),p.info(v.cyan(` 🏁 Total: ${t}ms`))}return{error:null,action:`extension`}}export async function initCompile(e,t=!0,n=`individual`){try{let r=k.getInstance(),i=c.extname(e),a=new Set([i]);if(await r.preloadForContext(n===`all`?`batch`:n,a),t&&f.TAILWIND){await r.ensureModuleLoaded(`tailwind`);let e=await(await N())();if(typeof e!=`boolean`)if(e?.success)p.info(`🎨 ${e.message}`);else{let t=`${e.message}${e.details?`
6
- `+e.details:``}`;await U(Error(t),`tailwind.config.js`,`tailwind`,n,f.VERBOSE===`true`)}}let o=Date.now(),s=normalizeRuta(e),l=getOutputPath(s);if((n===`watch`||n===`individual`)&&await Q(s))return f.VERBOSE===`true`&&p.info(`⏭️ Archivo omitido (cache): ${c.basename(s)}`),{success:!0,cached:!0,output:R.getOutputPath(s)||l,action:`cached`};n===`individual`&&f.VERBOSE===`true`&&p.info(`🔜 Fuente: ${s}`);let u=await me(s,l,n);if(u.error)throw Error(u.error);(n===`watch`||n===`individual`)&&await R.set(s,l);let d=g(Date.now()-o);if(n===`individual`){f.VERBOSE===`true`&&(p.info(`🔚 Destino: ${l}`),p.info(`⏱️ Tiempo: ${d}`));let e=await A();p.info(e.green(`✅ Compilación exitosa: ${c.basename(s)}`))}return{success:!0,output:l,action:u.action}}catch(t){let n=t instanceof Error?t.message:String(t);return f.VERBOSE===`true`&&p.error(`❌ Error en compilación de ${c.basename(e)}: ${n}`),{success:!1,output:``,error:n}}}let X=0;class Z{static instance;progressActive=!1;lastProgressLine=``;logBuffer=[];originalConsoleLog;originalConsoleError;originalConsoleWarn;hasProgressLine=!1;constructor(){this.originalConsoleLog=console.log,this.originalConsoleError=console.error,this.originalConsoleWarn=console.warn}static getInstance(){return Z.instance||(Z.instance=new Z),Z.instance}interceptConsole(){console.log=(...e)=>{this.addLog(e.map(e=>String(e)).join(` `))},console.error=(...e)=>{this.addLog(e.map(e=>String(e)).join(` `))},console.warn=(...e)=>{this.addLog(e.map(e=>String(e)).join(` `))}}restoreConsole(){console.log=this.originalConsoleLog,console.error=this.originalConsoleError,console.warn=this.originalConsoleWarn}startProgress(){this.progressActive=!0,this.logBuffer=[],this.hasProgressLine=!1,this.interceptConsole();let e=`━`.repeat(48);l.stdout.write(`
7
- \x1B[96m`+e+`\x1B[0m
8
- `),l.stdout.write(`\x1B[96m│ \x1B[97m\x1B[1m🚀 Iniciando Compilación\x1B[0m\x1B[96m`+` `.repeat(22)+`│\x1B[0m
9
- `),l.stdout.write(`\x1B[96m`+e+`\x1B[0m
10
- `)}updateProgress(e){if(!this.progressActive)return;if(this.logBuffer.length>0){this.hasProgressLine&&l.stdout.write(`\r\x1B[K`);for(let e of this.logBuffer)l.stdout.write((this.hasProgressLine?`
11
- `:``)+e+`
12
- `),this.hasProgressLine=!1;this.logBuffer=[]}this.hasProgressLine?l.stdout.write(`\r\x1B[K`):l.stdout.write(`
13
- \x1B[96m`+`▔`.repeat(50)+`\x1B[0m
14
- `);let t=this.getStageFromText(e),{bgColor:n,textColor:r,icon:i}=this.getProgressColors(t),a=`█`.repeat(3),o=`\x1b[${n}m\x1b[${r}m ${a} ${i} ${e} ${a} \x1b[0m`;l.stdout.write(o),this.hasProgressLine=!0,this.lastProgressLine=e}addLog(e){this.progressActive?this.logBuffer.push(e):this.originalConsoleLog(e)}addImmediateLog(e){this.progressActive?(this.hasProgressLine&&l.stdout.write(`\r\x1B[K`),l.stdout.write(`\x1B[90m│\x1B[0m `+e+`
15
- `),this.hasProgressLine=!1):this.originalConsoleLog(e)}endProgress(){if(this.progressActive){this.hasProgressLine&&l.stdout.write(`
16
- `),l.stdout.write(`
17
- \x1B[33m`+`-`.repeat(50)+`\x1B[0m
18
- `);let e=`█`.repeat(3),t=`\x1b[42m\x1b[30m ${e} PROCESO COMPLETADO 100% ${e} \x1b[0m`;l.stdout.write(t+`
19
- `);let n=`━`.repeat(48);if(l.stdout.write(`\x1B[92m`+n+`\x1B[0m
20
- `),l.stdout.write(`\x1B[92m│ \x1B[97m\x1B[1m✅ ¡Compilación Completada!\x1B[0m\x1B[92m`+` `.repeat(23)+`│\x1B[0m
21
- `),l.stdout.write(`\x1B[92m`+n+`\x1B[0m
22
-
23
- `),this.logBuffer.length>0)for(let e of this.logBuffer)l.stdout.write(e+`
24
- `)}this.restoreConsole(),this.progressActive=!1,this.lastProgressLine=``,this.logBuffer=[],this.hasProgressLine=!1}isActive(){return this.progressActive}getStageFromText(e){return e.includes(`Iniciando`)||e.includes(`Starting`)?`start`:e.includes(`Tailwind`)||e.includes(`CSS`)?`tailwind`:e.includes(`Recopilando`)||e.includes(`archivos`)||e.includes(`files`)?`files`:e.includes(`Compilando`)||e.includes(`workers`)?`compile`:e.includes(`cache`)||e.includes(`Guardando`)?`cache`:e.includes(`linter`)||e.includes(`Linter`)?`linter`:e.includes(`completado`)||e.includes(`Complete`)?`complete`:`default`}getProgressColors(e){return{start:{bgColor:`45`,textColor:`97`,icon:`🚀`},tailwind:{bgColor:`105`,textColor:`97`,icon:`🎨`},files:{bgColor:`43`,textColor:`30`,icon:`📁`},compile:{bgColor:`42`,textColor:`30`,icon:`⚙️`},cache:{bgColor:`44`,textColor:`97`,icon:`💾`},linter:{bgColor:`101`,textColor:`97`,icon:`🔍`},complete:{bgColor:`102`,textColor:`30`,icon:`✅`},default:{bgColor:`100`,textColor:`30`,icon:`⏳`}}[e]||{bgColor:`100`,textColor:`30`,icon:`⏳`}}}export async function runLinter(e=!1){let t=f.linter,n=[],r=[],i=!0;if(f.ENABLE_LINTER!==`true`)return!0;if(typeof t==`string`&&t.trim()!==``){p.info(`🔍 Ejecutando linting...`);try{let a=JSON.parse(t);if(Array.isArray(a)){let{ESLint:e,OxLint:t}=await j();for(let i of a)if(i.name.toLowerCase()===`eslint`){p.info(`🔧 Ejecutando ESLint con config: ${i.configFile||`por defecto`}`);let t=e(i).then(e=>{e&&e.json&&(Array.isArray(e.json)?e.json.forEach(e=>{let t=e.filePath||e.file||`archivo no especificado`;r.push({from:`eslint`,line:e.line||`N/A`,column:e.column||1,file:t,message:e.message,severity:e.severity===2?`error`:`warning`,help:e.ruleId?`Regla ESLint: ${e.ruleId}`:void 0})}):e.json.results&&Array.isArray(e.json.results)&&e.json.results.forEach(e=>{e.messages&&Array.isArray(e.messages)&&e.messages.forEach(t=>{let n=e.filePath||e.file||`archivo no especificado`;r.push({from:`eslint`,line:t.line||`N/A`,column:t.column||1,file:n,message:t.message,severity:t.severity===2?`error`:`warning`,help:t.ruleId?`Regla ESLint: ${t.ruleId}`:void 0})})}))}).catch(e=>{p.error(`❌ Error durante la ejecución de ESLint: ${e.message}`),r.push({file:i.configFile||`ESLint Config`,message:`Fallo al ejecutar ESLint: ${e.message}`,severity:`error`})});n.push(t)}else if(i.name.toLowerCase()===`oxlint`){p.info(`🔧 Ejecutando OxLint con config: ${i.configFile||`por defecto`}`);let e=t(i).then(e=>{e&&e.json&&Array.isArray(e.json.diagnostics)&&e.json.diagnostics.forEach(e=>{let t=e.filename||e.file||`archivo no especificado`,n=e.labels&&e.labels[0]&&e.labels[0].span?e.labels[0].span.line||e.labels[0].span.start?.line:`N/A`,i=e.labels&&e.labels[0]&&e.labels[0].span?e.labels[0].span.column||e.labels[0].span.start?.column:1;r.push({from:`oxlint`,line:n,column:i,file:t,message:e.message,severity:typeof e.severity==`string`?e.severity.toLowerCase():`error`,help:e.help||(e.code?`Regla Oxlint: ${e.code}`:void 0)})})}).catch(e=>{p.error(`❌ Error durante la ejecución de OxLint: ${e.message}`),r.push({file:i.configFile||`Oxlint Config`,message:`Fallo al ejecutar Oxlint: ${e.message}`,severity:`error`})});n.push(e)}}else p.warn(`⚠️ La configuración de linter no es un array válido.`);if(await Promise.all(n),e)if(r.length>0)await q(r);else{let e=await A();p.info(e.green(`✅ No se encontraron errores ni advertencias de linting.`))}else r.length>0&&(await q(r),p.warn(`🚨 Se encontraron errores o advertencias durante el linting.`),f.yes===`false`&&(await h(`¿Deseas continuar con la compilación a pesar de los errores de linting? (s/N): `)).toLowerCase()!==`s`&&(p.info(`🛑 Compilación cancelada por el usuario.`),i=!1))}catch(e){p.warn(`Error parseando configuración de linter: ${e instanceof Error?e.message:`Error desconocido`}, omitiendo...`)}}return i}function he(e,t,n=30){let r=Math.round(e/t*100),i=Math.round(r/100*n),a=n-i;return`[${`█`.repeat(i)}${` `.repeat(a)}] ${r}% (${e}/${t})`}async function Q(e){return await R.isValid(e)}async function ge(e,t=8){let n=[],r=[],i=e.length,a=0,o=0,s=0,u=Z.getInstance(),d=!1,m=0;function h(){let e=a+o+s,t=he(e,i),n=Math.round(e/i*100);if(e===0&&!d||n>X+1&&e>0||e===i){let r=`🚀 ${t} [✅ ${a} | ⏭️ ${o} | ❌ ${s}]`;u.updateProgress(r),e===0&&(d=!0),X=n}}h();for(let i of e){let e=(async()=>{try{if(f.VERBOSE===`true`&&p.info(`🔄 Compilando: ${c.basename(i)}`),await Q(i))return o++,f.VERBOSE===`true`&&p.info(`⏭️ Archivo omitido (cache): ${c.basename(i)}`),h(),{success:!0,cached:!0,output:R.getOutputPath(i)};let e=await initCompile(i,!1,`batch`);if(e.success&&e.output?(await R.set(i,e.output),f.VERBOSE===`true`&&p.info(`✅ Completado: ${c.basename(i)} → ${c.basename(e.output)}`)):f.VERBOSE===`true`&&p.info(`❌ Error en: ${c.basename(i)}`),a++,m++,m%20==0){try{typeof globalThis.gc==`function`&&globalThis.gc()}catch{}let e=l.memoryUsage().heapUsed/(1024*1024);if(e>300&&R.getStats().entries>50){console.log(`[Memory] Heap alto (${e.toFixed(1)}MB), limpiando cache...`);let t=R.cleanOldEntries(20);t>0&&console.log(`[Memory] Se removieron ${t} entradas del cache`)}}return h(),e}catch(e){if(s++,f.VERBOSE===`true`){let t=e instanceof Error?e.message:String(e);p.error(`💥 Falló: ${c.basename(i)} - ${t}`)}return h(),{success:!1,error:e instanceof Error?e.message:String(e)}}})();if(n.push(e),r.push(e),r.length>=t){let e=await Promise.race(r),t=r.indexOf(e);t!==-1&&r.splice(t,1)}e.then(()=>{let t=r.indexOf(e);t!==-1&&r.splice(t,1)})}await Promise.all(n)}export async function initCompileAll(){try{let e=Z.getInstance();if(e.startProgress(),e.updateProgress(`🚀 Iniciando compilación...`),G(),e.updateProgress(`📦 Cargando cache...`),await ue(),X=0,e.updateProgress(`🔍 Ejecutando linter...`),!await runLinter(!1)){e.endProgress();return}let n=Date.now(),r=f.PATH_SOURCE??``,i=f.PATH_DIST??``,a=r.replace(/\\/g,`/`),o=[`${a}/**/*.js`,`${a}/**/*.vue`,`${a}/**/*.ts`,`${a}/**/*.mjs`,`${a}/**/*.cjs`];p.info(`📝 Compilando todos los archivos...`),p.info(`🔜 Fuente: ${r}`),p.info(`🔚 Destino: ${i}\n`),e.updateProgress(`🎨 Generando TailwindCSS...`);let u=await(await N())();typeof u!=`boolean`&&(u?.success?p.info(`🎨 ${u.message}\n`):await U(Error(`${u.message}${u.details?`
25
- `+u.details:``}`),`tailwind.config.js`,`tailwind`,`all`,f.VERBOSE===`true`)),e.updateProgress(`📁 Recopilando archivos...`);let d=[];for await(let e of t(o))e.endsWith(`.d.ts`)||d.push(e);let m=s.cpus().length,h=d.length;if(m<4&&(m=4,f.VERBOSE===`true`&&p.warn(`⚠️ Solo se detectó ${s.cpus().length} CPU. Usando ${m} hilos por defecto.`)),l.env.VERSACOMPILER_MAX_THREADS){let e=parseInt(l.env.VERSACOMPILER_MAX_THREADS,10);!isNaN(e)&&e>0&&(m=e,f.VERBOSE===`true`&&p.info(`🔧 Usando ${m} hilos (variable de entorno VERSACOMPILER_MAX_THREADS)`))}let _=s.totalmem()/(1024*1024),v=s.freemem()/(1024*1024),y=(_-v)/_*100,b;b=y>90?Math.max(4,Math.floor(m*.5)):h<5?h:h<20?m:h<50?Math.floor(m*1.5):h<200?m*2:Math.min(m*3,48),h>10&&b<4&&(b=4,f.VERBOSE===`true`&&p.info(`⚡ Ajustando a mínimo de 4 hilos para proyecto de ${h} archivos`)),e.updateProgress(`⚙️ Configurando workers...`),p.info(`🚀 Compilando ${h} archivos con concurrencia optimizada (${b} hilos)...`),p.info(` 📊 CPUs detectados: ${s.cpus().length} (usando: ${m})`),p.info(` 💾 Memoria libre: ${v.toFixed(0)}MB / ${_.toFixed(0)}MB (${(100-y).toFixed(1)}% libre)`),p.info(` ⚡ Hilos configurados: ${b} (${(b/m).toFixed(1)}x CPUs)`);let x=Math.min(m*2,24);if(h>50&&b<x*.5){let e=await A();p.warn(e.yellow(`⚠️ Solo se usarán ${b} hilos para ${h} archivos.`)),p.info(e.yellow(` 💡 Tip: export VERSACOMPILER_MAX_THREADS=${x}`))}h>50&&b<8&&(p.warn(`⚠️ Solo se usarán ${b} hilos para ${h} archivos.`),p.warn(` 💡 Tip: Establece VERSACOMPILER_MAX_THREADS para forzar más hilos:`),p.warn(` 💡 export VERSACOMPILER_MAX_THREADS=16`));let S=k.getInstance(),C=new Set(d.map(e=>c.extname(e)));await S.preloadForContext(`batch`,C);try{let{TypeScriptWorkerPool:e}=await import(`./typescript-worker-pool.js`);e.getInstance().setMode(`batch`)}catch{}e.updateProgress(`🚀 Iniciando compilación de ${h} archivos...`),await ge(d,b),e.updateProgress(`💾 Guardando cache...`),await de(),e.updateProgress(`🧹 Limpiando módulos no usados...`),S.cleanupUnusedModules();let w=g(Date.now()-n);e.endProgress(),await K(f.VERBOSE===`true`,w)}catch(e){let t=Z.getInstance();t.isActive()&&t.endProgress();let n=e instanceof Error?e.message:String(e);p.error(`🚩 Error al compilar todos los archivos: ${n}`),await U(e instanceof Error?e:Error(String(e)),`compilación general`,`all`,`all`,f.VERBOSE===`true`),await K(f.VERBOSE===`true`)}}function _e(e){return{vue:`🎨`,typescript:`📘`,standardization:`💛`,minification:`🗜️`,tailwind:`🎨`,"file-read":`📖`,default:`⚙️`}[e]??`⚙️`}function $(e,t){let n=Math.round(e/100*t),r=t-n;return`${`\x1B[32m`+`█`.repeat(n)+`\x1B[0m`}${`\x1B[90m`+`░`.repeat(r)+`\x1B[0m`} ${e}%`}export async function compileFile(e){return await _(initCompile(e,!0,`individual`),6e4,`compilación de ${c.basename(e)}`)}export{Y as WatchModeOptimizer};
1
+ import { createHash } from 'node:crypto';
2
+ import { glob, mkdir, readFile, stat, unlink, writeFile, } from 'node:fs/promises';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ import * as process from 'node:process';
6
+ const { argv, cwd, env } = process;
7
+ // Lazy loading optimizations - Only import lightweight modules synchronously
8
+ import { logger, setProgressManagerGetter } from '../servicios/logger.js';
9
+ import { promptUser } from '../utils/promptUser.js';
10
+ import { showTimingForHumans } from '../utils/utils.js';
11
+ // Configurar el getter del ProgressManager para el logger
12
+ setProgressManagerGetter(() => ProgressManager.getInstance());
13
+ /**
14
+ * ✨ FIX #5: Wrapper con timeout para operaciones críticas
15
+ * Evita que promesas colgadas bloqueen la compilación indefinidamente
16
+ */
17
+ async function withTimeout(promise, timeoutMs, operationName) {
18
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error(`Timeout en ${operationName} (${timeoutMs}ms)`)), timeoutMs));
19
+ return Promise.race([promise, timeoutPromise]);
20
+ }
21
+ // Heavy dependencies will be loaded dynamically when needed
22
+ let chalk;
23
+ let ESLint;
24
+ let OxLint;
25
+ let minifyJS;
26
+ let getCodeFile;
27
+ let generateTailwindCSS;
28
+ let estandarizaCode;
29
+ let preCompileTS;
30
+ let preCompileVue;
31
+ // 🚀 Importar optimizador de transformaciones
32
+ let TransformOptimizer;
33
+ // 🚀 Importar optimizador de resolución de módulos
34
+ let ModuleResolutionOptimizer;
35
+ // 🚀 Sistema de Carga Inteligente de Módulos - VERSIÓN OPTIMIZADA V2
36
+ class OptimizedModuleManager {
37
+ static instance;
38
+ isInitialized = false;
39
+ loadedModules = new Set();
40
+ // ✨ NUEVAS OPTIMIZACIONES
41
+ modulePool = new Map(); // Pool de instancias reutilizables
42
+ loadingPromises = new Map(); // Prevenir cargas duplicadas
43
+ usageStats = new Map(); // Estadísticas de uso
44
+ preloadQueue = new Set(); // Cola de precarga
45
+ backgroundLoader = null; // Cargador en background
46
+ preloadLock = null; // Lock para evitar precargas concurrentes
47
+ // ✨ FIX #4: Límites estrictos de memoria para el pool
48
+ MAX_POOL_MEMORY = 100 * 1024 * 1024; // 100MB límite total
49
+ MAX_POOL_SIZE = 15; // Máximo 15 módulos en pool
50
+ // Módulos críticos que siempre se precargan
51
+ HOT_MODULES = ['chalk', 'parser'];
52
+ // Contexto actual para optimizar cargas
53
+ currentContext = null;
54
+ constructor() {
55
+ // Iniciar precarga en background inmediatamente
56
+ this.startBackgroundPreloading();
57
+ }
58
+ static getInstance() {
59
+ if (!OptimizedModuleManager.instance) {
60
+ OptimizedModuleManager.instance = new OptimizedModuleManager();
61
+ }
62
+ return OptimizedModuleManager.instance;
63
+ }
64
+ /**
65
+ * ✨ NUEVO: Precarga en background para módulos críticos
66
+ */
67
+ startBackgroundPreloading() {
68
+ this.backgroundLoader = this.preloadCriticalModules();
69
+ }
70
+ /**
71
+ * ✨ NUEVO: Precarga módulos críticos en background
72
+ */
73
+ async preloadCriticalModules() {
74
+ try {
75
+ // Precargar módulos críticos de forma asíncrona
76
+ const preloadPromises = this.HOT_MODULES.map(moduleName => this.ensureModuleLoaded(moduleName).catch(() => {
77
+ // Silenciar errores de precarga, se intentará cargar después
78
+ }));
79
+ await Promise.allSettled(preloadPromises);
80
+ }
81
+ catch {
82
+ // Fallos en precarga no deben afectar la funcionalidad principal
83
+ }
84
+ }
85
+ /**
86
+ * ✨ MEJORADO: Precarga contextual basada en tipos de archivo con lock para prevenir cargas concurrentes
87
+ */
88
+ async preloadForContext(context, fileTypes = new Set()) {
89
+ // Si ya hay una precarga en progreso, esperar a que termine
90
+ if (this.preloadLock) {
91
+ await this.preloadLock;
92
+ return;
93
+ }
94
+ // Crear el lock
95
+ this.preloadLock = this.doPreload(context, fileTypes);
96
+ try {
97
+ await this.preloadLock;
98
+ }
99
+ finally {
100
+ this.preloadLock = null;
101
+ }
102
+ }
103
+ /**
104
+ * ✨ Método interno de precarga
105
+ */
106
+ async doPreload(context, fileTypes = new Set()) {
107
+ this.currentContext = context;
108
+ // Esperar que termine la precarga crítica si está en progreso
109
+ if (this.backgroundLoader) {
110
+ await this.backgroundLoader;
111
+ }
112
+ const toPreload = []; // Precarga basada en contexto
113
+ if (context === 'batch' || context === 'watch') {
114
+ // En batch/watch, precargar todos los módulos comunes
115
+ toPreload.push('transforms', 'vue', 'typescript', 'module-resolution-optimizer');
116
+ }
117
+ else {
118
+ // En individual, cargar solo según tipos de archivo detectados
119
+ if (fileTypes.has('.vue'))
120
+ toPreload.push('vue');
121
+ if (fileTypes.has('.ts') || fileTypes.has('.vue'))
122
+ toPreload.push('typescript');
123
+ if (!this.loadedModules.has('transforms'))
124
+ toPreload.push('transforms');
125
+ }
126
+ // ✨ OPTIMIZACIÓN #10: Agrupar módulos compatibles y cargarlos en paralelo
127
+ // Grupos de módulos que NO comparten dependencias nativas problemáticas
128
+ const moduleGroups = [
129
+ ['chalk', 'parser'], // Grupo 1: Módulos ligeros sin node:crypto
130
+ ['transforms'], // Grupo 2: Puede usar node:crypto pero independiente
131
+ ['vue', 'typescript'], // Grupo 3: Comparten configuración
132
+ ['module-resolution-optimizer'], // Grupo 4: Independiente
133
+ ['minify'], // Grupo 5: Independiente
134
+ ];
135
+ // Cargar cada grupo en paralelo, pero grupos secuencialmente
136
+ for (const group of moduleGroups) {
137
+ const modulesToLoad = group.filter(name => toPreload.includes(name));
138
+ if (modulesToLoad.length > 0) {
139
+ // Cargar módulos del grupo en paralelo
140
+ await Promise.allSettled(modulesToLoad.map(async (moduleName) => {
141
+ try {
142
+ await this.ensureModuleLoaded(moduleName);
143
+ }
144
+ catch (error) {
145
+ // Silenciar errores de precarga - los módulos se cargarán bajo demanda
146
+ if (env.VERBOSE === 'true') {
147
+ const errorMessage = error instanceof Error
148
+ ? error.message
149
+ : String(error);
150
+ console.warn(`[Verbose] Warning: No se pudo precargar módulo ${moduleName}:`, errorMessage);
151
+ }
152
+ }
153
+ }));
154
+ }
155
+ }
156
+ }
157
+ /**
158
+ * ✨ MEJORADO: Carga inteligente con pooling y deduplicación
159
+ */
160
+ async ensureModuleLoaded(moduleName) {
161
+ // 1. Verificar pool de módulos primero
162
+ if (this.modulePool.has(moduleName)) {
163
+ this.updateUsageStats(moduleName);
164
+ return this.modulePool.get(moduleName);
165
+ }
166
+ // 2. Verificar si ya está cargando (deduplicación)
167
+ if (this.loadingPromises.has(moduleName)) {
168
+ return this.loadingPromises.get(moduleName);
169
+ }
170
+ // 3. Iniciar carga
171
+ const loadPromise = this.loadModuleInternal(moduleName);
172
+ this.loadingPromises.set(moduleName, loadPromise);
173
+ try {
174
+ const moduleInstance = await loadPromise;
175
+ // 4. Almacenar en pool y estadísticas
176
+ this.modulePool.set(moduleName, moduleInstance);
177
+ this.loadedModules.add(moduleName);
178
+ this.updateUsageStats(moduleName);
179
+ return moduleInstance;
180
+ }
181
+ finally {
182
+ // 5. Limpiar promesa de carga
183
+ this.loadingPromises.delete(moduleName);
184
+ }
185
+ }
186
+ /**
187
+ * ✨ NUEVO: Actualiza estadísticas de uso para optimizaciones futuras
188
+ */
189
+ updateUsageStats(moduleName) {
190
+ const currentCount = this.usageStats.get(moduleName) || 0;
191
+ this.usageStats.set(moduleName, currentCount + 1);
192
+ }
193
+ /**
194
+ * ✨ MEJORADO: Carga interna de módulos con mejor manejo de errores
195
+ */ async loadModuleInternal(moduleName) {
196
+ switch (moduleName) {
197
+ case 'chalk':
198
+ return this.loadChalk();
199
+ case 'parser':
200
+ return this.loadParser();
201
+ case 'transforms':
202
+ return this.loadTransforms();
203
+ case 'vue':
204
+ return this.loadVue();
205
+ case 'typescript':
206
+ return this.loadTypeScript();
207
+ case 'minify':
208
+ return this.loadMinify();
209
+ case 'tailwind':
210
+ return this.loadTailwind();
211
+ case 'linter':
212
+ return this.loadLinter();
213
+ case 'transform-optimizer':
214
+ return this.loadTransformOptimizer();
215
+ case 'module-resolution-optimizer':
216
+ return this.loadModuleResolutionOptimizer();
217
+ default:
218
+ throw new Error(`Módulo desconocido: ${moduleName}`);
219
+ }
220
+ }
221
+ // ✨ Métodos de carga específicos optimizados
222
+ async loadChalk() {
223
+ if (!chalk) {
224
+ chalk = (await import('chalk')).default;
225
+ }
226
+ return chalk;
227
+ }
228
+ async loadParser() {
229
+ if (!getCodeFile) {
230
+ const parserModule = await import('./parser.js');
231
+ getCodeFile = parserModule.getCodeFile;
232
+ }
233
+ return getCodeFile;
234
+ }
235
+ async loadTransforms() {
236
+ if (!estandarizaCode) {
237
+ const transformsModule = await import('./transforms.js');
238
+ estandarizaCode = transformsModule.estandarizaCode;
239
+ }
240
+ return estandarizaCode;
241
+ }
242
+ async loadVue() {
243
+ if (!preCompileVue) {
244
+ const vueModule = await import('./vuejs.js');
245
+ preCompileVue = vueModule.preCompileVue;
246
+ }
247
+ return preCompileVue;
248
+ }
249
+ async loadTypeScript() {
250
+ if (!preCompileTS) {
251
+ const typescriptModule = await import('./typescript-manager.js');
252
+ preCompileTS = typescriptModule.preCompileTS;
253
+ }
254
+ return preCompileTS;
255
+ }
256
+ async loadMinify() {
257
+ if (!minifyJS) {
258
+ const minifyModule = await import('./minify.js');
259
+ // ✨ Usar minifyWithTemplates para minificar templates HTML ANTES del JS
260
+ minifyJS = minifyModule.minifyWithTemplates;
261
+ }
262
+ return minifyJS;
263
+ }
264
+ async loadTailwind() {
265
+ if (!generateTailwindCSS) {
266
+ const tailwindModule = await import('./tailwindcss.js');
267
+ generateTailwindCSS = tailwindModule.generateTailwindCSS;
268
+ }
269
+ return generateTailwindCSS;
270
+ }
271
+ async loadLinter() {
272
+ if (!ESLint || !OxLint) {
273
+ const linterModule = await import('./linter.js');
274
+ ESLint = linterModule.ESLint;
275
+ OxLint = linterModule.OxLint;
276
+ }
277
+ return { ESLint, OxLint };
278
+ }
279
+ async loadTransformOptimizer() {
280
+ if (!TransformOptimizer) {
281
+ const transformModule = await import('./transform-optimizer.js');
282
+ TransformOptimizer =
283
+ transformModule.TransformOptimizer.getInstance();
284
+ }
285
+ return TransformOptimizer;
286
+ }
287
+ async loadModuleResolutionOptimizer() {
288
+ if (!ModuleResolutionOptimizer) {
289
+ const resolutionModule = await import('./module-resolution-optimizer.js');
290
+ ModuleResolutionOptimizer =
291
+ resolutionModule.ModuleResolutionOptimizer.getInstance();
292
+ }
293
+ return ModuleResolutionOptimizer;
294
+ }
295
+ /**
296
+ * ✨ NUEVO: Obtiene estadísticas de performance del manager
297
+ */
298
+ getPerformanceStats() {
299
+ const sortedByUsage = Array.from(this.usageStats.entries())
300
+ .sort((a, b) => b[1] - a[1])
301
+ .slice(0, 5)
302
+ .map(([name]) => name);
303
+ return {
304
+ loadedModules: Array.from(this.loadedModules),
305
+ usageStats: Object.fromEntries(this.usageStats),
306
+ poolSize: this.modulePool.size,
307
+ loadingInProgress: Array.from(this.loadingPromises.keys()),
308
+ mostUsedModules: sortedByUsage,
309
+ };
310
+ }
311
+ /**
312
+ * ✨ FIX #4: Estima el tamaño en memoria de un módulo
313
+ */
314
+ estimateModuleSize(moduleName) {
315
+ // Estimaciones basadas en tipos de módulo
316
+ const sizeMap = {
317
+ 'transform-optimizer': 5 * 1024 * 1024, // 5MB
318
+ typescript: 10 * 1024 * 1024, // 10MB
319
+ vue: 8 * 1024 * 1024, // 8MB
320
+ 'module-resolution-optimizer': 3 * 1024 * 1024, // 3MB
321
+ transforms: 2 * 1024 * 1024, // 2MB
322
+ minify: 2 * 1024 * 1024, // 2MB
323
+ linter: 1 * 1024 * 1024, // 1MB
324
+ parser: 500 * 1024, // 500KB
325
+ chalk: 100 * 1024, // 100KB
326
+ default: 500 * 1024, // 500KB por defecto
327
+ };
328
+ return sizeMap[moduleName] ?? sizeMap.default;
329
+ }
330
+ /**
331
+ * ✨ FIX #4: Obtiene el uso total de memoria del pool
332
+ */
333
+ getPoolMemoryUsage() {
334
+ let totalSize = 0;
335
+ for (const moduleName of this.modulePool.keys()) {
336
+ totalSize += this.estimateModuleSize(moduleName);
337
+ }
338
+ return totalSize;
339
+ }
340
+ /**
341
+ * ✨ FIX #4: Limpia módulos no utilizados con control de memoria LRU
342
+ */
343
+ cleanupUnusedModules() {
344
+ const currentMemory = this.getPoolMemoryUsage();
345
+ const currentSize = this.modulePool.size;
346
+ // Limpiar si excedemos límites de memoria o tamaño
347
+ if (currentMemory > this.MAX_POOL_MEMORY ||
348
+ currentSize > this.MAX_POOL_SIZE) {
349
+ // LRU: Ordenar por menos usado
350
+ const sortedModules = Array.from(this.usageStats.entries())
351
+ .sort((a, b) => a[1] - b[1]) // Ascendente por uso
352
+ .filter(([name]) => !this.HOT_MODULES.includes(name)); // No eliminar HOT_MODULES
353
+ // Eliminar módulos hasta estar por debajo del 70% del límite
354
+ const targetMemory = this.MAX_POOL_MEMORY * 0.7;
355
+ const targetSize = this.MAX_POOL_SIZE * 0.7;
356
+ for (const [moduleName] of sortedModules) {
357
+ this.modulePool.delete(moduleName);
358
+ this.loadedModules.delete(moduleName);
359
+ this.usageStats.delete(moduleName);
360
+ const newMemory = this.getPoolMemoryUsage();
361
+ const newSize = this.modulePool.size;
362
+ if (newMemory <= targetMemory && newSize <= targetSize) {
363
+ break;
364
+ }
365
+ }
366
+ if (env.VERBOSE === 'true') {
367
+ console.log(`[ModuleManager] Limpieza: ${currentSize} → ${this.modulePool.size} módulos, ` +
368
+ `${Math.round(currentMemory / 1024 / 1024)}MB → ${Math.round(this.getPoolMemoryUsage() / 1024 / 1024)}MB`);
369
+ }
370
+ }
371
+ }
372
+ /**
373
+ * Resetea el estado del manager (útil para tests)
374
+ */
375
+ reset() {
376
+ this.isInitialized = false;
377
+ this.loadedModules.clear();
378
+ this.modulePool.clear();
379
+ this.loadingPromises.clear();
380
+ this.usageStats.clear();
381
+ this.preloadQueue.clear();
382
+ this.currentContext = null;
383
+ this.backgroundLoader = null;
384
+ // Reiniciar precarga crítica
385
+ this.startBackgroundPreloading();
386
+ }
387
+ }
388
+ // Lazy loading helper functions
389
+ async function loadChalk() {
390
+ if (!chalk) {
391
+ chalk = (await import('chalk')).default;
392
+ }
393
+ return chalk;
394
+ }
395
+ async function loadLinter() {
396
+ if (!ESLint || !OxLint) {
397
+ const linterModule = await import('./linter.js');
398
+ ESLint = linterModule.ESLint;
399
+ OxLint = linterModule.OxLint;
400
+ }
401
+ return { ESLint, OxLint };
402
+ }
403
+ async function loadMinify() {
404
+ if (!minifyJS) {
405
+ const minifyModule = await import('./minify.js');
406
+ // ✨ Usar minifyWithTemplates para minificar templates HTML ANTES del JS
407
+ minifyJS = minifyModule.minifyWithTemplates;
408
+ }
409
+ return minifyJS;
410
+ }
411
+ async function loadParser() {
412
+ if (!getCodeFile) {
413
+ const parserModule = await import('./parser.js');
414
+ getCodeFile = parserModule.getCodeFile;
415
+ }
416
+ return getCodeFile;
417
+ }
418
+ async function loadTailwind() {
419
+ if (!generateTailwindCSS) {
420
+ const tailwindModule = await import('./tailwindcss.js');
421
+ generateTailwindCSS = tailwindModule.generateTailwindCSS;
422
+ }
423
+ return generateTailwindCSS;
424
+ }
425
+ async function loadTransforms() {
426
+ if (!estandarizaCode) {
427
+ const transformsModule = await import('./transforms.js');
428
+ estandarizaCode = transformsModule.estandarizaCode;
429
+ }
430
+ return estandarizaCode;
431
+ }
432
+ async function loadTypeScript() {
433
+ if (!preCompileTS) {
434
+ const typescriptModule = await import('./typescript-manager.js');
435
+ preCompileTS = typescriptModule.preCompileTS;
436
+ }
437
+ return preCompileTS;
438
+ }
439
+ async function loadVue() {
440
+ if (!preCompileVue) {
441
+ const vueModule = await import('./vuejs.js');
442
+ preCompileVue = vueModule.preCompileVue;
443
+ }
444
+ return preCompileVue;
445
+ }
446
+ // Almacenamiento global de errores y resultados
447
+ const compilationErrors = [];
448
+ const compilationResults = [];
449
+ // Variables de entorno relevantes para compilación
450
+ const COMPILATION_ENV_VARS = [
451
+ 'NODE_ENV',
452
+ 'isPROD',
453
+ 'TAILWIND',
454
+ 'ENABLE_LINTER',
455
+ 'VERBOSE',
456
+ 'typeCheck',
457
+ 'PATH_ALIAS',
458
+ 'tailwindcss',
459
+ 'linter',
460
+ 'tsconfigFile',
461
+ ];
462
+ class SmartCompilationCache {
463
+ cache = new Map();
464
+ maxEntries = 200; // Reducido para tests de estrés
465
+ maxMemory = 50 * 1024 * 1024; // 50MB límite (reducido)
466
+ currentMemoryUsage = 0;
467
+ // ✨ ISSUE #3: Sistema de vigilancia de dependencias
468
+ fileWatchers = new Map(); // chokidar watchers
469
+ dependencyGraph = new Map(); // archivo -> dependencias
470
+ reverseDependencyGraph = new Map(); // dependencia -> archivos que la usan
471
+ packageJsonPath = path.join(cwd(), 'package.json');
472
+ nodeModulesPath = path.join(cwd(), 'node_modules');
473
+ isWatchingDependencies = false;
474
+ /**
475
+ * Genera hash SHA-256 del contenido del archivo
476
+ */ async generateContentHash(filePath) {
477
+ try {
478
+ const content = await readFile(filePath, 'utf8');
479
+ return createHash('sha256').update(content).digest('hex');
480
+ }
481
+ catch {
482
+ // Si no se puede leer el archivo, generar hash único basado en la ruta y timestamp
483
+ const fallback = `${filePath}-${Date.now()}`;
484
+ return createHash('sha256').update(fallback).digest('hex');
485
+ }
486
+ }
487
+ /**
488
+ * Genera hash de la configuración del compilador
489
+ */
490
+ generateConfigHash() {
491
+ try {
492
+ // Recopilar configuración relevante de variables de entorno
493
+ const config = {
494
+ isPROD: env.isPROD || 'false',
495
+ TAILWIND: env.TAILWIND || 'false',
496
+ ENABLE_LINTER: env.ENABLE_LINTER || 'false',
497
+ PATH_ALIAS: env.PATH_ALIAS || '{}',
498
+ tailwindcss: env.tailwindcss || 'false',
499
+ linter: env.linter || 'false',
500
+ tsconfigFile: env.tsconfigFile || './tsconfig.json',
501
+ };
502
+ const configStr = JSON.stringify(config, Object.keys(config).sort());
503
+ return createHash('sha256')
504
+ .update(configStr)
505
+ .digest('hex')
506
+ .substring(0, 12);
507
+ }
508
+ catch {
509
+ return 'no-config';
510
+ }
511
+ }
512
+ /**
513
+ * Genera hash de variables de entorno relevantes
514
+ */
515
+ generateEnvHash() {
516
+ try {
517
+ const envVars = COMPILATION_ENV_VARS.map(key => `${key}=${env[key] || ''}`).join('|');
518
+ return createHash('sha256')
519
+ .update(envVars)
520
+ .digest('hex')
521
+ .substring(0, 12);
522
+ }
523
+ catch {
524
+ return 'no-env';
525
+ }
526
+ } /**
527
+ * ✨ ISSUE #3: Genera hash avanzado de dependencias del proyecto
528
+ * Incluye vigilancia de package.json, node_modules y versiones instaladas
529
+ */
530
+ async generateDependencyHash() {
531
+ try {
532
+ const hash = createHash('sha256');
533
+ // 1. Hash del package.json con versiones
534
+ const packagePath = path.join(cwd(), 'package.json');
535
+ const packageContent = await readFile(packagePath, 'utf8');
536
+ const pkg = JSON.parse(packageContent);
537
+ const deps = {
538
+ ...pkg.dependencies,
539
+ ...pkg.devDependencies,
540
+ };
541
+ const depsStr = JSON.stringify(deps, Object.keys(deps).sort());
542
+ hash.update(`package:${depsStr}`);
543
+ // 2. Hash del package-lock.json si existe (versiones exactas instaladas)
544
+ try {
545
+ const lockPath = path.join(cwd(), 'package-lock.json');
546
+ const lockContent = await readFile(lockPath, 'utf8');
547
+ const lockData = JSON.parse(lockContent);
548
+ // Solo incluir las versiones instaladas, no todo el lockfile
549
+ const installedVersions = {};
550
+ if (lockData.packages) {
551
+ for (const [pkgPath, pkgInfo] of Object.entries(lockData.packages)) {
552
+ if (pkgPath &&
553
+ pkgPath !== '' &&
554
+ typeof pkgInfo === 'object' &&
555
+ pkgInfo !== null) {
556
+ const pkgName = pkgPath.replace('node_modules/', '');
557
+ if (pkgInfo.version) {
558
+ installedVersions[pkgName] = pkgInfo.version;
559
+ }
560
+ }
561
+ }
562
+ }
563
+ hash.update(`lock:${JSON.stringify(installedVersions, Object.keys(installedVersions).sort())}`);
564
+ }
565
+ catch {
566
+ // Ignorar si no existe package-lock.json
567
+ }
568
+ // 3. ✨ NUEVO: Hash de timestamps críticos de node_modules
569
+ try {
570
+ const nodeModulesPath = path.join(cwd(), 'node_modules');
571
+ const nodeModulesStat = await stat(nodeModulesPath);
572
+ hash.update(`nmtime:${nodeModulesStat.mtimeMs}`);
573
+ // Verificar timestamps de dependencias críticas instaladas
574
+ const criticalDeps = Object.keys(deps).slice(0, 10); // Top 10 para performance
575
+ for (const dep of criticalDeps) {
576
+ try {
577
+ const depPath = path.join(nodeModulesPath, dep);
578
+ const depStat = await stat(depPath);
579
+ hash.update(`${dep}:${depStat.mtimeMs}`);
580
+ }
581
+ catch {
582
+ // Dependencia no instalada o error
583
+ hash.update(`${dep}:missing`);
584
+ }
585
+ }
586
+ }
587
+ catch {
588
+ // node_modules no existe
589
+ hash.update('nmtime:none');
590
+ }
591
+ return hash.digest('hex').substring(0, 16);
592
+ }
593
+ catch (error) {
594
+ // Incluir información del error en el hash para debugging
595
+ return createHash('sha256')
596
+ .update(`error:${error instanceof Error ? error.message : 'unknown'}`)
597
+ .digest('hex')
598
+ .substring(0, 16);
599
+ }
600
+ }
601
+ /**
602
+ * Genera clave de cache granular que incluye todos los factores
603
+ */
604
+ async generateCacheKey(filePath) {
605
+ const contentHash = await this.generateContentHash(filePath);
606
+ const configHash = this.generateConfigHash();
607
+ const envHash = this.generateEnvHash();
608
+ const dependencyHash = await this.generateDependencyHash();
609
+ // Usar | como separador para evitar problemas con rutas de Windows
610
+ return `${filePath}|${contentHash.substring(0, 12)}|${configHash}|${envHash}|${dependencyHash}`;
611
+ } /**
612
+ * Verifica si una entrada de cache es válida
613
+ */
614
+ async isValid(filePath) {
615
+ const entry = this.cache.get(filePath);
616
+ if (!entry)
617
+ return false;
618
+ try {
619
+ // Verificar si el archivo de salida existe
620
+ await stat(entry.outputPath);
621
+ // Verificar si el contenido ha cambiado
622
+ const currentContentHash = await this.generateContentHash(filePath);
623
+ if (entry.contentHash !== currentContentHash) {
624
+ this.cache.delete(filePath);
625
+ return false;
626
+ }
627
+ // Verificar si la configuración ha cambiado
628
+ const currentConfigHash = this.generateConfigHash();
629
+ if (entry.configHash !== currentConfigHash) {
630
+ this.cache.delete(filePath);
631
+ return false;
632
+ }
633
+ // Verificar si las variables de entorno han cambiado
634
+ const currentEnvHash = this.generateEnvHash();
635
+ if (entry.envHash !== currentEnvHash) {
636
+ this.cache.delete(filePath);
637
+ return false;
638
+ }
639
+ // Verificar si las dependencias han cambiado
640
+ const currentDependencyHash = await this.generateDependencyHash();
641
+ if (entry.dependencyHash !== currentDependencyHash) {
642
+ this.cache.delete(filePath);
643
+ return false;
644
+ }
645
+ // Verificar tiempo de modificación como backup
646
+ const stats = await stat(filePath);
647
+ if (stats.mtimeMs > entry.mtime) {
648
+ this.cache.delete(filePath);
649
+ return false;
650
+ }
651
+ // Actualizar tiempo de uso para LRU
652
+ entry.lastUsed = Date.now();
653
+ return true;
654
+ }
655
+ catch {
656
+ // Si hay error verificando, eliminar del cache
657
+ this.cache.delete(filePath);
658
+ return false;
659
+ }
660
+ } /**
661
+ * Añade una entrada al cache
662
+ */
663
+ async set(filePath, outputPath) {
664
+ try {
665
+ const stats = await stat(filePath);
666
+ const contentHash = await this.generateContentHash(filePath);
667
+ const configHash = this.generateConfigHash();
668
+ const envHash = this.generateEnvHash();
669
+ const dependencyHash = await this.generateDependencyHash();
670
+ const entry = {
671
+ contentHash,
672
+ configHash,
673
+ envHash,
674
+ dependencyHash,
675
+ mtime: stats.mtimeMs,
676
+ outputPath,
677
+ lastUsed: Date.now(),
678
+ size: stats.size,
679
+ };
680
+ // Aplicar límites de memoria y entradas antes de agregar
681
+ this.evictIfNeeded(entry.size);
682
+ this.cache.set(filePath, entry);
683
+ this.currentMemoryUsage += entry.size;
684
+ }
685
+ catch (error) {
686
+ // Si hay error, no cachear
687
+ console.warn(`Warning: No se pudo cachear ${filePath}:`, error);
688
+ }
689
+ } /**
690
+ * Aplica política LRU para liberar espacio
691
+ */
692
+ evictIfNeeded(newEntrySize) {
693
+ // Verificar límite de entradas más agresivamente
694
+ while (this.cache.size >= this.maxEntries * 0.8) {
695
+ // Limpiar cuando llegue al 80%
696
+ this.evictLRU();
697
+ }
698
+ // Verificar límite de memoria más agresivamente
699
+ while (this.currentMemoryUsage + newEntrySize > this.maxMemory * 0.8 && // Limpiar cuando llegue al 80%
700
+ this.cache.size > 0) {
701
+ this.evictLRU();
702
+ }
703
+ // Eviction adicional si la memoria total del proceso es alta
704
+ const memUsage = process.memoryUsage();
705
+ const heapUsedMB = memUsage.heapUsed / (1024 * 1024);
706
+ if (heapUsedMB > 200 && this.cache.size > 50) {
707
+ // Si heap > 200MB, limpiar más agresivamente
708
+ const entriesToRemove = Math.min(this.cache.size - 50, 10);
709
+ for (let i = 0; i < entriesToRemove; i++) {
710
+ this.evictLRU();
711
+ }
712
+ }
713
+ } /**
714
+ * Elimina la entrada menos recientemente usada
715
+ */
716
+ evictLRU() {
717
+ let oldestKey = '';
718
+ let oldestTime = Infinity;
719
+ for (const [key, entry] of this.cache) {
720
+ if (entry.lastUsed < oldestTime) {
721
+ oldestTime = entry.lastUsed;
722
+ oldestKey = key;
723
+ }
724
+ }
725
+ if (oldestKey) {
726
+ const entry = this.cache.get(oldestKey);
727
+ if (entry) {
728
+ this.currentMemoryUsage -= entry.size;
729
+ this.cache.delete(oldestKey);
730
+ }
731
+ }
732
+ }
733
+ /**
734
+ * Método público para limpiar entradas del cache cuando sea necesario
735
+ */
736
+ cleanOldEntries(maxEntriesToRemove = 20) {
737
+ let removedCount = 0;
738
+ for (let i = 0; i < maxEntriesToRemove && this.cache.size > 0; i++) {
739
+ const sizeBefore = this.cache.size;
740
+ this.evictLRU();
741
+ if (this.cache.size < sizeBefore) {
742
+ removedCount++;
743
+ }
744
+ else {
745
+ break; // No se pudo remover más entradas
746
+ }
747
+ }
748
+ return removedCount;
749
+ }
750
+ /**
751
+ * Carga el cache desde disco
752
+ */
753
+ async load(cacheFile) {
754
+ try {
755
+ if (env.cleanCache === 'true') {
756
+ this.cache.clear();
757
+ this.currentMemoryUsage = 0;
758
+ try {
759
+ await unlink(cacheFile);
760
+ }
761
+ catch {
762
+ // Ignorar errores al eliminar el archivo
763
+ }
764
+ return;
765
+ }
766
+ const cacheData = await readFile(cacheFile, 'utf-8');
767
+ const parsed = JSON.parse(cacheData);
768
+ // Validar y cargar entradas del cache
769
+ for (const [key, value] of Object.entries(parsed)) {
770
+ const entry = value;
771
+ if (entry.contentHash && entry.outputPath && entry.mtime) {
772
+ this.cache.set(key, entry);
773
+ this.currentMemoryUsage += entry.size || 0;
774
+ }
775
+ }
776
+ }
777
+ catch {
778
+ // Cache file doesn't exist or is invalid, start fresh
779
+ this.cache.clear();
780
+ this.currentMemoryUsage = 0;
781
+ }
782
+ }
783
+ /**
784
+ * Guarda el cache a disco
785
+ */
786
+ async save(cacheFile, cacheDir) {
787
+ try {
788
+ await mkdir(cacheDir, { recursive: true });
789
+ const cacheData = Object.fromEntries(this.cache);
790
+ await writeFile(cacheFile, JSON.stringify(cacheData, null, 2));
791
+ }
792
+ catch (error) {
793
+ console.warn('Warning: No se pudo guardar el cache:', error);
794
+ }
795
+ }
796
+ /**
797
+ * Limpia completamente el cache
798
+ */
799
+ clear() {
800
+ this.cache.clear();
801
+ this.currentMemoryUsage = 0;
802
+ } /**
803
+ * Obtiene la ruta de salida para un archivo cacheado
804
+ */
805
+ getOutputPath(filePath) {
806
+ const entry = this.cache.get(filePath);
807
+ return entry?.outputPath || '';
808
+ } /**
809
+ * Obtiene estadísticas del cache
810
+ */
811
+ getStats() {
812
+ return {
813
+ entries: this.cache.size,
814
+ memoryUsage: this.currentMemoryUsage,
815
+ hitRate: 0, // Se calculará externamente
816
+ };
817
+ }
818
+ // ✨ ISSUE #3: Métodos de vigilancia y invalidación cascada
819
+ /**
820
+ * Inicializa vigilancia de package.json y node_modules
821
+ */
822
+ async startDependencyWatching() {
823
+ if (this.isWatchingDependencies)
824
+ return;
825
+ try {
826
+ // Lazy load chokidar para evitar problemas de importación
827
+ const chokidar = await import('chokidar');
828
+ // Vigilar package.json
829
+ if (await this.fileExists(this.packageJsonPath)) {
830
+ const packageWatcher = chokidar.watch(this.packageJsonPath, {
831
+ persistent: false, // No mantener el proceso vivo
832
+ ignoreInitial: true,
833
+ });
834
+ packageWatcher.on('change', () => {
835
+ logger.info('📦 package.json modificado - invalidando cache de dependencias');
836
+ this.invalidateByDependencyChange();
837
+ });
838
+ this.fileWatchers.set('package.json', packageWatcher);
839
+ }
840
+ // Vigilar node_modules (solo cambios en el directorio raíz para performance)
841
+ if (await this.fileExists(this.nodeModulesPath)) {
842
+ const nodeModulesWatcher = chokidar.watch(this.nodeModulesPath, {
843
+ persistent: false,
844
+ ignoreInitial: true,
845
+ depth: 1, // Solo primer nivel para performance
846
+ ignored: /(^|[/\\])\../, // Ignorar archivos ocultos
847
+ });
848
+ nodeModulesWatcher.on('addDir', (path) => {
849
+ logger.info(`📦 Nueva dependencia instalada: ${path.split(/[/\\]/).pop()}`);
850
+ this.invalidateByDependencyChange();
851
+ });
852
+ nodeModulesWatcher.on('unlinkDir', (path) => {
853
+ logger.info(`📦 Dependencia eliminada: ${path.split(/[/\\]/).pop()}`);
854
+ this.invalidateByDependencyChange();
855
+ });
856
+ this.fileWatchers.set('node_modules', nodeModulesWatcher);
857
+ }
858
+ this.isWatchingDependencies = true;
859
+ logger.info('🔍 Vigilancia de dependencias iniciada');
860
+ }
861
+ catch (error) {
862
+ logger.warn('⚠️ No se pudo iniciar vigilancia de dependencias:', error);
863
+ }
864
+ }
865
+ /**
866
+ * Detiene la vigilancia de dependencias
867
+ */
868
+ async stopDependencyWatching() {
869
+ for (const [name, watcher] of this.fileWatchers) {
870
+ try {
871
+ await watcher.close();
872
+ logger.info(`🛑 Vigilancia detenida: ${name}`);
873
+ }
874
+ catch (error) {
875
+ logger.warn(`⚠️ Error cerrando watcher ${name}:`, error);
876
+ }
877
+ }
878
+ this.fileWatchers.clear();
879
+ this.isWatchingDependencies = false;
880
+ }
881
+ /**
882
+ * Registra dependencias de un archivo para invalidación cascada
883
+ */
884
+ registerDependencies(filePath, dependencies) {
885
+ // Limpiar dependencias anteriores
886
+ const oldDeps = this.dependencyGraph.get(filePath);
887
+ if (oldDeps) {
888
+ for (const dep of oldDeps) {
889
+ const reverseDeps = this.reverseDependencyGraph.get(dep);
890
+ if (reverseDeps) {
891
+ reverseDeps.delete(filePath);
892
+ if (reverseDeps.size === 0) {
893
+ this.reverseDependencyGraph.delete(dep);
894
+ }
895
+ }
896
+ }
897
+ }
898
+ // Registrar nuevas dependencias
899
+ const newDeps = new Set(dependencies);
900
+ this.dependencyGraph.set(filePath, newDeps);
901
+ for (const dep of newDeps) {
902
+ if (!this.reverseDependencyGraph.has(dep)) {
903
+ this.reverseDependencyGraph.set(dep, new Set());
904
+ }
905
+ this.reverseDependencyGraph.get(dep).add(filePath);
906
+ }
907
+ }
908
+ /**
909
+ * Invalida cache por cambios en dependencias
910
+ */
911
+ invalidateByDependencyChange() {
912
+ let invalidatedCount = 0;
913
+ // Invalidar todos los archivos que dependen de dependencias externas
914
+ for (const [filePath] of this.cache) {
915
+ this.cache.delete(filePath);
916
+ invalidatedCount++;
917
+ }
918
+ // Limpiar grafos de dependencias
919
+ this.dependencyGraph.clear();
920
+ this.reverseDependencyGraph.clear();
921
+ this.currentMemoryUsage = 0;
922
+ logger.info(`🗑️ Cache invalidado: ${invalidatedCount} archivos (cambio en dependencias)`);
923
+ }
924
+ /**
925
+ * Invalida cascada cuando un archivo específico cambia
926
+ */
927
+ invalidateCascade(changedFile) {
928
+ const invalidated = [];
929
+ const toInvalidate = new Set([changedFile]);
930
+ // BFS para encontrar todos los archivos afectados
931
+ const queue = [changedFile];
932
+ while (queue.length > 0) {
933
+ const current = queue.shift();
934
+ const dependents = this.reverseDependencyGraph.get(current);
935
+ if (dependents) {
936
+ for (const dependent of dependents) {
937
+ if (!toInvalidate.has(dependent)) {
938
+ toInvalidate.add(dependent);
939
+ queue.push(dependent);
940
+ }
941
+ }
942
+ }
943
+ }
944
+ // Invalidar archivos
945
+ for (const filePath of toInvalidate) {
946
+ if (this.cache.has(filePath)) {
947
+ const entry = this.cache.get(filePath);
948
+ this.currentMemoryUsage -= entry.size;
949
+ this.cache.delete(filePath);
950
+ invalidated.push(filePath);
951
+ }
952
+ }
953
+ if (invalidated.length > 0) {
954
+ logger.info(`🔄 Invalidación cascada: ${invalidated.length} archivos afectados por ${changedFile}`);
955
+ }
956
+ return invalidated;
957
+ }
958
+ /**
959
+ * Verifica si un archivo existe
960
+ */
961
+ async fileExists(filePath) {
962
+ try {
963
+ await stat(filePath);
964
+ return true;
965
+ }
966
+ catch {
967
+ return false;
968
+ }
969
+ }
970
+ /**
971
+ * Obtiene estadísticas avanzadas del cache
972
+ */
973
+ getAdvancedStats() {
974
+ return {
975
+ entries: this.cache.size,
976
+ memoryUsage: this.currentMemoryUsage,
977
+ hitRate: 0,
978
+ dependencyNodes: this.dependencyGraph.size,
979
+ watchingDependencies: this.isWatchingDependencies,
980
+ activeWatchers: this.fileWatchers.size,
981
+ };
982
+ }
983
+ }
984
+ // Instancia global del cache inteligente
985
+ const smartCache = new SmartCompilationCache();
986
+ const CACHE_DIR = path.join(path.resolve(env.PATH_PROY || cwd(), 'compiler'), '.cache');
987
+ const CACHE_FILE = path.join(CACHE_DIR, 'versacompile-cache.json');
988
+ async function loadCache() {
989
+ await smartCache.load(CACHE_FILE);
990
+ // ✨ ISSUE #3: Iniciar vigilancia de dependencias en modo watch
991
+ if (env.WATCH_MODE === 'true' ||
992
+ argv.includes('--watch') ||
993
+ argv.includes('-w')) {
994
+ await smartCache.startDependencyWatching();
995
+ }
996
+ }
997
+ async function saveCache() {
998
+ await smartCache.save(CACHE_FILE, CACHE_DIR);
999
+ }
1000
+ // 🎯 Funciones del Sistema Unificado de Manejo de Errores
1001
+ /**
1002
+ * Registra un error de compilación en el sistema unificado
1003
+ */
1004
+ function registerCompilationError(file, stage, message, severity = 'error', details, help) {
1005
+ compilationErrors.push({
1006
+ file,
1007
+ stage,
1008
+ message,
1009
+ severity,
1010
+ details,
1011
+ help,
1012
+ timestamp: Date.now(),
1013
+ });
1014
+ }
1015
+ /**
1016
+ * Registra un resultado de compilación (éxitos/errores por etapa)
1017
+ */
1018
+ function registerCompilationResult(stage, errors, success, files = []) {
1019
+ const existingResult = compilationResults.find(r => r.stage === stage);
1020
+ if (existingResult) {
1021
+ existingResult.errors += errors;
1022
+ existingResult.success += success;
1023
+ existingResult.files.push(...files);
1024
+ }
1025
+ else {
1026
+ compilationResults.push({
1027
+ stage,
1028
+ errors,
1029
+ success,
1030
+ files: [...files],
1031
+ });
1032
+ }
1033
+ }
1034
+ /**
1035
+ * Maneja errores según el modo de compilación
1036
+ */
1037
+ async function handleCompilationError(error, fileName, stage, mode, isVerbose = false) {
1038
+ const errorMessage = error instanceof Error ? error.message : error;
1039
+ const errorDetails = error instanceof Error ? error.stack : undefined;
1040
+ // Registrar el error en el sistema unificado
1041
+ registerCompilationError(fileName, stage, errorMessage, 'error', errorDetails);
1042
+ registerCompilationResult(stage, 1, 0, [fileName]); // Mostrar error inmediatamente solo en modo individual y watch
1043
+ if (mode === 'individual' || mode === 'watch') {
1044
+ const chalk = await loadChalk();
1045
+ const baseName = path.basename(fileName);
1046
+ const stageColor = await getStageColor(stage);
1047
+ if (isVerbose) {
1048
+ // Modo verbose: Mostrar error completo con contexto
1049
+ logger.error(chalk.red(`❌ Error en etapa ${stageColor(stage)} - ${baseName}:`));
1050
+ logger.error(chalk.red(errorMessage));
1051
+ if (errorDetails && (stage === 'typescript' || stage === 'vue')) {
1052
+ // Mostrar stack trace limitado para TypeScript y Vue
1053
+ const stackLines = errorDetails.split('\n').slice(0, 5);
1054
+ stackLines.forEach(line => {
1055
+ if (line.trim()) {
1056
+ logger.error(chalk.gray(` ${line.trim()}`));
1057
+ }
1058
+ });
1059
+ }
1060
+ }
1061
+ else {
1062
+ // Modo normal: Mostrar error simplificado
1063
+ const firstLine = errorMessage.split('\n')[0];
1064
+ logger.error(chalk.red(`❌ Error en ${stageColor(stage)}: ${baseName}`));
1065
+ logger.error(chalk.red(` ${firstLine}`));
1066
+ logger.info(chalk.yellow(`💡 Usa --verbose para ver detalles completos`));
1067
+ }
1068
+ }
1069
+ // En modo 'all', los errores se acumulan silenciosamente para el resumen final
1070
+ }
1071
+ /**
1072
+ * Registra un éxito de compilación
1073
+ */
1074
+ function registerCompilationSuccess(fileName, stage) {
1075
+ registerCompilationResult(stage, 0, 1, [fileName]);
1076
+ }
1077
+ /**
1078
+ * Limpia todos los errores y resultados acumulados
1079
+ */
1080
+ function clearCompilationState() {
1081
+ compilationErrors.length = 0;
1082
+ compilationResults.length = 0;
1083
+ }
1084
+ /**
1085
+ * Muestra un resumen detallado de todos los errores de compilación
1086
+ */
1087
+ async function displayCompilationSummary(isVerbose = false, totalTime) {
1088
+ const chalk = await loadChalk();
1089
+ if (compilationErrors.length === 0 && compilationResults.length === 0) {
1090
+ logger.info(chalk.green('✅ No hay errores de compilación para mostrar.'));
1091
+ if (totalTime) {
1092
+ logger.info(chalk.bold(`\n⏱️ TIEMPO TOTAL DE COMPILACIÓN: ${totalTime}`));
1093
+ }
1094
+ return;
1095
+ }
1096
+ // 🎨 Header moderno del resumen
1097
+ const summaryLine = '━'.repeat(40);
1098
+ logger.info('');
1099
+ logger.info(chalk.bold.cyan('📊 Resumen de Compilación'));
1100
+ logger.info(chalk.gray(summaryLine)); // ⏱️ Tiempo total con formato elegante
1101
+ if (totalTime) {
1102
+ logger.info(chalk.bold(`⏱️ Tiempo Total: ${chalk.green(totalTime)}`));
1103
+ logger.info('');
1104
+ } // 🔧 Estadísticas por etapa con mejor formato
1105
+ if (compilationResults.length > 0) {
1106
+ logger.info(chalk.bold.blue('🔧 Estadísticas por Etapa:'));
1107
+ for (const result of compilationResults) {
1108
+ const totalFiles = result.success + result.errors;
1109
+ const successRate = totalFiles > 0
1110
+ ? Math.round((result.success / totalFiles) * 100)
1111
+ : 0;
1112
+ // Iconos y colores dinámicos por etapa
1113
+ const stageIcon = getStageIcon(result.stage);
1114
+ const statusColor = result.errors === 0 ? chalk.green : chalk.red;
1115
+ const progressBar = createProgressBarWithPercentage(successRate, 20);
1116
+ logger.info(` ${stageIcon} ${chalk.bold(result.stage)}`);
1117
+ logger.info(` ${statusColor('●')} ${result.success}/${totalFiles} archivos ${statusColor(`(${successRate}%)`)}`);
1118
+ logger.info(` ${progressBar}`);
1119
+ if (result.errors > 0) {
1120
+ logger.info(` ${chalk.red('⚠')} ${result.errors} ${result.errors === 1 ? 'error' : 'errores'}`);
1121
+ }
1122
+ logger.info('');
1123
+ }
1124
+ }
1125
+ // Mostrar errores detallados
1126
+ if (compilationErrors.length > 0) {
1127
+ logger.info(chalk.red(`\n❌ Se encontraron ${compilationErrors.length} errores:`));
1128
+ // Agrupar errores por archivo para mejor organización
1129
+ const errorsByFile = new Map();
1130
+ compilationErrors.forEach(error => {
1131
+ if (!errorsByFile.has(error.file)) {
1132
+ errorsByFile.set(error.file, []);
1133
+ }
1134
+ errorsByFile.get(error.file).push(error);
1135
+ });
1136
+ // Mostrar errores por archivo
1137
+ let fileIndex = 1;
1138
+ for (const [filePath, fileErrors] of errorsByFile) {
1139
+ const baseName = path.basename(filePath);
1140
+ const errorCount = fileErrors.filter(e => e.severity === 'error').length;
1141
+ const warningCount = fileErrors.filter(e => e.severity === 'warning').length;
1142
+ logger.info(chalk.cyan(`\n📄 ${fileIndex}. ${baseName}`));
1143
+ logger.info(chalk.gray(` Ruta: ${filePath}`));
1144
+ logger.info(chalk.yellow(` ${errorCount} errores, ${warningCount} advertencias`));
1145
+ for (const error of fileErrors) {
1146
+ const icon = error.severity === 'error' ? '❌' : '⚠️';
1147
+ const stageColor = await getStageColor(error.stage);
1148
+ logger.info(` ${icon} [${stageColor(error.stage)}] ${error.message}`);
1149
+ if (isVerbose && error.details) {
1150
+ // En modo verbose, mostrar detalles adicionales
1151
+ const detailLines = error.details.split('\n').slice(0, 5);
1152
+ detailLines.forEach(line => {
1153
+ if (line.trim()) {
1154
+ logger.info(chalk.gray(` ${line.trim()}`));
1155
+ }
1156
+ });
1157
+ }
1158
+ if (error.help) {
1159
+ logger.info(chalk.blue(` 💡 ${error.help}`));
1160
+ }
1161
+ }
1162
+ fileIndex++;
1163
+ } // 📊 Mostrar totales finales con diseño moderno
1164
+ const totalErrors = compilationErrors.filter(e => e.severity === 'error').length;
1165
+ const totalWarnings = compilationErrors.filter(e => e.severity === 'warning').length;
1166
+ const totalFiles = errorsByFile.size;
1167
+ // Header elegante para estadísticas finales
1168
+ const statLine = '═'.repeat(50);
1169
+ logger.info('');
1170
+ logger.info(chalk.bold.cyan(statLine));
1171
+ logger.info(chalk.bold.cyan(' 📊 RESUMEN FINAL'));
1172
+ logger.info(chalk.bold.cyan(statLine));
1173
+ // Estadísticas con iconos y colores modernos
1174
+ logger.info('');
1175
+ logger.info(chalk.bold('🎯 Resultados:'));
1176
+ logger.info(` 📁 Archivos afectados: ${chalk.cyan.bold(totalFiles)}`);
1177
+ logger.info(` ${totalErrors > 0 ? chalk.red('●') : chalk.green('○')} Errores: ${totalErrors > 0 ? chalk.red.bold(totalErrors) : chalk.green.bold('0')}`);
1178
+ logger.info(` ${totalWarnings > 0 ? chalk.yellow('●') : chalk.green('○')} Advertencias: ${totalWarnings > 0 ? chalk.yellow.bold(totalWarnings) : chalk.green.bold('0')}`);
1179
+ logger.info('');
1180
+ // Estado final con diseño visual atractivo
1181
+ if (totalErrors > 0) {
1182
+ logger.info(chalk.red.bold('🚨 COMPILACIÓN COMPLETADA CON ERRORES'));
1183
+ logger.info(chalk.red(' Por favor revisa y corrige los problemas anteriores.'));
1184
+ }
1185
+ else if (totalWarnings > 0) {
1186
+ logger.info(chalk.yellow.bold('⚠️ COMPILACIÓN COMPLETADA CON ADVERTENCIAS'));
1187
+ logger.info(chalk.yellow(' Considera revisar las advertencias anteriores.'));
1188
+ }
1189
+ else {
1190
+ logger.info(chalk.green.bold('✅ COMPILACIÓN EXITOSA'));
1191
+ logger.info(chalk.green(' ¡Todos los archivos se compilaron sin problemas!'));
1192
+ }
1193
+ logger.info('');
1194
+ logger.info(chalk.bold.cyan(statLine));
1195
+ }
1196
+ else {
1197
+ // Caso exitoso sin errores
1198
+ const successLine = '═'.repeat(50);
1199
+ logger.info('');
1200
+ logger.info(chalk.bold.green(successLine));
1201
+ logger.info(chalk.bold.green(' ✨ ÉXITO'));
1202
+ logger.info(chalk.bold.green(successLine));
1203
+ logger.info('');
1204
+ logger.info(chalk.green.bold('🎉 COMPILACIÓN COMPLETADA EXITOSAMENTE'));
1205
+ logger.info(chalk.green(' ¡No se encontraron errores ni advertencias!'));
1206
+ logger.info('');
1207
+ logger.info(chalk.bold.green(successLine));
1208
+ }
1209
+ logger.info('');
1210
+ }
1211
+ /**
1212
+ * Muestra errores del linter con formato visual moderno y profesional
1213
+ */
1214
+ async function displayLinterErrors(errors) {
1215
+ const chalk = await loadChalk();
1216
+ // Agrupar errores por archivo
1217
+ const errorsByFile = new Map();
1218
+ errors.forEach(error => {
1219
+ if (!errorsByFile.has(error.file)) {
1220
+ errorsByFile.set(error.file, []);
1221
+ }
1222
+ errorsByFile.get(error.file).push(error);
1223
+ });
1224
+ const totalErrors = errors.filter(e => e.severity === 'error').length;
1225
+ const totalWarnings = errors.filter(e => e.severity === 'warning').length;
1226
+ const totalFiles = errorsByFile.size;
1227
+ // Header estilo moderno con gradiente visual
1228
+ logger.info(chalk.bold.rgb(255, 120, 120)('╭─────────────────────────────────────────────────────────────╮'));
1229
+ logger.info(chalk.bold.rgb(255, 120, 120)('│ ') +
1230
+ chalk.bold.white('🔍 LINTER REPORT') +
1231
+ chalk.bold.rgb(255, 120, 120)(' │'));
1232
+ logger.info(chalk.bold.rgb(255, 120, 120)('╰─────────────────────────────────────────────────────────────╯'));
1233
+ // Resumen con iconos profesionales
1234
+ const errorIcon = totalErrors > 0 ? chalk.red('●') : chalk.green('○');
1235
+ const warningIcon = totalWarnings > 0 ? chalk.yellow('●') : chalk.green('○');
1236
+ logger.info('');
1237
+ logger.info(chalk.bold('📊 Summary:'));
1238
+ logger.info(` ${errorIcon} ${chalk.bold(totalErrors)} ${chalk.red('errors')}`);
1239
+ logger.info(` ${warningIcon} ${chalk.bold(totalWarnings)} ${chalk.yellow('warnings')}`);
1240
+ logger.info(` 📁 ${chalk.bold(totalFiles)} ${chalk.cyan('files')}`);
1241
+ logger.info('');
1242
+ if (totalErrors === 0 && totalWarnings === 0) {
1243
+ logger.info(chalk.green.bold('✨ All checks passed! No issues found.'));
1244
+ return;
1245
+ }
1246
+ // Mostrar errores por archivo con formato elegante
1247
+ let fileIndex = 1;
1248
+ for (const [filePath, fileErrors] of errorsByFile) {
1249
+ await displayFileErrorsGroup(filePath, fileErrors, fileIndex, totalFiles);
1250
+ fileIndex++;
1251
+ if (fileIndex <= totalFiles) {
1252
+ logger.info(chalk.gray('─'.repeat(80))); // Separador entre archivos
1253
+ }
1254
+ }
1255
+ // Footer con estadísticas
1256
+ logger.info('');
1257
+ logger.info(chalk.bold.rgb(255, 120, 120)('╭─────────────────────────────────────────────────────────────╮'));
1258
+ logger.info(chalk.bold.rgb(255, 120, 120)('│ ') +
1259
+ chalk.bold.white(`Found ${totalErrors + totalWarnings} issues in ${totalFiles} files`) +
1260
+ ' '.repeat(Math.max(0, 52 -
1261
+ `Found ${totalErrors + totalWarnings} issues in ${totalFiles} files`
1262
+ .length)) +
1263
+ chalk.bold.rgb(255, 120, 120)(' │'));
1264
+ logger.info(chalk.bold.rgb(255, 120, 120)('╰─────────────────────────────────────────────────────────────╯'));
1265
+ }
1266
+ /**
1267
+ * Muestra un grupo de errores para un archivo específico con formato moderno
1268
+ */
1269
+ async function displayFileErrorsGroup(filePath, fileErrors, _fileIndex, _totalFiles) {
1270
+ const chalk = await loadChalk();
1271
+ // Header del archivo con iconos de estado
1272
+ const errorCount = fileErrors.filter(e => e.severity === 'error').length;
1273
+ const warningCount = fileErrors.filter(e => e.severity === 'warning').length;
1274
+ const statusIcon = errorCount > 0 ? chalk.red('✕') : chalk.yellow('⚠');
1275
+ const fileIcon = filePath.endsWith('.vue')
1276
+ ? '🎨'
1277
+ : filePath.endsWith('.ts')
1278
+ ? '📘'
1279
+ : filePath.endsWith('.js')
1280
+ ? '📜'
1281
+ : '📄';
1282
+ logger.info('');
1283
+ logger.info(chalk.bold(`${statusIcon} ${fileIcon} ${chalk.cyan(path.relative(process.cwd(), filePath))}`));
1284
+ logger.info(chalk.gray(` ${errorCount} errors, ${warningCount} warnings`));
1285
+ logger.info('');
1286
+ // Mostrar cada error con formato elegante
1287
+ for (let i = 0; i < fileErrors.length; i++) {
1288
+ const error = fileErrors[i];
1289
+ await displayModernLinterError(error, filePath, i + 1, fileErrors.length);
1290
+ }
1291
+ }
1292
+ /**
1293
+ * Muestra un error individual con formato visual moderno tipo ESLint/Prettier
1294
+ */
1295
+ async function displayModernLinterError(error, filePath, errorIndex, totalErrorsInFile) {
1296
+ const chalk = await loadChalk();
1297
+ const fs = await import('node:fs/promises');
1298
+ // Determinar tipo y color del error
1299
+ const isError = error.severity === 'error';
1300
+ const typeColor = isError ? chalk.red : chalk.yellow;
1301
+ const typeIcon = isError ? '✕' : '⚠';
1302
+ const line = error.line || 1;
1303
+ const column = error.column || 1;
1304
+ const ruleId = error.ruleId || error.from || 'unknown';
1305
+ // Línea principal del error con formato moderno
1306
+ const errorHeader = ` ${typeColor(typeIcon)} ${chalk.bold(error.message)}`;
1307
+ const ruleInfo = `${chalk.gray(ruleId)}`;
1308
+ const locationInfo = `${chalk.blue(`${line}:${column}`)}`;
1309
+ logger.info(errorHeader);
1310
+ logger.info(` ${chalk.gray('at')} ${locationInfo} ${chalk.gray('·')} ${ruleInfo}`);
1311
+ // Mostrar código con contexto
1312
+ try {
1313
+ const absolutePath = path.resolve(filePath);
1314
+ const fileContent = await fs.readFile(absolutePath, 'utf-8');
1315
+ const lines = fileContent.split('\n');
1316
+ const lineNum = parseInt(line.toString()) - 1;
1317
+ if (lineNum >= 0 && lineNum < lines.length) {
1318
+ logger.info('');
1319
+ // Mostrar líneas de contexto con numeración elegante
1320
+ const startLine = Math.max(0, lineNum - 1);
1321
+ const endLine = Math.min(lines.length - 1, lineNum + 1);
1322
+ const maxLineNumWidth = (endLine + 1).toString().length;
1323
+ for (let i = startLine; i <= endLine; i++) {
1324
+ const currentLineNum = i + 1;
1325
+ const currentLine = lines[i] || '';
1326
+ const lineNumStr = currentLineNum
1327
+ .toString()
1328
+ .padStart(maxLineNumWidth, ' ');
1329
+ const isErrorLine = i === lineNum;
1330
+ if (isErrorLine) {
1331
+ // Línea con el error - destacada
1332
+ logger.info(` ${chalk.red('>')} ${chalk.gray(lineNumStr)} ${chalk.gray('│')} ${currentLine}`);
1333
+ // Indicador de posición del error
1334
+ const pointer = ' '.repeat(Math.max(0, column - 1)) + typeColor('^');
1335
+ logger.info(` ${chalk.gray(' ')} ${chalk.gray(' '.repeat(maxLineNumWidth))} ${chalk.gray('│')} ${pointer}`);
1336
+ }
1337
+ else {
1338
+ // Líneas de contexto
1339
+ logger.info(` ${chalk.gray(' ')} ${chalk.gray(lineNumStr)} ${chalk.gray('│')} ${chalk.gray(currentLine)}`);
1340
+ }
1341
+ }
1342
+ }
1343
+ }
1344
+ catch {
1345
+ // Si no se puede leer el archivo, mostrar formato simplificado
1346
+ logger.info(` ${chalk.gray('│')} ${chalk.gray('(Unable to read file content)')}`);
1347
+ }
1348
+ // Mostrar ayuda si está disponible
1349
+ if (error.help) {
1350
+ logger.info('');
1351
+ const helpText = error.help.replace(/^Regla \w+: /, '').trim();
1352
+ logger.info(` ${chalk.blue('💡')} ${chalk.blue('Help:')} ${chalk.gray(helpText)}`);
1353
+ }
1354
+ // Separador entre errores (solo si no es el último)
1355
+ if (errorIndex < totalErrorsInFile) {
1356
+ logger.info('');
1357
+ }
1358
+ }
1359
+ /**
1360
+ * Muestra un solo error del linter con formato visual mejorado
1361
+ * @deprecated Use displayModernLinterError instead
1362
+ */
1363
+ async function _displaySingleLinterError(error, filePath) {
1364
+ const chalk = await loadChalk();
1365
+ const fs = await import('node:fs/promises');
1366
+ const icon = error.severity === 'error' ? '×' : '⚠';
1367
+ const ruleInfo = error.help || '';
1368
+ const line = error.line || 'N/A';
1369
+ const column = error.column || 10; // Columna por defecto si no está disponible
1370
+ // Línea principal del error
1371
+ const mainErrorLine = `${chalk.red(icon)} ${chalk.cyan(`${error.from}(${ruleInfo.replace(/^Regla \w+: /, '')})`)}: ${error.message}`;
1372
+ logger.info(mainErrorLine);
1373
+ // Intentar leer el contenido del archivo para mostrar contexto
1374
+ try {
1375
+ const absolutePath = path.resolve(filePath);
1376
+ const fileContent = await fs.readFile(absolutePath, 'utf-8');
1377
+ const lines = fileContent.split('\n');
1378
+ const lineNum = parseInt(line.toString()) - 1; // Convertir a índice 0-based
1379
+ if (lineNum >= 0 && lineNum < lines.length) {
1380
+ // Mostrar ubicación
1381
+ logger.info(chalk.blue(` ╭─[${filePath}:${line}:${column}]`));
1382
+ // Mostrar líneas de contexto
1383
+ const startLine = Math.max(0, lineNum - 1);
1384
+ const endLine = Math.min(lines.length - 1, lineNum + 1);
1385
+ for (let i = startLine; i <= endLine; i++) {
1386
+ const currentLineNum = i + 1;
1387
+ const currentLine = lines[i] || '';
1388
+ const prefix = currentLineNum.toString().padStart(2, ' ');
1389
+ if (i === lineNum) {
1390
+ // Línea con el error
1391
+ logger.info(chalk.blue(` ${prefix} │ `) + currentLine);
1392
+ // Mostrar el indicador de error
1393
+ const indent = ' '.repeat(prefix.length + 3); // Espacios para alinear
1394
+ const pointer = ' '.repeat(Math.max(0, (column || 1) - 1)) +
1395
+ chalk.red('───────┬──────');
1396
+ logger.info(chalk.blue(indent + '·') + pointer);
1397
+ // Mensaje de ubicación específica
1398
+ const messageIndent = ' '.repeat(Math.max(0, (column || 1) + 6));
1399
+ logger.info(chalk.blue(indent + '·') +
1400
+ messageIndent +
1401
+ chalk.red('╰── ') +
1402
+ chalk.gray(getErrorLocationMessage(error)));
1403
+ }
1404
+ else {
1405
+ // Líneas de contexto
1406
+ logger.info(chalk.blue(` ${prefix} │ `) + chalk.gray(currentLine));
1407
+ }
1408
+ }
1409
+ logger.info(chalk.blue(' ╰────'));
1410
+ }
1411
+ }
1412
+ catch {
1413
+ // Si no se puede leer el archivo, mostrar formato simplificado
1414
+ logger.info(chalk.blue(` ╭─[${filePath}:${line}:${column}]`));
1415
+ logger.info(chalk.blue(' │ ') +
1416
+ chalk.gray('(No se pudo leer el contenido del archivo)'));
1417
+ logger.info(chalk.blue(' ╰────'));
1418
+ }
1419
+ // Mostrar ayuda si está disponible
1420
+ if (error.help) {
1421
+ const helpMessage = error.help.replace(/^Regla \w+: /, '');
1422
+ logger.info(chalk.blue(' help: ') + chalk.yellow(helpMessage));
1423
+ }
1424
+ logger.info(''); // Espacio entre errores
1425
+ }
1426
+ /**
1427
+ * Genera un mensaje descriptivo para la ubicación específica del error
1428
+ */
1429
+ function getErrorLocationMessage(error) {
1430
+ if (error.message.includes('declared but never used')) {
1431
+ const match = error.message.match(/'([^']+)'/);
1432
+ if (match) {
1433
+ return `'${match[1]}' is declared here`;
1434
+ }
1435
+ }
1436
+ if (error.message.includes('Unexpected var')) {
1437
+ return 'var declaration found here';
1438
+ }
1439
+ if (error.message.includes('never reassigned')) {
1440
+ const match = error.message.match(/'([^']+)'/);
1441
+ if (match) {
1442
+ return `'${match[1]}' is assigned here`;
1443
+ }
1444
+ }
1445
+ return 'error location';
1446
+ }
1447
+ /**
1448
+ * Obtiene el color apropiado para cada etapa de compilación
1449
+ */
1450
+ async function getStageColor(stage) {
1451
+ const chalk = await loadChalk();
1452
+ switch (stage) {
1453
+ case 'vue':
1454
+ return chalk.green;
1455
+ case 'typescript':
1456
+ return chalk.blue;
1457
+ case 'standardization':
1458
+ return chalk.yellow;
1459
+ case 'minification':
1460
+ return chalk.red;
1461
+ case 'tailwind':
1462
+ return chalk.magenta;
1463
+ case 'file-read':
1464
+ return chalk.gray;
1465
+ default:
1466
+ return chalk.white;
1467
+ }
1468
+ }
1469
+ export function normalizeRuta(ruta) {
1470
+ if (path.isAbsolute(ruta)) {
1471
+ return path.normalize(ruta).replace(/\\/g, '/');
1472
+ }
1473
+ const file = path
1474
+ .normalize(!ruta.startsWith('.') ? './' + ruta : ruta)
1475
+ .replace(/\\/g, '/');
1476
+ const sourceForDist = file.startsWith('./') ? file : `./${file}`;
1477
+ return sourceForDist;
1478
+ }
1479
+ export function getOutputPath(ruta) {
1480
+ const pathSource = env.PATH_SOURCE ?? '';
1481
+ const pathDist = env.PATH_DIST ?? '';
1482
+ if (!pathSource || !pathDist) {
1483
+ return ruta.replace(/\.(vue|ts)$/, '.js');
1484
+ }
1485
+ const normalizedRuta = path.normalize(ruta).replace(/\\/g, '/');
1486
+ const normalizedSource = path.normalize(pathSource).replace(/\\/g, '/');
1487
+ const normalizedDist = path.normalize(pathDist).replace(/\\/g, '/');
1488
+ let outputPath;
1489
+ if (normalizedRuta.includes(normalizedSource)) {
1490
+ const relativePath = normalizedRuta
1491
+ .substring(normalizedRuta.indexOf(normalizedSource) +
1492
+ normalizedSource.length)
1493
+ .replace(/^[/\\]/, '');
1494
+ outputPath = path
1495
+ .join(normalizedDist, relativePath)
1496
+ .replace(/\\/g, '/');
1497
+ }
1498
+ else {
1499
+ const fileName = path.basename(normalizedRuta);
1500
+ outputPath = path.join(normalizedDist, fileName).replace(/\\/g, '/');
1501
+ }
1502
+ if (outputPath.includes('vue') || outputPath.includes('ts')) {
1503
+ return outputPath.replace(/\.(vue|ts)$/, '.js');
1504
+ }
1505
+ else {
1506
+ return outputPath;
1507
+ }
1508
+ }
1509
+ // Optimización para modo watch: debouncing y cache de archivos
1510
+ class WatchModeOptimizer {
1511
+ static instance;
1512
+ fileSystemCache = new Map();
1513
+ debounceTimers = new Map();
1514
+ DEBOUNCE_DELAY = 100; // 100ms debounce
1515
+ static getInstance() {
1516
+ if (!WatchModeOptimizer.instance) {
1517
+ WatchModeOptimizer.instance = new WatchModeOptimizer();
1518
+ }
1519
+ return WatchModeOptimizer.instance;
1520
+ }
1521
+ async compileForWatch(filePath, compileFn) {
1522
+ return new Promise(resolve => {
1523
+ const existingTimer = this.debounceTimers.get(filePath);
1524
+ if (existingTimer) {
1525
+ clearTimeout(existingTimer);
1526
+ }
1527
+ const timer = setTimeout(async () => {
1528
+ this.debounceTimers.delete(filePath);
1529
+ try {
1530
+ const stats = await stat(filePath);
1531
+ const cached = this.fileSystemCache.get(filePath);
1532
+ if (cached && cached.mtime >= stats.mtimeMs) {
1533
+ resolve({ success: true, cached: true });
1534
+ return;
1535
+ } // Configurar worker pool para modo watch
1536
+ const { TypeScriptWorkerPool } = (await import('./typescript-worker-pool.js'));
1537
+ const workerPool = TypeScriptWorkerPool.getInstance();
1538
+ workerPool.setMode('watch');
1539
+ const result = await compileFn(filePath);
1540
+ this.fileSystemCache.set(filePath, {
1541
+ mtime: stats.mtimeMs,
1542
+ });
1543
+ resolve(result);
1544
+ }
1545
+ catch (error) {
1546
+ resolve({ success: false, error });
1547
+ }
1548
+ }, this.DEBOUNCE_DELAY);
1549
+ this.debounceTimers.set(filePath, timer);
1550
+ });
1551
+ }
1552
+ cleanup() {
1553
+ this.debounceTimers.forEach(timer => clearTimeout(timer));
1554
+ this.debounceTimers.clear();
1555
+ this.fileSystemCache.clear();
1556
+ }
1557
+ }
1558
+ async function compileJS(inPath, outPath, mode = 'individual') {
1559
+ const timings = {};
1560
+ // Si la ruta ya es absoluta, no la resolvamos de nuevo
1561
+ inPath = path.isAbsolute(inPath)
1562
+ ? normalizeRuta(inPath)
1563
+ : normalizeRuta(path.resolve(inPath)); // 🚀 Usar OptimizedModuleManager para carga optimizada
1564
+ const moduleManager = OptimizedModuleManager.getInstance();
1565
+ // Timing de lectura
1566
+ let start = Date.now();
1567
+ const extension = path.extname(inPath); // Asegurar que el parser esté cargado
1568
+ await moduleManager.ensureModuleLoaded('parser');
1569
+ const getCodeFile = await loadParser();
1570
+ const result = await getCodeFile(inPath);
1571
+ let code = result.code;
1572
+ const error = result.error;
1573
+ timings.fileRead = Date.now() - start;
1574
+ if (error) {
1575
+ await handleCompilationError(error instanceof Error ? error : new Error(String(error)), inPath, 'file-read', mode, env.VERBOSE === 'true');
1576
+ throw new Error(error instanceof Error ? error.message : String(error));
1577
+ }
1578
+ if (!code ||
1579
+ code.trim().length === 0 ||
1580
+ code === 'undefined' ||
1581
+ code === 'null') {
1582
+ await handleCompilationError(new Error('El archivo está vacío o no se pudo leer.'), inPath, 'file-read', mode, env.VERBOSE === 'true');
1583
+ throw new Error('El archivo está vacío o no se pudo leer.');
1584
+ } // Logs detallados en modo verbose
1585
+ const shouldShowDetailedLogs = env.VERBOSE === 'true'; // Compilación de Vue
1586
+ let vueResult;
1587
+ if (extension === '.vue') {
1588
+ start = Date.now();
1589
+ if (shouldShowDetailedLogs) {
1590
+ logger.info(chalk.green(`💚 Precompilando VUE: ${path.basename(inPath)}`));
1591
+ }
1592
+ // Asegurar que el módulo Vue esté cargado
1593
+ await moduleManager.ensureModuleLoaded('vue');
1594
+ const preCompileVue = await loadVue();
1595
+ if (typeof preCompileVue !== 'function') {
1596
+ throw new Error(`loadVue devolvió ${typeof preCompileVue} en lugar de una función para archivo: ${inPath}`);
1597
+ }
1598
+ vueResult = await preCompileVue(code, inPath, env.isPROD === 'true');
1599
+ timings.vueCompile = Date.now() - start;
1600
+ if (vueResult === undefined || vueResult === null) {
1601
+ throw new Error(`preCompileVue devolvió ${vueResult} para archivo: ${inPath}`);
1602
+ }
1603
+ if (vueResult.error) {
1604
+ await handleCompilationError(vueResult.error instanceof Error
1605
+ ? vueResult.error
1606
+ : new Error(String(vueResult.error)), inPath, 'vue', mode, env.VERBOSE === 'true');
1607
+ throw new Error(vueResult.error instanceof Error
1608
+ ? vueResult.error.message
1609
+ : String(vueResult.error));
1610
+ }
1611
+ registerCompilationSuccess(inPath, 'vue');
1612
+ code = vueResult.data;
1613
+ }
1614
+ if (!code || code.trim().length === 0) {
1615
+ await handleCompilationError(new Error('El código Vue compilado está vacío.'), inPath, 'vue', mode, env.VERBOSE === 'true');
1616
+ throw new Error('El código Vue compilado está vacío.');
1617
+ }
1618
+ // Compilación de TypeScript
1619
+ let tsResult;
1620
+ if (extension === '.ts' || vueResult?.lang === 'ts') {
1621
+ start = Date.now();
1622
+ if (shouldShowDetailedLogs) {
1623
+ logger.info(chalk.blue(`🔄️ Precompilando TS: ${path.basename(inPath)}`));
1624
+ }
1625
+ // Asegurar que el módulo TypeScript esté cargado
1626
+ await moduleManager.ensureModuleLoaded('typescript');
1627
+ const preCompileTS = await loadTypeScript();
1628
+ if (typeof preCompileTS !== 'function') {
1629
+ throw new Error(`loadTypeScript devolvió ${typeof preCompileTS} en lugar de una función para archivo: ${inPath}`);
1630
+ }
1631
+ tsResult = await preCompileTS(code, inPath);
1632
+ timings.tsCompile = Date.now() - start;
1633
+ if (tsResult === undefined || tsResult === null) {
1634
+ throw new Error(`preCompileTS devolvió ${tsResult} para archivo: ${inPath}`);
1635
+ }
1636
+ if (tsResult.error) {
1637
+ if (mode === 'all') {
1638
+ // En modo --all, registrar el error pero continuar la compilación
1639
+ registerCompilationError(inPath, 'typescript', tsResult.error instanceof Error
1640
+ ? tsResult.error.message
1641
+ : String(tsResult.error), 'error');
1642
+ }
1643
+ else {
1644
+ await handleCompilationError(tsResult.error, inPath, 'typescript', mode, env.VERBOSE === 'true');
1645
+ throw new Error(tsResult.error instanceof Error
1646
+ ? tsResult.error.message
1647
+ : String(tsResult.error));
1648
+ }
1649
+ }
1650
+ else {
1651
+ registerCompilationSuccess(inPath, 'typescript');
1652
+ code = tsResult.data;
1653
+ }
1654
+ }
1655
+ if (!code || code.trim().length === 0) {
1656
+ await handleCompilationError(new Error('El código TypeScript compilado está vacío.'), inPath, 'typescript', mode, env.VERBOSE === 'true');
1657
+ throw new Error('El código TypeScript compilado está vacío.');
1658
+ } // Estandarización
1659
+ if (shouldShowDetailedLogs) {
1660
+ logger.info(chalk.yellow(`💛 Estandarizando: ${path.basename(inPath)}`));
1661
+ }
1662
+ start = Date.now();
1663
+ // Asegurar que el módulo de transformaciones esté cargado
1664
+ await moduleManager.ensureModuleLoaded('transforms');
1665
+ const estandarizaCode = await loadTransforms();
1666
+ const resultSTD = await estandarizaCode(code, inPath);
1667
+ timings.standardization = Date.now() - start;
1668
+ if (resultSTD === undefined || resultSTD === null) {
1669
+ throw new Error(`estandarizaCode devolvió ${resultSTD} para archivo: ${inPath}`);
1670
+ }
1671
+ if (resultSTD.error) {
1672
+ await handleCompilationError(new Error(resultSTD.error), inPath, 'standardization', mode, env.VERBOSE === 'true');
1673
+ throw new Error(resultSTD.error);
1674
+ }
1675
+ registerCompilationSuccess(inPath, 'standardization');
1676
+ code = resultSTD.code;
1677
+ if (!code || code.trim().length === 0) {
1678
+ await handleCompilationError(new Error('El código estandarizado está vacío.'), inPath, 'standardization', mode, env.VERBOSE === 'true');
1679
+ throw new Error('El código estandarizado está vacío.');
1680
+ }
1681
+ // Minificación (solo en producción)
1682
+ if (env.isPROD === 'true') {
1683
+ start = Date.now();
1684
+ if (shouldShowDetailedLogs) {
1685
+ logger.info(chalk.red(`🤖 Minificando: ${path.basename(inPath)}`));
1686
+ }
1687
+ // Asegurar que el módulo de minificación esté cargado
1688
+ await moduleManager.ensureModuleLoaded('minify');
1689
+ const minifyJS = await loadMinify();
1690
+ const resultMinify = await minifyJS(code, inPath, true);
1691
+ timings.minification = Date.now() - start;
1692
+ if (resultMinify === undefined || resultMinify === null) {
1693
+ throw new Error(`minifyJS devolvió ${resultMinify} para archivo: ${inPath}`);
1694
+ }
1695
+ if (resultMinify.error) {
1696
+ await handleCompilationError(resultMinify.error instanceof Error
1697
+ ? resultMinify.error
1698
+ : new Error(String(resultMinify.error)), inPath, 'minification', mode, env.VERBOSE === 'true');
1699
+ throw new Error(resultMinify.error instanceof Error
1700
+ ? resultMinify.error.message
1701
+ : String(resultMinify.error));
1702
+ }
1703
+ registerCompilationSuccess(inPath, 'minification');
1704
+ code = resultMinify.code;
1705
+ } // Escribir archivo final
1706
+ const destinationDir = path.dirname(outPath);
1707
+ await mkdir(destinationDir, { recursive: true });
1708
+ await writeFile(outPath, code, 'utf-8');
1709
+ // Logs de timing detallados en modo verbose
1710
+ if (shouldShowDetailedLogs) {
1711
+ const totalTime = Object.values(timings).reduce((sum, time) => sum + time, 0);
1712
+ logger.info(chalk.cyan(`⏱️ Timing para ${path.basename(inPath)}:`));
1713
+ if (timings.fileRead)
1714
+ logger.info(chalk.cyan(` 📖 Lectura: ${timings.fileRead}ms`));
1715
+ if (timings.vueCompile)
1716
+ logger.info(chalk.cyan(` 💚 Vue: ${timings.vueCompile}ms`));
1717
+ if (timings.tsCompile)
1718
+ logger.info(chalk.cyan(` 🔄️ TypeScript: ${timings.tsCompile}ms`));
1719
+ if (timings.standardization)
1720
+ logger.info(chalk.cyan(` 💛 Estandarización: ${timings.standardization}ms`));
1721
+ if (timings.minification)
1722
+ logger.info(chalk.cyan(` 🤖 Minificación: ${timings.minification}ms`));
1723
+ logger.info(chalk.cyan(` 🏁 Total: ${totalTime}ms`));
1724
+ }
1725
+ return {
1726
+ error: null,
1727
+ action: 'extension',
1728
+ };
1729
+ }
1730
+ export async function initCompile(ruta, compileTailwind = true, mode = 'individual') {
1731
+ try {
1732
+ // 🚀 Sistema de Carga Inteligente de Módulos
1733
+ const moduleManager = OptimizedModuleManager.getInstance();
1734
+ const fileExtension = path.extname(ruta);
1735
+ const fileExtensions = new Set([fileExtension]);
1736
+ // Inicializar módulos según el contexto
1737
+ await moduleManager.preloadForContext(mode === 'all' ? 'batch' : mode, fileExtensions);
1738
+ // Generar TailwindCSS si está habilitado
1739
+ if (compileTailwind && Boolean(env.TAILWIND)) {
1740
+ await moduleManager.ensureModuleLoaded('tailwind');
1741
+ const generateTailwindCSS = await loadTailwind();
1742
+ const resultTW = await generateTailwindCSS();
1743
+ if (typeof resultTW !== 'boolean') {
1744
+ if (resultTW?.success) {
1745
+ logger.info(`🎨 ${resultTW.message}`);
1746
+ }
1747
+ else {
1748
+ const errorMsg = `${resultTW.message}${resultTW.details ? '\n' + resultTW.details : ''}`;
1749
+ await handleCompilationError(new Error(errorMsg), 'tailwind.config.js', 'tailwind', mode, env.VERBOSE === 'true');
1750
+ // No hacer throw aquí, permitir que la compilación continúe
1751
+ }
1752
+ }
1753
+ }
1754
+ const startTime = Date.now();
1755
+ const file = normalizeRuta(ruta);
1756
+ const outFile = getOutputPath(file);
1757
+ // 🚀 Verificar cache antes de compilar (especialmente importante en modo watch)
1758
+ if (mode === 'watch' || mode === 'individual') {
1759
+ if (await shouldSkipFile(file)) {
1760
+ if (env.VERBOSE === 'true') {
1761
+ logger.info(`⏭️ Archivo omitido (cache): ${path.basename(file)}`);
1762
+ }
1763
+ return {
1764
+ success: true,
1765
+ cached: true,
1766
+ output: smartCache.getOutputPath(file) || outFile,
1767
+ action: 'cached',
1768
+ };
1769
+ }
1770
+ }
1771
+ if (mode === 'individual' && env.VERBOSE === 'true') {
1772
+ logger.info(`🔜 Fuente: ${file}`);
1773
+ }
1774
+ const result = await compileJS(file, outFile, mode);
1775
+ if (result.error) {
1776
+ throw new Error(result.error);
1777
+ }
1778
+ // 🚀 Actualizar cache después de compilación exitosa (especialmente en modo watch)
1779
+ if (mode === 'watch' || mode === 'individual') {
1780
+ await smartCache.set(file, outFile);
1781
+ }
1782
+ const endTime = Date.now();
1783
+ const elapsedTime = showTimingForHumans(endTime - startTime);
1784
+ if (mode === 'individual') {
1785
+ if (env.VERBOSE === 'true') {
1786
+ logger.info(`🔚 Destino: ${outFile}`);
1787
+ logger.info(`⏱️ Tiempo: ${elapsedTime}`);
1788
+ }
1789
+ const chalk = await loadChalk();
1790
+ logger.info(chalk.green(`✅ Compilación exitosa: ${path.basename(file)}`));
1791
+ }
1792
+ return {
1793
+ success: true,
1794
+ output: outFile,
1795
+ action: result.action,
1796
+ };
1797
+ }
1798
+ catch (error) {
1799
+ const errorMessage = error instanceof Error ? error.message : String(error);
1800
+ if (env.VERBOSE === 'true') {
1801
+ logger.error(`❌ Error en compilación de ${path.basename(ruta)}: ${errorMessage}`);
1802
+ }
1803
+ return {
1804
+ success: false,
1805
+ output: '',
1806
+ error: errorMessage,
1807
+ };
1808
+ }
1809
+ }
1810
+ // Variable para el último progreso mostrado (evitar spam)
1811
+ let lastProgressUpdate = 0;
1812
+ // Sistema de gestión de progreso persistente (como Jest)
1813
+ class ProgressManager {
1814
+ static instance;
1815
+ progressActive = false;
1816
+ lastProgressLine = '';
1817
+ logBuffer = [];
1818
+ originalConsoleLog;
1819
+ originalConsoleError;
1820
+ originalConsoleWarn;
1821
+ hasProgressLine = false;
1822
+ constructor() {
1823
+ // Guardar referencias originales
1824
+ this.originalConsoleLog = console.log;
1825
+ this.originalConsoleError = console.error;
1826
+ this.originalConsoleWarn = console.warn;
1827
+ }
1828
+ static getInstance() {
1829
+ if (!ProgressManager.instance) {
1830
+ ProgressManager.instance = new ProgressManager();
1831
+ }
1832
+ return ProgressManager.instance;
1833
+ }
1834
+ interceptConsole() {
1835
+ // Interceptar console.log y similares
1836
+ console.log = (...args) => {
1837
+ this.addLog(args.map(arg => String(arg)).join(' '));
1838
+ };
1839
+ console.error = (...args) => {
1840
+ this.addLog(args.map(arg => String(arg)).join(' '));
1841
+ };
1842
+ console.warn = (...args) => {
1843
+ this.addLog(args.map(arg => String(arg)).join(' '));
1844
+ };
1845
+ }
1846
+ restoreConsole() {
1847
+ console.log = this.originalConsoleLog;
1848
+ console.error = this.originalConsoleError;
1849
+ console.warn = this.originalConsoleWarn;
1850
+ }
1851
+ startProgress() {
1852
+ this.progressActive = true;
1853
+ this.logBuffer = [];
1854
+ this.hasProgressLine = false;
1855
+ this.interceptConsole();
1856
+ // 🎨 Header moderno de inicio de compilación
1857
+ const headerLine = '━'.repeat(48);
1858
+ process.stdout.write('\n\x1b[96m' + headerLine + '\x1b[0m\n');
1859
+ process.stdout.write('\x1b[96m│ \x1b[97m\x1b[1m🚀 Iniciando Compilación\x1b[0m\x1b[96m' +
1860
+ ' '.repeat(22) +
1861
+ '│\x1b[0m\n');
1862
+ process.stdout.write('\x1b[96m' + headerLine + '\x1b[0m\n');
1863
+ }
1864
+ updateProgress(progressText) {
1865
+ if (!this.progressActive)
1866
+ return;
1867
+ // Si hay logs pendientes, mostrarlos primero
1868
+ if (this.logBuffer.length > 0) {
1869
+ // Si ya hay una línea de progreso, limpiarla primero
1870
+ if (this.hasProgressLine) {
1871
+ process.stdout.write('\r\x1b[K');
1872
+ }
1873
+ // Escribir todos los logs pendientes
1874
+ for (const log of this.logBuffer) {
1875
+ process.stdout.write((this.hasProgressLine ? '\n' : '') + log + '\n');
1876
+ this.hasProgressLine = false;
1877
+ }
1878
+ this.logBuffer = [];
1879
+ } // Escribir separador elegante antes del progreso
1880
+ if (this.hasProgressLine) {
1881
+ process.stdout.write('\r\x1b[K');
1882
+ }
1883
+ else {
1884
+ process.stdout.write('\n\x1b[96m' + '▔'.repeat(50) + '\x1b[0m\n');
1885
+ }
1886
+ // 🎨 Barra de progreso con colores dinámicos
1887
+ const stage = this.getStageFromText(progressText);
1888
+ const { bgColor, textColor, icon } = this.getProgressColors(stage);
1889
+ const progressBar = '█'.repeat(3);
1890
+ const enhancedProgress = `\x1b[${bgColor}m\x1b[${textColor}m ${progressBar} ${icon} ${progressText} ${progressBar} \x1b[0m`;
1891
+ process.stdout.write(enhancedProgress);
1892
+ this.hasProgressLine = true;
1893
+ this.lastProgressLine = progressText;
1894
+ }
1895
+ addLog(message) {
1896
+ if (this.progressActive) {
1897
+ this.logBuffer.push(message);
1898
+ }
1899
+ else {
1900
+ this.originalConsoleLog(message);
1901
+ }
1902
+ }
1903
+ addImmediateLog(message) {
1904
+ if (this.progressActive) {
1905
+ if (this.hasProgressLine) {
1906
+ process.stdout.write('\r\x1b[K');
1907
+ }
1908
+ // Añadir un punto de separación visual para logs inmediatos
1909
+ process.stdout.write('\x1b[90m│\x1b[0m ' + message + '\n');
1910
+ this.hasProgressLine = false;
1911
+ }
1912
+ else {
1913
+ this.originalConsoleLog(message);
1914
+ }
1915
+ }
1916
+ endProgress() {
1917
+ if (this.progressActive) {
1918
+ if (this.hasProgressLine) {
1919
+ process.stdout.write('\n');
1920
+ }
1921
+ // Mostrar barra de progreso final completa antes del separador
1922
+ process.stdout.write('\n\x1b[33m' + '-'.repeat(50) + '\x1b[0m\n');
1923
+ const finalProgressBar = '█'.repeat(3);
1924
+ const finalProgress = `\x1b[42m\x1b[30m ${finalProgressBar} ✅ PROCESO COMPLETADO 100% ${finalProgressBar} \x1b[0m`;
1925
+ process.stdout.write(finalProgress + '\n');
1926
+ // 🎨 Footer moderno de finalización
1927
+ const footerLine = '━'.repeat(48);
1928
+ process.stdout.write('\x1b[92m' + footerLine + '\x1b[0m\n');
1929
+ process.stdout.write('\x1b[92m│ \x1b[97m\x1b[1m✅ ¡Compilación Completada!\x1b[0m\x1b[92m' +
1930
+ ' '.repeat(23) +
1931
+ '│\x1b[0m\n');
1932
+ process.stdout.write('\x1b[92m' + footerLine + '\x1b[0m\n\n');
1933
+ // Escribir logs finales pendientes
1934
+ if (this.logBuffer.length > 0) {
1935
+ for (const log of this.logBuffer) {
1936
+ process.stdout.write(log + '\n');
1937
+ }
1938
+ }
1939
+ }
1940
+ this.restoreConsole();
1941
+ this.progressActive = false;
1942
+ this.lastProgressLine = '';
1943
+ this.logBuffer = [];
1944
+ this.hasProgressLine = false;
1945
+ }
1946
+ isActive() {
1947
+ return this.progressActive;
1948
+ }
1949
+ /**
1950
+ * 🎨 Determina la etapa del progreso basándose en el texto
1951
+ */
1952
+ getStageFromText(text) {
1953
+ if (text.includes('Iniciando') || text.includes('Starting'))
1954
+ return 'start';
1955
+ if (text.includes('Tailwind') || text.includes('CSS'))
1956
+ return 'tailwind';
1957
+ if (text.includes('Recopilando') ||
1958
+ text.includes('archivos') ||
1959
+ text.includes('files'))
1960
+ return 'files';
1961
+ if (text.includes('Compilando') || text.includes('workers'))
1962
+ return 'compile';
1963
+ if (text.includes('cache') || text.includes('Guardando'))
1964
+ return 'cache';
1965
+ if (text.includes('linter') || text.includes('Linter'))
1966
+ return 'linter';
1967
+ if (text.includes('completado') || text.includes('Complete'))
1968
+ return 'complete';
1969
+ return 'default';
1970
+ }
1971
+ /**
1972
+ * 🌈 Obtiene colores dinámicos para cada etapa
1973
+ */
1974
+ getProgressColors(stage) {
1975
+ const colorSchemes = {
1976
+ start: { bgColor: '45', textColor: '97', icon: '🚀' }, // Cyan brillante
1977
+ tailwind: { bgColor: '105', textColor: '97', icon: '🎨' }, // Magenta
1978
+ files: { bgColor: '43', textColor: '30', icon: '📁' }, // Amarillo
1979
+ compile: { bgColor: '42', textColor: '30', icon: '⚙️' }, // Verde
1980
+ cache: { bgColor: '44', textColor: '97', icon: '💾' }, // Azul
1981
+ linter: { bgColor: '101', textColor: '97', icon: '🔍' }, // Rojo claro
1982
+ complete: { bgColor: '102', textColor: '30', icon: '✅' }, // Verde claro
1983
+ default: { bgColor: '100', textColor: '30', icon: '⏳' }, // Gris claro
1984
+ };
1985
+ const defaultColors = { bgColor: '100', textColor: '30', icon: '⏳' };
1986
+ return colorSchemes[stage] || defaultColors;
1987
+ }
1988
+ }
1989
+ // Función para ejecutar el linter antes de la compilación
1990
+ export async function runLinter(showResult = false) {
1991
+ const linterENV = env.linter;
1992
+ const linterPromises = [];
1993
+ const linterErrors = [];
1994
+ let proceedWithCompilation = true;
1995
+ if (env.ENABLE_LINTER !== 'true') {
1996
+ return true;
1997
+ }
1998
+ if (typeof linterENV === 'string' && linterENV.trim() !== '') {
1999
+ logger.info('🔍 Ejecutando linting...');
2000
+ try {
2001
+ const parsedLinterEnv = JSON.parse(linterENV);
2002
+ if (Array.isArray(parsedLinterEnv)) {
2003
+ // Cargar dependencias de linting de forma lazy
2004
+ const { ESLint, OxLint } = await loadLinter();
2005
+ for (const item of parsedLinterEnv) {
2006
+ if (item.name.toLowerCase() === 'eslint') {
2007
+ logger.info(`🔧 Ejecutando ESLint con config: ${item.configFile || 'por defecto'}`);
2008
+ const eslintPromise = ESLint(item)
2009
+ .then((eslintResult) => {
2010
+ if (eslintResult && eslintResult.json) {
2011
+ // Procesar resultados de ESLint
2012
+ if (Array.isArray(eslintResult.json)) {
2013
+ eslintResult.json.forEach((result) => {
2014
+ const filePath = result.filePath ||
2015
+ result.file ||
2016
+ 'archivo no especificado';
2017
+ linterErrors.push({
2018
+ from: 'eslint',
2019
+ line: result.line || 'N/A',
2020
+ column: result.column || 1,
2021
+ file: filePath,
2022
+ message: result.message,
2023
+ severity: result.severity === 2
2024
+ ? 'error'
2025
+ : 'warning',
2026
+ help: result.ruleId
2027
+ ? `Regla ESLint: ${result.ruleId}`
2028
+ : undefined,
2029
+ });
2030
+ });
2031
+ }
2032
+ else if (eslintResult.json.results &&
2033
+ Array.isArray(eslintResult.json.results)) {
2034
+ eslintResult.json.results.forEach((fileResult) => {
2035
+ if (fileResult.messages &&
2036
+ Array.isArray(fileResult.messages)) {
2037
+ fileResult.messages.forEach((msg) => {
2038
+ const filePath = fileResult.filePath ||
2039
+ fileResult.file ||
2040
+ 'archivo no especificado';
2041
+ linterErrors.push({
2042
+ from: 'eslint',
2043
+ line: msg.line ||
2044
+ 'N/A',
2045
+ column: msg.column ||
2046
+ 1,
2047
+ file: filePath,
2048
+ message: msg.message,
2049
+ severity: msg.severity ===
2050
+ 2
2051
+ ? 'error'
2052
+ : 'warning',
2053
+ help: msg.ruleId
2054
+ ? `Regla ESLint: ${msg.ruleId}`
2055
+ : undefined,
2056
+ });
2057
+ });
2058
+ }
2059
+ });
2060
+ }
2061
+ }
2062
+ })
2063
+ .catch((err) => {
2064
+ logger.error(`❌ Error durante la ejecución de ESLint: ${err.message}`);
2065
+ linterErrors.push({
2066
+ file: item.configFile || 'ESLint Config',
2067
+ message: `Fallo al ejecutar ESLint: ${err.message}`,
2068
+ severity: 'error',
2069
+ });
2070
+ });
2071
+ linterPromises.push(eslintPromise);
2072
+ }
2073
+ else if (item.name.toLowerCase() === 'oxlint') {
2074
+ logger.info(`🔧 Ejecutando OxLint con config: ${item.configFile || 'por defecto'}`);
2075
+ const oxlintPromise = OxLint(item)
2076
+ .then((oxlintResult) => {
2077
+ if (oxlintResult &&
2078
+ oxlintResult['json'] &&
2079
+ Array.isArray(oxlintResult['json']['diagnostics'])) {
2080
+ oxlintResult['json']['diagnostics'].forEach((result) => {
2081
+ const filePath = result.filename ||
2082
+ result.file ||
2083
+ 'archivo no especificado';
2084
+ const lineNumber = result.labels &&
2085
+ result.labels[0] &&
2086
+ result.labels[0].span
2087
+ ? result.labels[0].span
2088
+ .line ||
2089
+ result.labels[0].span
2090
+ .start?.line
2091
+ : 'N/A';
2092
+ const columnNumber = result.labels &&
2093
+ result.labels[0] &&
2094
+ result.labels[0].span
2095
+ ? result.labels[0].span
2096
+ .column ||
2097
+ result.labels[0].span
2098
+ .start?.column
2099
+ : 1;
2100
+ linterErrors.push({
2101
+ from: 'oxlint',
2102
+ line: lineNumber,
2103
+ column: columnNumber,
2104
+ file: filePath,
2105
+ message: result.message,
2106
+ severity: typeof result.severity ===
2107
+ 'string'
2108
+ ? result.severity.toLowerCase()
2109
+ : 'error',
2110
+ help: result.help ||
2111
+ (result.code
2112
+ ? `Regla Oxlint: ${result.code}`
2113
+ : undefined),
2114
+ });
2115
+ });
2116
+ }
2117
+ })
2118
+ .catch((err) => {
2119
+ logger.error(`❌ Error durante la ejecución de OxLint: ${err.message}`);
2120
+ linterErrors.push({
2121
+ file: item.configFile || 'Oxlint Config',
2122
+ message: `Fallo al ejecutar Oxlint: ${err.message}`,
2123
+ severity: 'error',
2124
+ });
2125
+ });
2126
+ linterPromises.push(oxlintPromise);
2127
+ }
2128
+ }
2129
+ }
2130
+ else {
2131
+ logger.warn('⚠️ La configuración de linter no es un array válido.');
2132
+ }
2133
+ await Promise.all(linterPromises);
2134
+ if (showResult) {
2135
+ // Modo --linter: Solo mostrar resultados sin preguntar
2136
+ if (linterErrors.length > 0) {
2137
+ await displayLinterErrors(linterErrors);
2138
+ }
2139
+ else {
2140
+ const chalk = await loadChalk();
2141
+ logger.info(chalk.green('✅ No se encontraron errores ni advertencias de linting.'));
2142
+ }
2143
+ }
2144
+ else {
2145
+ // Modo compilación: Mostrar errores si los hay y preguntar al usuario
2146
+ if (linterErrors.length > 0) {
2147
+ await displayLinterErrors(linterErrors);
2148
+ logger.warn('🚨 Se encontraron errores o advertencias durante el linting.');
2149
+ if (env.yes === 'false') {
2150
+ const result = await promptUser('¿Deseas continuar con la compilación a pesar de los errores de linting? (s/N): ');
2151
+ if (result.toLowerCase() !== 's') {
2152
+ logger.info('🛑 Compilación cancelada por el usuario.');
2153
+ proceedWithCompilation = false;
2154
+ }
2155
+ }
2156
+ }
2157
+ }
2158
+ }
2159
+ catch (parseError) {
2160
+ logger.warn(`Error parseando configuración de linter: ${parseError instanceof Error ? parseError.message : 'Error desconocido'}, omitiendo...`);
2161
+ }
2162
+ }
2163
+ return proceedWithCompilation;
2164
+ }
2165
+ // Función para crear una barra de progreso visual
2166
+ function createProgressBar(current, total, width = 30) {
2167
+ const percentage = Math.round((current / total) * 100);
2168
+ const filled = Math.round((percentage / 100) * width);
2169
+ const empty = width - filled;
2170
+ return `[${'█'.repeat(filled)}${' '.repeat(empty)}] ${percentage}% (${current}/${total})`;
2171
+ }
2172
+ // Función helper para verificar si un archivo debe ser omitido por cache
2173
+ async function shouldSkipFile(filePath) {
2174
+ return await smartCache.isValid(filePath);
2175
+ }
2176
+ // Función para compilar archivos con límite de concurrencia
2177
+ async function compileWithConcurrencyLimit(files, maxConcurrency = 8) {
2178
+ const results = [];
2179
+ const executing = [];
2180
+ const total = files.length;
2181
+ let completed = 0;
2182
+ let skipped = 0;
2183
+ let failed = 0;
2184
+ // Usar el gestor de progreso existente (ya iniciado en initCompileAll)
2185
+ const progressManager = ProgressManager.getInstance();
2186
+ // Variable para controlar el progreso inicial
2187
+ let hasShownInitialProgress = false;
2188
+ // Contador para limpieza periódica de memoria
2189
+ let compilationCounter = 0;
2190
+ const CLEANUP_INTERVAL = 20; // Limpiar cada 20 compilaciones
2191
+ // Función para mostrar progreso
2192
+ function showProgress() {
2193
+ const currentTotal = completed + skipped + failed;
2194
+ const progressBar = createProgressBar(currentTotal, total);
2195
+ const progressPercent = Math.round((currentTotal / total) * 100);
2196
+ // Mostrar progreso inicial cuando se inicie O cuando haya progreso real
2197
+ if ((currentTotal === 0 && !hasShownInitialProgress) ||
2198
+ (progressPercent > lastProgressUpdate + 1 && currentTotal > 0) ||
2199
+ currentTotal === total) {
2200
+ const progressText = `🚀 ${progressBar} [✅ ${completed} | ⏭️ ${skipped} | ❌ ${failed}]`;
2201
+ progressManager.updateProgress(progressText);
2202
+ if (currentTotal === 0) {
2203
+ hasShownInitialProgress = true;
2204
+ }
2205
+ lastProgressUpdate = progressPercent;
2206
+ // NO terminar el progreso aquí - se termina en initCompileAll
2207
+ }
2208
+ }
2209
+ // Mostrar progreso inicial
2210
+ showProgress();
2211
+ for (const file of files) {
2212
+ const promise = (async () => {
2213
+ try {
2214
+ // Log verbose: Iniciando compilación del archivo
2215
+ if (env.VERBOSE === 'true') {
2216
+ logger.info(`🔄 Compilando: ${path.basename(file)}`);
2217
+ }
2218
+ // Verificar cache antes de compilar
2219
+ if (await shouldSkipFile(file)) {
2220
+ skipped++;
2221
+ if (env.VERBOSE === 'true') {
2222
+ logger.info(`⏭️ Archivo omitido (cache): ${path.basename(file)}`);
2223
+ }
2224
+ showProgress();
2225
+ return {
2226
+ success: true,
2227
+ cached: true,
2228
+ output: smartCache.getOutputPath(file),
2229
+ };
2230
+ }
2231
+ const result = await initCompile(file, false, 'batch');
2232
+ // Actualizar cache si la compilación fue exitosa
2233
+ if (result.success && result.output) {
2234
+ await smartCache.set(file, result.output);
2235
+ if (env.VERBOSE === 'true') {
2236
+ logger.info(`✅ Completado: ${path.basename(file)} → ${path.basename(result.output)}`);
2237
+ }
2238
+ }
2239
+ else if (env.VERBOSE === 'true') {
2240
+ logger.info(`❌ Error en: ${path.basename(file)}`);
2241
+ }
2242
+ completed++;
2243
+ compilationCounter++; // Limpieza periódica de memoria
2244
+ if (compilationCounter % CLEANUP_INTERVAL === 0) {
2245
+ // Forzar garbage collection si está disponible
2246
+ try {
2247
+ if (typeof globalThis.gc === 'function') {
2248
+ globalThis.gc();
2249
+ }
2250
+ }
2251
+ catch {
2252
+ // gc no disponible, continuar normalmente
2253
+ }
2254
+ // Limpiar cache si la memoria es alta
2255
+ const memUsage = process.memoryUsage();
2256
+ const heapUsedMB = memUsage.heapUsed / (1024 * 1024);
2257
+ if (heapUsedMB > 300) {
2258
+ // Si el heap supera 300MB
2259
+ const cacheEntries = smartCache.getStats().entries;
2260
+ if (cacheEntries > 50) {
2261
+ console.log(`[Memory] Heap alto (${heapUsedMB.toFixed(1)}MB), limpiando cache...`);
2262
+ // Limpiar entradas más antiguas del cache
2263
+ const removedEntries = smartCache.cleanOldEntries(20);
2264
+ if (removedEntries > 0) {
2265
+ console.log(`[Memory] Se removieron ${removedEntries} entradas del cache`);
2266
+ }
2267
+ }
2268
+ }
2269
+ }
2270
+ showProgress();
2271
+ return result;
2272
+ }
2273
+ catch (error) {
2274
+ failed++;
2275
+ if (env.VERBOSE === 'true') {
2276
+ const errorMsg = error instanceof Error ? error.message : String(error);
2277
+ logger.error(`💥 Falló: ${path.basename(file)} - ${errorMsg}`);
2278
+ }
2279
+ showProgress();
2280
+ return {
2281
+ success: false,
2282
+ error: error instanceof Error ? error.message : String(error),
2283
+ };
2284
+ }
2285
+ })();
2286
+ results.push(promise);
2287
+ executing.push(promise);
2288
+ // ✅ FIX: Esperar correctamente a que termine alguna promesa antes de continuar
2289
+ if (executing.length >= maxConcurrency) {
2290
+ // Esperar a que termine cualquier promesa
2291
+ const completedPromise = await Promise.race(executing);
2292
+ // Remover la promesa completada del array executing
2293
+ const index = executing.indexOf(completedPromise);
2294
+ if (index !== -1) {
2295
+ executing.splice(index, 1);
2296
+ }
2297
+ }
2298
+ // Limpiar promesas completadas del array executing
2299
+ promise.then(() => {
2300
+ const index = executing.indexOf(promise);
2301
+ if (index !== -1) {
2302
+ executing.splice(index, 1);
2303
+ }
2304
+ });
2305
+ }
2306
+ await Promise.all(results);
2307
+ // El progreso ya se termina automáticamente en showProgress() cuando se completa
2308
+ }
2309
+ export async function initCompileAll() {
2310
+ try {
2311
+ // Inicializar el gestor de progreso desde el inicio
2312
+ const progressManager = ProgressManager.getInstance();
2313
+ progressManager.startProgress();
2314
+ // Fase 1: Preparación inicial
2315
+ progressManager.updateProgress('🚀 Iniciando compilación...');
2316
+ // Limpiar estado de compilación anterior
2317
+ clearCompilationState();
2318
+ // Cargar cache al inicio
2319
+ progressManager.updateProgress('📦 Cargando cache...');
2320
+ await loadCache();
2321
+ lastProgressUpdate = 0; // Fase 2: Linting
2322
+ progressManager.updateProgress('🔍 Ejecutando linter...');
2323
+ const shouldContinue = await runLinter(false); // false = mostrar errores y preguntar si hay errores
2324
+ if (!shouldContinue) {
2325
+ // await displayCompilationSummary(env.VERBOSE === 'true');
2326
+ progressManager.endProgress();
2327
+ return;
2328
+ }
2329
+ const startTime = Date.now();
2330
+ const rawPathSource = env.PATH_SOURCE ?? '';
2331
+ const pathDist = env.PATH_DIST ?? '';
2332
+ const normalizedGlobPathSource = rawPathSource.replace(/\\/g, '/');
2333
+ const patterns = [
2334
+ `${normalizedGlobPathSource}/**/*.js`,
2335
+ `${normalizedGlobPathSource}/**/*.vue`,
2336
+ `${normalizedGlobPathSource}/**/*.ts`,
2337
+ `${normalizedGlobPathSource}/**/*.mjs`,
2338
+ `${normalizedGlobPathSource}/**/*.cjs`,
2339
+ ];
2340
+ logger.info(`📝 Compilando todos los archivos...`);
2341
+ logger.info(`🔜 Fuente: ${rawPathSource}`);
2342
+ logger.info(`🔚 Destino: ${pathDist}\n`);
2343
+ // Fase 3: TailwindCSS
2344
+ progressManager.updateProgress('🎨 Generando TailwindCSS...');
2345
+ const generateTailwindCSS = await loadTailwind();
2346
+ const resultTW = await generateTailwindCSS();
2347
+ if (typeof resultTW !== 'boolean') {
2348
+ if (resultTW?.success) {
2349
+ logger.info(`🎨 ${resultTW.message}\n`);
2350
+ }
2351
+ else {
2352
+ await handleCompilationError(new Error(`${resultTW.message}${resultTW.details ? '\n' + resultTW.details : ''}`), 'tailwind.config.js', 'tailwind', 'all', env.VERBOSE === 'true');
2353
+ }
2354
+ }
2355
+ // Fase 4: Recopilando archivos
2356
+ progressManager.updateProgress('📁 Recopilando archivos...');
2357
+ const filesToCompile = [];
2358
+ for await (const file of glob(patterns)) {
2359
+ if (file.endsWith('.d.ts')) {
2360
+ continue;
2361
+ }
2362
+ // Usar la ruta tal como viene de glob, sin modificar
2363
+ filesToCompile.push(file);
2364
+ }
2365
+ // ✨ OPTIMIZACIÓN: Determinar concurrencia basada en CPUs y tipo de operación
2366
+ let cpuCount = os.cpus().length;
2367
+ const fileCount = filesToCompile.length;
2368
+ // ✅ FIX: En algunos entornos (Docker, VMs), os.cpus() retorna 1
2369
+ // Establecer un mínimo razonable basado en el tipo de sistema
2370
+ if (cpuCount < 4) {
2371
+ // Probablemente un contenedor o VM mal configurado
2372
+ // Usar un valor conservador pero razonable
2373
+ cpuCount = 4;
2374
+ if (env.VERBOSE === 'true') {
2375
+ logger.warn(`⚠️ Solo se detectó ${os.cpus().length} CPU. Usando ${cpuCount} hilos por defecto.`);
2376
+ }
2377
+ }
2378
+ // ✅ OVERRIDE MANUAL: Permitir al usuario forzar un número de hilos
2379
+ if (process.env.VERSACOMPILER_MAX_THREADS) {
2380
+ const envThreads = parseInt(process.env.VERSACOMPILER_MAX_THREADS, 10);
2381
+ if (!isNaN(envThreads) && envThreads > 0) {
2382
+ cpuCount = envThreads;
2383
+ if (env.VERBOSE === 'true') {
2384
+ logger.info(`🔧 Usando ${cpuCount} hilos (variable de entorno VERSACOMPILER_MAX_THREADS)`);
2385
+ }
2386
+ }
2387
+ }
2388
+ // Obtener memoria total del sistema (no solo heap)
2389
+ const totalMemoryMB = os.totalmem() / (1024 * 1024);
2390
+ const freeMemoryMB = os.freemem() / (1024 * 1024);
2391
+ const memoryUsagePercent = ((totalMemoryMB - freeMemoryMB) / totalMemoryMB) * 100;
2392
+ let maxConcurrency;
2393
+ // ✅ ESTRATEGIA AGRESIVA: Usar TODOS los cores disponibles por defecto
2394
+ // La compilación de archivos es I/O bound, no CPU bound, así que podemos ser agresivos
2395
+ if (memoryUsagePercent > 90) {
2396
+ // Solo si la memoria del SISTEMA está al 90%, reducir hilos
2397
+ maxConcurrency = Math.max(4, Math.floor(cpuCount * 0.5));
2398
+ }
2399
+ else if (fileCount < 5) {
2400
+ // Muy pocos archivos: no tiene sentido más hilos que archivos
2401
+ maxConcurrency = fileCount;
2402
+ }
2403
+ else if (fileCount < 20) {
2404
+ // Pocos archivos: usar todos los CPUs
2405
+ maxConcurrency = cpuCount;
2406
+ }
2407
+ else if (fileCount < 50) {
2408
+ // Archivos moderados: 1.5x los CPUs (I/O permite más hilos)
2409
+ maxConcurrency = Math.floor(cpuCount * 1.5);
2410
+ }
2411
+ else if (fileCount < 200) {
2412
+ // Muchos archivos: 2x los CPUs
2413
+ maxConcurrency = cpuCount * 2;
2414
+ }
2415
+ else {
2416
+ // Proyectos grandes: máxima concurrencia
2417
+ maxConcurrency = Math.min(cpuCount * 3, 48);
2418
+ }
2419
+ // ✅ GARANTIZAR MÍNIMO RAZONABLE: Nunca menos de 4 hilos en proyectos > 10 archivos
2420
+ if (fileCount > 10 && maxConcurrency < 4) {
2421
+ maxConcurrency = 4;
2422
+ if (env.VERBOSE === 'true') {
2423
+ logger.info(`⚡ Ajustando a mínimo de 4 hilos para proyecto de ${fileCount} archivos`);
2424
+ }
2425
+ }
2426
+ // Fase 5: Configurando workers y precargando módulos
2427
+ progressManager.updateProgress('⚙️ Configurando workers...');
2428
+ // ✅ Logging mejorado con información de recursos
2429
+ logger.info(`🚀 Compilando ${fileCount} archivos con concurrencia optimizada (${maxConcurrency} hilos)...`);
2430
+ // ✅ SIEMPRE mostrar info de CPUs/memoria para detectar problemas
2431
+ logger.info(` 📊 CPUs detectados: ${os.cpus().length} (usando: ${cpuCount})`);
2432
+ logger.info(` 💾 Memoria libre: ${freeMemoryMB.toFixed(0)}MB / ${totalMemoryMB.toFixed(0)}MB (${(100 - memoryUsagePercent).toFixed(1)}% libre)`);
2433
+ logger.info(` ⚡ Hilos configurados: ${maxConcurrency} (${(maxConcurrency / cpuCount).toFixed(1)}x CPUs)`);
2434
+ // ⚠️ Warning si los hilos son muy pocos para el tamaño del proyecto
2435
+ const optimalThreads = Math.min(cpuCount * 2, 24);
2436
+ if (fileCount > 50 && maxConcurrency < optimalThreads * 0.5) {
2437
+ const chalk = await loadChalk();
2438
+ logger.warn(chalk.yellow(`⚠️ Solo se usarán ${maxConcurrency} hilos para ${fileCount} archivos.`));
2439
+ logger.info(chalk.yellow(` 💡 Tip: export VERSACOMPILER_MAX_THREADS=${optimalThreads}`));
2440
+ }
2441
+ // ⚠️ ADVERTENCIA: Si los hilos son muy bajos para el tamaño del proyecto
2442
+ if (fileCount > 50 && maxConcurrency < 8) {
2443
+ logger.warn(`⚠️ Solo se usarán ${maxConcurrency} hilos para ${fileCount} archivos.`);
2444
+ logger.warn(` 💡 Tip: Establece VERSACOMPILER_MAX_THREADS para forzar más hilos:`);
2445
+ logger.warn(` 💡 export VERSACOMPILER_MAX_THREADS=16`);
2446
+ }
2447
+ // Precargar módulos ANTES de iniciar la compilación concurrente
2448
+ // Esto evita que múltiples hilos intenten cargar node:crypto simultáneamente
2449
+ const moduleManager = OptimizedModuleManager.getInstance();
2450
+ const fileExtensions = new Set(filesToCompile.map(f => path.extname(f)));
2451
+ await moduleManager.preloadForContext('batch', fileExtensions);
2452
+ // Configurar worker pool para modo batch
2453
+ try {
2454
+ const { TypeScriptWorkerPool } = (await import('./typescript-worker-pool.js'));
2455
+ const workerPool = TypeScriptWorkerPool.getInstance();
2456
+ workerPool.setMode('batch');
2457
+ }
2458
+ catch {
2459
+ // Error silencioso en configuración del pool
2460
+ }
2461
+ // Fase 6: Compilación (el progreso continúa en compileWithConcurrencyLimit)
2462
+ progressManager.updateProgress(`🚀 Iniciando compilación de ${fileCount} archivos...`);
2463
+ await compileWithConcurrencyLimit(filesToCompile, maxConcurrency); // Guardar cache al final
2464
+ progressManager.updateProgress('💾 Guardando cache...');
2465
+ await saveCache();
2466
+ // ✨ FIX #4: Limpiar módulos no usados después de compilación masiva
2467
+ progressManager.updateProgress('🧹 Limpiando módulos no usados...');
2468
+ moduleManager.cleanupUnusedModules();
2469
+ const endTime = Date.now();
2470
+ const elapsedTime = showTimingForHumans(endTime - startTime); // Finalizar progreso
2471
+ progressManager.endProgress();
2472
+ // Mostrar resumen de compilación con tiempo total
2473
+ await displayCompilationSummary(env.VERBOSE === 'true', elapsedTime);
2474
+ }
2475
+ catch (error) {
2476
+ // Asegurar que el progreso termine en caso de error
2477
+ const progressManager = ProgressManager.getInstance();
2478
+ if (progressManager.isActive()) {
2479
+ progressManager.endProgress();
2480
+ }
2481
+ const errorMessage = error instanceof Error ? error.message : String(error);
2482
+ logger.error(`🚩 Error al compilar todos los archivos: ${errorMessage}`);
2483
+ // Registrar el error en el sistema unificado
2484
+ await handleCompilationError(error instanceof Error ? error : new Error(String(error)), 'compilación general', 'all', 'all', env.VERBOSE === 'true'); // Mostrar resumen incluso si hay errores generales
2485
+ await displayCompilationSummary(env.VERBOSE === 'true');
2486
+ }
2487
+ }
2488
+ /**
2489
+ * 🎨 Obtiene icono apropiado para cada etapa
2490
+ */
2491
+ function getStageIcon(stage) {
2492
+ const icons = {
2493
+ vue: '🎨',
2494
+ typescript: '📘',
2495
+ standardization: '💛',
2496
+ minification: '🗜️',
2497
+ tailwind: '🎨',
2498
+ 'file-read': '📖',
2499
+ default: '⚙️',
2500
+ };
2501
+ return icons[stage] ?? '⚙️';
2502
+ }
2503
+ /**
2504
+ * Crea una barra de progreso visual con porcentaje
2505
+ */
2506
+ function createProgressBarWithPercentage(percentage, width) {
2507
+ const filled = Math.round((percentage / 100) * width);
2508
+ const empty = width - filled;
2509
+ // Usar código directo para evitar problemas de importación
2510
+ const greenBar = '\x1b[32m' + '█'.repeat(filled) + '\x1b[0m';
2511
+ const grayBar = '\x1b[90m' + '░'.repeat(empty) + '\x1b[0m';
2512
+ return `${greenBar}${grayBar} ${percentage}%`;
2513
+ }
2514
+ // Función wrapper para compatibilidad con tests
2515
+ // ✨ FIX #5: Con timeout de 60 segundos para compilación individual
2516
+ export async function compileFile(filePath) {
2517
+ return await withTimeout(initCompile(filePath, true, 'individual'), 60000, `compilación de ${path.basename(filePath)}`);
2518
+ }
2519
+ export { WatchModeOptimizer };
2520
+ //# sourceMappingURL=compile.js.map