versacompiler 2.0.2 → 2.0.4

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.
@@ -2,11 +2,13 @@ import { createHash } from 'node:crypto';
2
2
  import { glob, mkdir, readFile, stat, unlink, writeFile, } from 'node:fs/promises';
3
3
  import os from 'node:os';
4
4
  import path from 'node:path';
5
- import { argv, cwd, env, stdout } from 'node:process';
5
+ import process, { argv, cwd, env } from 'node:process';
6
6
  // Lazy loading optimizations - Only import lightweight modules synchronously
7
- import { logger } from '../servicios/logger.js';
7
+ import { logger, setProgressManagerGetter } from '../servicios/logger.js';
8
8
  import { promptUser } from '../utils/promptUser.js';
9
9
  import { showTimingForHumans } from '../utils/utils.js';
10
+ // Configurar el getter del ProgressManager para el logger
11
+ setProgressManagerGetter(() => ProgressManager.getInstance());
10
12
  // Heavy dependencies will be loaded dynamically when needed
11
13
  let chalk;
12
14
  let ESLint;
@@ -355,8 +357,8 @@ const COMPILATION_ENV_VARS = [
355
357
  ];
356
358
  class SmartCompilationCache {
357
359
  cache = new Map();
358
- maxEntries = 500; // Máximo archivos en cache
359
- maxMemory = 100 * 1024 * 1024; // 100MB límite
360
+ maxEntries = 200; // Reducido para tests de estrés
361
+ maxMemory = 50 * 1024 * 1024; // 50MB límite (reducido)
360
362
  currentMemoryUsage = 0;
361
363
  // ✨ ISSUE #3: Sistema de vigilancia de dependencias
362
364
  fileWatchers = new Map(); // chokidar watchers
@@ -580,22 +582,31 @@ class SmartCompilationCache {
580
582
  // Si hay error, no cachear
581
583
  console.warn(`Warning: No se pudo cachear ${filePath}:`, error);
582
584
  }
583
- }
584
- /**
585
+ } /**
585
586
  * Aplica política LRU para liberar espacio
586
587
  */
587
588
  evictIfNeeded(newEntrySize) {
588
- // Verificar límite de entradas
589
- while (this.cache.size >= this.maxEntries) {
589
+ // Verificar límite de entradas más agresivamente
590
+ while (this.cache.size >= this.maxEntries * 0.8) {
591
+ // Limpiar cuando llegue al 80%
590
592
  this.evictLRU();
591
593
  }
592
- // Verificar límite de memoria
593
- while (this.currentMemoryUsage + newEntrySize > this.maxMemory &&
594
+ // Verificar límite de memoria más agresivamente
595
+ while (this.currentMemoryUsage + newEntrySize > this.maxMemory * 0.8 && // Limpiar cuando llegue al 80%
594
596
  this.cache.size > 0) {
595
597
  this.evictLRU();
596
598
  }
597
- }
598
- /**
599
+ // Eviction adicional si la memoria total del proceso es alta
600
+ const memUsage = process.memoryUsage();
601
+ const heapUsedMB = memUsage.heapUsed / (1024 * 1024);
602
+ if (heapUsedMB > 200 && this.cache.size > 50) {
603
+ // Si heap > 200MB, limpiar más agresivamente
604
+ const entriesToRemove = Math.min(this.cache.size - 50, 10);
605
+ for (let i = 0; i < entriesToRemove; i++) {
606
+ this.evictLRU();
607
+ }
608
+ }
609
+ } /**
599
610
  * Elimina la entrada menos recientemente usada
600
611
  */
601
612
  evictLRU() {
@@ -615,6 +626,23 @@ class SmartCompilationCache {
615
626
  }
616
627
  }
617
628
  }
629
+ /**
630
+ * Método público para limpiar entradas del cache cuando sea necesario
631
+ */
632
+ cleanOldEntries(maxEntriesToRemove = 20) {
633
+ let removedCount = 0;
634
+ for (let i = 0; i < maxEntriesToRemove && this.cache.size > 0; i++) {
635
+ const sizeBefore = this.cache.size;
636
+ this.evictLRU();
637
+ if (this.cache.size < sizeBefore) {
638
+ removedCount++;
639
+ }
640
+ else {
641
+ break; // No se pudo remover más entradas
642
+ }
643
+ }
644
+ return removedCount;
645
+ }
618
646
  /**
619
647
  * Carga el cache desde disco
620
648
  */
@@ -711,7 +739,7 @@ class SmartCompilationCache {
711
739
  persistent: false,
712
740
  ignoreInitial: true,
713
741
  depth: 1, // Solo primer nivel para performance
714
- ignored: /(^|[\/\\])\../, // Ignorar archivos ocultos
742
+ ignored: /(^|[/\\])\../, // Ignorar archivos ocultos
715
743
  });
