versacompiler 1.0.5 → 2.0.1
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/README.md +633 -145
- package/dist/compiler/compile.js +1362 -0
- package/dist/compiler/error-reporter.js +467 -0
- package/dist/compiler/linter.js +72 -0
- package/dist/compiler/minify.js +229 -0
- package/dist/compiler/module-resolution-optimizer.js +844 -0
- package/dist/compiler/parser.js +203 -0
- package/dist/compiler/performance-monitor.js +192 -0
- package/dist/compiler/tailwindcss.js +39 -0
- package/dist/compiler/transform-optimizer.js +287 -0
- package/dist/compiler/transformTStoJS.js +16 -0
- package/dist/compiler/transforms.js +578 -0
- package/dist/compiler/typescript-compiler.js +379 -0
- package/dist/compiler/typescript-error-parser.js +281 -0
- package/dist/compiler/typescript-manager.js +378 -0
- package/dist/compiler/typescript-sync-validator.js +228 -0
- package/dist/compiler/typescript-worker-pool.js +479 -0
- package/dist/compiler/typescript-worker-thread.cjs +457 -0
- package/dist/compiler/typescript-worker.js +339 -0
- package/dist/compiler/vuejs.js +390 -0
- package/dist/hrm/VueHRM.js +353 -0
- package/dist/hrm/errorScreen.js +23 -1
- package/dist/hrm/getInstanciaVue.js +313 -0
- package/dist/hrm/initHRM.js +140 -0
- package/dist/main.js +286 -0
- package/dist/servicios/browserSync.js +469 -0
- package/dist/servicios/file-watcher.js +316 -0
- package/dist/servicios/logger.js +34 -0
- package/dist/servicios/readConfig.js +430 -0
- package/dist/utils/module-resolver.js +495 -0
- package/dist/utils/promptUser.js +48 -0
- package/dist/utils/resolve-bin.js +48 -0
- package/dist/utils/utils.js +8 -35
- package/dist/wrappers/eslint-node.js +145 -0
- package/dist/wrappers/oxlint-node.js +120 -0
- package/dist/wrappers/tailwind-node.js +92 -0
- package/package.json +62 -17
- package/dist/hrm/devMode.js +0 -346
- package/dist/hrm/instanciaVue.js +0 -35
- package/dist/hrm/setupHMR.js +0 -57
- package/dist/index.js +0 -1010
- package/dist/services/acorn.js +0 -29
- package/dist/services/linter.js +0 -55
- package/dist/services/minify.js +0 -31
- package/dist/services/typescript.js +0 -89
- package/dist/services/vueLoader.js +0 -326
- package/dist/services/vuejs.js +0 -259
- package/dist/utils/transformWithAcorn.js +0 -316
|
@@ -0,0 +1,1362 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { glob, mkdir, readFile, stat, unlink, writeFile, } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { cwd, env, stdout } from 'node:process';
|
|
6
|
+
// Lazy loading optimizations - Only import lightweight modules synchronously
|
|
7
|
+
import { logger } from '../servicios/logger.js';
|
|
8
|
+
import { promptUser } from '../utils/promptUser.js';
|
|
9
|
+
import { showTimingForHumans } from '../utils/utils.js';
|
|
10
|
+
// Heavy dependencies will be loaded dynamically when needed
|
|
11
|
+
let chalk;
|
|
12
|
+
let ESLint;
|
|
13
|
+
let OxLint;
|
|
14
|
+
let minifyJS;
|
|
15
|
+
let getCodeFile;
|
|
16
|
+
let generateTailwindCSS;
|
|
17
|
+
let estandarizaCode;
|
|
18
|
+
let preCompileTS;
|
|
19
|
+
let preCompileVue;
|
|
20
|
+
// 🚀 Importar optimizador de transformaciones
|
|
21
|
+
let TransformOptimizer;
|
|
22
|
+
// 🚀 Importar optimizador de resolución de módulos
|
|
23
|
+
let ModuleResolutionOptimizer;
|
|
24
|
+
// 🚀 Sistema de Carga Inteligente de Módulos - VERSIÓN OPTIMIZADA V2
|
|
25
|
+
class OptimizedModuleManager {
|
|
26
|
+
static instance;
|
|
27
|
+
isInitialized = false;
|
|
28
|
+
loadedModules = new Set();
|
|
29
|
+
// ✨ NUEVAS OPTIMIZACIONES
|
|
30
|
+
modulePool = new Map(); // Pool de instancias reutilizables
|
|
31
|
+
loadingPromises = new Map(); // Prevenir cargas duplicadas
|
|
32
|
+
usageStats = new Map(); // Estadísticas de uso
|
|
33
|
+
preloadQueue = new Set(); // Cola de precarga
|
|
34
|
+
backgroundLoader = null; // Cargador en background
|
|
35
|
+
// Módulos críticos que siempre se precargan
|
|
36
|
+
HOT_MODULES = ['chalk', 'parser'];
|
|
37
|
+
// Contexto actual para optimizar cargas
|
|
38
|
+
currentContext = null;
|
|
39
|
+
constructor() {
|
|
40
|
+
// Iniciar precarga en background inmediatamente
|
|
41
|
+
this.startBackgroundPreloading();
|
|
42
|
+
}
|
|
43
|
+
static getInstance() {
|
|
44
|
+
if (!OptimizedModuleManager.instance) {
|
|
45
|
+
OptimizedModuleManager.instance = new OptimizedModuleManager();
|
|
46
|
+
}
|
|
47
|
+
return OptimizedModuleManager.instance;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* ✨ NUEVO: Precarga en background para módulos críticos
|
|
51
|
+
*/
|
|
52
|
+
startBackgroundPreloading() {
|
|
53
|
+
this.backgroundLoader = this.preloadCriticalModules();
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* ✨ NUEVO: Precarga módulos críticos en background
|
|
57
|
+
*/
|
|
58
|
+
async preloadCriticalModules() {
|
|
59
|
+
try {
|
|
60
|
+
// Precargar módulos críticos de forma asíncrona
|
|
61
|
+
const preloadPromises = this.HOT_MODULES.map(moduleName => this.ensureModuleLoaded(moduleName).catch(() => {
|
|
62
|
+
// Silenciar errores de precarga, se intentará cargar después
|
|
63
|
+
}));
|
|
64
|
+
await Promise.allSettled(preloadPromises);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Fallos en precarga no deben afectar la funcionalidad principal
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* ✨ MEJORADO: Precarga contextual basada en tipos de archivo
|
|
72
|
+
*/
|
|
73
|
+
async preloadForContext(context, fileTypes = new Set()) {
|
|
74
|
+
this.currentContext = context;
|
|
75
|
+
// Esperar que termine la precarga crítica si está en progreso
|
|
76
|
+
if (this.backgroundLoader) {
|
|
77
|
+
await this.backgroundLoader;
|
|
78
|
+
}
|
|
79
|
+
const toPreload = []; // Precarga basada en contexto
|
|
80
|
+
if (context === 'batch' || context === 'watch') {
|
|
81
|
+
// En batch/watch, precargar todos los módulos comunes
|
|
82
|
+
toPreload.push('transforms', 'vue', 'typescript', 'module-resolution-optimizer');
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
// En individual, cargar solo según tipos de archivo detectados
|
|
86
|
+
if (fileTypes.has('.vue'))
|
|
87
|
+
toPreload.push('vue');
|
|
88
|
+
if (fileTypes.has('.ts') || fileTypes.has('.vue'))
|
|
89
|
+
toPreload.push('typescript');
|
|
90
|
+
if (!this.loadedModules.has('transforms'))
|
|
91
|
+
toPreload.push('transforms');
|
|
92
|
+
}
|
|
93
|
+
// Precargar en paralelo
|
|
94
|
+
const preloadPromises = toPreload.map(moduleName => this.ensureModuleLoaded(moduleName).catch(() => {
|
|
95
|
+
// Log warning pero no fallar
|
|
96
|
+
console.warn(`Warning: No se pudo precargar módulo ${moduleName}`);
|
|
97
|
+
}));
|
|
98
|
+
await Promise.allSettled(preloadPromises);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* ✨ MEJORADO: Carga inteligente con pooling y deduplicación
|
|
102
|
+
*/
|
|
103
|
+
async ensureModuleLoaded(moduleName) {
|
|
104
|
+
// 1. Verificar pool de módulos primero
|
|
105
|
+
if (this.modulePool.has(moduleName)) {
|
|
106
|
+
this.updateUsageStats(moduleName);
|
|
107
|
+
return this.modulePool.get(moduleName);
|
|
108
|
+
}
|
|
109
|
+
// 2. Verificar si ya está cargando (deduplicación)
|
|
110
|
+
if (this.loadingPromises.has(moduleName)) {
|
|
111
|
+
return this.loadingPromises.get(moduleName);
|
|
112
|
+
}
|
|
113
|
+
// 3. Iniciar carga
|
|
114
|
+
const loadPromise = this.loadModuleInternal(moduleName);
|
|
115
|
+
this.loadingPromises.set(moduleName, loadPromise);
|
|
116
|
+
try {
|
|
117
|
+
const moduleInstance = await loadPromise;
|
|
118
|
+
// 4. Almacenar en pool y estadísticas
|
|
119
|
+
this.modulePool.set(moduleName, moduleInstance);
|
|
120
|
+
this.loadedModules.add(moduleName);
|
|
121
|
+
this.updateUsageStats(moduleName);
|
|
122
|
+
return moduleInstance;
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
// 5. Limpiar promesa de carga
|
|
126
|
+
this.loadingPromises.delete(moduleName);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* ✨ NUEVO: Actualiza estadísticas de uso para optimizaciones futuras
|
|
131
|
+
*/
|
|
132
|
+
updateUsageStats(moduleName) {
|
|
133
|
+
const currentCount = this.usageStats.get(moduleName) || 0;
|
|
134
|
+
this.usageStats.set(moduleName, currentCount + 1);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* ✨ MEJORADO: Carga interna de módulos con mejor manejo de errores
|
|
138
|
+
*/ async loadModuleInternal(moduleName) {
|
|
139
|
+
switch (moduleName) {
|
|
140
|
+
case 'chalk':
|
|
141
|
+
return this.loadChalk();
|
|
142
|
+
case 'parser':
|
|
143
|
+
return this.loadParser();
|
|
144
|
+
case 'transforms':
|
|
145
|
+
return this.loadTransforms();
|
|
146
|
+
case 'vue':
|
|
147
|
+
return this.loadVue();
|
|
148
|
+
case 'typescript':
|
|
149
|
+
return this.loadTypeScript();
|
|
150
|
+
case 'minify':
|
|
151
|
+
return this.loadMinify();
|
|
152
|
+
case 'tailwind':
|
|
153
|
+
return this.loadTailwind();
|
|
154
|
+
case 'linter':
|
|
155
|
+
return this.loadLinter();
|
|
156
|
+
case 'transform-optimizer':
|
|
157
|
+
return this.loadTransformOptimizer();
|
|
158
|
+
case 'module-resolution-optimizer':
|
|
159
|
+
return this.loadModuleResolutionOptimizer();
|
|
160
|
+
default:
|
|
161
|
+
throw new Error(`Módulo desconocido: ${moduleName}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// ✨ Métodos de carga específicos optimizados
|
|
165
|
+
async loadChalk() {
|
|
166
|
+
if (!chalk) {
|
|
167
|
+
chalk = (await import('chalk')).default;
|
|
168
|
+
}
|
|
169
|
+
return chalk;
|
|
170
|
+
}
|
|
171
|
+
async loadParser() {
|
|
172
|
+
if (!getCodeFile) {
|
|
173
|
+
const parserModule = await import('./parser.js');
|
|
174
|
+
getCodeFile = parserModule.getCodeFile;
|
|
175
|
+
}
|
|
176
|
+
return getCodeFile;
|
|
177
|
+
}
|
|
178
|
+
async loadTransforms() {
|
|
179
|
+
if (!estandarizaCode) {
|
|
180
|
+
const transformsModule = await import('./transforms.js');
|
|
181
|
+
estandarizaCode = transformsModule.estandarizaCode;
|
|
182
|
+
}
|
|
183
|
+
return estandarizaCode;
|
|
184
|
+
}
|
|
185
|
+
async loadVue() {
|
|
186
|
+
if (!preCompileVue) {
|
|
187
|
+
const vueModule = await import('./vuejs.js');
|
|
188
|
+
preCompileVue = vueModule.preCompileVue;
|
|
189
|
+
}
|
|
190
|
+
return preCompileVue;
|
|
191
|
+
}
|
|
192
|
+
async loadTypeScript() {
|
|
193
|
+
if (!preCompileTS) {
|
|
194
|
+
const typescriptModule = await import('./typescript-manager.js');
|
|
195
|
+
preCompileTS = typescriptModule.preCompileTS;
|
|
196
|
+
}
|
|
197
|
+
return preCompileTS;
|
|
198
|
+
}
|
|
199
|
+
async loadMinify() {
|
|
200
|
+
if (!minifyJS) {
|
|
201
|
+
const minifyModule = await import('./minify.js');
|
|
202
|
+
minifyJS = minifyModule.minifyJS;
|
|
203
|
+
}
|
|
204
|
+
return minifyJS;
|
|
205
|
+
}
|
|
206
|
+
async loadTailwind() {
|
|
207
|
+
if (!generateTailwindCSS) {
|
|
208
|
+
const tailwindModule = await import('./tailwindcss.js');
|
|
209
|
+
generateTailwindCSS = tailwindModule.generateTailwindCSS;
|
|
210
|
+
}
|
|
211
|
+
return generateTailwindCSS;
|
|
212
|
+
}
|
|
213
|
+
async loadLinter() {
|
|
214
|
+
if (!ESLint || !OxLint) {
|
|
215
|
+
const linterModule = await import('./linter.js');
|
|
216
|
+
ESLint = linterModule.ESLint;
|
|
217
|
+
OxLint = linterModule.OxLint;
|
|
218
|
+
}
|
|
219
|
+
return { ESLint, OxLint };
|
|
220
|
+
}
|
|
221
|
+
async loadTransformOptimizer() {
|
|
222
|
+
if (!TransformOptimizer) {
|
|
223
|
+
const transformModule = await import('./transform-optimizer.js');
|
|
224
|
+
TransformOptimizer =
|
|
225
|
+
transformModule.TransformOptimizer.getInstance();
|
|
226
|
+
}
|
|
227
|
+
return TransformOptimizer;
|
|
228
|
+
}
|
|
229
|
+
async loadModuleResolutionOptimizer() {
|
|
230
|
+
if (!ModuleResolutionOptimizer) {
|
|
231
|
+
const resolutionModule = await import('./module-resolution-optimizer.js');
|
|
232
|
+
ModuleResolutionOptimizer =
|
|
233
|
+
resolutionModule.ModuleResolutionOptimizer.getInstance();
|
|
234
|
+
}
|
|
235
|
+
return ModuleResolutionOptimizer;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* ✨ NUEVO: Obtiene estadísticas de performance del manager
|
|
239
|
+
*/
|
|
240
|
+
getPerformanceStats() {
|
|
241
|
+
const sortedByUsage = Array.from(this.usageStats.entries())
|
|
242
|
+
.sort((a, b) => b[1] - a[1])
|
|
243
|
+
.slice(0, 5)
|
|
244
|
+
.map(([name]) => name);
|
|
245
|
+
return {
|
|
246
|
+
loadedModules: Array.from(this.loadedModules),
|
|
247
|
+
usageStats: Object.fromEntries(this.usageStats),
|
|
248
|
+
poolSize: this.modulePool.size,
|
|
249
|
+
loadingInProgress: Array.from(this.loadingPromises.keys()),
|
|
250
|
+
mostUsedModules: sortedByUsage,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* ✨ NUEVO: Limpia módulos no utilizados para liberar memoria
|
|
255
|
+
*/
|
|
256
|
+
cleanupUnusedModules() {
|
|
257
|
+
const threshold = 1; // Mínimo de usos para mantener en pool
|
|
258
|
+
for (const [moduleName, usageCount] of this.usageStats) {
|
|
259
|
+
if (usageCount < threshold &&
|
|
260
|
+
!this.HOT_MODULES.includes(moduleName)) {
|
|
261
|
+
this.modulePool.delete(moduleName);
|
|
262
|
+
this.loadedModules.delete(moduleName);
|
|
263
|
+
this.usageStats.delete(moduleName);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Resetea el estado del manager (útil para tests)
|
|
269
|
+
*/
|
|
270
|
+
reset() {
|
|
271
|
+
this.isInitialized = false;
|
|
272
|
+
this.loadedModules.clear();
|
|
273
|
+
this.modulePool.clear();
|
|
274
|
+
this.loadingPromises.clear();
|
|
275
|
+
this.usageStats.clear();
|
|
276
|
+
this.preloadQueue.clear();
|
|
277
|
+
this.currentContext = null;
|
|
278
|
+
this.backgroundLoader = null;
|
|
279
|
+
// Reiniciar precarga crítica
|
|
280
|
+
this.startBackgroundPreloading();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// Lazy loading helper functions
|
|
284
|
+
async function loadChalk() {
|
|
285
|
+
if (!chalk) {
|
|
286
|
+
chalk = (await import('chalk')).default;
|
|
287
|
+
}
|
|
288
|
+
return chalk;
|
|
289
|
+
}
|
|
290
|
+
async function loadLinter() {
|
|
291
|
+
if (!ESLint || !OxLint) {
|
|
292
|
+
const linterModule = await import('./linter.js');
|
|
293
|
+
ESLint = linterModule.ESLint;
|
|
294
|
+
OxLint = linterModule.OxLint;
|
|
295
|
+
}
|
|
296
|
+
return { ESLint, OxLint };
|
|
297
|
+
}
|
|
298
|
+
async function loadMinify() {
|
|
299
|
+
if (!minifyJS) {
|
|
300
|
+
const minifyModule = await import('./minify.js');
|
|
301
|
+
minifyJS = minifyModule.minifyJS;
|
|
302
|
+
}
|
|
303
|
+
return minifyJS;
|
|
304
|
+
}
|
|
305
|
+
async function loadParser() {
|
|
306
|
+
if (!getCodeFile) {
|
|
307
|
+
const parserModule = await import('./parser.js');
|
|
308
|
+
getCodeFile = parserModule.getCodeFile;
|
|
309
|
+
}
|
|
310
|
+
return getCodeFile;
|
|
311
|
+
}
|
|
312
|
+
async function loadTailwind() {
|
|
313
|
+
if (!generateTailwindCSS) {
|
|
314
|
+
const tailwindModule = await import('./tailwindcss.js');
|
|
315
|
+
generateTailwindCSS = tailwindModule.generateTailwindCSS;
|
|
316
|
+
}
|
|
317
|
+
return generateTailwindCSS;
|
|
318
|
+
}
|
|
319
|
+
async function loadTransforms() {
|
|
320
|
+
if (!estandarizaCode) {
|
|
321
|
+
const transformsModule = await import('./transforms.js');
|
|
322
|
+
estandarizaCode = transformsModule.estandarizaCode;
|
|
323
|
+
}
|
|
324
|
+
return estandarizaCode;
|
|
325
|
+
}
|
|
326
|
+
async function loadTypeScript() {
|
|
327
|
+
if (!preCompileTS) {
|
|
328
|
+
const typescriptModule = await import('./typescript-manager.js');
|
|
329
|
+
preCompileTS = typescriptModule.preCompileTS;
|
|
330
|
+
}
|
|
331
|
+
return preCompileTS;
|
|
332
|
+
}
|
|
333
|
+
async function loadVue() {
|
|
334
|
+
if (!preCompileVue) {
|
|
335
|
+
const vueModule = await import('./vuejs.js');
|
|
336
|
+
preCompileVue = vueModule.preCompileVue;
|
|
337
|
+
}
|
|
338
|
+
return preCompileVue;
|
|
339
|
+
}
|
|
340
|
+
// Almacenamiento global de errores y resultados
|
|
341
|
+
const compilationErrors = [];
|
|
342
|
+
const compilationResults = [];
|
|
343
|
+
class SmartCompilationCache {
|
|
344
|
+
cache = new Map();
|
|
345
|
+
maxEntries = 500; // Máximo archivos en cache
|
|
346
|
+
maxMemory = 100 * 1024 * 1024; // 100MB límite
|
|
347
|
+
currentMemoryUsage = 0;
|
|
348
|
+
/**
|
|
349
|
+
* Genera hash SHA-256 del contenido del archivo
|
|
350
|
+
*/ async generateContentHash(filePath) {
|
|
351
|
+
try {
|
|
352
|
+
const content = await readFile(filePath, 'utf8');
|
|
353
|
+
return createHash('sha256').update(content).digest('hex');
|
|
354
|
+
}
|
|
355
|
+
catch {
|
|
356
|
+
// Si no se puede leer el archivo, generar hash único basado en la ruta y timestamp
|
|
357
|
+
const fallback = `${filePath}-${Date.now()}`;
|
|
358
|
+
return createHash('sha256').update(fallback).digest('hex');
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Verifica si una entrada de cache es válida
|
|
363
|
+
*/
|
|
364
|
+
async isValid(filePath) {
|
|
365
|
+
const entry = this.cache.get(filePath);
|
|
366
|
+
if (!entry)
|
|
367
|
+
return false;
|
|
368
|
+
try {
|
|
369
|
+
// Verificar si el archivo de salida existe
|
|
370
|
+
await stat(entry.outputPath);
|
|
371
|
+
// Verificar si el contenido ha cambiado
|
|
372
|
+
const currentHash = await this.generateContentHash(filePath);
|
|
373
|
+
if (entry.contentHash !== currentHash) {
|
|
374
|
+
this.cache.delete(filePath);
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
// Verificar tiempo de modificación como backup
|
|
378
|
+
const stats = await stat(filePath);
|
|
379
|
+
if (stats.mtimeMs > entry.mtime) {
|
|
380
|
+
this.cache.delete(filePath);
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
// Actualizar tiempo de uso para LRU
|
|
384
|
+
entry.lastUsed = Date.now();
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
// Si hay error verificando, eliminar del cache
|
|
389
|
+
this.cache.delete(filePath);
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Añade una entrada al cache
|
|
395
|
+
*/
|
|
396
|
+
async set(filePath, outputPath) {
|
|
397
|
+
try {
|
|
398
|
+
const stats = await stat(filePath);
|
|
399
|
+
const contentHash = await this.generateContentHash(filePath);
|
|
400
|
+
const entry = {
|
|
401
|
+
contentHash,
|
|
402
|
+
mtime: stats.mtimeMs,
|
|
403
|
+
outputPath,
|
|
404
|
+
lastUsed: Date.now(),
|
|
405
|
+
size: stats.size,
|
|
406
|
+
};
|
|
407
|
+
// Aplicar límites de memoria y entradas antes de agregar
|
|
408
|
+
this.evictIfNeeded(entry.size);
|
|
409
|
+
this.cache.set(filePath, entry);
|
|
410
|
+
this.currentMemoryUsage += entry.size;
|
|
411
|
+
}
|
|
412
|
+
catch (error) {
|
|
413
|
+
// Si hay error, no cachear
|
|
414
|
+
console.warn(`Warning: No se pudo cachear ${filePath}:`, error);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Aplica política LRU para liberar espacio
|
|
419
|
+
*/
|
|
420
|
+
evictIfNeeded(newEntrySize) {
|
|
421
|
+
// Verificar límite de entradas
|
|
422
|
+
while (this.cache.size >= this.maxEntries) {
|
|
423
|
+
this.evictLRU();
|
|
424
|
+
}
|
|
425
|
+
// Verificar límite de memoria
|
|
426
|
+
while (this.currentMemoryUsage + newEntrySize > this.maxMemory &&
|
|
427
|
+
this.cache.size > 0) {
|
|
428
|
+
this.evictLRU();
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Elimina la entrada menos recientemente usada
|
|
433
|
+
*/
|
|
434
|
+
evictLRU() {
|
|
435
|
+
let oldestKey = '';
|
|
436
|
+
let oldestTime = Infinity;
|
|
437
|
+
for (const [key, entry] of this.cache) {
|
|
438
|
+
if (entry.lastUsed < oldestTime) {
|
|
439
|
+
oldestTime = entry.lastUsed;
|
|
440
|
+
oldestKey = key;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
if (oldestKey) {
|
|
444
|
+
const entry = this.cache.get(oldestKey);
|
|
445
|
+
if (entry) {
|
|
446
|
+
this.currentMemoryUsage -= entry.size;
|
|
447
|
+
this.cache.delete(oldestKey);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Carga el cache desde disco
|
|
453
|
+
*/
|
|
454
|
+
async load(cacheFile) {
|
|
455
|
+
try {
|
|
456
|
+
if (env.cleanCache === 'true') {
|
|
457
|
+
this.cache.clear();
|
|
458
|
+
this.currentMemoryUsage = 0;
|
|
459
|
+
try {
|
|
460
|
+
await unlink(cacheFile);
|
|
461
|
+
}
|
|
462
|
+
catch {
|
|
463
|
+
// Ignorar errores al eliminar el archivo
|
|
464
|
+
}
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
const cacheData = await readFile(cacheFile, 'utf-8');
|
|
468
|
+
const parsed = JSON.parse(cacheData);
|
|
469
|
+
// Validar y cargar entradas del cache
|
|
470
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
471
|
+
const entry = value;
|
|
472
|
+
if (entry.contentHash && entry.outputPath && entry.mtime) {
|
|
473
|
+
this.cache.set(key, entry);
|
|
474
|
+
this.currentMemoryUsage += entry.size || 0;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
catch {
|
|
479
|
+
// Cache file doesn't exist or is invalid, start fresh
|
|
480
|
+
this.cache.clear();
|
|
481
|
+
this.currentMemoryUsage = 0;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Guarda el cache a disco
|
|
486
|
+
*/
|
|
487
|
+
async save(cacheFile, cacheDir) {
|
|
488
|
+
try {
|
|
489
|
+
await mkdir(cacheDir, { recursive: true });
|
|
490
|
+
const cacheData = Object.fromEntries(this.cache);
|
|
491
|
+
await writeFile(cacheFile, JSON.stringify(cacheData, null, 2));
|
|
492
|
+
}
|
|
493
|
+
catch (error) {
|
|
494
|
+
console.warn('Warning: No se pudo guardar el cache:', error);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Limpia completamente el cache
|
|
499
|
+
*/
|
|
500
|
+
clear() {
|
|
501
|
+
this.cache.clear();
|
|
502
|
+
this.currentMemoryUsage = 0;
|
|
503
|
+
} /**
|
|
504
|
+
* Obtiene la ruta de salida para un archivo cacheado
|
|
505
|
+
*/
|
|
506
|
+
getOutputPath(filePath) {
|
|
507
|
+
const entry = this.cache.get(filePath);
|
|
508
|
+
return entry?.outputPath || '';
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Obtiene estadísticas del cache
|
|
512
|
+
*/
|
|
513
|
+
getStats() {
|
|
514
|
+
return {
|
|
515
|
+
entries: this.cache.size,
|
|
516
|
+
memoryUsage: this.currentMemoryUsage,
|
|
517
|
+
hitRate: 0, // Se calculará externamente
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
// Instancia global del cache inteligente
|
|
522
|
+
const smartCache = new SmartCompilationCache();
|
|
523
|
+
const CACHE_DIR = path.join(path.resolve(env.PATH_PROY || cwd(), 'compiler'), '.cache');
|
|
524
|
+
const CACHE_FILE = path.join(CACHE_DIR, 'versacompile-cache.json');
|
|
525
|
+
async function loadCache() {
|
|
526
|
+
await smartCache.load(CACHE_FILE);
|
|
527
|
+
}
|
|
528
|
+
async function saveCache() {
|
|
529
|
+
await smartCache.save(CACHE_FILE, CACHE_DIR);
|
|
530
|
+
}
|
|
531
|
+
// 🎯 Funciones del Sistema Unificado de Manejo de Errores
|
|
532
|
+
/**
|
|
533
|
+
* Registra un error de compilación en el sistema unificado
|
|
534
|
+
*/
|
|
535
|
+
function registerCompilationError(file, stage, message, severity = 'error', details, help) {
|
|
536
|
+
compilationErrors.push({
|
|
537
|
+
file,
|
|
538
|
+
stage,
|
|
539
|
+
message,
|
|
540
|
+
severity,
|
|
541
|
+
details,
|
|
542
|
+
help,
|
|
543
|
+
timestamp: Date.now(),
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Registra un resultado de compilación (éxitos/errores por etapa)
|
|
548
|
+
*/
|
|
549
|
+
function registerCompilationResult(stage, errors, success, files = []) {
|
|
550
|
+
const existingResult = compilationResults.find(r => r.stage === stage);
|
|
551
|
+
if (existingResult) {
|
|
552
|
+
existingResult.errors += errors;
|
|
553
|
+
existingResult.success += success;
|
|
554
|
+
existingResult.files.push(...files);
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
compilationResults.push({
|
|
558
|
+
stage,
|
|
559
|
+
errors,
|
|
560
|
+
success,
|
|
561
|
+
files: [...files],
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Maneja errores según el modo de compilación
|
|
567
|
+
*/
|
|
568
|
+
async function handleCompilationError(error, fileName, stage, mode, isVerbose = false) {
|
|
569
|
+
const errorMessage = error instanceof Error ? error.message : error;
|
|
570
|
+
const errorDetails = error instanceof Error ? error.stack : undefined;
|
|
571
|
+
// Registrar el error en el sistema unificado
|
|
572
|
+
registerCompilationError(fileName, stage, errorMessage, 'error', errorDetails);
|
|
573
|
+
registerCompilationResult(stage, 1, 0, [fileName]); // Mostrar error inmediatamente solo en modo individual y watch
|
|
574
|
+
if (mode === 'individual' || mode === 'watch') {
|
|
575
|
+
const chalk = await loadChalk();
|
|
576
|
+
const baseName = path.basename(fileName);
|
|
577
|
+
const stageColor = await getStageColor(stage);
|
|
578
|
+
if (isVerbose) {
|
|
579
|
+
// Modo verbose: Mostrar error completo con contexto
|
|
580
|
+
logger.error(chalk.red(`❌ Error en etapa ${stageColor(stage)} - ${baseName}:`));
|
|
581
|
+
logger.error(chalk.red(errorMessage));
|
|
582
|
+
if (errorDetails && (stage === 'typescript' || stage === 'vue')) {
|
|
583
|
+
// Mostrar stack trace limitado para TypeScript y Vue
|
|
584
|
+
const stackLines = errorDetails.split('\n').slice(0, 5);
|
|
585
|
+
stackLines.forEach(line => {
|
|
586
|
+
if (line.trim()) {
|
|
587
|
+
logger.error(chalk.gray(` ${line.trim()}`));
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
// Modo normal: Mostrar error simplificado
|
|
594
|
+
const firstLine = errorMessage.split('\n')[0];
|
|
595
|
+
logger.error(chalk.red(`❌ Error en ${stageColor(stage)}: ${baseName}`));
|
|
596
|
+
logger.error(chalk.red(` ${firstLine}`));
|
|
597
|
+
logger.info(chalk.yellow(`💡 Usa --verbose para ver detalles completos`));
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
// En modo 'all', los errores se acumulan silenciosamente para el resumen final
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Registra un éxito de compilación
|
|
604
|
+
*/
|
|
605
|
+
function registerCompilationSuccess(fileName, stage) {
|
|
606
|
+
registerCompilationResult(stage, 0, 1, [fileName]);
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Limpia todos los errores y resultados acumulados
|
|
610
|
+
*/
|
|
611
|
+
function clearCompilationState() {
|
|
612
|
+
compilationErrors.length = 0;
|
|
613
|
+
compilationResults.length = 0;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Muestra un resumen detallado de todos los errores de compilación
|
|
617
|
+
*/
|
|
618
|
+
async function displayCompilationSummary(isVerbose = false) {
|
|
619
|
+
const chalk = await loadChalk();
|
|
620
|
+
if (compilationErrors.length === 0 && compilationResults.length === 0) {
|
|
621
|
+
logger.info(chalk.green('✅ No hay errores de compilación para mostrar.'));
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
logger.info(chalk.bold('\n--- 📊 RESUMEN DE COMPILACIÓN ---'));
|
|
625
|
+
// Mostrar estadísticas por etapa
|
|
626
|
+
if (compilationResults.length > 0) {
|
|
627
|
+
logger.info(chalk.blue('\n🔍 Estadísticas por etapa:'));
|
|
628
|
+
for (const result of compilationResults) {
|
|
629
|
+
const totalFiles = result.success + result.errors;
|
|
630
|
+
const successRate = totalFiles > 0
|
|
631
|
+
? Math.round((result.success / totalFiles) * 100)
|
|
632
|
+
: 0;
|
|
633
|
+
const statusIcon = result.errors === 0 ? '✅' : '❌';
|
|
634
|
+
const stageColor = await getStageColor(result.stage);
|
|
635
|
+
const statusText = `${result.success} éxitos, ${result.errors} errores`;
|
|
636
|
+
const coloredStatusText = result.errors === 0
|
|
637
|
+
? chalk.green(statusText)
|
|
638
|
+
: chalk.red(statusText);
|
|
639
|
+
logger.info(`${statusIcon} ${stageColor(result.stage)}: ${coloredStatusText} (${successRate}% éxito)`);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
// Mostrar errores detallados
|
|
643
|
+
if (compilationErrors.length > 0) {
|
|
644
|
+
logger.info(chalk.red(`\n❌ Se encontraron ${compilationErrors.length} errores:`));
|
|
645
|
+
// Agrupar errores por archivo para mejor organización
|
|
646
|
+
const errorsByFile = new Map();
|
|
647
|
+
compilationErrors.forEach(error => {
|
|
648
|
+
if (!errorsByFile.has(error.file)) {
|
|
649
|
+
errorsByFile.set(error.file, []);
|
|
650
|
+
}
|
|
651
|
+
errorsByFile.get(error.file).push(error);
|
|
652
|
+
});
|
|
653
|
+
// Mostrar errores por archivo
|
|
654
|
+
let fileIndex = 1;
|
|
655
|
+
for (const [filePath, fileErrors] of errorsByFile) {
|
|
656
|
+
const baseName = path.basename(filePath);
|
|
657
|
+
const errorCount = fileErrors.filter(e => e.severity === 'error').length;
|
|
658
|
+
const warningCount = fileErrors.filter(e => e.severity === 'warning').length;
|
|
659
|
+
logger.info(chalk.cyan(`\n📄 ${fileIndex}. ${baseName}`));
|
|
660
|
+
logger.info(chalk.gray(` Ruta: ${filePath}`));
|
|
661
|
+
logger.info(chalk.yellow(` ${errorCount} errores, ${warningCount} advertencias`));
|
|
662
|
+
for (const error of fileErrors) {
|
|
663
|
+
const icon = error.severity === 'error' ? '❌' : '⚠️';
|
|
664
|
+
const stageColor = await getStageColor(error.stage);
|
|
665
|
+
logger.info(` ${icon} [${stageColor(error.stage)}] ${error.message}`);
|
|
666
|
+
if (isVerbose && error.details) {
|
|
667
|
+
// En modo verbose, mostrar detalles adicionales
|
|
668
|
+
const detailLines = error.details.split('\n').slice(0, 5);
|
|
669
|
+
detailLines.forEach(line => {
|
|
670
|
+
if (line.trim()) {
|
|
671
|
+
logger.info(chalk.gray(` ${line.trim()}`));
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
if (error.help) {
|
|
676
|
+
logger.info(chalk.blue(` 💡 ${error.help}`));
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
fileIndex++;
|
|
680
|
+
}
|
|
681
|
+
// Mostrar totales finales
|
|
682
|
+
const totalErrors = compilationErrors.filter(e => e.severity === 'error').length;
|
|
683
|
+
const totalWarnings = compilationErrors.filter(e => e.severity === 'warning').length;
|
|
684
|
+
const totalFiles = errorsByFile.size;
|
|
685
|
+
logger.info(chalk.bold('\n--- 📈 ESTADÍSTICAS FINALES ---'));
|
|
686
|
+
logger.info(`📁 Archivos con errores: ${totalFiles}`);
|
|
687
|
+
logger.info(`❌ Total de errores: ${totalErrors}`);
|
|
688
|
+
logger.info(`⚠️ Total de advertencias: ${totalWarnings}`);
|
|
689
|
+
if (totalErrors > 0) {
|
|
690
|
+
logger.info(chalk.red('🚨 Compilación completada con errores que requieren atención.'));
|
|
691
|
+
}
|
|
692
|
+
else {
|
|
693
|
+
logger.info(chalk.yellow('✅ Compilación completada con solo advertencias.'));
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
else {
|
|
697
|
+
logger.info(chalk.green('✅ ¡Compilación exitosa sin errores!'));
|
|
698
|
+
}
|
|
699
|
+
logger.info(chalk.bold('--- FIN DEL RESUMEN ---\n'));
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Muestra errores del linter de forma detallada
|
|
703
|
+
*/
|
|
704
|
+
async function displayLinterErrors(errors) {
|
|
705
|
+
const chalk = await loadChalk();
|
|
706
|
+
logger.info(chalk.bold('--- Errores y Advertencias de Linting ---'));
|
|
707
|
+
const errorsByFile = new Map();
|
|
708
|
+
errors.forEach(error => {
|
|
709
|
+
if (!errorsByFile.has(error.file)) {
|
|
710
|
+
errorsByFile.set(error.file, []);
|
|
711
|
+
}
|
|
712
|
+
errorsByFile.get(error.file).push(error);
|
|
713
|
+
});
|
|
714
|
+
const totalErrors = errors.filter(e => e.severity === 'error').length;
|
|
715
|
+
const totalWarnings = errors.filter(e => e.severity === 'warning').length;
|
|
716
|
+
const totalFiles = errorsByFile.size;
|
|
717
|
+
logger.info(chalk.yellow(`📊 Resumen: ${totalErrors} errores, ${totalWarnings} advertencias en ${totalFiles} archivos\n`));
|
|
718
|
+
errorsByFile.forEach((fileErrors, filePath) => {
|
|
719
|
+
const baseName = path.basename(filePath);
|
|
720
|
+
logger.info(chalk.cyan(`\n📄 ${baseName}`));
|
|
721
|
+
fileErrors.forEach(error => {
|
|
722
|
+
const icon = error.severity === 'error' ? '❌' : '⚠️';
|
|
723
|
+
logger.info(`${icon} ${error.message}`);
|
|
724
|
+
if (error.help) {
|
|
725
|
+
logger.info(` └─ ${error.help}`);
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
});
|
|
729
|
+
logger.info(chalk.bold('--- Fin de Errores y Advertencias ---\n'));
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Obtiene el color apropiado para cada etapa de compilación
|
|
733
|
+
*/
|
|
734
|
+
async function getStageColor(stage) {
|
|
735
|
+
const chalk = await loadChalk();
|
|
736
|
+
switch (stage) {
|
|
737
|
+
case 'vue':
|
|
738
|
+
return chalk.green;
|
|
739
|
+
case 'typescript':
|
|
740
|
+
return chalk.blue;
|
|
741
|
+
case 'standardization':
|
|
742
|
+
return chalk.yellow;
|
|
743
|
+
case 'minification':
|
|
744
|
+
return chalk.red;
|
|
745
|
+
case 'tailwind':
|
|
746
|
+
return chalk.magenta;
|
|
747
|
+
case 'file-read':
|
|
748
|
+
return chalk.gray;
|
|
749
|
+
default:
|
|
750
|
+
return chalk.white;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
export function normalizeRuta(ruta) {
|
|
754
|
+
if (path.isAbsolute(ruta)) {
|
|
755
|
+
return path.normalize(ruta).replace(/\\/g, '/');
|
|
756
|
+
}
|
|
757
|
+
const file = path
|
|
758
|
+
.normalize(!ruta.startsWith('.') ? './' + ruta : ruta)
|
|
759
|
+
.replace(/\\/g, '/');
|
|
760
|
+
const sourceForDist = file.startsWith('./') ? file : `./${file}`;
|
|
761
|
+
return sourceForDist;
|
|
762
|
+
}
|
|
763
|
+
export function getOutputPath(ruta) {
|
|
764
|
+
const pathSource = env.PATH_SOURCE ?? '';
|
|
765
|
+
const pathDist = env.PATH_DIST ?? '';
|
|
766
|
+
if (!pathSource || !pathDist) {
|
|
767
|
+
return ruta.replace(/\.(vue|ts)$/, '.js');
|
|
768
|
+
}
|
|
769
|
+
const normalizedRuta = path.normalize(ruta).replace(/\\/g, '/');
|
|
770
|
+
const normalizedSource = path.normalize(pathSource).replace(/\\/g, '/');
|
|
771
|
+
const normalizedDist = path.normalize(pathDist).replace(/\\/g, '/');
|
|
772
|
+
let outputPath;
|
|
773
|
+
if (normalizedRuta.includes(normalizedSource)) {
|
|
774
|
+
const relativePath = normalizedRuta
|
|
775
|
+
.substring(normalizedRuta.indexOf(normalizedSource) +
|
|
776
|
+
normalizedSource.length)
|
|
777
|
+
.replace(/^[/\\]/, '');
|
|
778
|
+
outputPath = path
|
|
779
|
+
.join(normalizedDist, relativePath)
|
|
780
|
+
.replace(/\\/g, '/');
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
const fileName = path.basename(normalizedRuta);
|
|
784
|
+
outputPath = path.join(normalizedDist, fileName).replace(/\\/g, '/');
|
|
785
|
+
}
|
|
786
|
+
if (outputPath.includes('vue') || outputPath.includes('ts')) {
|
|
787
|
+
return outputPath.replace(/\.(vue|ts)$/, '.js');
|
|
788
|
+
}
|
|
789
|
+
else {
|
|
790
|
+
return outputPath;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
// Optimización para modo watch: debouncing y cache de archivos
|
|
794
|
+
class WatchModeOptimizer {
|
|
795
|
+
static instance;
|
|
796
|
+
fileSystemCache = new Map();
|
|
797
|
+
debounceTimers = new Map();
|
|
798
|
+
DEBOUNCE_DELAY = 100; // 100ms debounce
|
|
799
|
+
static getInstance() {
|
|
800
|
+
if (!WatchModeOptimizer.instance) {
|
|
801
|
+
WatchModeOptimizer.instance = new WatchModeOptimizer();
|
|
802
|
+
}
|
|
803
|
+
return WatchModeOptimizer.instance;
|
|
804
|
+
}
|
|
805
|
+
async compileForWatch(filePath, compileFn) {
|
|
806
|
+
return new Promise(resolve => {
|
|
807
|
+
const existingTimer = this.debounceTimers.get(filePath);
|
|
808
|
+
if (existingTimer) {
|
|
809
|
+
clearTimeout(existingTimer);
|
|
810
|
+
}
|
|
811
|
+
const timer = setTimeout(async () => {
|
|
812
|
+
this.debounceTimers.delete(filePath);
|
|
813
|
+
try {
|
|
814
|
+
const stats = await stat(filePath);
|
|
815
|
+
const cached = this.fileSystemCache.get(filePath);
|
|
816
|
+
if (cached && cached.mtime >= stats.mtimeMs) {
|
|
817
|
+
resolve({ success: true, cached: true });
|
|
818
|
+
return;
|
|
819
|
+
} // Configurar worker pool para modo watch
|
|
820
|
+
const { TypeScriptWorkerPool } = await import('./typescript-worker-pool.js');
|
|
821
|
+
const workerPool = TypeScriptWorkerPool.getInstance();
|
|
822
|
+
workerPool.setMode('watch');
|
|
823
|
+
const result = await compileFn(filePath);
|
|
824
|
+
this.fileSystemCache.set(filePath, {
|
|
825
|
+
mtime: stats.mtimeMs,
|
|
826
|
+
});
|
|
827
|
+
resolve(result);
|
|
828
|
+
}
|
|
829
|
+
catch (error) {
|
|
830
|
+
resolve({ success: false, error });
|
|
831
|
+
}
|
|
832
|
+
}, this.DEBOUNCE_DELAY);
|
|
833
|
+
this.debounceTimers.set(filePath, timer);
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
cleanup() {
|
|
837
|
+
this.debounceTimers.forEach(timer => clearTimeout(timer));
|
|
838
|
+
this.debounceTimers.clear();
|
|
839
|
+
this.fileSystemCache.clear();
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
async function compileJS(inPath, outPath, mode = 'individual') {
|
|
843
|
+
const timings = {};
|
|
844
|
+
// Si la ruta ya es absoluta, no la resolvamos de nuevo
|
|
845
|
+
inPath = path.isAbsolute(inPath)
|
|
846
|
+
? normalizeRuta(inPath)
|
|
847
|
+
: normalizeRuta(path.resolve(inPath)); // 🚀 Usar OptimizedModuleManager para carga optimizada
|
|
848
|
+
const moduleManager = OptimizedModuleManager.getInstance();
|
|
849
|
+
// Timing de lectura
|
|
850
|
+
let start = Date.now();
|
|
851
|
+
const extension = path.extname(inPath);
|
|
852
|
+
// Asegurar que el parser esté cargado
|
|
853
|
+
await moduleManager.ensureModuleLoaded('parser');
|
|
854
|
+
const getCodeFile = await loadParser();
|
|
855
|
+
let { code, error } = await getCodeFile(inPath);
|
|
856
|
+
timings.fileRead = Date.now() - start;
|
|
857
|
+
if (error) {
|
|
858
|
+
await handleCompilationError(error instanceof Error ? error : new Error(String(error)), inPath, 'file-read', mode, env.VERBOSE === 'true');
|
|
859
|
+
throw new Error(error instanceof Error ? error.message : String(error));
|
|
860
|
+
}
|
|
861
|
+
if (!code ||
|
|
862
|
+
code.trim().length === 0 ||
|
|
863
|
+
code === 'undefined' ||
|
|
864
|
+
code === 'null') {
|
|
865
|
+
await handleCompilationError(new Error('El archivo está vacío o no se pudo leer.'), inPath, 'file-read', mode, env.VERBOSE === 'true');
|
|
866
|
+
throw new Error('El archivo está vacío o no se pudo leer.');
|
|
867
|
+
}
|
|
868
|
+
// Logs detallados solo en modo verbose + all
|
|
869
|
+
const shouldShowDetailedLogs = env.VERBOSE === 'true' && mode === 'all';
|
|
870
|
+
// Compilación de Vue
|
|
871
|
+
let vueResult;
|
|
872
|
+
if (extension === '.vue') {
|
|
873
|
+
start = Date.now();
|
|
874
|
+
if (shouldShowDetailedLogs) {
|
|
875
|
+
logger.info(chalk.green(`💚 Precompilando VUE: ${inPath}`));
|
|
876
|
+
}
|
|
877
|
+
// Asegurar que el módulo Vue esté cargado
|
|
878
|
+
await moduleManager.ensureModuleLoaded('vue');
|
|
879
|
+
const preCompileVue = await loadVue();
|
|
880
|
+
if (typeof preCompileVue !== 'function') {
|
|
881
|
+
throw new Error(`loadVue devolvió ${typeof preCompileVue} en lugar de una función para archivo: ${inPath}`);
|
|
882
|
+
}
|
|
883
|
+
vueResult = await preCompileVue(code, inPath, env.isPROD === 'true');
|
|
884
|
+
timings.vueCompile = Date.now() - start;
|
|
885
|
+
if (vueResult === undefined || vueResult === null) {
|
|
886
|
+
throw new Error(`preCompileVue devolvió ${vueResult} para archivo: ${inPath}`);
|
|
887
|
+
}
|
|
888
|
+
if (vueResult.error) {
|
|
889
|
+
await handleCompilationError(vueResult.error instanceof Error
|
|
890
|
+
? vueResult.error
|
|
891
|
+
: new Error(String(vueResult.error)), inPath, 'vue', mode, env.VERBOSE === 'true');
|
|
892
|
+
throw new Error(vueResult.error instanceof Error
|
|
893
|
+
? vueResult.error.message
|
|
894
|
+
: String(vueResult.error));
|
|
895
|
+
}
|
|
896
|
+
registerCompilationSuccess(inPath, 'vue');
|
|
897
|
+
code = vueResult.data;
|
|
898
|
+
}
|
|
899
|
+
if (!code || code.trim().length === 0) {
|
|
900
|
+
await handleCompilationError(new Error('El código Vue compilado está vacío.'), inPath, 'vue', mode, env.VERBOSE === 'true');
|
|
901
|
+
throw new Error('El código Vue compilado está vacío.');
|
|
902
|
+
}
|
|
903
|
+
// Compilación de TypeScript
|
|
904
|
+
let tsResult;
|
|
905
|
+
if (extension === '.ts' || vueResult?.lang === 'ts') {
|
|
906
|
+
start = Date.now();
|
|
907
|
+
if (shouldShowDetailedLogs) {
|
|
908
|
+
logger.info(chalk.blue(`🔄️ Precompilando TS: ${inPath}`));
|
|
909
|
+
}
|
|
910
|
+
// Asegurar que el módulo TypeScript esté cargado
|
|
911
|
+
await moduleManager.ensureModuleLoaded('typescript');
|
|
912
|
+
const preCompileTS = await loadTypeScript();
|
|
913
|
+
if (typeof preCompileTS !== 'function') {
|
|
914
|
+
throw new Error(`loadTypeScript devolvió ${typeof preCompileTS} en lugar de una función para archivo: ${inPath}`);
|
|
915
|
+
}
|
|
916
|
+
tsResult = await preCompileTS(code, inPath);
|
|
917
|
+
timings.tsCompile = Date.now() - start;
|
|
918
|
+
if (tsResult === undefined || tsResult === null) {
|
|
919
|
+
throw new Error(`preCompileTS devolvió ${tsResult} para archivo: ${inPath}`);
|
|
920
|
+
}
|
|
921
|
+
if (tsResult.error) {
|
|
922
|
+
if (mode === 'all') {
|
|
923
|
+
// En modo --all, registrar el error pero continuar la compilación
|
|
924
|
+
registerCompilationError(inPath, 'typescript', tsResult.error instanceof Error
|
|
925
|
+
? tsResult.error.message
|
|
926
|
+
: String(tsResult.error), 'error');
|
|
927
|
+
}
|
|
928
|
+
else {
|
|
929
|
+
await handleCompilationError(tsResult.error, inPath, 'typescript', mode, env.VERBOSE === 'true');
|
|
930
|
+
throw new Error(tsResult.error instanceof Error
|
|
931
|
+
? tsResult.error.message
|
|
932
|
+
: String(tsResult.error));
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
else {
|
|
936
|
+
registerCompilationSuccess(inPath, 'typescript');
|
|
937
|
+
code = tsResult.data;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
if (!code || code.trim().length === 0) {
|
|
941
|
+
await handleCompilationError(new Error('El código TypeScript compilado está vacío.'), inPath, 'typescript', mode, env.VERBOSE === 'true');
|
|
942
|
+
throw new Error('El código TypeScript compilado está vacío.');
|
|
943
|
+
}
|
|
944
|
+
// Estandarización
|
|
945
|
+
if (shouldShowDetailedLogs) {
|
|
946
|
+
logger.info(chalk.yellow(`💛 Estandarizando: ${inPath}`));
|
|
947
|
+
}
|
|
948
|
+
start = Date.now();
|
|
949
|
+
// Asegurar que el módulo de transformaciones esté cargado
|
|
950
|
+
await moduleManager.ensureModuleLoaded('transforms');
|
|
951
|
+
const estandarizaCode = await loadTransforms();
|
|
952
|
+
const resultSTD = await estandarizaCode(code, inPath);
|
|
953
|
+
timings.standardization = Date.now() - start;
|
|
954
|
+
if (resultSTD === undefined || resultSTD === null) {
|
|
955
|
+
throw new Error(`estandarizaCode devolvió ${resultSTD} para archivo: ${inPath}`);
|
|
956
|
+
}
|
|
957
|
+
if (resultSTD.error) {
|
|
958
|
+
await handleCompilationError(new Error(resultSTD.error), inPath, 'standardization', mode, env.VERBOSE === 'true');
|
|
959
|
+
throw new Error(resultSTD.error);
|
|
960
|
+
}
|
|
961
|
+
registerCompilationSuccess(inPath, 'standardization');
|
|
962
|
+
code = resultSTD.code;
|
|
963
|
+
if (!code || code.trim().length === 0) {
|
|
964
|
+
await handleCompilationError(new Error('El código estandarizado está vacío.'), inPath, 'standardization', mode, env.VERBOSE === 'true');
|
|
965
|
+
throw new Error('El código estandarizado está vacío.');
|
|
966
|
+
}
|
|
967
|
+
// Minificación (solo en producción)
|
|
968
|
+
if (env.isPROD === 'true') {
|
|
969
|
+
start = Date.now();
|
|
970
|
+
if (shouldShowDetailedLogs) {
|
|
971
|
+
logger.info(chalk.red(`🤖 Minificando: ${inPath}`));
|
|
972
|
+
}
|
|
973
|
+
// Asegurar que el módulo de minificación esté cargado
|
|
974
|
+
await moduleManager.ensureModuleLoaded('minify');
|
|
975
|
+
const minifyJS = await loadMinify();
|
|
976
|
+
const resultMinify = await minifyJS(code, inPath, true);
|
|
977
|
+
timings.minification = Date.now() - start;
|
|
978
|
+
if (resultMinify === undefined || resultMinify === null) {
|
|
979
|
+
throw new Error(`minifyJS devolvió ${resultMinify} para archivo: ${inPath}`);
|
|
980
|
+
}
|
|
981
|
+
if (resultMinify.error) {
|
|
982
|
+
await handleCompilationError(resultMinify.error instanceof Error
|
|
983
|
+
? resultMinify.error
|
|
984
|
+
: new Error(String(resultMinify.error)), inPath, 'minification', mode, env.VERBOSE === 'true');
|
|
985
|
+
throw new Error(resultMinify.error instanceof Error
|
|
986
|
+
? resultMinify.error.message
|
|
987
|
+
: String(resultMinify.error));
|
|
988
|
+
}
|
|
989
|
+
registerCompilationSuccess(inPath, 'minification');
|
|
990
|
+
code = resultMinify.code;
|
|
991
|
+
}
|
|
992
|
+
// Escribir archivo final
|
|
993
|
+
const destinationDir = path.dirname(outPath);
|
|
994
|
+
await mkdir(destinationDir, { recursive: true });
|
|
995
|
+
await writeFile(outPath, code, 'utf-8');
|
|
996
|
+
return {
|
|
997
|
+
error: null,
|
|
998
|
+
action: 'extension',
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
export async function initCompile(ruta, compileTailwind = true, mode = 'individual') {
|
|
1002
|
+
try {
|
|
1003
|
+
// 🚀 Sistema de Carga Inteligente de Módulos
|
|
1004
|
+
const moduleManager = OptimizedModuleManager.getInstance();
|
|
1005
|
+
const fileExtension = path.extname(ruta);
|
|
1006
|
+
const fileExtensions = new Set([fileExtension]);
|
|
1007
|
+
// Inicializar módulos según el contexto
|
|
1008
|
+
await moduleManager.preloadForContext(mode === 'all' ? 'batch' : mode, fileExtensions);
|
|
1009
|
+
// Generar TailwindCSS si está habilitado
|
|
1010
|
+
if (compileTailwind && Boolean(env.TAILWIND)) {
|
|
1011
|
+
await moduleManager.ensureModuleLoaded('tailwind');
|
|
1012
|
+
const generateTailwindCSS = await loadTailwind();
|
|
1013
|
+
const resultTW = await generateTailwindCSS();
|
|
1014
|
+
if (typeof resultTW !== 'boolean') {
|
|
1015
|
+
if (resultTW?.success) {
|
|
1016
|
+
logger.info(`🎨 ${resultTW.message}`);
|
|
1017
|
+
}
|
|
1018
|
+
else {
|
|
1019
|
+
const errorMsg = `${resultTW.message}${resultTW.details ? '\n' + resultTW.details : ''}`;
|
|
1020
|
+
await handleCompilationError(new Error(errorMsg), 'tailwind.config.js', 'tailwind', mode, env.VERBOSE === 'true');
|
|
1021
|
+
// No hacer throw aquí, permitir que la compilación continúe
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
const startTime = Date.now();
|
|
1026
|
+
const file = normalizeRuta(ruta);
|
|
1027
|
+
const outFile = getOutputPath(file);
|
|
1028
|
+
if (mode === 'individual' && env.VERBOSE === 'true') {
|
|
1029
|
+
logger.info(`🔜 Fuente: ${file}`);
|
|
1030
|
+
}
|
|
1031
|
+
const result = await compileJS(file, outFile, mode);
|
|
1032
|
+
if (result.error) {
|
|
1033
|
+
throw new Error(result.error);
|
|
1034
|
+
}
|
|
1035
|
+
const endTime = Date.now();
|
|
1036
|
+
const elapsedTime = showTimingForHumans(endTime - startTime);
|
|
1037
|
+
if (mode === 'individual') {
|
|
1038
|
+
if (env.VERBOSE === 'true') {
|
|
1039
|
+
logger.info(`🔚 Destino: ${outFile}`);
|
|
1040
|
+
logger.info(`⏱️ Tiempo: ${elapsedTime}`);
|
|
1041
|
+
}
|
|
1042
|
+
const chalk = await loadChalk();
|
|
1043
|
+
logger.info(chalk.green(`✅ Compilación exitosa: ${path.basename(file)}`));
|
|
1044
|
+
}
|
|
1045
|
+
return {
|
|
1046
|
+
success: true,
|
|
1047
|
+
output: outFile,
|
|
1048
|
+
action: result.action,
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
catch (error) {
|
|
1052
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1053
|
+
if (env.VERBOSE === 'true') {
|
|
1054
|
+
logger.error(`❌ Error en compilación de ${path.basename(ruta)}: ${errorMessage}`);
|
|
1055
|
+
}
|
|
1056
|
+
return {
|
|
1057
|
+
success: false,
|
|
1058
|
+
output: '',
|
|
1059
|
+
error: errorMessage,
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
// Variable para el último progreso mostrado (evitar spam)
|
|
1064
|
+
let lastProgressUpdate = 0;
|
|
1065
|
+
// Función para ejecutar el linter antes de la compilación
|
|
1066
|
+
export async function runLinter(showResult = false) {
|
|
1067
|
+
const linterENV = env.linter;
|
|
1068
|
+
const linterPromises = [];
|
|
1069
|
+
const linterErrors = [];
|
|
1070
|
+
let proceedWithCompilation = true;
|
|
1071
|
+
if (env.ENABLE_LINTER !== 'true') {
|
|
1072
|
+
return true;
|
|
1073
|
+
}
|
|
1074
|
+
if (typeof linterENV === 'string' && linterENV.trim() !== '') {
|
|
1075
|
+
logger.info('🔍 Ejecutando linting...');
|
|
1076
|
+
try {
|
|
1077
|
+
const parsedLinterEnv = JSON.parse(linterENV);
|
|
1078
|
+
if (Array.isArray(parsedLinterEnv)) {
|
|
1079
|
+
// Cargar dependencias de linting de forma lazy
|
|
1080
|
+
const { ESLint, OxLint } = await loadLinter();
|
|
1081
|
+
for (const item of parsedLinterEnv) {
|
|
1082
|
+
if (item.name.toLowerCase() === 'eslint') {
|
|
1083
|
+
logger.info(`🔧 Ejecutando ESLint con config: ${item.configFile || 'por defecto'}`);
|
|
1084
|
+
const eslintPromise = ESLint(item)
|
|
1085
|
+
.then((eslintResult) => {
|
|
1086
|
+
if (eslintResult && eslintResult.json) {
|
|
1087
|
+
// Procesar resultados de ESLint
|
|
1088
|
+
if (Array.isArray(eslintResult.json)) {
|
|
1089
|
+
eslintResult.json.forEach((result) => {
|
|
1090
|
+
linterErrors.push({
|
|
1091
|
+
file: result.filePath ||
|
|
1092
|
+
'archivo no especificado',
|
|
1093
|
+
message: result.message,
|
|
1094
|
+
severity: result.severity === 2
|
|
1095
|
+
? 'error'
|
|
1096
|
+
: 'warning',
|
|
1097
|
+
help: result.ruleId
|
|
1098
|
+
? `Regla ESLint: ${result.ruleId}`
|
|
1099
|
+
: undefined,
|
|
1100
|
+
});
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
else if (eslintResult.json.results &&
|
|
1104
|
+
Array.isArray(eslintResult.json.results)) {
|
|
1105
|
+
eslintResult.json.results.forEach((fileResult) => {
|
|
1106
|
+
if (fileResult.messages &&
|
|
1107
|
+
Array.isArray(fileResult.messages)) {
|
|
1108
|
+
fileResult.messages.forEach((msg) => {
|
|
1109
|
+
linterErrors.push({
|
|
1110
|
+
file: fileResult.filePath ||
|
|
1111
|
+
'archivo no especificado',
|
|
1112
|
+
message: msg.message,
|
|
1113
|
+
severity: msg.severity ===
|
|
1114
|
+
2
|
|
1115
|
+
? 'error'
|
|
1116
|
+
: 'warning',
|
|
1117
|
+
help: msg.ruleId
|
|
1118
|
+
? `Regla ESLint: ${msg.ruleId}`
|
|
1119
|
+
: undefined,
|
|
1120
|
+
});
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
})
|
|
1127
|
+
.catch((err) => {
|
|
1128
|
+
logger.error(`❌ Error durante la ejecución de ESLint: ${err.message}`);
|
|
1129
|
+
linterErrors.push({
|
|
1130
|
+
file: item.configFile || 'ESLint Config',
|
|
1131
|
+
message: `Fallo al ejecutar ESLint: ${err.message}`,
|
|
1132
|
+
severity: 'error',
|
|
1133
|
+
});
|
|
1134
|
+
});
|
|
1135
|
+
linterPromises.push(eslintPromise);
|
|
1136
|
+
}
|
|
1137
|
+
else if (item.name.toLowerCase() === 'oxlint') {
|
|
1138
|
+
logger.info(`🔧 Ejecutando OxLint con config: ${item.configFile || 'por defecto'}`);
|
|
1139
|
+
const oxlintPromise = OxLint(item)
|
|
1140
|
+
.then((oxlintResult) => {
|
|
1141
|
+
if (oxlintResult &&
|
|
1142
|
+
oxlintResult['json'] &&
|
|
1143
|
+
Array.isArray(oxlintResult['json'])) {
|
|
1144
|
+
oxlintResult['json'].forEach((result) => {
|
|
1145
|
+
linterErrors.push({
|
|
1146
|
+
file: result.filename ||
|
|
1147
|
+
result.file ||
|
|
1148
|
+
'archivo no especificado',
|
|
1149
|
+
message: result.message,
|
|
1150
|
+
severity: typeof result.severity ===
|
|
1151
|
+
'string'
|
|
1152
|
+
? result.severity.toLowerCase()
|
|
1153
|
+
: 'error',
|
|
1154
|
+
help: result.help ||
|
|
1155
|
+
(result.rule_id
|
|
1156
|
+
? `Regla Oxlint: ${result.rule_id}`
|
|
1157
|
+
: undefined),
|
|
1158
|
+
});
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
})
|
|
1162
|
+
.catch((err) => {
|
|
1163
|
+
logger.error(`❌ Error durante la ejecución de OxLint: ${err.message}`);
|
|
1164
|
+
linterErrors.push({
|
|
1165
|
+
file: item.configFile || 'Oxlint Config',
|
|
1166
|
+
message: `Fallo al ejecutar Oxlint: ${err.message}`,
|
|
1167
|
+
severity: 'error',
|
|
1168
|
+
});
|
|
1169
|
+
});
|
|
1170
|
+
linterPromises.push(oxlintPromise);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
else {
|
|
1175
|
+
logger.warn('⚠️ La configuración de linter no es un array válido.');
|
|
1176
|
+
}
|
|
1177
|
+
await Promise.all(linterPromises);
|
|
1178
|
+
if (showResult) {
|
|
1179
|
+
if (linterErrors.length > 0) {
|
|
1180
|
+
await displayLinterErrors(linterErrors);
|
|
1181
|
+
}
|
|
1182
|
+
else {
|
|
1183
|
+
const chalk = await loadChalk();
|
|
1184
|
+
logger.info(chalk.green('✅ No se encontraron errores ni advertencias de linting.'));
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
catch (parseError) {
|
|
1189
|
+
logger.warn(`Error parseando configuración de linter: ${parseError instanceof Error ? parseError.message : 'Error desconocido'}, omitiendo...`);
|
|
1190
|
+
}
|
|
1191
|
+
if (!showResult && linterErrors.length > 0) {
|
|
1192
|
+
await displayLinterErrors(linterErrors);
|
|
1193
|
+
logger.warn('🚨 Se encontraron errores o advertencias durante el linting.');
|
|
1194
|
+
if (env.yes === 'false') {
|
|
1195
|
+
const result = await promptUser('¿Deseas continuar con la compilación a pesar de los errores de linting? (s/N): ');
|
|
1196
|
+
if (result.toLowerCase() !== 's') {
|
|
1197
|
+
logger.info('🛑 Compilación cancelada por el usuario.');
|
|
1198
|
+
proceedWithCompilation = false;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
return proceedWithCompilation;
|
|
1204
|
+
}
|
|
1205
|
+
// Función para crear una barra de progreso visual
|
|
1206
|
+
function createProgressBar(current, total, width = 30) {
|
|
1207
|
+
const percentage = Math.round((current / total) * 100);
|
|
1208
|
+
const filled = Math.round((percentage / 100) * width);
|
|
1209
|
+
const empty = width - filled;
|
|
1210
|
+
return `[${'█'.repeat(filled)}${' '.repeat(empty)}] ${percentage}% (${current}/${total})`;
|
|
1211
|
+
}
|
|
1212
|
+
// Función helper para verificar si un archivo debe ser omitido por cache
|
|
1213
|
+
async function shouldSkipFile(filePath) {
|
|
1214
|
+
return await smartCache.isValid(filePath);
|
|
1215
|
+
}
|
|
1216
|
+
// Función para compilar archivos con límite de concurrencia
|
|
1217
|
+
async function compileWithConcurrencyLimit(files, maxConcurrency = 8) {
|
|
1218
|
+
const results = [];
|
|
1219
|
+
const executing = [];
|
|
1220
|
+
const total = files.length;
|
|
1221
|
+
let completed = 0;
|
|
1222
|
+
let skipped = 0;
|
|
1223
|
+
let failed = 0; // Función para mostrar progreso
|
|
1224
|
+
function showProgress() {
|
|
1225
|
+
const currentTotal = completed + skipped + failed;
|
|
1226
|
+
const progressBar = createProgressBar(currentTotal, total);
|
|
1227
|
+
const progressPercent = Math.round((currentTotal / total) * 100);
|
|
1228
|
+
if (progressPercent > lastProgressUpdate + 1 ||
|
|
1229
|
+
currentTotal === total) {
|
|
1230
|
+
stdout.write(`\r🚀 ${progressBar} [✅ ${completed} | ⏭️ ${skipped} | ❌ ${failed}]`);
|
|
1231
|
+
lastProgressUpdate = progressPercent;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
for (const file of files) {
|
|
1235
|
+
const promise = (async () => {
|
|
1236
|
+
try {
|
|
1237
|
+
// Verificar cache antes de compilar
|
|
1238
|
+
if (await shouldSkipFile(file)) {
|
|
1239
|
+
skipped++;
|
|
1240
|
+
showProgress();
|
|
1241
|
+
return {
|
|
1242
|
+
success: true,
|
|
1243
|
+
cached: true,
|
|
1244
|
+
output: smartCache.getOutputPath(file),
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
const result = await initCompile(file, false, 'batch'); // Actualizar cache si la compilación fue exitosa
|
|
1248
|
+
if (result.success && result.output) {
|
|
1249
|
+
await smartCache.set(file, result.output);
|
|
1250
|
+
}
|
|
1251
|
+
completed++;
|
|
1252
|
+
showProgress();
|
|
1253
|
+
return result;
|
|
1254
|
+
}
|
|
1255
|
+
catch (error) {
|
|
1256
|
+
failed++;
|
|
1257
|
+
showProgress();
|
|
1258
|
+
return {
|
|
1259
|
+
success: false,
|
|
1260
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1261
|
+
};
|
|
1262
|
+
}
|
|
1263
|
+
})();
|
|
1264
|
+
results.push(promise);
|
|
1265
|
+
executing.push(promise);
|
|
1266
|
+
if (executing.length >= maxConcurrency) {
|
|
1267
|
+
await Promise.race(executing);
|
|
1268
|
+
executing.splice(executing.findIndex(p => p === promise), 1);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
await Promise.all(results);
|
|
1272
|
+
console.log('\n'); // Nueva línea después de la barra de progreso
|
|
1273
|
+
}
|
|
1274
|
+
export async function initCompileAll() {
|
|
1275
|
+
try {
|
|
1276
|
+
// Limpiar estado de compilación anterior
|
|
1277
|
+
clearCompilationState();
|
|
1278
|
+
// Cargar cache al inicio
|
|
1279
|
+
await loadCache();
|
|
1280
|
+
lastProgressUpdate = 0;
|
|
1281
|
+
const shouldContinue = await runLinter();
|
|
1282
|
+
if (!shouldContinue) {
|
|
1283
|
+
// await displayCompilationSummary(env.VERBOSE === 'true');
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
const startTime = Date.now();
|
|
1287
|
+
const rawPathSource = env.PATH_SOURCE ?? '';
|
|
1288
|
+
const pathDist = env.PATH_DIST ?? '';
|
|
1289
|
+
const normalizedGlobPathSource = rawPathSource.replace(/\\/g, '/');
|
|
1290
|
+
const patterns = [
|
|
1291
|
+
`${normalizedGlobPathSource}/**/*.js`,
|
|
1292
|
+
`${normalizedGlobPathSource}/**/*.vue`,
|
|
1293
|
+
`${normalizedGlobPathSource}/**/*.ts`,
|
|
1294
|
+
`${normalizedGlobPathSource}/**/*.mjs`,
|
|
1295
|
+
`${normalizedGlobPathSource}/**/*.cjs`,
|
|
1296
|
+
];
|
|
1297
|
+
logger.info(`📝 Compilando todos los archivos...`);
|
|
1298
|
+
logger.info(`🔜 Fuente: ${rawPathSource}`);
|
|
1299
|
+
logger.info(`🔚 Destino: ${pathDist}\n`); // Generar TailwindCSS
|
|
1300
|
+
const generateTailwindCSS = await loadTailwind();
|
|
1301
|
+
const resultTW = await generateTailwindCSS();
|
|
1302
|
+
if (typeof resultTW !== 'boolean') {
|
|
1303
|
+
if (resultTW?.success) {
|
|
1304
|
+
logger.info(`🎨 ${resultTW.message}\n`);
|
|
1305
|
+
}
|
|
1306
|
+
else {
|
|
1307
|
+
await handleCompilationError(new Error(`${resultTW.message}${resultTW.details ? '\n' + resultTW.details : ''}`), 'tailwind.config.js', 'tailwind', 'all', env.VERBOSE === 'true');
|
|
1308
|
+
}
|
|
1309
|
+
} // Recopilar todos los archivos
|
|
1310
|
+
const filesToCompile = [];
|
|
1311
|
+
for await (const file of glob(patterns)) {
|
|
1312
|
+
if (file.endsWith('.d.ts')) {
|
|
1313
|
+
continue;
|
|
1314
|
+
}
|
|
1315
|
+
// Usar la ruta tal como viene de glob, sin modificar
|
|
1316
|
+
filesToCompile.push(file);
|
|
1317
|
+
}
|
|
1318
|
+
// Determinar concurrencia óptima
|
|
1319
|
+
const cpuCount = os.cpus().length;
|
|
1320
|
+
const fileCount = filesToCompile.length;
|
|
1321
|
+
let maxConcurrency;
|
|
1322
|
+
if (fileCount < 10) {
|
|
1323
|
+
maxConcurrency = Math.min(fileCount, cpuCount);
|
|
1324
|
+
}
|
|
1325
|
+
else if (fileCount < 50) {
|
|
1326
|
+
maxConcurrency = Math.min(cpuCount * 2, 12);
|
|
1327
|
+
}
|
|
1328
|
+
else {
|
|
1329
|
+
maxConcurrency = Math.min(cpuCount * 2, 16);
|
|
1330
|
+
}
|
|
1331
|
+
logger.info(`🚀 Compilando ${fileCount} archivos con concurrencia optimizada (${maxConcurrency} hilos)...`); // Configurar worker pool para modo batch
|
|
1332
|
+
try {
|
|
1333
|
+
const { TypeScriptWorkerPool } = await import('./typescript-worker-pool.js');
|
|
1334
|
+
const workerPool = TypeScriptWorkerPool.getInstance();
|
|
1335
|
+
workerPool.setMode('batch');
|
|
1336
|
+
}
|
|
1337
|
+
catch {
|
|
1338
|
+
// Error silencioso en configuración del pool
|
|
1339
|
+
}
|
|
1340
|
+
await compileWithConcurrencyLimit(filesToCompile, maxConcurrency);
|
|
1341
|
+
// Guardar cache al final
|
|
1342
|
+
await saveCache();
|
|
1343
|
+
const endTime = Date.now();
|
|
1344
|
+
const elapsedTime = showTimingForHumans(endTime - startTime);
|
|
1345
|
+
logger.info(`⏱️ Tiempo total de compilación: ${elapsedTime}\n`); // Mostrar resumen de compilación
|
|
1346
|
+
await displayCompilationSummary(env.VERBOSE === 'true');
|
|
1347
|
+
}
|
|
1348
|
+
catch (error) {
|
|
1349
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1350
|
+
logger.error(`🚩 Error al compilar todos los archivos: ${errorMessage}`);
|
|
1351
|
+
// Registrar el error en el sistema unificado
|
|
1352
|
+
await handleCompilationError(error instanceof Error ? error : new Error(String(error)), 'compilación general', 'all', 'all', env.VERBOSE === 'true');
|
|
1353
|
+
// Mostrar resumen incluso si hay errores generales
|
|
1354
|
+
await displayCompilationSummary(env.VERBOSE === 'true');
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
// Función wrapper para compatibilidad con tests
|
|
1358
|
+
export async function compileFile(filePath) {
|
|
1359
|
+
return await initCompile(filePath, true, 'individual');
|
|
1360
|
+
}
|
|
1361
|
+
export { WatchModeOptimizer };
|
|
1362
|
+
//# sourceMappingURL=compile.js.map
|