versacompiler 2.0.7 → 2.0.8
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.
- package/dist/compiler/compile.js +26 -2332
- package/dist/compiler/error-reporter.js +38 -467
- package/dist/compiler/linter.js +1 -72
- package/dist/compiler/minify.js +1 -229
- package/dist/compiler/module-resolution-optimizer.js +1 -821
- package/dist/compiler/parser.js +1 -203
- package/dist/compiler/performance-monitor.js +56 -192
- package/dist/compiler/tailwindcss.js +1 -39
- package/dist/compiler/transform-optimizer.js +1 -392
- package/dist/compiler/transformTStoJS.js +1 -16
- package/dist/compiler/transforms.js +1 -550
- package/dist/compiler/typescript-compiler.js +2 -172
- package/dist/compiler/typescript-error-parser.js +10 -281
- package/dist/compiler/typescript-manager.js +2 -273
- package/dist/compiler/typescript-sync-validator.js +31 -295
- package/dist/compiler/typescript-worker-pool.js +1 -842
- package/dist/compiler/typescript-worker-thread.cjs +41 -466
- package/dist/compiler/typescript-worker.js +1 -339
- package/dist/compiler/vuejs.js +37 -392
- package/dist/hrm/VueHRM.js +1 -353
- package/dist/hrm/errorScreen.js +1 -83
- package/dist/hrm/getInstanciaVue.js +1 -313
- package/dist/hrm/initHRM.js +1 -141
- package/dist/main.js +7 -347
- package/dist/servicios/browserSync.js +5 -501
- package/dist/servicios/file-watcher.js +4 -379
- package/dist/servicios/logger.js +3 -63
- package/dist/servicios/readConfig.js +105 -430
- package/dist/utils/excluded-modules.js +1 -36
- package/dist/utils/module-resolver.js +1 -466
- package/dist/utils/promptUser.js +2 -48
- package/dist/utils/proxyValidator.js +1 -68
- package/dist/utils/resolve-bin.js +1 -48
- package/dist/utils/utils.js +1 -21
- package/dist/utils/vue-types-setup.js +241 -435
- package/dist/wrappers/eslint-node.js +1 -145
- package/dist/wrappers/oxlint-node.js +1 -120
- package/dist/wrappers/tailwind-node.js +1 -92
- package/package.json +36 -35
package/dist/compiler/compile.js
CHANGED
|
@@ -1,2332 +1,26 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
let
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
let
|
|
20
|
-
let
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
let
|
|
26
|
-
|
|
27
|
-
class OptimizedModuleManager {
|
|
28
|
-
static instance;
|
|
29
|
-
isInitialized = false;
|
|
30
|
-
loadedModules = new Set();
|
|
31
|
-
// ✨ NUEVAS OPTIMIZACIONES
|
|
32
|
-
modulePool = new Map(); // Pool de instancias reutilizables
|
|
33
|
-
loadingPromises = new Map(); // Prevenir cargas duplicadas
|
|
34
|
-
usageStats = new Map(); // Estadísticas de uso
|
|
35
|
-
preloadQueue = new Set(); // Cola de precarga
|
|
36
|
-
backgroundLoader = null; // Cargador en background
|
|
37
|
-
// Módulos críticos que siempre se precargan
|
|
38
|
-
HOT_MODULES = ['chalk', 'parser'];
|
|
39
|
-
// Contexto actual para optimizar cargas
|
|
40
|
-
currentContext = null;
|
|
41
|
-
constructor() {
|
|
42
|
-
// Iniciar precarga en background inmediatamente
|
|
43
|
-
this.startBackgroundPreloading();
|
|
44
|
-
}
|
|
45
|
-
static getInstance() {
|
|
46
|
-
if (!OptimizedModuleManager.instance) {
|
|
47
|
-
OptimizedModuleManager.instance = new OptimizedModuleManager();
|
|
48
|
-
}
|
|
49
|
-
return OptimizedModuleManager.instance;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* ✨ NUEVO: Precarga en background para módulos críticos
|
|
53
|
-
*/
|
|
54
|
-
startBackgroundPreloading() {
|
|
55
|
-
this.backgroundLoader = this.preloadCriticalModules();
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* ✨ NUEVO: Precarga módulos críticos en background
|
|
59
|
-
*/
|
|
60
|
-
async preloadCriticalModules() {
|
|
61
|
-
try {
|
|
62
|
-
// Precargar módulos críticos de forma asíncrona
|
|
63
|
-
const preloadPromises = this.HOT_MODULES.map(moduleName => this.ensureModuleLoaded(moduleName).catch(() => {
|
|
64
|
-
// Silenciar errores de precarga, se intentará cargar después
|
|
65
|
-
}));
|
|
66
|
-
await Promise.allSettled(preloadPromises);
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
// Fallos en precarga no deben afectar la funcionalidad principal
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* ✨ MEJORADO: Precarga contextual basada en tipos de archivo
|
|
74
|
-
*/
|
|
75
|
-
async preloadForContext(context, fileTypes = new Set()) {
|
|
76
|
-
this.currentContext = context;
|
|
77
|
-
// Esperar que termine la precarga crítica si está en progreso
|
|
78
|
-
if (this.backgroundLoader) {
|
|
79
|
-
await this.backgroundLoader;
|
|
80
|
-
}
|
|
81
|
-
const toPreload = []; // Precarga basada en contexto
|
|
82
|
-
if (context === 'batch' || context === 'watch') {
|
|
83
|
-
// En batch/watch, precargar todos los módulos comunes
|
|
84
|
-
toPreload.push('transforms', 'vue', 'typescript', 'module-resolution-optimizer');
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
// En individual, cargar solo según tipos de archivo detectados
|
|
88
|
-
if (fileTypes.has('.vue'))
|
|
89
|
-
toPreload.push('vue');
|
|
90
|
-
if (fileTypes.has('.ts') || fileTypes.has('.vue'))
|
|
91
|
-
toPreload.push('typescript');
|
|
92
|
-
if (!this.loadedModules.has('transforms'))
|
|
93
|
-
toPreload.push('transforms');
|
|
94
|
-
}
|
|
95
|
-
// Precargar en paralelo
|
|
96
|
-
const preloadPromises = toPreload.map(moduleName => this.ensureModuleLoaded(moduleName).catch(() => {
|
|
97
|
-
// Log warning pero no fallar
|
|
98
|
-
console.warn(`Warning: No se pudo precargar módulo ${moduleName}`);
|
|
99
|
-
}));
|
|
100
|
-
await Promise.allSettled(preloadPromises);
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* ✨ MEJORADO: Carga inteligente con pooling y deduplicación
|
|
104
|
-
*/
|
|
105
|
-
async ensureModuleLoaded(moduleName) {
|
|
106
|
-
// 1. Verificar pool de módulos primero
|
|
107
|
-
if (this.modulePool.has(moduleName)) {
|
|
108
|
-
this.updateUsageStats(moduleName);
|
|
109
|
-
return this.modulePool.get(moduleName);
|
|
110
|
-
}
|
|
111
|
-
// 2. Verificar si ya está cargando (deduplicación)
|
|
112
|
-
if (this.loadingPromises.has(moduleName)) {
|
|
113
|
-
return this.loadingPromises.get(moduleName);
|
|
114
|
-
}
|
|
115
|
-
// 3. Iniciar carga
|
|
116
|
-
const loadPromise = this.loadModuleInternal(moduleName);
|
|
117
|
-
this.loadingPromises.set(moduleName, loadPromise);
|
|
118
|
-
try {
|
|
119
|
-
const moduleInstance = await loadPromise;
|
|
120
|
-
// 4. Almacenar en pool y estadísticas
|
|
121
|
-
this.modulePool.set(moduleName, moduleInstance);
|
|
122
|
-
this.loadedModules.add(moduleName);
|
|
123
|
-
this.updateUsageStats(moduleName);
|
|
124
|
-
return moduleInstance;
|
|
125
|
-
}
|
|
126
|
-
finally {
|
|
127
|
-
// 5. Limpiar promesa de carga
|
|
128
|
-
this.loadingPromises.delete(moduleName);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* ✨ NUEVO: Actualiza estadísticas de uso para optimizaciones futuras
|
|
133
|
-
*/
|
|
134
|
-
updateUsageStats(moduleName) {
|
|
135
|
-
const currentCount = this.usageStats.get(moduleName) || 0;
|
|
136
|
-
this.usageStats.set(moduleName, currentCount + 1);
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* ✨ MEJORADO: Carga interna de módulos con mejor manejo de errores
|
|
140
|
-
*/ async loadModuleInternal(moduleName) {
|
|
141
|
-
switch (moduleName) {
|
|
142
|
-
case 'chalk':
|
|
143
|
-
return this.loadChalk();
|
|
144
|
-
case 'parser':
|
|
145
|
-
return this.loadParser();
|
|
146
|
-
case 'transforms':
|
|
147
|
-
return this.loadTransforms();
|
|
148
|
-
case 'vue':
|
|
149
|
-
return this.loadVue();
|
|
150
|
-
case 'typescript':
|
|
151
|
-
return this.loadTypeScript();
|
|
152
|
-
case 'minify':
|
|
153
|
-
return this.loadMinify();
|
|
154
|
-
case 'tailwind':
|
|
155
|
-
return this.loadTailwind();
|
|
156
|
-
case 'linter':
|
|
157
|
-
return this.loadLinter();
|
|
158
|
-
case 'transform-optimizer':
|
|
159
|
-
return this.loadTransformOptimizer();
|
|
160
|
-
case 'module-resolution-optimizer':
|
|
161
|
-
return this.loadModuleResolutionOptimizer();
|
|
162
|
-
default:
|
|
163
|
-
throw new Error(`Módulo desconocido: ${moduleName}`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
// ✨ Métodos de carga específicos optimizados
|
|
167
|
-
async loadChalk() {
|
|
168
|
-
if (!chalk) {
|
|
169
|
-
chalk = (await import('chalk')).default;
|
|
170
|
-
}
|
|
171
|
-
return chalk;
|
|
172
|
-
}
|
|
173
|
-
async loadParser() {
|
|
174
|
-
if (!getCodeFile) {
|
|
175
|
-
const parserModule = await import('./parser.js');
|
|
176
|
-
getCodeFile = parserModule.getCodeFile;
|
|
177
|
-
}
|
|
178
|
-
return getCodeFile;
|
|
179
|
-
}
|
|
180
|
-
async loadTransforms() {
|
|
181
|
-
if (!estandarizaCode) {
|
|
182
|
-
const transformsModule = await import('./transforms.js');
|
|
183
|
-
estandarizaCode = transformsModule.estandarizaCode;
|
|
184
|
-
}
|
|
185
|
-
return estandarizaCode;
|
|
186
|
-
}
|
|
187
|
-
async loadVue() {
|
|
188
|
-
if (!preCompileVue) {
|
|
189
|
-
const vueModule = await import('./vuejs.js');
|
|
190
|
-
preCompileVue = vueModule.preCompileVue;
|
|
191
|
-
}
|
|
192
|
-
return preCompileVue;
|
|
193
|
-
}
|
|
194
|
-
async loadTypeScript() {
|
|
195
|
-
if (!preCompileTS) {
|
|
196
|
-
const typescriptModule = await import('./typescript-manager.js');
|
|
197
|
-
preCompileTS = typescriptModule.preCompileTS;
|
|
198
|
-
}
|
|
199
|
-
return preCompileTS;
|
|
200
|
-
}
|
|
201
|
-
async loadMinify() {
|
|
202
|
-
if (!minifyJS) {
|
|
203
|
-
const minifyModule = await import('./minify.js');
|
|
204
|
-
minifyJS = minifyModule.minifyJS;
|
|
205
|
-
}
|
|
206
|
-
return minifyJS;
|
|
207
|
-
}
|
|
208
|
-
async loadTailwind() {
|
|
209
|
-
if (!generateTailwindCSS) {
|
|
210
|
-
const tailwindModule = await import('./tailwindcss.js');
|
|
211
|
-
generateTailwindCSS = tailwindModule.generateTailwindCSS;
|
|
212
|
-
}
|
|
213
|
-
return generateTailwindCSS;
|
|
214
|
-
}
|
|
215
|
-
async loadLinter() {
|
|
216
|
-
if (!ESLint || !OxLint) {
|
|
217
|
-
const linterModule = await import('./linter.js');
|
|
218
|
-
ESLint = linterModule.ESLint;
|
|
219
|
-
OxLint = linterModule.OxLint;
|
|
220
|
-
}
|
|
221
|
-
return { ESLint, OxLint };
|
|
222
|
-
}
|
|
223
|
-
async loadTransformOptimizer() {
|
|
224
|
-
if (!TransformOptimizer) {
|
|
225
|
-
const transformModule = await import('./transform-optimizer.js');
|
|
226
|
-
TransformOptimizer =
|
|
227
|
-
transformModule.TransformOptimizer.getInstance();
|
|
228
|
-
}
|
|
229
|
-
return TransformOptimizer;
|
|
230
|
-
}
|
|
231
|
-
async loadModuleResolutionOptimizer() {
|
|
232
|
-
if (!ModuleResolutionOptimizer) {
|
|
233
|
-
const resolutionModule = await import('./module-resolution-optimizer.js');
|
|
234
|
-
ModuleResolutionOptimizer =
|
|
235
|
-
resolutionModule.ModuleResolutionOptimizer.getInstance();
|
|
236
|
-
}
|
|
237
|
-
return ModuleResolutionOptimizer;
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* ✨ NUEVO: Obtiene estadísticas de performance del manager
|
|
241
|
-
*/
|
|
242
|
-
getPerformanceStats() {
|
|
243
|
-
const sortedByUsage = Array.from(this.usageStats.entries())
|
|
244
|
-
.sort((a, b) => b[1] - a[1])
|
|
245
|
-
.slice(0, 5)
|
|
246
|
-
.map(([name]) => name);
|
|
247
|
-
return {
|
|
248
|
-
loadedModules: Array.from(this.loadedModules),
|
|
249
|
-
usageStats: Object.fromEntries(this.usageStats),
|
|
250
|
-
poolSize: this.modulePool.size,
|
|
251
|
-
loadingInProgress: Array.from(this.loadingPromises.keys()),
|
|
252
|
-
mostUsedModules: sortedByUsage,
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* ✨ NUEVO: Limpia módulos no utilizados para liberar memoria
|
|
257
|
-
*/
|
|
258
|
-
cleanupUnusedModules() {
|
|
259
|
-
const threshold = 1; // Mínimo de usos para mantener en pool
|
|
260
|
-
for (const [moduleName, usageCount] of this.usageStats) {
|
|
261
|
-
if (usageCount < threshold &&
|
|
262
|
-
!this.HOT_MODULES.includes(moduleName)) {
|
|
263
|
-
this.modulePool.delete(moduleName);
|
|
264
|
-
this.loadedModules.delete(moduleName);
|
|
265
|
-
this.usageStats.delete(moduleName);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* Resetea el estado del manager (útil para tests)
|
|
271
|
-
*/
|
|
272
|
-
reset() {
|
|
273
|
-
this.isInitialized = false;
|
|
274
|
-
this.loadedModules.clear();
|
|
275
|
-
this.modulePool.clear();
|
|
276
|
-
this.loadingPromises.clear();
|
|
277
|
-
this.usageStats.clear();
|
|
278
|
-
this.preloadQueue.clear();
|
|
279
|
-
this.currentContext = null;
|
|
280
|
-
this.backgroundLoader = null;
|
|
281
|
-
// Reiniciar precarga crítica
|
|
282
|
-
this.startBackgroundPreloading();
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
// Lazy loading helper functions
|
|
286
|
-
async function loadChalk() {
|
|
287
|
-
if (!chalk) {
|
|
288
|
-
chalk = (await import('chalk')).default;
|
|
289
|
-
}
|
|
290
|
-
return chalk;
|
|
291
|
-
}
|
|
292
|
-
async function loadLinter() {
|
|
293
|
-
if (!ESLint || !OxLint) {
|
|
294
|
-
const linterModule = await import('./linter.js');
|
|
295
|
-
ESLint = linterModule.ESLint;
|
|
296
|
-
OxLint = linterModule.OxLint;
|
|
297
|
-
}
|
|
298
|
-
return { ESLint, OxLint };
|
|
299
|
-
}
|
|
300
|
-
async function loadMinify() {
|
|
301
|
-
if (!minifyJS) {
|
|
302
|
-
const minifyModule = await import('./minify.js');
|
|
303
|
-
minifyJS = minifyModule.minifyJS;
|
|
304
|
-
}
|
|
305
|
-
return minifyJS;
|
|
306
|
-
}
|
|
307
|
-
async function loadParser() {
|
|
308
|
-
if (!getCodeFile) {
|
|
309
|
-
const parserModule = await import('./parser.js');
|
|
310
|
-
getCodeFile = parserModule.getCodeFile;
|
|
311
|
-
}
|
|
312
|
-
return getCodeFile;
|
|
313
|
-
}
|
|
314
|
-
async function loadTailwind() {
|
|
315
|
-
if (!generateTailwindCSS) {
|
|
316
|
-
const tailwindModule = await import('./tailwindcss.js');
|
|
317
|
-
generateTailwindCSS = tailwindModule.generateTailwindCSS;
|
|
318
|
-
}
|
|
319
|
-
return generateTailwindCSS;
|
|
320
|
-
}
|
|
321
|
-
async function loadTransforms() {
|
|
322
|
-
if (!estandarizaCode) {
|
|
323
|
-
const transformsModule = await import('./transforms.js');
|
|
324
|
-
estandarizaCode = transformsModule.estandarizaCode;
|
|
325
|
-
}
|
|
326
|
-
return estandarizaCode;
|
|
327
|
-
}
|
|
328
|
-
async function loadTypeScript() {
|
|
329
|
-
if (!preCompileTS) {
|
|
330
|
-
const typescriptModule = await import('./typescript-manager.js');
|
|
331
|
-
preCompileTS = typescriptModule.preCompileTS;
|
|
332
|
-
}
|
|
333
|
-
return preCompileTS;
|
|
334
|
-
}
|
|
335
|
-
async function loadVue() {
|
|
336
|
-
if (!preCompileVue) {
|
|
337
|
-
const vueModule = await import('./vuejs.js');
|
|
338
|
-
preCompileVue = vueModule.preCompileVue;
|
|
339
|
-
}
|
|
340
|
-
return preCompileVue;
|
|
341
|
-
}
|
|
342
|
-
// Almacenamiento global de errores y resultados
|
|
343
|
-
const compilationErrors = [];
|
|
344
|
-
const compilationResults = [];
|
|
345
|
-
// Variables de entorno relevantes para compilación
|
|
346
|
-
const COMPILATION_ENV_VARS = [
|
|
347
|
-
'NODE_ENV',
|
|
348
|
-
'isPROD',
|
|
349
|
-
'TAILWIND',
|
|
350
|
-
'ENABLE_LINTER',
|
|
351
|
-
'VERBOSE',
|
|
352
|
-
'typeCheck',
|
|
353
|
-
'PATH_ALIAS',
|
|
354
|
-
'tailwindcss',
|
|
355
|
-
'linter',
|
|
356
|
-
'tsconfigFile',
|
|
357
|
-
];
|
|
358
|
-
class SmartCompilationCache {
|
|
359
|
-
cache = new Map();
|
|
360
|
-
maxEntries = 200; // Reducido para tests de estrés
|
|
361
|
-
maxMemory = 50 * 1024 * 1024; // 50MB límite (reducido)
|
|
362
|
-
currentMemoryUsage = 0;
|
|
363
|
-
// ✨ ISSUE #3: Sistema de vigilancia de dependencias
|
|
364
|
-
fileWatchers = new Map(); // chokidar watchers
|
|
365
|
-
dependencyGraph = new Map(); // archivo -> dependencias
|
|
366
|
-
reverseDependencyGraph = new Map(); // dependencia -> archivos que la usan
|
|
367
|
-
packageJsonPath = path.join(cwd(), 'package.json');
|
|
368
|
-
nodeModulesPath = path.join(cwd(), 'node_modules');
|
|
369
|
-
isWatchingDependencies = false;
|
|
370
|
-
/**
|
|
371
|
-
* Genera hash SHA-256 del contenido del archivo
|
|
372
|
-
*/ async generateContentHash(filePath) {
|
|
373
|
-
try {
|
|
374
|
-
const content = await readFile(filePath, 'utf8');
|
|
375
|
-
return createHash('sha256').update(content).digest('hex');
|
|
376
|
-
}
|
|
377
|
-
catch {
|
|
378
|
-
// Si no se puede leer el archivo, generar hash único basado en la ruta y timestamp
|
|
379
|
-
const fallback = `${filePath}-${Date.now()}`;
|
|
380
|
-
return createHash('sha256').update(fallback).digest('hex');
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* Genera hash de la configuración del compilador
|
|
385
|
-
*/
|
|
386
|
-
generateConfigHash() {
|
|
387
|
-
try {
|
|
388
|
-
// Recopilar configuración relevante de variables de entorno
|
|
389
|
-
const config = {
|
|
390
|
-
isPROD: env.isPROD || 'false',
|
|
391
|
-
TAILWIND: env.TAILWIND || 'false',
|
|
392
|
-
ENABLE_LINTER: env.ENABLE_LINTER || 'false',
|
|
393
|
-
PATH_ALIAS: env.PATH_ALIAS || '{}',
|
|
394
|
-
tailwindcss: env.tailwindcss || 'false',
|
|
395
|
-
linter: env.linter || 'false',
|
|
396
|
-
tsconfigFile: env.tsconfigFile || './tsconfig.json',
|
|
397
|
-
};
|
|
398
|
-
const configStr = JSON.stringify(config, Object.keys(config).sort());
|
|
399
|
-
return createHash('sha256')
|
|
400
|
-
.update(configStr)
|
|
401
|
-
.digest('hex')
|
|
402
|
-
.substring(0, 12);
|
|
403
|
-
}
|
|
404
|
-
catch {
|
|
405
|
-
return 'no-config';
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
/**
|
|
409
|
-
* Genera hash de variables de entorno relevantes
|
|
410
|
-
*/
|
|
411
|
-
generateEnvHash() {
|
|
412
|
-
try {
|
|
413
|
-
const envVars = COMPILATION_ENV_VARS.map(key => `${key}=${env[key] || ''}`).join('|');
|
|
414
|
-
return createHash('sha256')
|
|
415
|
-
.update(envVars)
|
|
416
|
-
.digest('hex')
|
|
417
|
-
.substring(0, 12);
|
|
418
|
-
}
|
|
419
|
-
catch {
|
|
420
|
-
return 'no-env';
|
|
421
|
-
}
|
|
422
|
-
} /**
|
|
423
|
-
* ✨ ISSUE #3: Genera hash avanzado de dependencias del proyecto
|
|
424
|
-
* Incluye vigilancia de package.json, node_modules y versiones instaladas
|
|
425
|
-
*/
|
|
426
|
-
async generateDependencyHash() {
|
|
427
|
-
try {
|
|
428
|
-
const hash = createHash('sha256');
|
|
429
|
-
// 1. Hash del package.json con versiones
|
|
430
|
-
const packagePath = path.join(cwd(), 'package.json');
|
|
431
|
-
const packageContent = await readFile(packagePath, 'utf8');
|
|
432
|
-
const pkg = JSON.parse(packageContent);
|
|
433
|
-
const deps = {
|
|
434
|
-
...pkg.dependencies,
|
|
435
|
-
...pkg.devDependencies,
|
|
436
|
-
};
|
|
437
|
-
const depsStr = JSON.stringify(deps, Object.keys(deps).sort());
|
|
438
|
-
hash.update(`package:${depsStr}`);
|
|
439
|
-
// 2. Hash del package-lock.json si existe (versiones exactas instaladas)
|
|
440
|
-
try {
|
|
441
|
-
const lockPath = path.join(cwd(), 'package-lock.json');
|
|
442
|
-
const lockContent = await readFile(lockPath, 'utf8');
|
|
443
|
-
const lockData = JSON.parse(lockContent);
|
|
444
|
-
// Solo incluir las versiones instaladas, no todo el lockfile
|
|
445
|
-
const installedVersions = {};
|
|
446
|
-
if (lockData.packages) {
|
|
447
|
-
for (const [pkgPath, pkgInfo] of Object.entries(lockData.packages)) {
|
|
448
|
-
if (pkgPath &&
|
|
449
|
-
pkgPath !== '' &&
|
|
450
|
-
typeof pkgInfo === 'object' &&
|
|
451
|
-
pkgInfo !== null) {
|
|
452
|
-
const pkgName = pkgPath.replace('node_modules/', '');
|
|
453
|
-
if (pkgInfo.version) {
|
|
454
|
-
installedVersions[pkgName] = pkgInfo.version;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
hash.update(`lock:${JSON.stringify(installedVersions, Object.keys(installedVersions).sort())}`);
|
|
460
|
-
}
|
|
461
|
-
catch {
|
|
462
|
-
// Ignorar si no existe package-lock.json
|
|
463
|
-
}
|
|
464
|
-
// 3. ✨ NUEVO: Hash de timestamps críticos de node_modules
|
|
465
|
-
try {
|
|
466
|
-
const nodeModulesPath = path.join(cwd(), 'node_modules');
|
|
467
|
-
const nodeModulesStat = await stat(nodeModulesPath);
|
|
468
|
-
hash.update(`nmtime:${nodeModulesStat.mtimeMs}`);
|
|
469
|
-
// Verificar timestamps de dependencias críticas instaladas
|
|
470
|
-
const criticalDeps = Object.keys(deps).slice(0, 10); // Top 10 para performance
|
|
471
|
-
for (const dep of criticalDeps) {
|
|
472
|
-
try {
|
|
473
|
-
const depPath = path.join(nodeModulesPath, dep);
|
|
474
|
-
const depStat = await stat(depPath);
|
|
475
|
-
hash.update(`${dep}:${depStat.mtimeMs}`);
|
|
476
|
-
}
|
|
477
|
-
catch {
|
|
478
|
-
// Dependencia no instalada o error
|
|
479
|
-
hash.update(`${dep}:missing`);
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
catch {
|
|
484
|
-
// node_modules no existe
|
|
485
|
-
hash.update('nmtime:none');
|
|
486
|
-
}
|
|
487
|
-
return hash.digest('hex').substring(0, 16);
|
|
488
|
-
}
|
|
489
|
-
catch (error) {
|
|
490
|
-
// Incluir información del error en el hash para debugging
|
|
491
|
-
return createHash('sha256')
|
|
492
|
-
.update(`error:${error instanceof Error ? error.message : 'unknown'}`)
|
|
493
|
-
.digest('hex')
|
|
494
|
-
.substring(0, 16);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
/**
|
|
498
|
-
* Genera clave de cache granular que incluye todos los factores
|
|
499
|
-
*/
|
|
500
|
-
async generateCacheKey(filePath) {
|
|
501
|
-
const contentHash = await this.generateContentHash(filePath);
|
|
502
|
-
const configHash = this.generateConfigHash();
|
|
503
|
-
const envHash = this.generateEnvHash();
|
|
504
|
-
const dependencyHash = await this.generateDependencyHash();
|
|
505
|
-
// Usar | como separador para evitar problemas con rutas de Windows
|
|
506
|
-
return `${filePath}|${contentHash.substring(0, 12)}|${configHash}|${envHash}|${dependencyHash}`;
|
|
507
|
-
} /**
|
|
508
|
-
* Verifica si una entrada de cache es válida
|
|
509
|
-
*/
|
|
510
|
-
async isValid(filePath) {
|
|
511
|
-
const entry = this.cache.get(filePath);
|
|
512
|
-
if (!entry)
|
|
513
|
-
return false;
|
|
514
|
-
try {
|
|
515
|
-
// Verificar si el archivo de salida existe
|
|
516
|
-
await stat(entry.outputPath);
|
|
517
|
-
// Verificar si el contenido ha cambiado
|
|
518
|
-
const currentContentHash = await this.generateContentHash(filePath);
|
|
519
|
-
if (entry.contentHash !== currentContentHash) {
|
|
520
|
-
this.cache.delete(filePath);
|
|
521
|
-
return false;
|
|
522
|
-
}
|
|
523
|
-
// Verificar si la configuración ha cambiado
|
|
524
|
-
const currentConfigHash = this.generateConfigHash();
|
|
525
|
-
if (entry.configHash !== currentConfigHash) {
|
|
526
|
-
this.cache.delete(filePath);
|
|
527
|
-
return false;
|
|
528
|
-
}
|
|
529
|
-
// Verificar si las variables de entorno han cambiado
|
|
530
|
-
const currentEnvHash = this.generateEnvHash();
|
|
531
|
-
if (entry.envHash !== currentEnvHash) {
|
|
532
|
-
this.cache.delete(filePath);
|
|
533
|
-
return false;
|
|
534
|
-
}
|
|
535
|
-
// Verificar si las dependencias han cambiado
|
|
536
|
-
const currentDependencyHash = await this.generateDependencyHash();
|
|
537
|
-
if (entry.dependencyHash !== currentDependencyHash) {
|
|
538
|
-
this.cache.delete(filePath);
|
|
539
|
-
return false;
|
|
540
|
-
}
|
|
541
|
-
// Verificar tiempo de modificación como backup
|
|
542
|
-
const stats = await stat(filePath);
|
|
543
|
-
if (stats.mtimeMs > entry.mtime) {
|
|
544
|
-
this.cache.delete(filePath);
|
|
545
|
-
return false;
|
|
546
|
-
}
|
|
547
|
-
// Actualizar tiempo de uso para LRU
|
|
548
|
-
entry.lastUsed = Date.now();
|
|
549
|
-
return true;
|
|
550
|
-
}
|
|
551
|
-
catch {
|
|
552
|
-
// Si hay error verificando, eliminar del cache
|
|
553
|
-
this.cache.delete(filePath);
|
|
554
|
-
return false;
|
|
555
|
-
}
|
|
556
|
-
} /**
|
|
557
|
-
* Añade una entrada al cache
|
|
558
|
-
*/
|
|
559
|
-
async set(filePath, outputPath) {
|
|
560
|
-
try {
|
|
561
|
-
const stats = await stat(filePath);
|
|
562
|
-
const contentHash = await this.generateContentHash(filePath);
|
|
563
|
-
const configHash = this.generateConfigHash();
|
|
564
|
-
const envHash = this.generateEnvHash();
|
|
565
|
-
const dependencyHash = await this.generateDependencyHash();
|
|
566
|
-
const entry = {
|
|
567
|
-
contentHash,
|
|
568
|
-
configHash,
|
|
569
|
-
envHash,
|
|
570
|
-
dependencyHash,
|
|
571
|
-
mtime: stats.mtimeMs,
|
|
572
|
-
outputPath,
|
|
573
|
-
lastUsed: Date.now(),
|
|
574
|
-
size: stats.size,
|
|
575
|
-
};
|
|
576
|
-
// Aplicar límites de memoria y entradas antes de agregar
|
|
577
|
-
this.evictIfNeeded(entry.size);
|
|
578
|
-
this.cache.set(filePath, entry);
|
|
579
|
-
this.currentMemoryUsage += entry.size;
|
|
580
|
-
}
|
|
581
|
-
catch (error) {
|
|
582
|
-
// Si hay error, no cachear
|
|
583
|
-
console.warn(`Warning: No se pudo cachear ${filePath}:`, error);
|
|
584
|
-
}
|
|
585
|
-
} /**
|
|
586
|
-
* Aplica política LRU para liberar espacio
|
|
587
|
-
*/
|
|
588
|
-
evictIfNeeded(newEntrySize) {
|
|
589
|
-
// Verificar límite de entradas más agresivamente
|
|
590
|
-
while (this.cache.size >= this.maxEntries * 0.8) {
|
|
591
|
-
// Limpiar cuando llegue al 80%
|
|
592
|
-
this.evictLRU();
|
|
593
|
-
}
|
|
594
|
-
// Verificar límite de memoria más agresivamente
|
|
595
|
-
while (this.currentMemoryUsage + newEntrySize > this.maxMemory * 0.8 && // Limpiar cuando llegue al 80%
|
|
596
|
-
this.cache.size > 0) {
|
|
597
|
-
this.evictLRU();
|
|
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
|
-
} /**
|
|
610
|
-
* Elimina la entrada menos recientemente usada
|
|
611
|
-
*/
|
|
612
|
-
evictLRU() {
|
|
613
|
-
let oldestKey = '';
|
|
614
|
-
let oldestTime = Infinity;
|
|
615
|
-
for (const [key, entry] of this.cache) {
|
|
616
|
-
if (entry.lastUsed < oldestTime) {
|
|
617
|
-
oldestTime = entry.lastUsed;
|
|
618
|
-
oldestKey = key;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
if (oldestKey) {
|
|
622
|
-
const entry = this.cache.get(oldestKey);
|
|
623
|
-
if (entry) {
|
|
624
|
-
this.currentMemoryUsage -= entry.size;
|
|
625
|
-
this.cache.delete(oldestKey);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
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
|
-
}
|
|
646
|
-
/**
|
|
647
|
-
* Carga el cache desde disco
|
|
648
|
-
*/
|
|
649
|
-
async load(cacheFile) {
|
|
650
|
-
try {
|
|
651
|
-
if (env.cleanCache === 'true') {
|
|
652
|
-
this.cache.clear();
|
|
653
|
-
this.currentMemoryUsage = 0;
|
|
654
|
-
try {
|
|
655
|
-
await unlink(cacheFile);
|
|
656
|
-
}
|
|
657
|
-
catch {
|
|
658
|
-
// Ignorar errores al eliminar el archivo
|
|
659
|
-
}
|
|
660
|
-
return;
|
|
661
|
-
}
|
|
662
|
-
const cacheData = await readFile(cacheFile, 'utf-8');
|
|
663
|
-
const parsed = JSON.parse(cacheData);
|
|
664
|
-
// Validar y cargar entradas del cache
|
|
665
|
-
for (const [key, value] of Object.entries(parsed)) {
|
|
666
|
-
const entry = value;
|
|
667
|
-
if (entry.contentHash && entry.outputPath && entry.mtime) {
|
|
668
|
-
this.cache.set(key, entry);
|
|
669
|
-
this.currentMemoryUsage += entry.size || 0;
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
catch {
|
|
674
|
-
// Cache file doesn't exist or is invalid, start fresh
|
|
675
|
-
this.cache.clear();
|
|
676
|
-
this.currentMemoryUsage = 0;
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
/**
|
|
680
|
-
* Guarda el cache a disco
|
|
681
|
-
*/
|
|
682
|
-
async save(cacheFile, cacheDir) {
|
|
683
|
-
try {
|
|
684
|
-
await mkdir(cacheDir, { recursive: true });
|
|
685
|
-
const cacheData = Object.fromEntries(this.cache);
|
|
686
|
-
await writeFile(cacheFile, JSON.stringify(cacheData, null, 2));
|
|
687
|
-
}
|
|
688
|
-
catch (error) {
|
|
689
|
-
console.warn('Warning: No se pudo guardar el cache:', error);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
/**
|
|
693
|
-
* Limpia completamente el cache
|
|
694
|
-
*/
|
|
695
|
-
clear() {
|
|
696
|
-
this.cache.clear();
|
|
697
|
-
this.currentMemoryUsage = 0;
|
|
698
|
-
} /**
|
|
699
|
-
* Obtiene la ruta de salida para un archivo cacheado
|
|
700
|
-
*/
|
|
701
|
-
getOutputPath(filePath) {
|
|
702
|
-
const entry = this.cache.get(filePath);
|
|
703
|
-
return entry?.outputPath || '';
|
|
704
|
-
} /**
|
|
705
|
-
* Obtiene estadísticas del cache
|
|
706
|
-
*/
|
|
707
|
-
getStats() {
|
|
708
|
-
return {
|
|
709
|
-
entries: this.cache.size,
|
|
710
|
-
memoryUsage: this.currentMemoryUsage,
|
|
711
|
-
hitRate: 0, // Se calculará externamente
|
|
712
|
-
};
|
|
713
|
-
}
|
|
714
|
-
// ✨ ISSUE #3: Métodos de vigilancia y invalidación cascada
|
|
715
|
-
/**
|
|
716
|
-
* Inicializa vigilancia de package.json y node_modules
|
|
717
|
-
*/
|
|
718
|
-
async startDependencyWatching() {
|
|
719
|
-
if (this.isWatchingDependencies)
|
|
720
|
-
return;
|
|
721
|
-
try {
|
|
722
|
-
// Lazy load chokidar para evitar problemas de importación
|
|
723
|
-
const chokidar = await import('chokidar');
|
|
724
|
-
// Vigilar package.json
|
|
725
|
-
if (await this.fileExists(this.packageJsonPath)) {
|
|
726
|
-
const packageWatcher = chokidar.watch(this.packageJsonPath, {
|
|
727
|
-
persistent: false, // No mantener el proceso vivo
|
|
728
|
-
ignoreInitial: true,
|
|
729
|
-
});
|
|
730
|
-
packageWatcher.on('change', () => {
|
|
731
|
-
logger.info('📦 package.json modificado - invalidando cache de dependencias');
|
|
732
|
-
this.invalidateByDependencyChange();
|
|
733
|
-
});
|
|
734
|
-
this.fileWatchers.set('package.json', packageWatcher);
|
|
735
|
-
}
|
|
736
|
-
// Vigilar node_modules (solo cambios en el directorio raíz para performance)
|
|
737
|
-
if (await this.fileExists(this.nodeModulesPath)) {
|
|
738
|
-
const nodeModulesWatcher = chokidar.watch(this.nodeModulesPath, {
|
|
739
|
-
persistent: false,
|
|
740
|
-
ignoreInitial: true,
|
|
741
|
-
depth: 1, // Solo primer nivel para performance
|
|
742
|
-
ignored: /(^|[/\\])\../, // Ignorar archivos ocultos
|
|
743
|
-
});
|
|
744
|
-
nodeModulesWatcher.on('addDir', (path) => {
|
|
745
|
-
logger.info(`📦 Nueva dependencia instalada: ${path.split(/[/\\]/).pop()}`);
|
|
746
|
-
this.invalidateByDependencyChange();
|
|
747
|
-
});
|
|
748
|
-
nodeModulesWatcher.on('unlinkDir', (path) => {
|
|
749
|
-
logger.info(`📦 Dependencia eliminada: ${path.split(/[/\\]/).pop()}`);
|
|
750
|
-
this.invalidateByDependencyChange();
|
|
751
|
-
});
|
|
752
|
-
this.fileWatchers.set('node_modules', nodeModulesWatcher);
|
|
753
|
-
}
|
|
754
|
-
this.isWatchingDependencies = true;
|
|
755
|
-
logger.info('🔍 Vigilancia de dependencias iniciada');
|
|
756
|
-
}
|
|
757
|
-
catch (error) {
|
|
758
|
-
logger.warn('⚠️ No se pudo iniciar vigilancia de dependencias:', error);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
/**
|
|
762
|
-
* Detiene la vigilancia de dependencias
|
|
763
|
-
*/
|
|
764
|
-
async stopDependencyWatching() {
|
|
765
|
-
for (const [name, watcher] of this.fileWatchers) {
|
|
766
|
-
try {
|
|
767
|
-
await watcher.close();
|
|
768
|
-
logger.info(`🛑 Vigilancia detenida: ${name}`);
|
|
769
|
-
}
|
|
770
|
-
catch (error) {
|
|
771
|
-
logger.warn(`⚠️ Error cerrando watcher ${name}:`, error);
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
this.fileWatchers.clear();
|
|
775
|
-
this.isWatchingDependencies = false;
|
|
776
|
-
}
|
|
777
|
-
/**
|
|
778
|
-
* Registra dependencias de un archivo para invalidación cascada
|
|
779
|
-
*/
|
|
780
|
-
registerDependencies(filePath, dependencies) {
|
|
781
|
-
// Limpiar dependencias anteriores
|
|
782
|
-
const oldDeps = this.dependencyGraph.get(filePath);
|
|
783
|
-
if (oldDeps) {
|
|
784
|
-
for (const dep of oldDeps) {
|
|
785
|
-
const reverseDeps = this.reverseDependencyGraph.get(dep);
|
|
786
|
-
if (reverseDeps) {
|
|
787
|
-
reverseDeps.delete(filePath);
|
|
788
|
-
if (reverseDeps.size === 0) {
|
|
789
|
-
this.reverseDependencyGraph.delete(dep);
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
// Registrar nuevas dependencias
|
|
795
|
-
const newDeps = new Set(dependencies);
|
|
796
|
-
this.dependencyGraph.set(filePath, newDeps);
|
|
797
|
-
for (const dep of newDeps) {
|
|
798
|
-
if (!this.reverseDependencyGraph.has(dep)) {
|
|
799
|
-
this.reverseDependencyGraph.set(dep, new Set());
|
|
800
|
-
}
|
|
801
|
-
this.reverseDependencyGraph.get(dep).add(filePath);
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
/**
|
|
805
|
-
* Invalida cache por cambios en dependencias
|
|
806
|
-
*/
|
|
807
|
-
invalidateByDependencyChange() {
|
|
808
|
-
let invalidatedCount = 0;
|
|
809
|
-
// Invalidar todos los archivos que dependen de dependencias externas
|
|
810
|
-
for (const [filePath] of this.cache) {
|
|
811
|
-
this.cache.delete(filePath);
|
|
812
|
-
invalidatedCount++;
|
|
813
|
-
}
|
|
814
|
-
// Limpiar grafos de dependencias
|
|
815
|
-
this.dependencyGraph.clear();
|
|
816
|
-
this.reverseDependencyGraph.clear();
|
|
817
|
-
this.currentMemoryUsage = 0;
|
|
818
|
-
logger.info(`🗑️ Cache invalidado: ${invalidatedCount} archivos (cambio en dependencias)`);
|
|
819
|
-
}
|
|
820
|
-
/**
|
|
821
|
-
* Invalida cascada cuando un archivo específico cambia
|
|
822
|
-
*/
|
|
823
|
-
invalidateCascade(changedFile) {
|
|
824
|
-
const invalidated = [];
|
|
825
|
-
const toInvalidate = new Set([changedFile]);
|
|
826
|
-
// BFS para encontrar todos los archivos afectados
|
|
827
|
-
const queue = [changedFile];
|
|
828
|
-
while (queue.length > 0) {
|
|
829
|
-
const current = queue.shift();
|
|
830
|
-
const dependents = this.reverseDependencyGraph.get(current);
|
|
831
|
-
if (dependents) {
|
|
832
|
-
for (const dependent of dependents) {
|
|
833
|
-
if (!toInvalidate.has(dependent)) {
|
|
834
|
-
toInvalidate.add(dependent);
|
|
835
|
-
queue.push(dependent);
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
// Invalidar archivos
|
|
841
|
-
for (const filePath of toInvalidate) {
|
|
842
|
-
if (this.cache.has(filePath)) {
|
|
843
|
-
const entry = this.cache.get(filePath);
|
|
844
|
-
this.currentMemoryUsage -= entry.size;
|
|
845
|
-
this.cache.delete(filePath);
|
|
846
|
-
invalidated.push(filePath);
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
if (invalidated.length > 0) {
|
|
850
|
-
logger.info(`🔄 Invalidación cascada: ${invalidated.length} archivos afectados por ${changedFile}`);
|
|
851
|
-
}
|
|
852
|
-
return invalidated;
|
|
853
|
-
}
|
|
854
|
-
/**
|
|
855
|
-
* Verifica si un archivo existe
|
|
856
|
-
*/
|
|
857
|
-
async fileExists(filePath) {
|
|
858
|
-
try {
|
|
859
|
-
await stat(filePath);
|
|
860
|
-
return true;
|
|
861
|
-
}
|
|
862
|
-
catch {
|
|
863
|
-
return false;
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
/**
|
|
867
|
-
* Obtiene estadísticas avanzadas del cache
|
|
868
|
-
*/
|
|
869
|
-
getAdvancedStats() {
|
|
870
|
-
return {
|
|
871
|
-
entries: this.cache.size,
|
|
872
|
-
memoryUsage: this.currentMemoryUsage,
|
|
873
|
-
hitRate: 0,
|
|
874
|
-
dependencyNodes: this.dependencyGraph.size,
|
|
875
|
-
watchingDependencies: this.isWatchingDependencies,
|
|
876
|
-
activeWatchers: this.fileWatchers.size,
|
|
877
|
-
};
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
// Instancia global del cache inteligente
|
|
881
|
-
const smartCache = new SmartCompilationCache();
|
|
882
|
-
const CACHE_DIR = path.join(path.resolve(env.PATH_PROY || cwd(), 'compiler'), '.cache');
|
|
883
|
-
const CACHE_FILE = path.join(CACHE_DIR, 'versacompile-cache.json');
|
|
884
|
-
async function loadCache() {
|
|
885
|
-
await smartCache.load(CACHE_FILE);
|
|
886
|
-
// ✨ ISSUE #3: Iniciar vigilancia de dependencias en modo watch
|
|
887
|
-
if (env.WATCH_MODE === 'true' ||
|
|
888
|
-
argv.includes('--watch') ||
|
|
889
|
-
argv.includes('-w')) {
|
|
890
|
-
await smartCache.startDependencyWatching();
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
async function saveCache() {
|
|
894
|
-
await smartCache.save(CACHE_FILE, CACHE_DIR);
|
|
895
|
-
}
|
|
896
|
-
// 🎯 Funciones del Sistema Unificado de Manejo de Errores
|
|
897
|
-
/**
|
|
898
|
-
* Registra un error de compilación en el sistema unificado
|
|
899
|
-
*/
|
|
900
|
-
function registerCompilationError(file, stage, message, severity = 'error', details, help) {
|
|
901
|
-
compilationErrors.push({
|
|
902
|
-
file,
|
|
903
|
-
stage,
|
|
904
|
-
message,
|
|
905
|
-
severity,
|
|
906
|
-
details,
|
|
907
|
-
help,
|
|
908
|
-
timestamp: Date.now(),
|
|
909
|
-
});
|
|
910
|
-
}
|
|
911
|
-
/**
|
|
912
|
-
* Registra un resultado de compilación (éxitos/errores por etapa)
|
|
913
|
-
*/
|
|
914
|
-
function registerCompilationResult(stage, errors, success, files = []) {
|
|
915
|
-
const existingResult = compilationResults.find(r => r.stage === stage);
|
|
916
|
-
if (existingResult) {
|
|
917
|
-
existingResult.errors += errors;
|
|
918
|
-
existingResult.success += success;
|
|
919
|
-
existingResult.files.push(...files);
|
|
920
|
-
}
|
|
921
|
-
else {
|
|
922
|
-
compilationResults.push({
|
|
923
|
-
stage,
|
|
924
|
-
errors,
|
|
925
|
-
success,
|
|
926
|
-
files: [...files],
|
|
927
|
-
});
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
/**
|
|
931
|
-
* Maneja errores según el modo de compilación
|
|
932
|
-
*/
|
|
933
|
-
async function handleCompilationError(error, fileName, stage, mode, isVerbose = false) {
|
|
934
|
-
const errorMessage = error instanceof Error ? error.message : error;
|
|
935
|
-
const errorDetails = error instanceof Error ? error.stack : undefined;
|
|
936
|
-
// Registrar el error en el sistema unificado
|
|
937
|
-
registerCompilationError(fileName, stage, errorMessage, 'error', errorDetails);
|
|
938
|
-
registerCompilationResult(stage, 1, 0, [fileName]); // Mostrar error inmediatamente solo en modo individual y watch
|
|
939
|
-
if (mode === 'individual' || mode === 'watch') {
|
|
940
|
-
const chalk = await loadChalk();
|
|
941
|
-
const baseName = path.basename(fileName);
|
|
942
|
-
const stageColor = await getStageColor(stage);
|
|
943
|
-
if (isVerbose) {
|
|
944
|
-
// Modo verbose: Mostrar error completo con contexto
|
|
945
|
-
logger.error(chalk.red(`❌ Error en etapa ${stageColor(stage)} - ${baseName}:`));
|
|
946
|
-
logger.error(chalk.red(errorMessage));
|
|
947
|
-
if (errorDetails && (stage === 'typescript' || stage === 'vue')) {
|
|
948
|
-
// Mostrar stack trace limitado para TypeScript y Vue
|
|
949
|
-
const stackLines = errorDetails.split('\n').slice(0, 5);
|
|
950
|
-
stackLines.forEach(line => {
|
|
951
|
-
if (line.trim()) {
|
|
952
|
-
logger.error(chalk.gray(` ${line.trim()}`));
|
|
953
|
-
}
|
|
954
|
-
});
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
else {
|
|
958
|
-
// Modo normal: Mostrar error simplificado
|
|
959
|
-
const firstLine = errorMessage.split('\n')[0];
|
|
960
|
-
logger.error(chalk.red(`❌ Error en ${stageColor(stage)}: ${baseName}`));
|
|
961
|
-
logger.error(chalk.red(` ${firstLine}`));
|
|
962
|
-
logger.info(chalk.yellow(`💡 Usa --verbose para ver detalles completos`));
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
// En modo 'all', los errores se acumulan silenciosamente para el resumen final
|
|
966
|
-
}
|
|
967
|
-
/**
|
|
968
|
-
* Registra un éxito de compilación
|
|
969
|
-
*/
|
|
970
|
-
function registerCompilationSuccess(fileName, stage) {
|
|
971
|
-
registerCompilationResult(stage, 0, 1, [fileName]);
|
|
972
|
-
}
|
|
973
|
-
/**
|
|
974
|
-
* Limpia todos los errores y resultados acumulados
|
|
975
|
-
*/
|
|
976
|
-
function clearCompilationState() {
|
|
977
|
-
compilationErrors.length = 0;
|
|
978
|
-
compilationResults.length = 0;
|
|
979
|
-
}
|
|
980
|
-
/**
|
|
981
|
-
* Muestra un resumen detallado de todos los errores de compilación
|
|
982
|
-
*/
|
|
983
|
-
async function displayCompilationSummary(isVerbose = false, totalTime) {
|
|
984
|
-
const chalk = await loadChalk();
|
|
985
|
-
if (compilationErrors.length === 0 && compilationResults.length === 0) {
|
|
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
|
-
}
|
|
990
|
-
return;
|
|
991
|
-
}
|
|
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
|
|
1001
|
-
if (compilationResults.length > 0) {
|
|
1002
|
-
logger.info(chalk.bold.blue('🔧 Estadísticas por Etapa:'));
|
|
1003
|
-
for (const result of compilationResults) {
|
|
1004
|
-
const totalFiles = result.success + result.errors;
|
|
1005
|
-
const successRate = totalFiles > 0
|
|
1006
|
-
? Math.round((result.success / totalFiles) * 100)
|
|
1007
|
-
: 0;
|
|
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('');
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
// Mostrar errores detallados
|
|
1022
|
-
if (compilationErrors.length > 0) {
|
|
1023
|
-
logger.info(chalk.red(`\n❌ Se encontraron ${compilationErrors.length} errores:`));
|
|
1024
|
-
// Agrupar errores por archivo para mejor organización
|
|
1025
|
-
const errorsByFile = new Map();
|
|
1026
|
-
compilationErrors.forEach(error => {
|
|
1027
|
-
if (!errorsByFile.has(error.file)) {
|
|
1028
|
-
errorsByFile.set(error.file, []);
|
|
1029
|
-
}
|
|
1030
|
-
errorsByFile.get(error.file).push(error);
|
|
1031
|
-
});
|
|
1032
|
-
// Mostrar errores por archivo
|
|
1033
|
-
let fileIndex = 1;
|
|
1034
|
-
for (const [filePath, fileErrors] of errorsByFile) {
|
|
1035
|
-
const baseName = path.basename(filePath);
|
|
1036
|
-
const errorCount = fileErrors.filter(e => e.severity === 'error').length;
|
|
1037
|
-
const warningCount = fileErrors.filter(e => e.severity === 'warning').length;
|
|
1038
|
-
logger.info(chalk.cyan(`\n📄 ${fileIndex}. ${baseName}`));
|
|
1039
|
-
logger.info(chalk.gray(` Ruta: ${filePath}`));
|
|
1040
|
-
logger.info(chalk.yellow(` ${errorCount} errores, ${warningCount} advertencias`));
|
|
1041
|
-
for (const error of fileErrors) {
|
|
1042
|
-
const icon = error.severity === 'error' ? '❌' : '⚠️';
|
|
1043
|
-
const stageColor = await getStageColor(error.stage);
|
|
1044
|
-
logger.info(` ${icon} [${stageColor(error.stage)}] ${error.message}`);
|
|
1045
|
-
if (isVerbose && error.details) {
|
|
1046
|
-
// En modo verbose, mostrar detalles adicionales
|
|
1047
|
-
const detailLines = error.details.split('\n').slice(0, 5);
|
|
1048
|
-
detailLines.forEach(line => {
|
|
1049
|
-
if (line.trim()) {
|
|
1050
|
-
logger.info(chalk.gray(` ${line.trim()}`));
|
|
1051
|
-
}
|
|
1052
|
-
});
|
|
1053
|
-
}
|
|
1054
|
-
if (error.help) {
|
|
1055
|
-
logger.info(chalk.blue(` 💡 ${error.help}`));
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
fileIndex++;
|
|
1059
|
-
} // 📊 Mostrar totales finales con diseño moderno
|
|
1060
|
-
const totalErrors = compilationErrors.filter(e => e.severity === 'error').length;
|
|
1061
|
-
const totalWarnings = compilationErrors.filter(e => e.severity === 'warning').length;
|
|
1062
|
-
const totalFiles = errorsByFile.size;
|
|
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
|
|
1077
|
-
if (totalErrors > 0) {
|
|
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.'));
|
|
1084
|
-
}
|
|
1085
|
-
else {
|
|
1086
|
-
logger.info(chalk.green.bold('✅ COMPILACIÓN EXITOSA'));
|
|
1087
|
-
logger.info(chalk.green(' ¡Todos los archivos se compilaron sin problemas!'));
|
|
1088
|
-
}
|
|
1089
|
-
logger.info('');
|
|
1090
|
-
logger.info(chalk.bold.cyan(statLine));
|
|
1091
|
-
}
|
|
1092
|
-
else {
|
|
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('');
|
|
1106
|
-
}
|
|
1107
|
-
/**
|
|
1108
|
-
* Muestra errores del linter con formato visual moderno y profesional
|
|
1109
|
-
*/
|
|
1110
|
-
async function displayLinterErrors(errors) {
|
|
1111
|
-
const chalk = await loadChalk();
|
|
1112
|
-
// Agrupar errores por archivo
|
|
1113
|
-
const errorsByFile = new Map();
|
|
1114
|
-
errors.forEach(error => {
|
|
1115
|
-
if (!errorsByFile.has(error.file)) {
|
|
1116
|
-
errorsByFile.set(error.file, []);
|
|
1117
|
-
}
|
|
1118
|
-
errorsByFile.get(error.file).push(error);
|
|
1119
|
-
});
|
|
1120
|
-
const totalErrors = errors.filter(e => e.severity === 'error').length;
|
|
1121
|
-
const totalWarnings = errors.filter(e => e.severity === 'warning').length;
|
|
1122
|
-
const totalFiles = errorsByFile.size;
|
|
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
|
-
}
|
|
1237
|
-
}
|
|
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';
|
|
1342
|
-
}
|
|
1343
|
-
/**
|
|
1344
|
-
* Obtiene el color apropiado para cada etapa de compilación
|
|
1345
|
-
*/
|
|
1346
|
-
async function getStageColor(stage) {
|
|
1347
|
-
const chalk = await loadChalk();
|
|
1348
|
-
switch (stage) {
|
|
1349
|
-
case 'vue':
|
|
1350
|
-
return chalk.green;
|
|
1351
|
-
case 'typescript':
|
|
1352
|
-
return chalk.blue;
|
|
1353
|
-
case 'standardization':
|
|
1354
|
-
return chalk.yellow;
|
|
1355
|
-
case 'minification':
|
|
1356
|
-
return chalk.red;
|
|
1357
|
-
case 'tailwind':
|
|
1358
|
-
return chalk.magenta;
|
|
1359
|
-
case 'file-read':
|
|
1360
|
-
return chalk.gray;
|
|
1361
|
-
default:
|
|
1362
|
-
return chalk.white;
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
export function normalizeRuta(ruta) {
|
|
1366
|
-
if (path.isAbsolute(ruta)) {
|
|
1367
|
-
return path.normalize(ruta).replace(/\\/g, '/');
|
|
1368
|
-
}
|
|
1369
|
-
const file = path
|
|
1370
|
-
.normalize(!ruta.startsWith('.') ? './' + ruta : ruta)
|
|
1371
|
-
.replace(/\\/g, '/');
|
|
1372
|
-
const sourceForDist = file.startsWith('./') ? file : `./${file}`;
|
|
1373
|
-
return sourceForDist;
|
|
1374
|
-
}
|
|
1375
|
-
export function getOutputPath(ruta) {
|
|
1376
|
-
const pathSource = env.PATH_SOURCE ?? '';
|
|
1377
|
-
const pathDist = env.PATH_DIST ?? '';
|
|
1378
|
-
if (!pathSource || !pathDist) {
|
|
1379
|
-
return ruta.replace(/\.(vue|ts)$/, '.js');
|
|
1380
|
-
}
|
|
1381
|
-
const normalizedRuta = path.normalize(ruta).replace(/\\/g, '/');
|
|
1382
|
-
const normalizedSource = path.normalize(pathSource).replace(/\\/g, '/');
|
|
1383
|
-
const normalizedDist = path.normalize(pathDist).replace(/\\/g, '/');
|
|
1384
|
-
let outputPath;
|
|
1385
|
-
if (normalizedRuta.includes(normalizedSource)) {
|
|
1386
|
-
const relativePath = normalizedRuta
|
|
1387
|
-
.substring(normalizedRuta.indexOf(normalizedSource) +
|
|
1388
|
-
normalizedSource.length)
|
|
1389
|
-
.replace(/^[/\\]/, '');
|
|
1390
|
-
outputPath = path
|
|
1391
|
-
.join(normalizedDist, relativePath)
|
|
1392
|
-
.replace(/\\/g, '/');
|
|
1393
|
-
}
|
|
1394
|
-
else {
|
|
1395
|
-
const fileName = path.basename(normalizedRuta);
|
|
1396
|
-
outputPath = path.join(normalizedDist, fileName).replace(/\\/g, '/');
|
|
1397
|
-
}
|
|
1398
|
-
if (outputPath.includes('vue') || outputPath.includes('ts')) {
|
|
1399
|
-
return outputPath.replace(/\.(vue|ts)$/, '.js');
|
|
1400
|
-
}
|
|
1401
|
-
else {
|
|
1402
|
-
return outputPath;
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
// Optimización para modo watch: debouncing y cache de archivos
|
|
1406
|
-
class WatchModeOptimizer {
|
|
1407
|
-
static instance;
|
|
1408
|
-
fileSystemCache = new Map();
|
|
1409
|
-
debounceTimers = new Map();
|
|
1410
|
-
DEBOUNCE_DELAY = 100; // 100ms debounce
|
|
1411
|
-
static getInstance() {
|
|
1412
|
-
if (!WatchModeOptimizer.instance) {
|
|
1413
|
-
WatchModeOptimizer.instance = new WatchModeOptimizer();
|
|
1414
|
-
}
|
|
1415
|
-
return WatchModeOptimizer.instance;
|
|
1416
|
-
}
|
|
1417
|
-
async compileForWatch(filePath, compileFn) {
|
|
1418
|
-
return new Promise(resolve => {
|
|
1419
|
-
const existingTimer = this.debounceTimers.get(filePath);
|
|
1420
|
-
if (existingTimer) {
|
|
1421
|
-
clearTimeout(existingTimer);
|
|
1422
|
-
}
|
|
1423
|
-
const timer = setTimeout(async () => {
|
|
1424
|
-
this.debounceTimers.delete(filePath);
|
|
1425
|
-
try {
|
|
1426
|
-
const stats = await stat(filePath);
|
|
1427
|
-
const cached = this.fileSystemCache.get(filePath);
|
|
1428
|
-
if (cached && cached.mtime >= stats.mtimeMs) {
|
|
1429
|
-
resolve({ success: true, cached: true });
|
|
1430
|
-
return;
|
|
1431
|
-
} // Configurar worker pool para modo watch
|
|
1432
|
-
const { TypeScriptWorkerPool } = (await import('./typescript-worker-pool.js'));
|
|
1433
|
-
const workerPool = TypeScriptWorkerPool.getInstance();
|
|
1434
|
-
workerPool.setMode('watch');
|
|
1435
|
-
const result = await compileFn(filePath);
|
|
1436
|
-
this.fileSystemCache.set(filePath, {
|
|
1437
|
-
mtime: stats.mtimeMs,
|
|
1438
|
-
});
|
|
1439
|
-
resolve(result);
|
|
1440
|
-
}
|
|
1441
|
-
catch (error) {
|
|
1442
|
-
resolve({ success: false, error });
|
|
1443
|
-
}
|
|
1444
|
-
}, this.DEBOUNCE_DELAY);
|
|
1445
|
-
this.debounceTimers.set(filePath, timer);
|
|
1446
|
-
});
|
|
1447
|
-
}
|
|
1448
|
-
cleanup() {
|
|
1449
|
-
this.debounceTimers.forEach(timer => clearTimeout(timer));
|
|
1450
|
-
this.debounceTimers.clear();
|
|
1451
|
-
this.fileSystemCache.clear();
|
|
1452
|
-
}
|
|
1453
|
-
}
|
|
1454
|
-
async function compileJS(inPath, outPath, mode = 'individual') {
|
|
1455
|
-
const timings = {};
|
|
1456
|
-
// Si la ruta ya es absoluta, no la resolvamos de nuevo
|
|
1457
|
-
inPath = path.isAbsolute(inPath)
|
|
1458
|
-
? normalizeRuta(inPath)
|
|
1459
|
-
: normalizeRuta(path.resolve(inPath)); // 🚀 Usar OptimizedModuleManager para carga optimizada
|
|
1460
|
-
const moduleManager = OptimizedModuleManager.getInstance();
|
|
1461
|
-
// Timing de lectura
|
|
1462
|
-
let start = Date.now();
|
|
1463
|
-
const extension = path.extname(inPath); // Asegurar que el parser esté cargado
|
|
1464
|
-
await moduleManager.ensureModuleLoaded('parser');
|
|
1465
|
-
const getCodeFile = await loadParser();
|
|
1466
|
-
const result = await getCodeFile(inPath);
|
|
1467
|
-
let code = result.code;
|
|
1468
|
-
const error = result.error;
|
|
1469
|
-
timings.fileRead = Date.now() - start;
|
|
1470
|
-
if (error) {
|
|
1471
|
-
await handleCompilationError(error instanceof Error ? error : new Error(String(error)), inPath, 'file-read', mode, env.VERBOSE === 'true');
|
|
1472
|
-
throw new Error(error instanceof Error ? error.message : String(error));
|
|
1473
|
-
}
|
|
1474
|
-
if (!code ||
|
|
1475
|
-
code.trim().length === 0 ||
|
|
1476
|
-
code === 'undefined' ||
|
|
1477
|
-
code === 'null') {
|
|
1478
|
-
await handleCompilationError(new Error('El archivo está vacío o no se pudo leer.'), inPath, 'file-read', mode, env.VERBOSE === 'true');
|
|
1479
|
-
throw new Error('El archivo está vacío o no se pudo leer.');
|
|
1480
|
-
} // Logs detallados en modo verbose
|
|
1481
|
-
const shouldShowDetailedLogs = env.VERBOSE === 'true'; // Compilación de Vue
|
|
1482
|
-
let vueResult;
|
|
1483
|
-
if (extension === '.vue') {
|
|
1484
|
-
start = Date.now();
|
|
1485
|
-
if (shouldShowDetailedLogs) {
|
|
1486
|
-
logger.info(chalk.green(`💚 Precompilando VUE: ${path.basename(inPath)}`));
|
|
1487
|
-
}
|
|
1488
|
-
// Asegurar que el módulo Vue esté cargado
|
|
1489
|
-
await moduleManager.ensureModuleLoaded('vue');
|
|
1490
|
-
const preCompileVue = await loadVue();
|
|
1491
|
-
if (typeof preCompileVue !== 'function') {
|
|
1492
|
-
throw new Error(`loadVue devolvió ${typeof preCompileVue} en lugar de una función para archivo: ${inPath}`);
|
|
1493
|
-
}
|
|
1494
|
-
vueResult = await preCompileVue(code, inPath, env.isPROD === 'true');
|
|
1495
|
-
timings.vueCompile = Date.now() - start;
|
|
1496
|
-
if (vueResult === undefined || vueResult === null) {
|
|
1497
|
-
throw new Error(`preCompileVue devolvió ${vueResult} para archivo: ${inPath}`);
|
|
1498
|
-
}
|
|
1499
|
-
if (vueResult.error) {
|
|
1500
|
-
await handleCompilationError(vueResult.error instanceof Error
|
|
1501
|
-
? vueResult.error
|
|
1502
|
-
: new Error(String(vueResult.error)), inPath, 'vue', mode, env.VERBOSE === 'true');
|
|
1503
|
-
throw new Error(vueResult.error instanceof Error
|
|
1504
|
-
? vueResult.error.message
|
|
1505
|
-
: String(vueResult.error));
|
|
1506
|
-
}
|
|
1507
|
-
registerCompilationSuccess(inPath, 'vue');
|
|
1508
|
-
code = vueResult.data;
|
|
1509
|
-
}
|
|
1510
|
-
if (!code || code.trim().length === 0) {
|
|
1511
|
-
await handleCompilationError(new Error('El código Vue compilado está vacío.'), inPath, 'vue', mode, env.VERBOSE === 'true');
|
|
1512
|
-
throw new Error('El código Vue compilado está vacío.');
|
|
1513
|
-
}
|
|
1514
|
-
// Compilación de TypeScript
|
|
1515
|
-
let tsResult;
|
|
1516
|
-
if (extension === '.ts' || vueResult?.lang === 'ts') {
|
|
1517
|
-
start = Date.now();
|
|
1518
|
-
if (shouldShowDetailedLogs) {
|
|
1519
|
-
logger.info(chalk.blue(`🔄️ Precompilando TS: ${path.basename(inPath)}`));
|
|
1520
|
-
}
|
|
1521
|
-
// Asegurar que el módulo TypeScript esté cargado
|
|
1522
|
-
await moduleManager.ensureModuleLoaded('typescript');
|
|
1523
|
-
const preCompileTS = await loadTypeScript();
|
|
1524
|
-
if (typeof preCompileTS !== 'function') {
|
|
1525
|
-
throw new Error(`loadTypeScript devolvió ${typeof preCompileTS} en lugar de una función para archivo: ${inPath}`);
|
|
1526
|
-
}
|
|
1527
|
-
tsResult = await preCompileTS(code, inPath);
|
|
1528
|
-
timings.tsCompile = Date.now() - start;
|
|
1529
|
-
if (tsResult === undefined || tsResult === null) {
|
|
1530
|
-
throw new Error(`preCompileTS devolvió ${tsResult} para archivo: ${inPath}`);
|
|
1531
|
-
}
|
|
1532
|
-
if (tsResult.error) {
|
|
1533
|
-
if (mode === 'all') {
|
|
1534
|
-
// En modo --all, registrar el error pero continuar la compilación
|
|
1535
|
-
registerCompilationError(inPath, 'typescript', tsResult.error instanceof Error
|
|
1536
|
-
? tsResult.error.message
|
|
1537
|
-
: String(tsResult.error), 'error');
|
|
1538
|
-
}
|
|
1539
|
-
else {
|
|
1540
|
-
await handleCompilationError(tsResult.error, inPath, 'typescript', mode, env.VERBOSE === 'true');
|
|
1541
|
-
throw new Error(tsResult.error instanceof Error
|
|
1542
|
-
? tsResult.error.message
|
|
1543
|
-
: String(tsResult.error));
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
else {
|
|
1547
|
-
registerCompilationSuccess(inPath, 'typescript');
|
|
1548
|
-
code = tsResult.data;
|
|
1549
|
-
}
|
|
1550
|
-
}
|
|
1551
|
-
if (!code || code.trim().length === 0) {
|
|
1552
|
-
await handleCompilationError(new Error('El código TypeScript compilado está vacío.'), inPath, 'typescript', mode, env.VERBOSE === 'true');
|
|
1553
|
-
throw new Error('El código TypeScript compilado está vacío.');
|
|
1554
|
-
} // Estandarización
|
|
1555
|
-
if (shouldShowDetailedLogs) {
|
|
1556
|
-
logger.info(chalk.yellow(`💛 Estandarizando: ${path.basename(inPath)}`));
|
|
1557
|
-
}
|
|
1558
|
-
start = Date.now();
|
|
1559
|
-
// Asegurar que el módulo de transformaciones esté cargado
|
|
1560
|
-
await moduleManager.ensureModuleLoaded('transforms');
|
|
1561
|
-
const estandarizaCode = await loadTransforms();
|
|
1562
|
-
const resultSTD = await estandarizaCode(code, inPath);
|
|
1563
|
-
timings.standardization = Date.now() - start;
|
|
1564
|
-
if (resultSTD === undefined || resultSTD === null) {
|
|
1565
|
-
throw new Error(`estandarizaCode devolvió ${resultSTD} para archivo: ${inPath}`);
|
|
1566
|
-
}
|
|
1567
|
-
if (resultSTD.error) {
|
|
1568
|
-
await handleCompilationError(new Error(resultSTD.error), inPath, 'standardization', mode, env.VERBOSE === 'true');
|
|
1569
|
-
throw new Error(resultSTD.error);
|
|
1570
|
-
}
|
|
1571
|
-
registerCompilationSuccess(inPath, 'standardization');
|
|
1572
|
-
code = resultSTD.code;
|
|
1573
|
-
if (!code || code.trim().length === 0) {
|
|
1574
|
-
await handleCompilationError(new Error('El código estandarizado está vacío.'), inPath, 'standardization', mode, env.VERBOSE === 'true');
|
|
1575
|
-
throw new Error('El código estandarizado está vacío.');
|
|
1576
|
-
}
|
|
1577
|
-
// Minificación (solo en producción)
|
|
1578
|
-
if (env.isPROD === 'true') {
|
|
1579
|
-
start = Date.now();
|
|
1580
|
-
if (shouldShowDetailedLogs) {
|
|
1581
|
-
logger.info(chalk.red(`🤖 Minificando: ${path.basename(inPath)}`));
|
|
1582
|
-
}
|
|
1583
|
-
// Asegurar que el módulo de minificación esté cargado
|
|
1584
|
-
await moduleManager.ensureModuleLoaded('minify');
|
|
1585
|
-
const minifyJS = await loadMinify();
|
|
1586
|
-
const resultMinify = await minifyJS(code, inPath, true);
|
|
1587
|
-
timings.minification = Date.now() - start;
|
|
1588
|
-
if (resultMinify === undefined || resultMinify === null) {
|
|
1589
|
-
throw new Error(`minifyJS devolvió ${resultMinify} para archivo: ${inPath}`);
|
|
1590
|
-
}
|
|
1591
|
-
if (resultMinify.error) {
|
|
1592
|
-
await handleCompilationError(resultMinify.error instanceof Error
|
|
1593
|
-
? resultMinify.error
|
|
1594
|
-
: new Error(String(resultMinify.error)), inPath, 'minification', mode, env.VERBOSE === 'true');
|
|
1595
|
-
throw new Error(resultMinify.error instanceof Error
|
|
1596
|
-
? resultMinify.error.message
|
|
1597
|
-
: String(resultMinify.error));
|
|
1598
|
-
}
|
|
1599
|
-
registerCompilationSuccess(inPath, 'minification');
|
|
1600
|
-
code = resultMinify.code;
|
|
1601
|
-
} // Escribir archivo final
|
|
1602
|
-
const destinationDir = path.dirname(outPath);
|
|
1603
|
-
await mkdir(destinationDir, { recursive: true });
|
|
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
|
-
}
|
|
1621
|
-
return {
|
|
1622
|
-
error: null,
|
|
1623
|
-
action: 'extension',
|
|
1624
|
-
};
|
|
1625
|
-
}
|
|
1626
|
-
export async function initCompile(ruta, compileTailwind = true, mode = 'individual') {
|
|
1627
|
-
try {
|
|
1628
|
-
// 🚀 Sistema de Carga Inteligente de Módulos
|
|
1629
|
-
const moduleManager = OptimizedModuleManager.getInstance();
|
|
1630
|
-
const fileExtension = path.extname(ruta);
|
|
1631
|
-
const fileExtensions = new Set([fileExtension]);
|
|
1632
|
-
// Inicializar módulos según el contexto
|
|
1633
|
-
await moduleManager.preloadForContext(mode === 'all' ? 'batch' : mode, fileExtensions);
|
|
1634
|
-
// Generar TailwindCSS si está habilitado
|
|
1635
|
-
if (compileTailwind && Boolean(env.TAILWIND)) {
|
|
1636
|
-
await moduleManager.ensureModuleLoaded('tailwind');
|
|
1637
|
-
const generateTailwindCSS = await loadTailwind();
|
|
1638
|
-
const resultTW = await generateTailwindCSS();
|
|
1639
|
-
if (typeof resultTW !== 'boolean') {
|
|
1640
|
-
if (resultTW?.success) {
|
|
1641
|
-
logger.info(`🎨 ${resultTW.message}`);
|
|
1642
|
-
}
|
|
1643
|
-
else {
|
|
1644
|
-
const errorMsg = `${resultTW.message}${resultTW.details ? '\n' + resultTW.details : ''}`;
|
|
1645
|
-
await handleCompilationError(new Error(errorMsg), 'tailwind.config.js', 'tailwind', mode, env.VERBOSE === 'true');
|
|
1646
|
-
// No hacer throw aquí, permitir que la compilación continúe
|
|
1647
|
-
}
|
|
1648
|
-
}
|
|
1649
|
-
}
|
|
1650
|
-
const startTime = Date.now();
|
|
1651
|
-
const file = normalizeRuta(ruta);
|
|
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
|
-
}
|
|
1667
|
-
if (mode === 'individual' && env.VERBOSE === 'true') {
|
|
1668
|
-
logger.info(`🔜 Fuente: ${file}`);
|
|
1669
|
-
}
|
|
1670
|
-
const result = await compileJS(file, outFile, mode);
|
|
1671
|
-
if (result.error) {
|
|
1672
|
-
throw new Error(result.error);
|
|
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
|
-
}
|
|
1678
|
-
const endTime = Date.now();
|
|
1679
|
-
const elapsedTime = showTimingForHumans(endTime - startTime);
|
|
1680
|
-
if (mode === 'individual') {
|
|
1681
|
-
if (env.VERBOSE === 'true') {
|
|
1682
|
-
logger.info(`🔚 Destino: ${outFile}`);
|
|
1683
|
-
logger.info(`⏱️ Tiempo: ${elapsedTime}`);
|
|
1684
|
-
}
|
|
1685
|
-
const chalk = await loadChalk();
|
|
1686
|
-
logger.info(chalk.green(`✅ Compilación exitosa: ${path.basename(file)}`));
|
|
1687
|
-
}
|
|
1688
|
-
return {
|
|
1689
|
-
success: true,
|
|
1690
|
-
output: outFile,
|
|
1691
|
-
action: result.action,
|
|
1692
|
-
};
|
|
1693
|
-
}
|
|
1694
|
-
catch (error) {
|
|
1695
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1696
|
-
if (env.VERBOSE === 'true') {
|
|
1697
|
-
logger.error(`❌ Error en compilación de ${path.basename(ruta)}: ${errorMessage}`);
|
|
1698
|
-
}
|
|
1699
|
-
return {
|
|
1700
|
-
success: false,
|
|
1701
|
-
output: '',
|
|
1702
|
-
error: errorMessage,
|
|
1703
|
-
};
|
|
1704
|
-
}
|
|
1705
|
-
}
|
|
1706
|
-
// Variable para el último progreso mostrado (evitar spam)
|
|
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
|
-
}
|
|
1885
|
-
// Función para ejecutar el linter antes de la compilación
|
|
1886
|
-
export async function runLinter(showResult = false) {
|
|
1887
|
-
const linterENV = env.linter;
|
|
1888
|
-
const linterPromises = [];
|
|
1889
|
-
const linterErrors = [];
|
|
1890
|
-
let proceedWithCompilation = true;
|
|
1891
|
-
if (env.ENABLE_LINTER !== 'true') {
|
|
1892
|
-
return true;
|
|
1893
|
-
}
|
|
1894
|
-
if (typeof linterENV === 'string' && linterENV.trim() !== '') {
|
|
1895
|
-
logger.info('🔍 Ejecutando linting...');
|
|
1896
|
-
try {
|
|
1897
|
-
const parsedLinterEnv = JSON.parse(linterENV);
|
|
1898
|
-
if (Array.isArray(parsedLinterEnv)) {
|
|
1899
|
-
// Cargar dependencias de linting de forma lazy
|
|
1900
|
-
const { ESLint, OxLint } = await loadLinter();
|
|
1901
|
-
for (const item of parsedLinterEnv) {
|
|
1902
|
-
if (item.name.toLowerCase() === 'eslint') {
|
|
1903
|
-
logger.info(`🔧 Ejecutando ESLint con config: ${item.configFile || 'por defecto'}`);
|
|
1904
|
-
const eslintPromise = ESLint(item)
|
|
1905
|
-
.then((eslintResult) => {
|
|
1906
|
-
if (eslintResult && eslintResult.json) {
|
|
1907
|
-
// Procesar resultados de ESLint
|
|
1908
|
-
if (Array.isArray(eslintResult.json)) {
|
|
1909
|
-
eslintResult.json.forEach((result) => {
|
|
1910
|
-
const filePath = result.filePath ||
|
|
1911
|
-
result.file ||
|
|
1912
|
-
'archivo no especificado';
|
|
1913
|
-
linterErrors.push({
|
|
1914
|
-
from: 'eslint',
|
|
1915
|
-
line: result.line || 'N/A',
|
|
1916
|
-
column: result.column || 1,
|
|
1917
|
-
file: filePath,
|
|
1918
|
-
message: result.message,
|
|
1919
|
-
severity: result.severity === 2
|
|
1920
|
-
? 'error'
|
|
1921
|
-
: 'warning',
|
|
1922
|
-
help: result.ruleId
|
|
1923
|
-
? `Regla ESLint: ${result.ruleId}`
|
|
1924
|
-
: undefined,
|
|
1925
|
-
});
|
|
1926
|
-
});
|
|
1927
|
-
}
|
|
1928
|
-
else if (eslintResult.json.results &&
|
|
1929
|
-
Array.isArray(eslintResult.json.results)) {
|
|
1930
|
-
eslintResult.json.results.forEach((fileResult) => {
|
|
1931
|
-
if (fileResult.messages &&
|
|
1932
|
-
Array.isArray(fileResult.messages)) {
|
|
1933
|
-
fileResult.messages.forEach((msg) => {
|
|
1934
|
-
const filePath = fileResult.filePath ||
|
|
1935
|
-
fileResult.file ||
|
|
1936
|
-
'archivo no especificado';
|
|
1937
|
-
linterErrors.push({
|
|
1938
|
-
from: 'eslint',
|
|
1939
|
-
line: msg.line ||
|
|
1940
|
-
'N/A',
|
|
1941
|
-
column: msg.column ||
|
|
1942
|
-
1,
|
|
1943
|
-
file: filePath,
|
|
1944
|
-
message: msg.message,
|
|
1945
|
-
severity: msg.severity ===
|
|
1946
|
-
2
|
|
1947
|
-
? 'error'
|
|
1948
|
-
: 'warning',
|
|
1949
|
-
help: msg.ruleId
|
|
1950
|
-
? `Regla ESLint: ${msg.ruleId}`
|
|
1951
|
-
: undefined,
|
|
1952
|
-
});
|
|
1953
|
-
});
|
|
1954
|
-
}
|
|
1955
|
-
});
|
|
1956
|
-
}
|
|
1957
|
-
}
|
|
1958
|
-
})
|
|
1959
|
-
.catch((err) => {
|
|
1960
|
-
logger.error(`❌ Error durante la ejecución de ESLint: ${err.message}`);
|
|
1961
|
-
linterErrors.push({
|
|
1962
|
-
file: item.configFile || 'ESLint Config',
|
|
1963
|
-
message: `Fallo al ejecutar ESLint: ${err.message}`,
|
|
1964
|
-
severity: 'error',
|
|
1965
|
-
});
|
|
1966
|
-
});
|
|
1967
|
-
linterPromises.push(eslintPromise);
|
|
1968
|
-
}
|
|
1969
|
-
else if (item.name.toLowerCase() === 'oxlint') {
|
|
1970
|
-
logger.info(`🔧 Ejecutando OxLint con config: ${item.configFile || 'por defecto'}`);
|
|
1971
|
-
const oxlintPromise = OxLint(item)
|
|
1972
|
-
.then((oxlintResult) => {
|
|
1973
|
-
if (oxlintResult &&
|
|
1974
|
-
oxlintResult['json'] &&
|
|
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;
|
|
1996
|
-
linterErrors.push({
|
|
1997
|
-
from: 'oxlint',
|
|
1998
|
-
line: lineNumber,
|
|
1999
|
-
column: columnNumber,
|
|
2000
|
-
file: filePath,
|
|
2001
|
-
message: result.message,
|
|
2002
|
-
severity: typeof result.severity ===
|
|
2003
|
-
'string'
|
|
2004
|
-
? result.severity.toLowerCase()
|
|
2005
|
-
: 'error',
|
|
2006
|
-
help: result.help ||
|
|
2007
|
-
(result.code
|
|
2008
|
-
? `Regla Oxlint: ${result.code}`
|
|
2009
|
-
: undefined),
|
|
2010
|
-
});
|
|
2011
|
-
});
|
|
2012
|
-
}
|
|
2013
|
-
})
|
|
2014
|
-
.catch((err) => {
|
|
2015
|
-
logger.error(`❌ Error durante la ejecución de OxLint: ${err.message}`);
|
|
2016
|
-
linterErrors.push({
|
|
2017
|
-
file: item.configFile || 'Oxlint Config',
|
|
2018
|
-
message: `Fallo al ejecutar Oxlint: ${err.message}`,
|
|
2019
|
-
severity: 'error',
|
|
2020
|
-
});
|
|
2021
|
-
});
|
|
2022
|
-
linterPromises.push(oxlintPromise);
|
|
2023
|
-
}
|
|
2024
|
-
}
|
|
2025
|
-
}
|
|
2026
|
-
else {
|
|
2027
|
-
logger.warn('⚠️ La configuración de linter no es un array válido.');
|
|
2028
|
-
}
|
|
2029
|
-
await Promise.all(linterPromises);
|
|
2030
|
-
if (showResult) {
|
|
2031
|
-
// Modo --linter: Solo mostrar resultados sin preguntar
|
|
2032
|
-
if (linterErrors.length > 0) {
|
|
2033
|
-
await displayLinterErrors(linterErrors);
|
|
2034
|
-
}
|
|
2035
|
-
else {
|
|
2036
|
-
const chalk = await loadChalk();
|
|
2037
|
-
logger.info(chalk.green('✅ No se encontraron errores ni advertencias de linting.'));
|
|
2038
|
-
}
|
|
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
|
-
}
|
|
2054
|
-
}
|
|
2055
|
-
catch (parseError) {
|
|
2056
|
-
logger.warn(`Error parseando configuración de linter: ${parseError instanceof Error ? parseError.message : 'Error desconocido'}, omitiendo...`);
|
|
2057
|
-
}
|
|
2058
|
-
}
|
|
2059
|
-
return proceedWithCompilation;
|
|
2060
|
-
}
|
|
2061
|
-
// Función para crear una barra de progreso visual
|
|
2062
|
-
function createProgressBar(current, total, width = 30) {
|
|
2063
|
-
const percentage = Math.round((current / total) * 100);
|
|
2064
|
-
const filled = Math.round((percentage / 100) * width);
|
|
2065
|
-
const empty = width - filled;
|
|
2066
|
-
return `[${'█'.repeat(filled)}${' '.repeat(empty)}] ${percentage}% (${current}/${total})`;
|
|
2067
|
-
}
|
|
2068
|
-
// Función helper para verificar si un archivo debe ser omitido por cache
|
|
2069
|
-
async function shouldSkipFile(filePath) {
|
|
2070
|
-
return await smartCache.isValid(filePath);
|
|
2071
|
-
}
|
|
2072
|
-
// Función para compilar archivos con límite de concurrencia
|
|
2073
|
-
async function compileWithConcurrencyLimit(files, maxConcurrency = 8) {
|
|
2074
|
-
const results = [];
|
|
2075
|
-
const executing = [];
|
|
2076
|
-
const total = files.length;
|
|
2077
|
-
let completed = 0;
|
|
2078
|
-
let skipped = 0;
|
|
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
|
|
2088
|
-
function showProgress() {
|
|
2089
|
-
const currentTotal = completed + skipped + failed;
|
|
2090
|
-
const progressBar = createProgressBar(currentTotal, total);
|
|
2091
|
-
const progressPercent = Math.round((currentTotal / total) * 100);
|
|
2092
|
-
// Mostrar progreso inicial cuando se inicie O cuando haya progreso real
|
|
2093
|
-
if ((currentTotal === 0 && !hasShownInitialProgress) ||
|
|
2094
|
-
(progressPercent > lastProgressUpdate + 1 && currentTotal > 0) ||
|
|
2095
|
-
currentTotal === total) {
|
|
2096
|
-
const progressText = `🚀 ${progressBar} [✅ ${completed} | ⏭️ ${skipped} | ❌ ${failed}]`;
|
|
2097
|
-
progressManager.updateProgress(progressText);
|
|
2098
|
-
if (currentTotal === 0) {
|
|
2099
|
-
hasShownInitialProgress = true;
|
|
2100
|
-
}
|
|
2101
|
-
lastProgressUpdate = progressPercent;
|
|
2102
|
-
// NO terminar el progreso aquí - se termina en initCompileAll
|
|
2103
|
-
}
|
|
2104
|
-
}
|
|
2105
|
-
// Mostrar progreso inicial
|
|
2106
|
-
showProgress();
|
|
2107
|
-
for (const file of files) {
|
|
2108
|
-
const promise = (async () => {
|
|
2109
|
-
try {
|
|
2110
|
-
// Log verbose: Iniciando compilación del archivo
|
|
2111
|
-
if (env.VERBOSE === 'true') {
|
|
2112
|
-
logger.info(`🔄 Compilando: ${path.basename(file)}`);
|
|
2113
|
-
}
|
|
2114
|
-
// Verificar cache antes de compilar
|
|
2115
|
-
if (await shouldSkipFile(file)) {
|
|
2116
|
-
skipped++;
|
|
2117
|
-
if (env.VERBOSE === 'true') {
|
|
2118
|
-
logger.info(`⏭️ Archivo omitido (cache): ${path.basename(file)}`);
|
|
2119
|
-
}
|
|
2120
|
-
showProgress();
|
|
2121
|
-
return {
|
|
2122
|
-
success: true,
|
|
2123
|
-
cached: true,
|
|
2124
|
-
output: smartCache.getOutputPath(file),
|
|
2125
|
-
};
|
|
2126
|
-
}
|
|
2127
|
-
const result = await initCompile(file, false, 'batch');
|
|
2128
|
-
// Actualizar cache si la compilación fue exitosa
|
|
2129
|
-
if (result.success && result.output) {
|
|
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)}`);
|
|
2137
|
-
}
|
|
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
|
-
}
|
|
2166
|
-
showProgress();
|
|
2167
|
-
return result;
|
|
2168
|
-
}
|
|
2169
|
-
catch (error) {
|
|
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
|
-
}
|
|
2175
|
-
showProgress();
|
|
2176
|
-
return {
|
|
2177
|
-
success: false,
|
|
2178
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2179
|
-
};
|
|
2180
|
-
}
|
|
2181
|
-
})();
|
|
2182
|
-
results.push(promise);
|
|
2183
|
-
executing.push(promise);
|
|
2184
|
-
if (executing.length >= maxConcurrency) {
|
|
2185
|
-
await Promise.race(executing);
|
|
2186
|
-
executing.splice(executing.findIndex(p => p === promise), 1);
|
|
2187
|
-
}
|
|
2188
|
-
}
|
|
2189
|
-
await Promise.all(results);
|
|
2190
|
-
// El progreso ya se termina automáticamente en showProgress() cuando se completa
|
|
2191
|
-
}
|
|
2192
|
-
export async function initCompileAll() {
|
|
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...');
|
|
2199
|
-
// Limpiar estado de compilación anterior
|
|
2200
|
-
clearCompilationState();
|
|
2201
|
-
// Cargar cache al inicio
|
|
2202
|
-
progressManager.updateProgress('📦 Cargando cache...');
|
|
2203
|
-
await loadCache();
|
|
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
|
|
2207
|
-
if (!shouldContinue) {
|
|
2208
|
-
// await displayCompilationSummary(env.VERBOSE === 'true');
|
|
2209
|
-
progressManager.endProgress();
|
|
2210
|
-
return;
|
|
2211
|
-
}
|
|
2212
|
-
const startTime = Date.now();
|
|
2213
|
-
const rawPathSource = env.PATH_SOURCE ?? '';
|
|
2214
|
-
const pathDist = env.PATH_DIST ?? '';
|
|
2215
|
-
const normalizedGlobPathSource = rawPathSource.replace(/\\/g, '/');
|
|
2216
|
-
const patterns = [
|
|
2217
|
-
`${normalizedGlobPathSource}/**/*.js`,
|
|
2218
|
-
`${normalizedGlobPathSource}/**/*.vue`,
|
|
2219
|
-
`${normalizedGlobPathSource}/**/*.ts`,
|
|
2220
|
-
`${normalizedGlobPathSource}/**/*.mjs`,
|
|
2221
|
-
`${normalizedGlobPathSource}/**/*.cjs`,
|
|
2222
|
-
];
|
|
2223
|
-
logger.info(`📝 Compilando todos los archivos...`);
|
|
2224
|
-
logger.info(`🔜 Fuente: ${rawPathSource}`);
|
|
2225
|
-
logger.info(`🔚 Destino: ${pathDist}\n`);
|
|
2226
|
-
// Fase 3: TailwindCSS
|
|
2227
|
-
progressManager.updateProgress('🎨 Generando TailwindCSS...');
|
|
2228
|
-
const generateTailwindCSS = await loadTailwind();
|
|
2229
|
-
const resultTW = await generateTailwindCSS();
|
|
2230
|
-
if (typeof resultTW !== 'boolean') {
|
|
2231
|
-
if (resultTW?.success) {
|
|
2232
|
-
logger.info(`🎨 ${resultTW.message}\n`);
|
|
2233
|
-
}
|
|
2234
|
-
else {
|
|
2235
|
-
await handleCompilationError(new Error(`${resultTW.message}${resultTW.details ? '\n' + resultTW.details : ''}`), 'tailwind.config.js', 'tailwind', 'all', env.VERBOSE === 'true');
|
|
2236
|
-
}
|
|
2237
|
-
}
|
|
2238
|
-
// Fase 4: Recopilando archivos
|
|
2239
|
-
progressManager.updateProgress('📁 Recopilando archivos...');
|
|
2240
|
-
const filesToCompile = [];
|
|
2241
|
-
for await (const file of glob(patterns)) {
|
|
2242
|
-
if (file.endsWith('.d.ts')) {
|
|
2243
|
-
continue;
|
|
2244
|
-
}
|
|
2245
|
-
// Usar la ruta tal como viene de glob, sin modificar
|
|
2246
|
-
filesToCompile.push(file);
|
|
2247
|
-
} // Determinar concurrencia óptima considerando memoria disponible
|
|
2248
|
-
const cpuCount = os.cpus().length;
|
|
2249
|
-
const fileCount = filesToCompile.length;
|
|
2250
|
-
const memUsage = process.memoryUsage();
|
|
2251
|
-
const availableMemoryMB = (memUsage.heapTotal - memUsage.heapUsed) / (1024 * 1024);
|
|
2252
|
-
let maxConcurrency;
|
|
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));
|
|
2260
|
-
}
|
|
2261
|
-
else if (fileCount < 50) {
|
|
2262
|
-
maxConcurrency = Math.min(cpuCount, 6); // Reducido
|
|
2263
|
-
}
|
|
2264
|
-
else {
|
|
2265
|
-
maxConcurrency = Math.min(cpuCount, 8); // Reducido
|
|
2266
|
-
}
|
|
2267
|
-
// Fase 5: Configurando workers
|
|
2268
|
-
progressManager.updateProgress('⚙️ Configurando workers...');
|
|
2269
|
-
logger.info(`🚀 Compilando ${fileCount} archivos con concurrencia optimizada (${maxConcurrency} hilos)...`); // Configurar worker pool para modo batch
|
|
2270
|
-
try {
|
|
2271
|
-
const { TypeScriptWorkerPool } = (await import('./typescript-worker-pool.js'));
|
|
2272
|
-
const workerPool = TypeScriptWorkerPool.getInstance();
|
|
2273
|
-
workerPool.setMode('batch');
|
|
2274
|
-
}
|
|
2275
|
-
catch {
|
|
2276
|
-
// Error silencioso en configuración del pool
|
|
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...');
|
|
2281
|
-
await saveCache();
|
|
2282
|
-
const endTime = Date.now();
|
|
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);
|
|
2287
|
-
}
|
|
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
|
-
}
|
|
2294
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2295
|
-
logger.error(`🚩 Error al compilar todos los archivos: ${errorMessage}`);
|
|
2296
|
-
// Registrar el error en el sistema unificado
|
|
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
|
|
2298
|
-
await displayCompilationSummary(env.VERBOSE === 'true');
|
|
2299
|
-
}
|
|
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
|
-
}
|
|
2327
|
-
// Función wrapper para compatibilidad con tests
|
|
2328
|
-
export async function compileFile(filePath) {
|
|
2329
|
-
return await initCompile(filePath, true, 'individual');
|
|
2330
|
-
}
|
|
2331
|
-
export { WatchModeOptimizer };
|
|
2332
|
-
//# sourceMappingURL=compile.js.map
|
|
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(()=>Q.getInstance());let _,v,y,b,x,S,C,w,T,E,D;class O{static instance;isInitialized=!1;loadedModules=new Set;modulePool=new Map;loadingPromises=new Map;usageStats=new Map;preloadQueue=new Set;backgroundLoader=null;HOT_MODULES=[`chalk`,`parser`];currentContext=null;constructor(){this.startBackgroundPreloading()}static getInstance(){return O.instance||=new O,O.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){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`));let r=n.map(e=>this.ensureModuleLoaded(e).catch(()=>{console.warn(`Warning: No se pudo precargar módulo ${e}`)}));await Promise.allSettled(r)}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 _||=(await import(`chalk`)).default,_}async loadParser(){if(!x){let e=await import(`./parser.js`);x=e.getCodeFile}return x}async loadTransforms(){if(!C){let e=await import(`./transforms.js`);C=e.estandarizaCode}return C}async loadVue(){if(!T){let e=await import(`./vuejs.js`);T=e.preCompileVue}return T}async loadTypeScript(){if(!w){let e=await import(`./typescript-manager.js`);w=e.preCompileTS}return w}async loadMinify(){if(!b){let e=await import(`./minify.js`);b=e.minifyJS}return b}async loadTailwind(){if(!S){let e=await import(`./tailwindcss.js`);S=e.generateTailwindCSS}return S}async loadLinter(){if(!v||!y){let e=await import(`./linter.js`);v=e.ESLint,y=e.OxLint}return{ESLint:v,OxLint:y}}async loadTransformOptimizer(){if(!E){let e=await import(`./transform-optimizer.js`);E=e.TransformOptimizer.getInstance()}return E}async loadModuleResolutionOptimizer(){if(!D){let e=await import(`./module-resolution-optimizer.js`);D=e.ModuleResolutionOptimizer.getInstance()}return D}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}}cleanupUnusedModules(){let e=1;for(let[e,t]of this.usageStats)t<1&&!this.HOT_MODULES.includes(e)&&(this.modulePool.delete(e),this.loadedModules.delete(e),this.usageStats.delete(e))}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 k(){return _||=(await import(`chalk`)).default,_}async function oe(){if(!v||!y){let e=await import(`./linter.js`);v=e.ESLint,y=e.OxLint}return{ESLint:v,OxLint:y}}async function se(){if(!b){let e=await import(`./minify.js`);b=e.minifyJS}return b}async function A(){if(!x){let e=await import(`./parser.js`);x=e.getCodeFile}return x}async function j(){if(!S){let e=await import(`./tailwindcss.js`);S=e.generateTailwindCSS}return S}async function M(){if(!C){let e=await import(`./transforms.js`);C=e.estandarizaCode}return C}async function ce(){if(!w){let e=await import(`./typescript-manager.js`);w=e.preCompileTS}return w}async function le(){if(!T){let e=await import(`./vuejs.js`);T=e.preCompileVue}return T}const N=[],P=[],F=[`NODE_ENV`,`isPROD`,`TAILWIND`,`ENABLE_LINTER`,`VERBOSE`,`typeCheck`,`PATH_ALIAS`,`tailwindcss`,`linter`,`tsconfigFile`];class I{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=F.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=c.join(d(),`package.json`),a=await r(n,`utf8`),o=JSON.parse(a),s={...o.dependencies,...o.devDependencies},l=JSON.stringify(s,Object.keys(s).sort());t.update(`package:${l}`);try{let e=c.join(d(),`package-lock.json`),n=await r(e,`utf8`),i=JSON.parse(n),a={};if(i.packages){for(let[e,t]of Object.entries(i.packages))if(e&&e!==``&&typeof t==`object`&&t){let n=e.replace(`node_modules/`,``);t.version&&(a[n]=t.version)}}t.update(`lock:${JSON.stringify(a,Object.keys(a).sort())}`)}catch{}try{let e=c.join(d(),`node_modules`),n=await i(e);t.update(`nmtime:${n.mtimeMs}`);let r=Object.keys(s).slice(0,10);for(let n of r)try{let r=c.join(e,n),a=await i(r);t.update(`${n}:${a.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();if(t.dependencyHash!==o)return this.cache.delete(e),!1;let s=await i(e);return s.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=await this.generateContentHash(e),a=this.generateConfigHash(),o=this.generateEnvHash(),s=await this.generateDependencyHash(),c={contentHash:r,configHash:a,envHash:o,dependencyHash:s,mtime:n.mtimeMs,outputPath:t,lastUsed:Date.now(),size:n.size};this.evictIfNeeded(c.size),this.cache.set(e,c),this.currentMemoryUsage+=c.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();let t=l.memoryUsage(),n=t.heapUsed/(1024*1024);if(n>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){let t=this.cache.get(e);return t?.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 L=new I,R=c.join(c.resolve(f.PATH_PROY||d(),`compiler`),`.cache`),z=c.join(R,`versacompile-cache.json`);async function B(){await L.load(z),(f.WATCH_MODE===`true`||u.includes(`--watch`)||u.includes(`-w`))&&await L.startDependencyWatching()}async function ue(){await L.save(z,R)}function V(e,t,n,r=`error`,i,a){N.push({file:e,stage:t,message:n,severity:r,details:i,help:a,timestamp:Date.now()})}function H(e,t,n,r=[]){let i=P.find(t=>t.stage===e);i?(i.errors+=t,i.success+=n,i.files.push(...r)):P.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 k(),r=c.basename(t),s=await Y(n);if(i){if(p.error(e.red(`❌ Error en etapa ${s(n)} - ${r}:`)),p.error(e.red(a)),o&&(n===`typescript`||n===`vue`)){let t=o.split(`
|
|
2
|
+
`).slice(0,5);t.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(){N.length=0,P.length=0}async function K(e=!1,t){let n=await k();if(N.length===0&&P.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(``)),P.length>0){p.info(n.bold.blue(`🔧 Estadísticas por Etapa:`));for(let e of P){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=ve(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(N.length>0){p.info(n.red(`\n❌ Se encontraron ${N.length} errores:`));let t=new Map;N.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 Y(t.stage);if(p.info(` ${r} [${i(t.stage)}] ${t.message}`),e&&t.details){let e=t.details.split(`
|
|
4
|
+
`).slice(0,5);e.forEach(e=>{e.trim()&&p.info(n.gray(` ${e.trim()}`))})}t.help&&p.info(n.blue(` 💡 ${t.help}`))}r++}let i=N.filter(e=>e.severity===`error`).length,a=N.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 k(),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 J(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 J(e,t,n,r){let i=await k(),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 de(r,e,n+1,t.length)}}async function de(e,t,n,r){let i=await k(),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`),r=n.split(`
|
|
5
|
+
`),o=parseInt(u.toString())-1;if(o>=0&&o<r.length){p.info(``);let e=Math.max(0,o-1),t=Math.min(r.length-1,o+1),n=(t+1).toString().length;for(let a=e;a<=t;a++){let e=a+1,t=r[a]||``,c=e.toString().padStart(n,` `),l=a===o;if(l){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(n))} ${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 fe(e,t){let n=await k(),r=await import(`node:fs/promises`),i=e.severity===`error`?`×`:`⚠`,a=e.help||``,o=e.line||`N/A`,s=e.column||10,l=`${n.red(i)} ${n.cyan(`${e.from}(${a.replace(/^Regla \w+: /,``)})`)}: ${e.message}`;p.info(l);try{let i=c.resolve(t),a=await r.readFile(i,`utf-8`),l=a.split(`
|
|
6
|
+
`),u=parseInt(o.toString())-1;if(u>=0&&u<l.length){p.info(n.blue(` ╭─[${t}:${o}:${s}]`));let r=Math.max(0,u-1),i=Math.min(l.length-1,u+1);for(let t=r;t<=i;t++){let r=t+1,i=l[t]||``,a=r.toString().padStart(2,` `);if(t===u){p.info(n.blue(` ${a} │ `)+i);let t=` `.repeat(a.length+3),r=` `.repeat(Math.max(0,(s||1)-1))+n.red(`───────┬──────`);p.info(n.blue(t+`·`)+r);let o=` `.repeat(Math.max(0,(s||1)+6));p.info(n.blue(t+`·`)+o+n.red(`╰── `)+n.gray(pe(e)))}else p.info(n.blue(` ${a} │ `)+n.gray(i))}p.info(n.blue(` ╰────`))}}catch{p.info(n.blue(` ╭─[${t}:${o}:${s}]`)),p.info(n.blue(` │ `)+n.gray(`(No se pudo leer el contenido del archivo)`)),p.info(n.blue(` ╰────`))}if(e.help){let t=e.help.replace(/^Regla \w+: /,``);p.info(n.blue(` help: `)+n.yellow(t))}p.info(``)}function pe(e){if(e.message.includes(`declared but never used`)){let t=e.message.match(/'([^']+)'/);if(t)return`'${t[1]}' is declared here`}if(e.message.includes(`Unexpected var`))return`var declaration found here`;if(e.message.includes(`never reassigned`)){let t=e.message.match(/'([^']+)'/);if(t)return`'${t[1]}' is assigned here`}return`error location`}async function Y(e){let t=await k();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,`/`),n=t.startsWith(`./`)?t:`./${t}`;return n}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 X{static instance;fileSystemCache=new Map;debounceTimers=new Map;DEBOUNCE_DELAY=100;static getInstance(){return X.instance||=new X,X.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`),s=o.getInstance();s.setMode(`watch`);let c=await t(e);this.fileSystemCache.set(e,{mtime:r.mtimeMs}),n(c)}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=O.getInstance(),s=Date.now(),l=c.extname(e);await a.ensureModuleLoaded(`parser`);let u=await A(),d=await u(e),m=d.code,h=d.error;if(i.fileRead=Date.now()-s,h)throw await U(h instanceof Error?h:Error(String(h)),e,`file-read`,r,f.VERBOSE===`true`),Error(h instanceof Error?h.message:String(h));if(!m||m.trim().length===0||m===`undefined`||m===`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 g=f.VERBOSE===`true`,v;if(l===`.vue`){s=Date.now(),g&&p.info(_.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(v=await t(m,e,f.isPROD===`true`),i.vueCompile=Date.now()-s,v==null)throw Error(`preCompileVue devolvió ${v} para archivo: ${e}`);if(v.error)throw await U(v.error instanceof Error?v.error:Error(String(v.error)),e,`vue`,r,f.VERBOSE===`true`),Error(v.error instanceof Error?v.error.message:String(v.error));W(e,`vue`),m=v.data}if(!m||m.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 y;if(l===`.ts`||v?.lang===`ts`){s=Date.now(),g&&p.info(_.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(y=await t(m,e),i.tsCompile=Date.now()-s,y==null)throw Error(`preCompileTS devolvió ${y} para archivo: ${e}`);if(y.error)if(r===`all`)V(e,`typescript`,y.error instanceof Error?y.error.message:String(y.error),`error`);else throw await U(y.error,e,`typescript`,r,f.VERBOSE===`true`),Error(y.error instanceof Error?y.error.message:String(y.error));else W(e,`typescript`),m=y.data}if(!m||m.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.`);g&&p.info(_.yellow(`💛 Estandarizando: ${c.basename(e)}`)),s=Date.now(),await a.ensureModuleLoaded(`transforms`);let b=await M(),x=await b(m,e);if(i.standardization=Date.now()-s,x==null)throw Error(`estandarizaCode devolvió ${x} para archivo: ${e}`);if(x.error)throw await U(Error(x.error),e,`standardization`,r,f.VERBOSE===`true`),Error(x.error);if(W(e,`standardization`),m=x.code,!m||m.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(),g&&p.info(_.red(`🤖 Minificando: ${c.basename(e)}`)),await a.ensureModuleLoaded(`minify`);let t=await se(),n=await t(m,e,!0);if(i.minification=Date.now()-s,n==null)throw Error(`minifyJS devolvió ${n} para archivo: ${e}`);if(n.error)throw await U(n.error instanceof Error?n.error:Error(String(n.error)),e,`minification`,r,f.VERBOSE===`true`),Error(n.error instanceof Error?n.error.message:String(n.error));W(e,`minification`),m=n.code}let S=c.dirname(t);if(await n(S,{recursive:!0}),await o(t,m,`utf-8`),g){let t=Object.values(i).reduce((e,t)=>e+t,0);p.info(_.cyan(`⏱️ Timing para ${c.basename(e)}:`)),i.fileRead&&p.info(_.cyan(` 📖 Lectura: ${i.fileRead}ms`)),i.vueCompile&&p.info(_.cyan(` 💚 Vue: ${i.vueCompile}ms`)),i.tsCompile&&p.info(_.cyan(` 🔄️ TypeScript: ${i.tsCompile}ms`)),i.standardization&&p.info(_.cyan(` 💛 Estandarización: ${i.standardization}ms`)),i.minification&&p.info(_.cyan(` 🤖 Minificación: ${i.minification}ms`)),p.info(_.cyan(` 🏁 Total: ${t}ms`))}return{error:null,action:`extension`}}export async function initCompile(e,t=!0,n=`individual`){try{let r=O.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 j(),t=await e();if(typeof t!=`boolean`)if(t?.success)p.info(`🎨 ${t.message}`);else{let e=`${t.message}${t.details?`
|
|
7
|
+
`+t.details:``}`;await U(Error(e),`tailwind.config.js`,`tailwind`,n,f.VERBOSE===`true`)}}let o=Date.now(),s=normalizeRuta(e),l=getOutputPath(s);if((n===`watch`||n===`individual`)&&await $(s))return f.VERBOSE===`true`&&p.info(`⏭️ Archivo omitido (cache): ${c.basename(s)}`),{success:!0,cached:!0,output:L.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 L.set(s,l);let d=Date.now(),m=g(d-o);if(n===`individual`){f.VERBOSE===`true`&&(p.info(`🔚 Destino: ${l}`),p.info(`⏱️ Tiempo: ${m}`));let e=await k();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 Z=0;class Q{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 Q.instance||=new Q,Q.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(`
|
|
8
|
+
\x1B[96m`+e+`\x1B[0m
|
|
9
|
+
`),l.stdout.write(`\x1B[96m│ \x1B[97m\x1B[1m🚀 Iniciando Compilación\x1B[0m\x1B[96m`+` `.repeat(22)+`│\x1B[0m
|
|
10
|
+
`),l.stdout.write(`\x1B[96m`+e+`\x1B[0m
|
|
11
|
+
`)}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?`
|
|
12
|
+
`:``)+e+`
|
|
13
|
+
`),this.hasProgressLine=!1;this.logBuffer=[]}this.hasProgressLine?l.stdout.write(`\r\x1B[K`):l.stdout.write(`
|
|
14
|
+
\x1B[96m`+`▔`.repeat(50)+`\x1B[0m
|
|
15
|
+
`);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+`
|
|
16
|
+
`),this.hasProgressLine=!1):this.originalConsoleLog(e)}endProgress(){if(this.progressActive){this.hasProgressLine&&l.stdout.write(`
|
|
17
|
+
`),l.stdout.write(`
|
|
18
|
+
\x1B[33m`+`-`.repeat(50)+`\x1B[0m
|
|
19
|
+
`);let e=`█`.repeat(3),t=`\x1b[42m\x1b[30m ${e} ✅ PROCESO COMPLETADO 100% ${e} \x1b[0m`;l.stdout.write(t+`
|
|
20
|
+
`);let n=`━`.repeat(48);if(l.stdout.write(`\x1B[92m`+n+`\x1B[0m
|
|
21
|
+
`),l.stdout.write(`\x1B[92m│ \x1B[97m\x1B[1m✅ ¡Compilación Completada!\x1B[0m\x1B[92m`+` `.repeat(23)+`│\x1B[0m
|
|
22
|
+
`),l.stdout.write(`\x1B[92m`+n+`\x1B[0m
|
|
23
|
+
|
|
24
|
+
`),this.logBuffer.length>0)for(let e of this.logBuffer)l.stdout.write(e+`
|
|
25
|
+
`)}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){let t={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:`⏳`}},n={bgColor:`100`,textColor:`30`,icon:`⏳`};return t[e]||n}}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 oe();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 k();p.info(e.green(`✅ No se encontraron errores ni advertencias de linting.`))}else if(r.length>0&&(await q(r),p.warn(`🚨 Se encontraron errores o advertencias durante el linting.`),f.yes===`false`)){let e=await h(`¿Deseas continuar con la compilación a pesar de los errores de linting? (s/N): `);e.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 $(e){return await L.isValid(e)}async function ge(e,t=8){let n=[],r=[],i=e.length,a=0,o=0,s=0,u=Q.getInstance(),d=!1,m=0,h=20;function g(){let e=a+o+s,t=he(e,i),n=Math.round(e/i*100);if(e===0&&!d||n>Z+1&&e>0||e===i){let r=`🚀 ${t} [✅ ${a} | ⏭️ ${o} | ❌ ${s}]`;u.updateProgress(r),e===0&&(d=!0),Z=n}}g();for(let i of e){let e=(async()=>{try{if(f.VERBOSE===`true`&&p.info(`🔄 Compilando: ${c.basename(i)}`),await $(i))return o++,f.VERBOSE===`true`&&p.info(`⏭️ Archivo omitido (cache): ${c.basename(i)}`),g(),{success:!0,cached:!0,output:L.getOutputPath(i)};let e=await initCompile(i,!1,`batch`);if(e.success&&e.output?(await L.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(),t=e.heapUsed/(1024*1024);if(t>300){let e=L.getStats().entries;if(e>50){console.log(`[Memory] Heap alto (${t.toFixed(1)}MB), limpiando cache...`);let e=L.cleanOldEntries(20);e>0&&console.log(`[Memory] Se removieron ${e} entradas del cache`)}}}return g(),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 g(),{success:!1,error:e instanceof Error?e.message:String(e)}}})();n.push(e),r.push(e),r.length>=t&&(await Promise.race(r),r.splice(r.findIndex(t=>t===e),1))}await Promise.all(n)}export async function initCompileAll(){try{let e=Q.getInstance();e.startProgress(),e.updateProgress(`🚀 Iniciando compilación...`),G(),e.updateProgress(`📦 Cargando cache...`),await B(),Z=0,e.updateProgress(`🔍 Ejecutando linter...`);let n=await runLinter(!1);if(!n){e.endProgress();return}let r=Date.now(),i=f.PATH_SOURCE??``,a=f.PATH_DIST??``,o=i.replace(/\\/g,`/`),c=[`${o}/**/*.js`,`${o}/**/*.vue`,`${o}/**/*.ts`,`${o}/**/*.mjs`,`${o}/**/*.cjs`];p.info(`📝 Compilando todos los archivos...`),p.info(`🔜 Fuente: ${i}`),p.info(`🔚 Destino: ${a}\n`),e.updateProgress(`🎨 Generando TailwindCSS...`);let u=await j(),d=await u();typeof d!=`boolean`&&(d?.success?p.info(`🎨 ${d.message}\n`):await U(Error(`${d.message}${d.details?`
|
|
26
|
+
`+d.details:``}`),`tailwind.config.js`,`tailwind`,`all`,f.VERBOSE===`true`)),e.updateProgress(`📁 Recopilando archivos...`);let m=[];for await(let e of t(c)){if(e.endsWith(`.d.ts`))continue;m.push(e)}let h=s.cpus().length,_=m.length,v=l.memoryUsage(),y=(v.heapTotal-v.heapUsed)/(1024*1024),b;b=y<100?Math.min(2,h):_<10?Math.min(_,Math.min(h,4)):_<50?Math.min(h,6):Math.min(h,8),e.updateProgress(`⚙️ Configurando workers...`),p.info(`🚀 Compilando ${_} archivos con concurrencia optimizada (${b} hilos)...`);try{let{TypeScriptWorkerPool:e}=await import(`./typescript-worker-pool.js`),t=e.getInstance();t.setMode(`batch`)}catch{}e.updateProgress(`🚀 Iniciando compilación de ${_} archivos...`),await ge(m,b),e.updateProgress(`💾 Guardando cache...`),await ue();let x=Date.now(),S=g(x-r);e.endProgress(),await K(f.VERBOSE===`true`,S)}catch(e){let t=Q.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){let t={vue:`🎨`,typescript:`📘`,standardization:`💛`,minification:`🗜️`,tailwind:`🎨`,"file-read":`📖`,default:`⚙️`};return t[e]??`⚙️`}function ve(e,t){let n=Math.round(e/100*t),r=t-n,i=`\x1B[32m`+`█`.repeat(n)+`\x1B[0m`,a=`\x1B[90m`+`░`.repeat(r)+`\x1B[0m`;return`${i}${a} ${e}%`}export async function compileFile(e){return await initCompile(e,!0,`individual`)}export{X as WatchModeOptimizer};
|