716
744
  nodeModulesWatcher.on('addDir', (path) => {
717
745
  logger.info(`📦 Nueva dependencia instalada: ${path.split(/[/\\]/).pop()}`);
@@ -952,28 +980,42 @@ function clearCompilationState() {
952
980
  /**
953
981
  * Muestra un resumen detallado de todos los errores de compilación
954
982
  */
955
- async function displayCompilationSummary(isVerbose = false) {
983
+ async function displayCompilationSummary(isVerbose = false, totalTime) {
956
984
  const chalk = await loadChalk();
957
985
  if (compilationErrors.length === 0 && compilationResults.length === 0) {
958
986
  logger.info(chalk.green('✅ No hay errores de compilación para mostrar.'));
987
+ if (totalTime) {
988
+ logger.info(chalk.bold(`\n⏱️ TIEMPO TOTAL DE COMPILACIÓN: ${totalTime}`));
989
+ }
959
990
  return;
960
991
  }
961
- logger.info(chalk.bold('\n--- 📊 RESUMEN DE COMPILACIÓN ---'));
962
- // Mostrar estadísticas por etapa
992
+ // 🎨 Header moderno del resumen
993
+ const summaryLine = '━'.repeat(40);
994
+ logger.info('');
995
+ logger.info(chalk.bold.cyan('📊 Resumen de Compilación'));
996
+ logger.info(chalk.gray(summaryLine)); // ⏱️ Tiempo total con formato elegante
997
+ if (totalTime) {
998
+ logger.info(chalk.bold(`⏱️ Tiempo Total: ${chalk.green(totalTime)}`));
999
+ logger.info('');
1000
+ } // 🔧 Estadísticas por etapa con mejor formato
963
1001
  if (compilationResults.length > 0) {
964
- logger.info(chalk.blue('\n🔍 Estadísticas por etapa:'));
1002
+ logger.info(chalk.bold.blue('🔧 Estadísticas por Etapa:'));
965
1003
  for (const result of compilationResults) {
966
1004
  const totalFiles = result.success + result.errors;
967
1005
  const successRate = totalFiles > 0
968
1006
  ? Math.round((result.success / totalFiles) * 100)
969
1007
  : 0;
970
- const statusIcon = result.errors === 0 ? '✅' : '❌';
971
- const stageColor = await getStageColor(result.stage);
972
- const statusText = `${result.success} éxitos, ${result.errors} errores`;
973
- const coloredStatusText = result.errors === 0
974
- ? chalk.green(statusText)
975
- : chalk.red(statusText);
976
- logger.info(`${statusIcon} ${stageColor(result.stage)}: ${coloredStatusText} (${successRate}% éxito)`);
1008
+ // Iconos y colores dinámicos por etapa
1009
+ const stageIcon = getStageIcon(result.stage);
1010
+ const statusColor = result.errors === 0 ? chalk.green : chalk.red;
1011
+ const progressBar = createProgressBarWithPercentage(successRate, 20);
1012
+ logger.info(` ${stageIcon} ${chalk.bold(result.stage)}`);
1013
+ logger.info(` ${statusColor('●')} ${result.success}/${totalFiles} archivos ${statusColor(`(${successRate}%)`)}`);
1014
+ logger.info(` ${progressBar}`);
1015
+ if (result.errors > 0) {
1016
+ logger.info(` ${chalk.red('⚠')} ${result.errors} ${result.errors === 1 ? 'error' : 'errores'}`);
1017
+ }
1018
+ logger.info('');
977
1019
  }
978
1020
  }
979
1021
  // Mostrar errores detallados
@@ -1014,33 +1056,60 @@ async function displayCompilationSummary(isVerbose = false) {
1014
1056
  }
1015
1057
  }
1016
1058
  fileIndex++;
1017
- }
1018
- // Mostrar totales finales
1059
+ } // 📊 Mostrar totales finales con diseño moderno
1019
1060
  const totalErrors = compilationErrors.filter(e => e.severity === 'error').length;
1020
1061
  const totalWarnings = compilationErrors.filter(e => e.severity === 'warning').length;
1021
1062
  const totalFiles = errorsByFile.size;
1022
- logger.info(chalk.bold('\n--- 📈 ESTADÍSTICAS FINALES ---'));
1023
- logger.info(`📁 Archivos con errores: ${totalFiles}`);
1024
- logger.info(`❌ Total de errores: ${totalErrors}`);
1025
- logger.info(`⚠️ Total de advertencias: ${totalWarnings}`);
1063
+ // Header elegante para estadísticas finales
1064
+ const statLine = '═'.repeat(50);
1065
+ logger.info('');
1066
+ logger.info(chalk.bold.cyan(statLine));
1067
+ logger.info(chalk.bold.cyan(' 📊 RESUMEN FINAL'));
1068
+ logger.info(chalk.bold.cyan(statLine));
1069
+ // Estadísticas con iconos y colores modernos
1070
+ logger.info('');
1071
+ logger.info(chalk.bold('🎯 Resultados:'));
1072
+ logger.info(` 📁 Archivos afectados: ${chalk.cyan.bold(totalFiles)}`);
1073
+ logger.info(` ${totalErrors > 0 ? chalk.red('●') : chalk.green('○')} Errores: ${totalErrors > 0 ? chalk.red.bold(totalErrors) : chalk.green.bold('0')}`);
1074
+ logger.info(` ${totalWarnings > 0 ? chalk.yellow('●') : chalk.green('○')} Advertencias: ${totalWarnings > 0 ? chalk.yellow.bold(totalWarnings) : chalk.green.bold('0')}`);
1075
+ logger.info('');
1076
+ // Estado final con diseño visual atractivo
1026
1077
  if (totalErrors > 0) {
1027
- logger.info(chalk.red('🚨 Compilación completada con errores que requieren atención.'));
1078
+ logger.info(chalk.red.bold('🚨 COMPILACIÓN COMPLETADA CON ERRORES'));
1079
+ logger.info(chalk.red(' Por favor revisa y corrige los problemas anteriores.'));
1080
+ }
1081
+ else if (totalWarnings > 0) {
1082
+ logger.info(chalk.yellow.bold('⚠️ COMPILACIÓN COMPLETADA CON ADVERTENCIAS'));
1083
+ logger.info(chalk.yellow(' Considera revisar las advertencias anteriores.'));
1028
1084
  }
1029
1085
  else {
1030
- logger.info(chalk.yellow('✅ Compilación completada con solo advertencias.'));
1086
+ logger.info(chalk.green.bold('✅ COMPILACIÓN EXITOSA'));
1087
+ logger.info(chalk.green(' ¡Todos los archivos se compilaron sin problemas!'));
1031
1088
  }
1089
+ logger.info('');
1090
+ logger.info(chalk.bold.cyan(statLine));
1032
1091
  }
1033
1092
  else {
1034
- logger.info(chalk.green('✅ ¡Compilación exitosa sin errores!'));
1035
- }
1036
- logger.info(chalk.bold('--- FIN DEL RESUMEN ---\n'));
1093
+ // Caso exitoso sin errores
1094
+ const successLine = '═'.repeat(50);
1095
+ logger.info('');
1096
+ logger.info(chalk.bold.green(successLine));
1097
+ logger.info(chalk.bold.green(' ✨ ÉXITO'));
1098
+ logger.info(chalk.bold.green(successLine));
1099
+ logger.info('');
1100
+ logger.info(chalk.green.bold('🎉 COMPILACIÓN COMPLETADA EXITOSAMENTE'));
1101
+ logger.info(chalk.green(' ¡No se encontraron errores ni advertencias!'));
1102
+ logger.info('');
1103
+ logger.info(chalk.bold.green(successLine));
1104
+ }
1105
+ logger.info('');
1037
1106
  }
1038
1107
  /**
1039
- * Muestra errores del linter de forma detallada
1108
+ * Muestra errores del linter con formato visual moderno y profesional
1040
1109
  */
1041
1110
  async function displayLinterErrors(errors) {
1042
1111
  const chalk = await loadChalk();
1043
- logger.info(chalk.bold('--- Errores y Advertencias de Linting ---'));
1112
+ // Agrupar errores por archivo
1044
1113
  const errorsByFile = new Map();
1045
1114
  errors.forEach(error => {
1046
1115
  if (!errorsByFile.has(error.file)) {
@@ -1051,19 +1120,225 @@ async function displayLinterErrors(errors) {
1051
1120
  const totalErrors = errors.filter(e => e.severity === 'error').length;
1052
1121
  const totalWarnings = errors.filter(e => e.severity === 'warning').length;
1053
1122
  const totalFiles = errorsByFile.size;
1054
- logger.info(chalk.yellow(`📊 Resumen: ${totalErrors} errores, ${totalWarnings} advertencias en ${totalFiles} archivos\n`));
1055
- errorsByFile.forEach((fileErrors, filePath) => {
1056
- const baseName = path.basename(filePath);
1057
- logger.info(chalk.cyan(`\n📄 ${baseName}`));
1058
- fileErrors.forEach(error => {
1059
- const icon = error.severity === 'error' ? '❌' : '⚠️';
1060
- logger.info(`${icon} ${error.message}`);
1061
- if (error.help) {
1062
- logger.info(` └─ ${error.help}`);
1123
+ // Header estilo moderno con gradiente visual
1124
+ logger.info(chalk.bold.rgb(255, 120, 120)('╭─────────────────────────────────────────────────────────────╮'));
1125
+ logger.info(chalk.bold.rgb(255, 120, 120)('│ ') +
1126
+ chalk.bold.white('🔍 LINTER REPORT') +
1127
+ chalk.bold.rgb(255, 120, 120)(' │'));
1128
+ logger.info(chalk.bold.rgb(255, 120, 120)('╰─────────────────────────────────────────────────────────────╯'));
1129
+ // Resumen con iconos profesionales
1130
+ const errorIcon = totalErrors > 0 ? chalk.red('●') : chalk.green('○');
1131
+ const warningIcon = totalWarnings > 0 ? chalk.yellow('●') : chalk.green('○');
1132
+ logger.info('');
1133
+ logger.info(chalk.bold('📊 Summary:'));
1134
+ logger.info(` ${errorIcon} ${chalk.bold(totalErrors)} ${chalk.red('errors')}`);
1135
+ logger.info(` ${warningIcon} ${chalk.bold(totalWarnings)} ${chalk.yellow('warnings')}`);
1136
+ logger.info(` 📁 ${chalk.bold(totalFiles)} ${chalk.cyan('files')}`);
1137
+ logger.info('');
1138
+ if (totalErrors === 0 && totalWarnings === 0) {
1139
+ logger.info(chalk.green.bold('✨ All checks passed! No issues found.'));
1140
+ return;
1141
+ }
1142
+ // Mostrar errores por archivo con formato elegante
1143
+ let fileIndex = 1;
1144
+ for (const [filePath, fileErrors] of errorsByFile) {
1145
+ await displayFileErrorsGroup(filePath, fileErrors, fileIndex, totalFiles);
1146
+ fileIndex++;
1147
+ if (fileIndex <= totalFiles) {
1148
+ logger.info(chalk.gray('─'.repeat(80))); // Separador entre archivos
1149
+ }
1150
+ }
1151
+ // Footer con estadísticas
1152
+ logger.info('');
1153
+ logger.info(chalk.bold.rgb(255, 120, 120)('╭─────────────────────────────────────────────────────────────╮'));
1154
+ logger.info(chalk.bold.rgb(255, 120, 120)('│ ') +
1155
+ chalk.bold.white(`Found ${totalErrors + totalWarnings} issues in ${totalFiles} files`) +
1156
+ ' '.repeat(Math.max(0, 52 -
1157
+ `Found ${totalErrors + totalWarnings} issues in ${totalFiles} files`
1158
+ .length)) +
1159
+ chalk.bold.rgb(255, 120, 120)(' │'));
1160
+ logger.info(chalk.bold.rgb(255, 120, 120)('╰─────────────────────────────────────────────────────────────╯'));
1161
+ }
1162
+ /**
1163
+ * Muestra un grupo de errores para un archivo específico con formato moderno
1164
+ */
1165
+ async function displayFileErrorsGroup(filePath, fileErrors, _fileIndex, _totalFiles) {
1166
+ const chalk = await loadChalk();
1167
+ // Header del archivo con iconos de estado
1168
+ const errorCount = fileErrors.filter(e => e.severity === 'error').length;
1169
+ const warningCount = fileErrors.filter(e => e.severity === 'warning').length;
1170
+ const statusIcon = errorCount > 0 ? chalk.red('✕') : chalk.yellow('⚠');
1171
+ const fileIcon = filePath.endsWith('.vue')
1172
+ ? '🎨'
1173
+ : filePath.endsWith('.ts')
1174
+ ? '📘'
1175
+ : filePath.endsWith('.js')
1176
+ ? '📜'
1177
+ : '📄';
1178
+ logger.info('');
1179
+ logger.info(chalk.bold(`${statusIcon} ${fileIcon} ${chalk.cyan(path.relative(process.cwd(), filePath))}`));
1180
+ logger.info(chalk.gray(` ${errorCount} errors, ${warningCount} warnings`));
1181
+ logger.info('');
1182
+ // Mostrar cada error con formato elegante
1183
+ for (let i = 0; i < fileErrors.length; i++) {
1184
+ const error = fileErrors[i];
1185
+ await displayModernLinterError(error, filePath, i + 1, fileErrors.length);
1186
+ }
1187
+ }
1188
+ /**
1189
+ * Muestra un error individual con formato visual moderno tipo ESLint/Prettier
1190
+ */
1191
+ async function displayModernLinterError(error, filePath, errorIndex, totalErrorsInFile) {
1192
+ const chalk = await loadChalk();
1193
+ const fs = await import('node:fs/promises');
1194
+ // Determinar tipo y color del error
1195
+ const isError = error.severity === 'error';
1196
+ const typeColor = isError ? chalk.red : chalk.yellow;
1197
+ const typeIcon = isError ? '✕' : '⚠';
1198
+ const line = error.line || 1;
1199
+ const column = error.column || 1;
1200
+ const ruleId = error.ruleId || error.from || 'unknown';
1201
+ // Línea principal del error con formato moderno
1202
+ const errorHeader = ` ${typeColor(typeIcon)} ${chalk.bold(error.message)}`;
1203
+ const ruleInfo = `${chalk.gray(ruleId)}`;
1204
+ const locationInfo = `${chalk.blue(`${line}:${column}`)}`;
1205
+ logger.info(errorHeader);
1206
+ logger.info(` ${chalk.gray('at')} ${locationInfo} ${chalk.gray('·')} ${ruleInfo}`);
1207
+ // Mostrar código con contexto
1208
+ try {
1209
+ const absolutePath = path.resolve(filePath);
1210
+ const fileContent = await fs.readFile(absolutePath, 'utf-8');
1211
+ const lines = fileContent.split('\n');
1212
+ const lineNum = parseInt(line.toString()) - 1;
1213
+ if (lineNum >= 0 && lineNum < lines.length) {
1214
+ logger.info('');
1215
+ // Mostrar líneas de contexto con numeración elegante
1216
+ const startLine = Math.max(0, lineNum - 1);
1217
+ const endLine = Math.min(lines.length - 1, lineNum + 1);
1218
+ const maxLineNumWidth = (endLine + 1).toString().length;
1219
+ for (let i = startLine; i <= endLine; i++) {
1220
+ const currentLineNum = i + 1;
1221
+ const currentLine = lines[i] || '';
1222
+ const lineNumStr = currentLineNum
1223
+ .toString()
1224
+ .padStart(maxLineNumWidth, ' ');
1225
+ const isErrorLine = i === lineNum;
1226
+ if (isErrorLine) {
1227
+ // Línea con el error - destacada
1228
+ logger.info(` ${chalk.red('>')} ${chalk.gray(lineNumStr)} ${chalk.gray('│')} ${currentLine}`);
1229
+ // Indicador de posición del error
1230
+ const pointer = ' '.repeat(Math.max(0, column - 1)) + typeColor('^');
1231
+ logger.info(` ${chalk.gray(' ')} ${chalk.gray(' '.repeat(maxLineNumWidth))} ${chalk.gray('│')} ${pointer}`);
1232
+ }
1233
+ else {
1234
+ // Líneas de contexto
1235
+ logger.info(` ${chalk.gray(' ')} ${chalk.gray(lineNumStr)} ${chalk.gray('│')} ${chalk.gray(currentLine)}`);
1236
+ }
1063
1237
  }
1064
- });
1065
- });
1066
- logger.info(chalk.bold('--- Fin de Errores y Advertencias ---\n'));
1238
+ }
1239
+ }
1240
+ catch {
1241
+ // Si no se puede leer el archivo, mostrar formato simplificado
1242
+ logger.info(` ${chalk.gray('│')} ${chalk.gray('(Unable to read file content)')}`);
1243
+ }
1244
+ // Mostrar ayuda si está disponible
1245
+ if (error.help) {
1246
+ logger.info('');
1247
+ const helpText = error.help.replace(/^Regla \w+: /, '').trim();
1248
+ logger.info(` ${chalk.blue('💡')} ${chalk.blue('Help:')} ${chalk.gray(helpText)}`);
1249
+ }
1250
+ // Separador entre errores (solo si no es el último)
1251
+ if (errorIndex < totalErrorsInFile) {
1252
+ logger.info('');
1253
+ }
1254
+ }
1255
+ /**
1256
+ * Muestra un solo error del linter con formato visual mejorado
1257
+ * @deprecated Use displayModernLinterError instead
1258
+ */
1259
+ async function _displaySingleLinterError(error, filePath) {
1260
+ const chalk = await loadChalk();
1261
+ const fs = await import('node:fs/promises');
1262
+ const icon = error.severity === 'error' ? '×' : '⚠';
1263
+ const ruleInfo = error.help || '';
1264
+ const line = error.line || 'N/A';
1265
+ const column = error.column || 10; // Columna por defecto si no está disponible
1266
+ // Línea principal del error
1267
+ const mainErrorLine = `${chalk.red(icon)} ${chalk.cyan(`${error.from}(${ruleInfo.replace(/^Regla \w+: /, '')})`)}: ${error.message}`;
1268
+ logger.info(mainErrorLine);
1269
+ // Intentar leer el contenido del archivo para mostrar contexto
1270
+ try {
1271
+ const absolutePath = path.resolve(filePath);
1272
+ const fileContent = await fs.readFile(absolutePath, 'utf-8');
1273
+ const lines = fileContent.split('\n');
1274
+ const lineNum = parseInt(line.toString()) - 1; // Convertir a índice 0-based
1275
+ if (lineNum >= 0 && lineNum < lines.length) {
1276
+ // Mostrar ubicación
1277
+ logger.info(chalk.blue(` ╭─[${filePath}:${line}:${column}]`));
1278
+ // Mostrar líneas de contexto
1279
+ const startLine = Math.max(0, lineNum - 1);
1280
+ const endLine = Math.min(lines.length - 1, lineNum + 1);
1281
+ for (let i = startLine; i <= endLine; i++) {
1282
+ const currentLineNum = i + 1;
1283
+ const currentLine = lines[i] || '';
1284
+ const prefix = currentLineNum.toString().padStart(2, ' ');
1285
+ if (i === lineNum) {
1286
+ // Línea con el error
1287
+ logger.info(chalk.blue(` ${prefix} │ `) + currentLine);
1288
+ // Mostrar el indicador de error
1289
+ const indent = ' '.repeat(prefix.length + 3); // Espacios para alinear
1290
+ const pointer = ' '.repeat(Math.max(0, (column || 1) - 1)) +
1291
+ chalk.red('───────┬──────');
1292
+ logger.info(chalk.blue(indent + '·') + pointer);
1293
+ // Mensaje de ubicación específica
1294
+ const messageIndent = ' '.repeat(Math.max(0, (column || 1) + 6));
1295
+ logger.info(chalk.blue(indent + '·') +
1296
+ messageIndent +
1297
+ chalk.red('╰── ') +
1298
+ chalk.gray(getErrorLocationMessage(error)));
1299
+ }
1300
+ else {
1301
+ // Líneas de contexto
1302
+ logger.info(chalk.blue(` ${prefix} │ `) + chalk.gray(currentLine));
1303
+ }
1304
+ }
1305
+ logger.info(chalk.blue(' ╰────'));
1306
+ }
1307
+ }
1308
+ catch {
1309
+ // Si no se puede leer el archivo, mostrar formato simplificado
1310
+ logger.info(chalk.blue(` ╭─[${filePath}:${line}:${column}]`));
1311
+ logger.info(chalk.blue(' │ ') +
1312
+ chalk.gray('(No se pudo leer el contenido del archivo)'));
1313
+ logger.info(chalk.blue(' ╰────'));
1314
+ }
1315
+ // Mostrar ayuda si está disponible
1316
+ if (error.help) {
1317
+ const helpMessage = error.help.replace(/^Regla \w+: /, '');
1318
+ logger.info(chalk.blue(' help: ') + chalk.yellow(helpMessage));
1319
+ }
1320
+ logger.info(''); // Espacio entre errores
1321
+ }
1322
+ /**
1323
+ * Genera un mensaje descriptivo para la ubicación específica del error
1324
+ */
1325
+ function getErrorLocationMessage(error) {
1326
+ if (error.message.includes('declared but never used')) {
1327
+ const match = error.message.match(/'([^']+)'/);
1328
+ if (match) {
1329
+ return `'${match[1]}' is declared here`;
1330
+ }
1331
+ }
1332
+ if (error.message.includes('Unexpected var')) {
1333
+ return 'var declaration found here';
1334
+ }
1335
+ if (error.message.includes('never reassigned')) {
1336
+ const match = error.message.match(/'([^']+)'/);
1337
+ if (match) {
1338
+ return `'${match[1]}' is assigned here`;
1339
+ }
1340
+ }
1341
+ return 'error location';
1067
1342
  }
1068
1343
  /**
1069
1344
  * Obtiene el color apropiado para cada etapa de compilación
@@ -1154,7 +1429,7 @@ class WatchModeOptimizer {
1154
1429
  resolve({ success: true, cached: true });
1155
1430
  return;
1156
1431
  } // Configurar worker pool para modo watch
1157
- const { TypeScriptWorkerPool } = await import('./typescript-worker-pool.js');
1432
+ const { TypeScriptWorkerPool } = (await import('./typescript-worker-pool.js'));
1158
1433
  const workerPool = TypeScriptWorkerPool.getInstance();
1159
1434
  workerPool.setMode('watch');
1160
1435
  const result = await compileFn(filePath);
@@ -1185,11 +1460,12 @@ async function compileJS(inPath, outPath, mode = 'individual') {
1185
1460
  const moduleManager = OptimizedModuleManager.getInstance();
1186
1461
  // Timing de lectura
1187
1462
  let start = Date.now();
1188
- const extension = path.extname(inPath);
1189
- // Asegurar que el parser esté cargado
1463
+ const extension = path.extname(inPath); // Asegurar que el parser esté cargado
1190
1464
  await moduleManager.ensureModuleLoaded('parser');
1191
1465
  const getCodeFile = await loadParser();
1192
- let { code, error } = await getCodeFile(inPath);
1466
+ const result = await getCodeFile(inPath);
1467
+ let code = result.code;
1468
+ const error = result.error;
1193
1469
  timings.fileRead = Date.now() - start;
1194
1470
  if (error) {
1195
1471
  await handleCompilationError(error instanceof Error ? error : new Error(String(error)), inPath, 'file-read', mode, env.VERBOSE === 'true');
@@ -1201,15 +1477,13 @@ async function compileJS(inPath, outPath, mode = 'individual') {
1201
1477
  code === 'null') {
1202
1478
  await handleCompilationError(new Error('El archivo está vacío o no se pudo leer.'), inPath, 'file-read', mode, env.VERBOSE === 'true');
1203
1479
  throw new Error('El archivo está vacío o no se pudo leer.');
1204
- }
1205
- // Logs detallados solo en modo verbose + all
1206
- const shouldShowDetailedLogs = env.VERBOSE === 'true' && mode === 'all';
1207
- // Compilación de Vue
1480
+ } // Logs detallados en modo verbose
1481
+ const shouldShowDetailedLogs = env.VERBOSE === 'true'; // Compilación de Vue
1208
1482
  let vueResult;
1209
1483
  if (extension === '.vue') {
1210
1484
  start = Date.now();
1211
1485
  if (shouldShowDetailedLogs) {
1212
- logger.info(chalk.green(`💚 Precompilando VUE: ${inPath}`));
1486
+ logger.info(chalk.green(`💚 Precompilando VUE: ${path.basename(inPath)}`));
1213
1487
  }
1214
1488
  // Asegurar que el módulo Vue esté cargado
1215
1489
  await moduleManager.ensureModuleLoaded('vue');
@@ -1242,7 +1516,7 @@ async function compileJS(inPath, outPath, mode = 'individual') {
1242
1516
  if (extension === '.ts' || vueResult?.lang === 'ts') {
1243
1517
  start = Date.now();
1244
1518
  if (shouldShowDetailedLogs) {
1245
- logger.info(chalk.blue(`🔄️ Precompilando TS: ${inPath}`));
1519
+ logger.info(chalk.blue(`🔄️ Precompilando TS: ${path.basename(inPath)}`));
1246
1520
  }
1247
1521
  // Asegurar que el módulo TypeScript esté cargado
1248
1522
  await moduleManager.ensureModuleLoaded('typescript');
@@ -1277,10 +1551,9 @@ async function compileJS(inPath, outPath, mode = 'individual') {
1277
1551
  if (!code || code.trim().length === 0) {
1278
1552
  await handleCompilationError(new Error('El código TypeScript compilado está vacío.'), inPath, 'typescript', mode, env.VERBOSE === 'true');
1279
1553
  throw new Error('El código TypeScript compilado está vacío.');
1280
- }
1281
- // Estandarización
1554
+ } // Estandarización
1282
1555
  if (shouldShowDetailedLogs) {
1283
- logger.info(chalk.yellow(`💛 Estandarizando: ${inPath}`));
1556
+ logger.info(chalk.yellow(`💛 Estandarizando: ${path.basename(inPath)}`));
1284
1557
  }
1285
1558
  start = Date.now();
1286
1559
  // Asegurar que el módulo de transformaciones esté cargado
@@ -1305,7 +1578,7 @@ async function compileJS(inPath, outPath, mode = 'individual') {
1305
1578
  if (env.isPROD === 'true') {
1306
1579
  start = Date.now();
1307
1580
  if (shouldShowDetailedLogs) {
1308
- logger.info(chalk.red(`🤖 Minificando: ${inPath}`));
1581
+ logger.info(chalk.red(`🤖 Minificando: ${path.basename(inPath)}`));
1309
1582
  }
1310
1583
  // Asegurar que el módulo de minificación esté cargado
1311
1584
  await moduleManager.ensureModuleLoaded('minify');
@@ -1325,11 +1598,26 @@ async function compileJS(inPath, outPath, mode = 'individual') {
1325
1598
  }
1326
1599
  registerCompilationSuccess(inPath, 'minification');
1327
1600
  code = resultMinify.code;
1328
- }
1329
- // Escribir archivo final
1601
+ } // Escribir archivo final
1330
1602
  const destinationDir = path.dirname(outPath);
1331
1603
  await mkdir(destinationDir, { recursive: true });
1332
1604
  await writeFile(outPath, code, 'utf-8');
1605
+ // Logs de timing detallados en modo verbose
1606
+ if (shouldShowDetailedLogs) {
1607
+ const totalTime = Object.values(timings).reduce((sum, time) => sum + time, 0);
1608
+ logger.info(chalk.cyan(`⏱️ Timing para ${path.basename(inPath)}:`));
1609
+ if (timings.fileRead)
1610
+ logger.info(chalk.cyan(` 📖 Lectura: ${timings.fileRead}ms`));
1611
+ if (timings.vueCompile)
1612
+ logger.info(chalk.cyan(` 💚 Vue: ${timings.vueCompile}ms`));
1613
+ if (timings.tsCompile)
1614
+ logger.info(chalk.cyan(` 🔄️ TypeScript: ${timings.tsCompile}ms`));
1615
+ if (timings.standardization)
1616
+ logger.info(chalk.cyan(` 💛 Estandarización: ${timings.standardization}ms`));
1617
+ if (timings.minification)
1618
+ logger.info(chalk.cyan(` 🤖 Minificación: ${timings.minification}ms`));
1619
+ logger.info(chalk.cyan(` 🏁 Total: ${totalTime}ms`));
1620
+ }
1333
1621
  return {
1334
1622
  error: null,
1335
1623
  action: 'extension',
@@ -1362,6 +1650,20 @@ export async function initCompile(ruta, compileTailwind = true, mode = 'individu
1362
1650
  const startTime = Date.now();
1363
1651
  const file = normalizeRuta(ruta);
1364
1652
  const outFile = getOutputPath(file);
1653
+ // 🚀 Verificar cache antes de compilar (especialmente importante en modo watch)
1654
+ if (mode === 'watch' || mode === 'individual') {
1655
+ if (await shouldSkipFile(file)) {
1656
+ if (env.VERBOSE === 'true') {
1657
+ logger.info(`⏭️ Archivo omitido (cache): ${path.basename(file)}`);
1658
+ }
1659
+ return {
1660
+ success: true,
1661
+ cached: true,
1662
+ output: smartCache.getOutputPath(file) || outFile,
1663
+ action: 'cached',
1664
+ };
1665
+ }
1666
+ }
1365
1667
  if (mode === 'individual' && env.VERBOSE === 'true') {
1366
1668
  logger.info(`🔜 Fuente: ${file}`);
1367
1669
  }
@@ -1369,6 +1671,10 @@ export async function initCompile(ruta, compileTailwind = true, mode = 'individu
1369
1671
  if (result.error) {
1370
1672
  throw new Error(result.error);
1371
1673
  }
1674
+ // 🚀 Actualizar cache después de compilación exitosa (especialmente en modo watch)
1675
+ if (mode === 'watch' || mode === 'individual') {
1676
+ await smartCache.set(file, outFile);
1677
+ }
1372
1678
  const endTime = Date.now();
1373
1679
  const elapsedTime = showTimingForHumans(endTime - startTime);
1374
1680
  if (mode === 'individual') {
@@ -1399,6 +1705,183 @@ export async function initCompile(ruta, compileTailwind = true, mode = 'individu
1399
1705
  }
1400
1706
  // Variable para el último progreso mostrado (evitar spam)
1401
1707
  let lastProgressUpdate = 0;
1708
+ // Sistema de gestión de progreso persistente (como Jest)
1709
+ class ProgressManager {
1710
+ static instance;
1711
+ progressActive = false;
1712
+ lastProgressLine = '';
1713
+ logBuffer = [];
1714
+ originalConsoleLog;
1715
+ originalConsoleError;
1716
+ originalConsoleWarn;
1717
+ hasProgressLine = false;
1718
+ constructor() {
1719
+ // Guardar referencias originales
1720
+ this.originalConsoleLog = console.log;
1721
+ this.originalConsoleError = console.error;
1722
+ this.originalConsoleWarn = console.warn;
1723
+ }
1724
+ static getInstance() {
1725
+ if (!ProgressManager.instance) {
1726
+ ProgressManager.instance = new ProgressManager();
1727
+ }
1728
+ return ProgressManager.instance;
1729
+ }
1730
+ interceptConsole() {
1731
+ // Interceptar console.log y similares
1732
+ console.log = (...args) => {
1733
+ this.addLog(args.map(arg => String(arg)).join(' '));
1734
+ };
1735
+ console.error = (...args) => {
1736
+ this.addLog(args.map(arg => String(arg)).join(' '));
1737
+ };
1738
+ console.warn = (...args) => {
1739
+ this.addLog(args.map(arg => String(arg)).join(' '));
1740
+ };
1741
+ }
1742
+ restoreConsole() {
1743
+ console.log = this.originalConsoleLog;
1744
+ console.error = this.originalConsoleError;
1745
+ console.warn = this.originalConsoleWarn;
1746
+ }
1747
+ startProgress() {
1748
+ this.progressActive = true;
1749
+ this.logBuffer = [];
1750
+ this.hasProgressLine = false;
1751
+ this.interceptConsole();
1752
+ // 🎨 Header moderno de inicio de compilación
1753
+ const headerLine = '━'.repeat(48);
1754
+ process.stdout.write('\n\x1b[96m' + headerLine + '\x1b[0m\n');
1755
+ process.stdout.write('\x1b[96m│ \x1b[97m\x1b[1m🚀 Iniciando Compilación\x1b[0m\x1b[96m' +
1756
+ ' '.repeat(22) +
1757
+ '│\x1b[0m\n');
1758
+ process.stdout.write('\x1b[96m' + headerLine + '\x1b[0m\n');
1759
+ }
1760
+ updateProgress(progressText) {
1761
+ if (!this.progressActive)
1762
+ return;
1763
+ // Si hay logs pendientes, mostrarlos primero
1764
+ if (this.logBuffer.length > 0) {
1765
+ // Si ya hay una línea de progreso, limpiarla primero
1766
+ if (this.hasProgressLine) {
1767
+ process.stdout.write('\r\x1b[K');
1768
+ }
1769
+ // Escribir todos los logs pendientes
1770
+ for (const log of this.logBuffer) {
1771
+ process.stdout.write((this.hasProgressLine ? '\n' : '') + log + '\n');
1772
+ this.hasProgressLine = false;
1773
+ }
1774
+ this.logBuffer = [];
1775
+ } // Escribir separador elegante antes del progreso
1776
+ if (this.hasProgressLine) {
1777
+ process.stdout.write('\r\x1b[K');
1778
+ }
1779
+ else {
1780
+ process.stdout.write('\n\x1b[96m' + '▔'.repeat(50) + '\x1b[0m\n');
1781
+ }
1782
+ // 🎨 Barra de progreso con colores dinámicos
1783
+ const stage = this.getStageFromText(progressText);
1784
+ const { bgColor, textColor, icon } = this.getProgressColors(stage);
1785
+ const progressBar = '█'.repeat(3);
1786
+ const enhancedProgress = `\x1b[${bgColor}m\x1b[${textColor}m ${progressBar} ${icon} ${progressText} ${progressBar} \x1b[0m`;
1787
+ process.stdout.write(enhancedProgress);
1788
+ this.hasProgressLine = true;
1789
+ this.lastProgressLine = progressText;
1790
+ }
1791
+ addLog(message) {
1792
+ if (this.progressActive) {
1793
+ this.logBuffer.push(message);
1794
+ }
1795
+ else {
1796
+ this.originalConsoleLog(message);
1797
+ }
1798
+ }
1799
+ addImmediateLog(message) {
1800
+ if (this.progressActive) {
1801
+ if (this.hasProgressLine) {
1802
+ process.stdout.write('\r\x1b[K');
1803
+ }
1804
+ // Añadir un punto de separación visual para logs inmediatos
1805
+ process.stdout.write('\x1b[90m│\x1b[0m ' + message + '\n');
1806
+ this.hasProgressLine = false;
1807
+ }
1808
+ else {
1809
+ this.originalConsoleLog(message);
1810
+ }
1811
+ }
1812
+ endProgress() {
1813
+ if (this.progressActive) {
1814
+ if (this.hasProgressLine) {
1815
+ process.stdout.write('\n');
1816
+ }
1817
+ // Mostrar barra de progreso final completa antes del separador
1818
+ process.stdout.write('\n\x1b[33m' + '-'.repeat(50) + '\x1b[0m\n');
1819
+ const finalProgressBar = '█'.repeat(3);
1820
+ const finalProgress = `\x1b[42m\x1b[30m ${finalProgressBar} ✅ PROCESO COMPLETADO 100% ${finalProgressBar} \x1b[0m`;
1821
+ process.stdout.write(finalProgress + '\n');
1822
+ // 🎨 Footer moderno de finalización
1823
+ const footerLine = '━'.repeat(48);
1824
+ process.stdout.write('\x1b[92m' + footerLine + '\x1b[0m\n');
1825
+ process.stdout.write('\x1b[92m│ \x1b[97m\x1b[1m✅ ¡Compilación Completada!\x1b[0m\x1b[92m' +
1826
+ ' '.repeat(23) +
1827
+ '│\x1b[0m\n');
1828
+ process.stdout.write('\x1b[92m' + footerLine + '\x1b[0m\n\n');
1829
+ // Escribir logs finales pendientes
1830
+ if (this.logBuffer.length > 0) {
1831
+ for (const log of this.logBuffer) {
1832
+ process.stdout.write(log + '\n');
1833
+ }
1834
+ }
1835
+ }
1836
+ this.restoreConsole();
1837
+ this.progressActive = false;
1838
+ this.lastProgressLine = '';
1839
+ this.logBuffer = [];
1840
+ this.hasProgressLine = false;
1841
+ }
1842
+ isActive() {
1843
+ return this.progressActive;
1844
+ }
1845
+ /**
1846
+ * 🎨 Determina la etapa del progreso basándose en el texto
1847
+ */
1848
+ getStageFromText(text) {
1849
+ if (text.includes('Iniciando') || text.includes('Starting'))
1850
+ return 'start';
1851
+ if (text.includes('Tailwind') || text.includes('CSS'))
1852
+ return 'tailwind';
1853
+ if (text.includes('Recopilando') ||
1854
+ text.includes('archivos') ||
1855
+ text.includes('files'))
1856
+ return 'files';
1857
+ if (text.includes('Compilando') || text.includes('workers'))
1858
+ return 'compile';
1859
+ if (text.includes('cache') || text.includes('Guardando'))
1860
+ return 'cache';
1861
+ if (text.includes('linter') || text.includes('Linter'))
1862
+ return 'linter';
1863
+ if (text.includes('completado') || text.includes('Complete'))
1864
+ return 'complete';
1865
+ return 'default';
1866
+ }
1867
+ /**
1868
+ * 🌈 Obtiene colores dinámicos para cada etapa
1869
+ */
1870
+ getProgressColors(stage) {
1871
+ const colorSchemes = {
1872
+ start: { bgColor: '45', textColor: '97', icon: '🚀' }, // Cyan brillante
1873
+ tailwind: { bgColor: '105', textColor: '97', icon: '🎨' }, // Magenta
1874
+ files: { bgColor: '43', textColor: '30', icon: '📁' }, // Amarillo
1875
+ compile: { bgColor: '42', textColor: '30', icon: '⚙️' }, // Verde
1876
+ cache: { bgColor: '44', textColor: '97', icon: '💾' }, // Azul
1877
+ linter: { bgColor: '101', textColor: '97', icon: '🔍' }, // Rojo claro
1878
+ complete: { bgColor: '102', textColor: '30', icon: '✅' }, // Verde claro
1879
+ default: { bgColor: '100', textColor: '30', icon: '⏳' }, // Gris claro
1880
+ };
1881
+ const defaultColors = { bgColor: '100', textColor: '30', icon: '⏳' };
1882
+ return colorSchemes[stage] || defaultColors;
1883
+ }
1884
+ }
1402
1885
  // Función para ejecutar el linter antes de la compilación
1403
1886
  export async function runLinter(showResult = false) {
1404
1887
  const linterENV = env.linter;
@@ -1424,9 +1907,14 @@ export async function runLinter(showResult = false) {
1424
1907
  // Procesar resultados de ESLint
1425
1908
  if (Array.isArray(eslintResult.json)) {
1426
1909
  eslintResult.json.forEach((result) => {
1910
+ const filePath = result.filePath ||
1911
+ result.file ||
1912
+ 'archivo no especificado';
1427
1913
  linterErrors.push({
1428
- file: result.filePath ||
1429
- 'archivo no especificado',
1914
+ from: 'eslint',
1915
+ line: result.line || 'N/A',
1916
+ column: result.column || 1,
1917
+ file: filePath,
1430
1918
  message: result.message,
1431
1919
  severity: result.severity === 2
1432
1920
  ? 'error'
@@ -1443,9 +1931,16 @@ export async function runLinter(showResult = false) {
1443
1931
  if (fileResult.messages &&
1444
1932
  Array.isArray(fileResult.messages)) {
1445
1933
  fileResult.messages.forEach((msg) => {
1934
+ const filePath = fileResult.filePath ||
1935
+ fileResult.file ||
1936
+ 'archivo no especificado';
1446
1937
  linterErrors.push({
1447
- file: fileResult.filePath ||
1448
- 'archivo no especificado',
1938
+ from: 'eslint',
1939
+ line: msg.line ||
1940
+ 'N/A',
1941
+ column: msg.column ||
1942
+ 1,
1943
+ file: filePath,
1449
1944
  message: msg.message,
1450
1945
  severity: msg.severity ===
1451
1946
  2
@@ -1477,20 +1972,40 @@ export async function runLinter(showResult = false) {
1477
1972
  .then((oxlintResult) => {
1478
1973
  if (oxlintResult &&
1479
1974
  oxlintResult['json'] &&
1480
- Array.isArray(oxlintResult['json'])) {
1481
- oxlintResult['json'].forEach((result) => {
1975
+ Array.isArray(oxlintResult['json']['diagnostics'])) {
1976
+ oxlintResult['json']['diagnostics'].forEach((result) => {
1977
+ const filePath = result.filename ||
1978
+ result.file ||
1979
+ 'archivo no especificado';
1980
+ const lineNumber = result.labels &&
1981
+ result.labels[0] &&
1982
+ result.labels[0].span
1983
+ ? result.labels[0].span
1984
+ .line ||
1985
+ result.labels[0].span
1986
+ .start?.line
1987
+ : 'N/A';
1988
+ const columnNumber = result.labels &&
1989
+ result.labels[0] &&
1990
+ result.labels[0].span
1991
+ ? result.labels[0].span
1992
+ .column ||
1993
+ result.labels[0].span
1994
+ .start?.column
1995
+ : 1;
1482
1996
  linterErrors.push({
1483
- file: result.filename ||
1484
- result.file ||
1485
- 'archivo no especificado',
1997
+ from: 'oxlint',
1998
+ line: lineNumber,
1999
+ column: columnNumber,
2000
+ file: filePath,
1486
2001
  message: result.message,
1487
2002
  severity: typeof result.severity ===
1488
2003
  'string'
1489
2004
  ? result.severity.toLowerCase()
1490
2005
  : 'error',
1491
2006
  help: result.help ||
1492
- (result.rule_id
1493
- ? `Regla Oxlint: ${result.rule_id}`
2007
+ (result.code
2008
+ ? `Regla Oxlint: ${result.code}`
1494
2009
  : undefined),
1495
2010
  });
1496
2011
  });
@@ -1513,6 +2028,7 @@ export async function runLinter(showResult = false) {
1513
2028
  }
1514
2029
  await Promise.all(linterPromises);
1515
2030
  if (showResult) {
2031
+ // Modo --linter: Solo mostrar resultados sin preguntar
1516
2032
  if (linterErrors.length > 0) {
1517
2033
  await displayLinterErrors(linterErrors);
1518
2034
  }
@@ -1521,21 +2037,24 @@ export async function runLinter(showResult = false) {
1521
2037
  logger.info(chalk.green('✅ No se encontraron errores ni advertencias de linting.'));
1522
2038
  }
1523
2039
  }
2040
+ else {
2041
+ // Modo compilación: Mostrar errores si los hay y preguntar al usuario
2042
+ if (linterErrors.length > 0) {
2043
+ await displayLinterErrors(linterErrors);
2044
+ logger.warn('🚨 Se encontraron errores o advertencias durante el linting.');
2045
+ if (env.yes === 'false') {
2046
+ const result = await promptUser('¿Deseas continuar con la compilación a pesar de los errores de linting? (s/N): ');
2047
+ if (result.toLowerCase() !== 's') {
2048
+ logger.info('🛑 Compilación cancelada por el usuario.');
2049
+ proceedWithCompilation = false;
2050
+ }
2051
+ }
2052
+ }
2053
+ }
1524
2054
  }
1525
2055
  catch (parseError) {
1526
2056
  logger.warn(`Error parseando configuración de linter: ${parseError instanceof Error ? parseError.message : 'Error desconocido'}, omitiendo...`);
1527
2057
  }
1528
- if (!showResult && linterErrors.length > 0) {
1529
- await displayLinterErrors(linterErrors);
1530
- logger.warn('🚨 Se encontraron errores o advertencias durante el linting.');
1531
- if (env.yes === 'false') {
1532
- const result = await promptUser('¿Deseas continuar con la compilación a pesar de los errores de linting? (s/N): ');
1533
- if (result.toLowerCase() !== 's') {
1534
- logger.info('🛑 Compilación cancelada por el usuario.');
1535
- proceedWithCompilation = false;
1536
- }
1537
- }
1538
- }
1539
2058
  }
1540
2059
  return proceedWithCompilation;
1541
2060
  }
@@ -1557,23 +2076,47 @@ async function compileWithConcurrencyLimit(files, maxConcurrency = 8) {
1557
2076
  const total = files.length;
1558
2077
  let completed = 0;
1559
2078
  let skipped = 0;
1560
- let failed = 0; // Función para mostrar progreso
2079
+ let failed = 0;
2080
+ // Usar el gestor de progreso existente (ya iniciado en initCompileAll)
2081
+ const progressManager = ProgressManager.getInstance();
2082
+ // Variable para controlar el progreso inicial
2083
+ let hasShownInitialProgress = false;
2084
+ // Contador para limpieza periódica de memoria
2085
+ let compilationCounter = 0;
2086
+ const CLEANUP_INTERVAL = 20; // Limpiar cada 20 compilaciones
2087
+ // Función para mostrar progreso
1561
2088
  function showProgress() {
1562
2089
  const currentTotal = completed + skipped + failed;
1563
2090
  const progressBar = createProgressBar(currentTotal, total);
1564
2091
  const progressPercent = Math.round((currentTotal / total) * 100);
1565
- if (progressPercent > lastProgressUpdate + 1 ||
2092
+ // Mostrar progreso inicial cuando se inicie O cuando haya progreso real
2093
+ if ((currentTotal === 0 && !hasShownInitialProgress) ||
2094
+ (progressPercent > lastProgressUpdate + 1 && currentTotal > 0) ||
1566
2095
  currentTotal === total) {
1567
- stdout.write(`\r🚀 ${progressBar} [✅ ${completed} | ⏭️ ${skipped} | ❌ ${failed}]`);
2096
+ const progressText = `🚀 ${progressBar} [✅ ${completed} | ⏭️ ${skipped} | ❌ ${failed}]`;
2097
+ progressManager.updateProgress(progressText);
2098
+ if (currentTotal === 0) {
2099
+ hasShownInitialProgress = true;
2100
+ }
1568
2101
  lastProgressUpdate = progressPercent;
2102
+ // NO terminar el progreso aquí - se termina en initCompileAll
1569
2103
  }
1570
2104
  }
2105
+ // Mostrar progreso inicial
2106
+ showProgress();
1571
2107
  for (const file of files) {
1572
2108
  const promise = (async () => {
1573
2109
  try {
2110
+ // Log verbose: Iniciando compilación del archivo
2111
+ if (env.VERBOSE === 'true') {
2112
+ logger.info(`🔄 Compilando: ${path.basename(file)}`);
2113
+ }
1574
2114
  // Verificar cache antes de compilar
1575
2115
  if (await shouldSkipFile(file)) {
1576
2116
  skipped++;
2117
+ if (env.VERBOSE === 'true') {
2118
+ logger.info(`⏭️ Archivo omitido (cache): ${path.basename(file)}`);
2119
+ }
1577
2120
  showProgress();
1578
2121
  return {
1579
2122
  success: true,
@@ -1581,16 +2124,54 @@ async function compileWithConcurrencyLimit(files, maxConcurrency = 8) {
1581
2124
  output: smartCache.getOutputPath(file),
1582
2125
  };
1583
2126
  }
1584
- const result = await initCompile(file, false, 'batch'); // Actualizar cache si la compilación fue exitosa
2127
+ const result = await initCompile(file, false, 'batch');
2128
+ // Actualizar cache si la compilación fue exitosa
1585
2129
  if (result.success && result.output) {
1586
2130
  await smartCache.set(file, result.output);
2131
+ if (env.VERBOSE === 'true') {
2132
+ logger.info(`✅ Completado: ${path.basename(file)} → ${path.basename(result.output)}`);
2133
+ }
2134
+ }
2135
+ else if (env.VERBOSE === 'true') {
2136
+ logger.info(`❌ Error en: ${path.basename(file)}`);
1587
2137
  }
1588
2138
  completed++;
2139
+ compilationCounter++; // Limpieza periódica de memoria
2140
+ if (compilationCounter % CLEANUP_INTERVAL === 0) {
2141
+ // Forzar garbage collection si está disponible
2142
+ try {
2143
+ if (typeof globalThis.gc === 'function') {
2144
+ globalThis.gc();
2145
+ }
2146
+ }
2147
+ catch {
2148
+ // gc no disponible, continuar normalmente
2149
+ }
2150
+ // Limpiar cache si la memoria es alta
2151
+ const memUsage = process.memoryUsage();
2152
+ const heapUsedMB = memUsage.heapUsed / (1024 * 1024);
2153
+ if (heapUsedMB > 300) {
2154
+ // Si el heap supera 300MB
2155
+ const cacheEntries = smartCache.getStats().entries;
2156
+ if (cacheEntries > 50) {
2157
+ console.log(`[Memory] Heap alto (${heapUsedMB.toFixed(1)}MB), limpiando cache...`);
2158
+ // Limpiar entradas más antiguas del cache
2159
+ const removedEntries = smartCache.cleanOldEntries(20);
2160
+ if (removedEntries > 0) {
2161
+ console.log(`[Memory] Se removieron ${removedEntries} entradas del cache`);
2162
+ }
2163
+ }
2164
+ }
2165
+ }
1589
2166
  showProgress();
1590
2167
  return result;
1591
2168
  }
1592
2169
  catch (error) {
1593
2170
  failed++;
2171
+ if (env.VERBOSE === 'true') {
2172
+ const errorMsg = error instanceof Error ? error.message : String(error);
2173
+ logger.error(`💥 Falló: ${path.basename(file)} - ${errorMsg}`);
2174
+ }
1594
2175
  showProgress();
1595
2176
  return {
1596
2177
  success: false,
@@ -1606,18 +2187,26 @@ async function compileWithConcurrencyLimit(files, maxConcurrency = 8) {
1606
2187
  }
1607
2188
  }
1608
2189
  await Promise.all(results);
1609
- console.log('\n'); // Nueva línea después de la barra de progreso
2190
+ // El progreso ya se termina automáticamente en showProgress() cuando se completa
1610
2191
  }
1611
2192
  export async function initCompileAll() {
1612
2193
  try {
2194
+ // Inicializar el gestor de progreso desde el inicio
2195
+ const progressManager = ProgressManager.getInstance();
2196
+ progressManager.startProgress();
2197
+ // Fase 1: Preparación inicial
2198
+ progressManager.updateProgress('🚀 Iniciando compilación...');
1613
2199
  // Limpiar estado de compilación anterior
1614
2200
  clearCompilationState();
1615
2201
  // Cargar cache al inicio
2202
+ progressManager.updateProgress('📦 Cargando cache...');
1616
2203
  await loadCache();
1617
- lastProgressUpdate = 0;
1618
- const shouldContinue = await runLinter();
2204
+ lastProgressUpdate = 0; // Fase 2: Linting
2205
+ progressManager.updateProgress('🔍 Ejecutando linter...');
2206
+ const shouldContinue = await runLinter(false); // false = mostrar errores y preguntar si hay errores
1619
2207
  if (!shouldContinue) {
1620
2208
  // await displayCompilationSummary(env.VERBOSE === 'true');
2209
+ progressManager.endProgress();
1621
2210
  return;
1622
2211
  }
1623
2212
  const startTime = Date.now();
@@ -1633,7 +2222,9 @@ export async function initCompileAll() {
1633
2222
  ];
1634
2223
  logger.info(`📝 Compilando todos los archivos...`);
1635
2224
  logger.info(`🔜 Fuente: ${rawPathSource}`);
1636
- logger.info(`🔚 Destino: ${pathDist}\n`); // Generar TailwindCSS
2225
+ logger.info(`🔚 Destino: ${pathDist}\n`);
2226
+ // Fase 3: TailwindCSS
2227
+ progressManager.updateProgress('🎨 Generando TailwindCSS...');
1637
2228
  const generateTailwindCSS = await loadTailwind();
1638
2229
  const resultTW = await generateTailwindCSS();
1639
2230
  if (typeof resultTW !== 'boolean') {
@@ -1643,7 +2234,9 @@ export async function initCompileAll() {
1643
2234
  else {
1644
2235
  await handleCompilationError(new Error(`${resultTW.message}${resultTW.details ? '\n' + resultTW.details : ''}`), 'tailwind.config.js', 'tailwind', 'all', env.VERBOSE === 'true');
1645
2236
  }
1646
- } // Recopilar todos los archivos
2237
+ }
2238
+ // Fase 4: Recopilando archivos
2239
+ progressManager.updateProgress('📁 Recopilando archivos...');
1647
2240
  const filesToCompile = [];
1648
2241
  for await (const file of glob(patterns)) {
1649
2242
  if (file.endsWith('.d.ts')) {
@@ -1651,46 +2244,86 @@ export async function initCompileAll() {
1651
2244
  }
1652
2245
  // Usar la ruta tal como viene de glob, sin modificar
1653
2246
  filesToCompile.push(file);
1654
- }
1655
- // Determinar concurrencia óptima
2247
+ } // Determinar concurrencia óptima considerando memoria disponible
1656
2248
  const cpuCount = os.cpus().length;
1657
2249
  const fileCount = filesToCompile.length;
2250
+ const memUsage = process.memoryUsage();
2251
+ const availableMemoryMB = (memUsage.heapTotal - memUsage.heapUsed) / (1024 * 1024);
1658
2252
  let maxConcurrency;
1659
- if (fileCount < 10) {
1660
- maxConcurrency = Math.min(fileCount, cpuCount);
2253
+ // Ajustar concurrencia basado en memoria disponible y archivos
2254
+ if (availableMemoryMB < 100) {
2255
+ // Poca memoria disponible
2256
+ maxConcurrency = Math.min(2, cpuCount);
2257
+ }
2258
+ else if (fileCount < 10) {
2259
+ maxConcurrency = Math.min(fileCount, Math.min(cpuCount, 4));
1661
2260
  }
1662
2261
  else if (fileCount < 50) {
1663
- maxConcurrency = Math.min(cpuCount * 2, 12);
2262
+ maxConcurrency = Math.min(cpuCount, 6); // Reducido
1664
2263
  }
1665
2264
  else {
1666
- maxConcurrency = Math.min(cpuCount * 2, 16);
2265
+ maxConcurrency = Math.min(cpuCount, 8); // Reducido
1667
2266
  }
2267
+ // Fase 5: Configurando workers
2268
+ progressManager.updateProgress('⚙️ Configurando workers...');
1668
2269
  logger.info(`🚀 Compilando ${fileCount} archivos con concurrencia optimizada (${maxConcurrency} hilos)...`); // Configurar worker pool para modo batch
1669
2270
  try {
1670
- const { TypeScriptWorkerPool } = await import('./typescript-worker-pool.js');
2271
+ const { TypeScriptWorkerPool } = (await import('./typescript-worker-pool.js'));
1671
2272
  const workerPool = TypeScriptWorkerPool.getInstance();
1672
2273
  workerPool.setMode('batch');
1673
2274
  }
1674
2275
  catch {
1675
2276
  // Error silencioso en configuración del pool
1676
- }
1677
- await compileWithConcurrencyLimit(filesToCompile, maxConcurrency);
1678
- // Guardar cache al final
2277
+ } // Fase 6: Compilación (el progreso continúa en compileWithConcurrencyLimit)
2278
+ progressManager.updateProgress(`🚀 Iniciando compilación de ${fileCount} archivos...`);
2279
+ await compileWithConcurrencyLimit(filesToCompile, maxConcurrency); // Guardar cache al final
2280
+ progressManager.updateProgress('💾 Guardando cache...');
1679
2281
  await saveCache();
1680
2282
  const endTime = Date.now();
1681
- const elapsedTime = showTimingForHumans(endTime - startTime);
1682
- logger.info(`⏱️ Tiempo total de compilación: ${elapsedTime}\n`); // Mostrar resumen de compilación
1683
- await displayCompilationSummary(env.VERBOSE === 'true');
2283
+ const elapsedTime = showTimingForHumans(endTime - startTime); // Finalizar progreso
2284
+ progressManager.endProgress();
2285
+ // Mostrar resumen de compilación con tiempo total
2286
+ await displayCompilationSummary(env.VERBOSE === 'true', elapsedTime);
1684
2287
  }
1685
2288
  catch (error) {
2289
+ // Asegurar que el progreso termine en caso de error
2290
+ const progressManager = ProgressManager.getInstance();
2291
+ if (progressManager.isActive()) {
2292
+ progressManager.endProgress();
2293
+ }
1686
2294
  const errorMessage = error instanceof Error ? error.message : String(error);
1687
2295
  logger.error(`🚩 Error al compilar todos los archivos: ${errorMessage}`);
1688
2296
  // Registrar el error en el sistema unificado
1689
- await handleCompilationError(error instanceof Error ? error : new Error(String(error)), 'compilación general', 'all', 'all', env.VERBOSE === 'true');
1690
- // Mostrar resumen incluso si hay errores generales
2297
+ 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
1691
2298
  await displayCompilationSummary(env.VERBOSE === 'true');
1692
2299
  }
1693
2300
  }
2301
+ /**
2302
+ * 🎨 Obtiene icono apropiado para cada etapa
2303
+ */
2304
+ function getStageIcon(stage) {
2305
+ const icons = {
2306
+ vue: '🎨',
2307
+ typescript: '📘',
2308
+ standardization: '💛',
2309
+ minification: '🗜️',
2310
+ tailwind: '🎨',
2311
+ 'file-read': '📖',
2312
+ default: '⚙️',
2313
+ };
2314
+ return icons[stage] ?? '⚙️';
2315
+ }
2316
+ /**
2317
+ * Crea una barra de progreso visual con porcentaje
2318
+ */
2319
+ function createProgressBarWithPercentage(percentage, width) {
2320
+ const filled = Math.round((percentage / 100) * width);
2321
+ const empty = width - filled;
2322
+ // Usar código directo para evitar problemas de importación
2323
+ const greenBar = '\x1b[32m' + '█'.repeat(filled) + '\x1b[0m';
2324
+ const grayBar = '\x1b[90m' + '░'.repeat(empty) + '\x1b[0m';
2325
+ return `${greenBar}${grayBar} ${percentage}%`;
2326
+ }
1694
2327
  // Función wrapper para compatibilidad con tests
1695
2328
  export async function compileFile(filePath) {
1696
2329
  return await initCompile(filePath, true, 'individual');