versacompiler 2.0.0 → 2.0.2
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 +345 -69
- package/dist/compiler/compile.js +762 -183
- package/dist/compiler/minify.js +199 -10
- package/dist/compiler/module-resolution-optimizer.js +822 -0
- package/dist/compiler/parser.js +179 -6
- package/dist/compiler/performance-monitor.js +192 -0
- package/dist/compiler/transform-optimizer.js +392 -0
- package/dist/compiler/transforms.js +124 -118
- package/dist/compiler/{typescript.js → typescript-compiler.js} +27 -30
- package/dist/compiler/typescript-error-parser.js +6 -7
- package/dist/compiler/typescript-manager.js +378 -0
- package/dist/compiler/typescript-sync-validator.js +13 -15
- package/dist/compiler/typescript-worker-pool.js +845 -0
- package/dist/compiler/typescript-worker.js +51 -21
- package/dist/compiler/vuejs.js +131 -37
- package/dist/main.js +5 -6
- package/dist/servicios/browserSync.js +313 -21
- package/dist/servicios/file-watcher.js +367 -0
- package/dist/servicios/logger.js +1 -0
- package/dist/servicios/readConfig.js +1 -0
- package/dist/utils/excluded-modules.js +36 -0
- package/dist/utils/module-resolver.js +9 -48
- package/dist/utils/promptUser.js +1 -1
- package/dist/utils/resolve-bin.js +28 -9
- package/package.json +8 -7
- package/dist/servicios/chokidar.js +0 -178
package/dist/compiler/compile.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import { glob, mkdir, readFile, stat, unlink, writeFile, } from 'node:fs/promises';
|
|
2
3
|
import os from 'node:os';
|
|
3
4
|
import path from 'node:path';
|
|
4
|
-
import { env } from 'node:process';
|
|
5
|
+
import { argv, cwd, env, stdout } from 'node:process';
|
|
5
6
|
// Lazy loading optimizations - Only import lightweight modules synchronously
|
|
6
7
|
import { logger } from '../servicios/logger.js';
|
|
7
8
|
import { promptUser } from '../utils/promptUser.js';
|
|
@@ -16,157 +17,267 @@ let generateTailwindCSS;
|
|
|
16
17
|
let estandarizaCode;
|
|
17
18
|
let preCompileTS;
|
|
18
19
|
let preCompileVue;
|
|
19
|
-
// 🚀
|
|
20
|
-
|
|
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 {
|
|
21
26
|
static instance;
|
|
22
27
|
isInitialized = false;
|
|
23
|
-
initializationPromise = null;
|
|
24
28
|
loadedModules = new Set();
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
+
}
|
|
30
43
|
static getInstance() {
|
|
31
|
-
if (!
|
|
32
|
-
|
|
44
|
+
if (!OptimizedModuleManager.instance) {
|
|
45
|
+
OptimizedModuleManager.instance = new OptimizedModuleManager();
|
|
33
46
|
}
|
|
34
|
-
return
|
|
47
|
+
return OptimizedModuleManager.instance;
|
|
35
48
|
}
|
|
36
49
|
/**
|
|
37
|
-
* NUEVO: Precarga
|
|
50
|
+
* ✨ NUEVO: Precarga en background para módulos críticos
|
|
38
51
|
*/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
case 'chalk':
|
|
45
|
-
return this._loadModule('chalk', loadChalk);
|
|
46
|
-
case 'parser':
|
|
47
|
-
return this._loadModule('parser', loadParser);
|
|
48
|
-
case 'transforms':
|
|
49
|
-
return this._loadModule('transforms', loadTransforms);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
await Promise.all(preloadPromises);
|
|
54
|
-
// console.log('[ModuleManager] Precarga completada para modo watch');
|
|
55
|
-
} /**
|
|
56
|
-
* MEJORADO: Inicializa los módulos necesarios según el contexto de compilación
|
|
57
|
-
* @param context Contexto de compilación: 'individual', 'batch', 'watch'
|
|
58
|
-
* @param fileExtensions Extensiones de archivos a compilar para optimizar la carga
|
|
52
|
+
startBackgroundPreloading() {
|
|
53
|
+
this.backgroundLoader = this.preloadCriticalModules();
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* ✨ NUEVO: Precarga módulos críticos en background
|
|
59
57
|
*/
|
|
60
|
-
async
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
this.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
if (this.initializationPromise) {
|
|
70
|
-
return this.initializationPromise;
|
|
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);
|
|
71
65
|
}
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
catch {
|
|
67
|
+
// Fallos en precarga no deben afectar la funcionalidad principal
|
|
74
68
|
}
|
|
75
|
-
this.initializationPromise = this._performInitialization(context, fileExtensions);
|
|
76
|
-
await this.initializationPromise;
|
|
77
|
-
this.initializationPromise = null;
|
|
78
69
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
if (!this.loadedModules.has('transforms')) {
|
|
89
|
-
modulesToLoad.push(this._loadModule('transforms', loadTransforms));
|
|
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;
|
|
90
78
|
}
|
|
91
|
-
|
|
79
|
+
const toPreload = []; // Precarga basada en contexto
|
|
92
80
|
if (context === 'batch' || context === 'watch') {
|
|
93
|
-
// En
|
|
94
|
-
|
|
95
|
-
modulesToLoad.push(this._loadModule('vue', loadVue));
|
|
96
|
-
}
|
|
97
|
-
if (!this.loadedModules.has('typescript')) {
|
|
98
|
-
modulesToLoad.push(this._loadModule('typescript', loadTypeScript));
|
|
99
|
-
}
|
|
100
|
-
if (!this.loadedModules.has('minify')) {
|
|
101
|
-
modulesToLoad.push(this._loadModule('minify', loadMinify));
|
|
102
|
-
}
|
|
81
|
+
// En batch/watch, precargar todos los módulos comunes
|
|
82
|
+
toPreload.push('transforms', 'vue', 'typescript', 'module-resolution-optimizer');
|
|
103
83
|
}
|
|
104
84
|
else {
|
|
105
|
-
// En
|
|
106
|
-
if (
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
if (env.isPROD === 'true' && !this.loadedModules.has('minify')) {
|
|
114
|
-
modulesToLoad.push(this._loadModule('minify', loadMinify));
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
// Cargar módulos en paralelo
|
|
118
|
-
await Promise.all(modulesToLoad);
|
|
119
|
-
this.isInitialized = true;
|
|
120
|
-
}
|
|
121
|
-
async _loadModule(name, loadFunction) {
|
|
122
|
-
if (!this.loadedModules.has(name)) {
|
|
123
|
-
await loadFunction();
|
|
124
|
-
this.loadedModules.add(name);
|
|
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');
|
|
125
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);
|
|
126
99
|
}
|
|
127
100
|
/**
|
|
128
|
-
*
|
|
101
|
+
* ✨ MEJORADO: Carga inteligente con pooling y deduplicación
|
|
129
102
|
*/
|
|
130
103
|
async ensureModuleLoaded(moduleName) {
|
|
131
|
-
|
|
132
|
-
|
|
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;
|
|
133
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) {
|
|
134
139
|
switch (moduleName) {
|
|
140
|
+
case 'chalk':
|
|
141
|
+
return this.loadChalk();
|
|
142
|
+
case 'parser':
|
|
143
|
+
return this.loadParser();
|
|
144
|
+
case 'transforms':
|
|
145
|
+
return this.loadTransforms();
|
|
135
146
|
case 'vue':
|
|
136
|
-
|
|
137
|
-
break;
|
|
147
|
+
return this.loadVue();
|
|
138
148
|
case 'typescript':
|
|
139
|
-
|
|
140
|
-
break;
|
|
149
|
+
return this.loadTypeScript();
|
|
141
150
|
case 'minify':
|
|
142
|
-
|
|
143
|
-
break;
|
|
151
|
+
return this.loadMinify();
|
|
144
152
|
case 'tailwind':
|
|
145
|
-
|
|
146
|
-
break;
|
|
153
|
+
return this.loadTailwind();
|
|
147
154
|
case 'linter':
|
|
148
|
-
|
|
149
|
-
|
|
155
|
+
return this.loadLinter();
|
|
156
|
+
case 'transform-optimizer':
|
|
157
|
+
return this.loadTransformOptimizer();
|
|
158
|
+
case 'module-resolution-optimizer':
|
|
159
|
+
return this.loadModuleResolutionOptimizer();
|
|
150
160
|
default:
|
|
151
161
|
throw new Error(`Módulo desconocido: ${moduleName}`);
|
|
152
162
|
}
|
|
153
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
|
+
}
|
|
154
267
|
/**
|
|
155
268
|
* Resetea el estado del manager (útil para tests)
|
|
156
269
|
*/
|
|
157
270
|
reset() {
|
|
158
271
|
this.isInitialized = false;
|
|
159
|
-
this.initializationPromise = null;
|
|
160
272
|
this.loadedModules.clear();
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
};
|
|
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();
|
|
170
281
|
}
|
|
171
282
|
}
|
|
172
283
|
// Lazy loading helper functions
|
|
@@ -214,7 +325,7 @@ async function loadTransforms() {
|
|
|
214
325
|
}
|
|
215
326
|
async function loadTypeScript() {
|
|
216
327
|
if (!preCompileTS) {
|
|
217
|
-
const typescriptModule = await import('./typescript.js');
|
|
328
|
+
const typescriptModule = await import('./typescript-manager.js');
|
|
218
329
|
preCompileTS = typescriptModule.preCompileTS;
|
|
219
330
|
}
|
|
220
331
|
return preCompileTS;
|
|
@@ -229,40 +340,531 @@ async function loadVue() {
|
|
|
229
340
|
// Almacenamiento global de errores y resultados
|
|
230
341
|
const compilationErrors = [];
|
|
231
342
|
const compilationResults = [];
|
|
232
|
-
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
343
|
+
// Variables de entorno relevantes para compilación
|
|
344
|
+
const COMPILATION_ENV_VARS = [
|
|
345
|
+
'NODE_ENV',
|
|
346
|
+
'isPROD',
|
|
347
|
+
'TAILWIND',
|
|
348
|
+
'ENABLE_LINTER',
|
|
349
|
+
'VERBOSE',
|
|
350
|
+
'typeCheck',
|
|
351
|
+
'PATH_ALIAS',
|
|
352
|
+
'tailwindcss',
|
|
353
|
+
'linter',
|
|
354
|
+
'tsconfigFile',
|
|
355
|
+
];
|
|
356
|
+
class SmartCompilationCache {
|
|
357
|
+
cache = new Map();
|
|
358
|
+
maxEntries = 500; // Máximo archivos en cache
|
|
359
|
+
maxMemory = 100 * 1024 * 1024; // 100MB límite
|
|
360
|
+
currentMemoryUsage = 0;
|
|
361
|
+
// ✨ ISSUE #3: Sistema de vigilancia de dependencias
|
|
362
|
+
fileWatchers = new Map(); // chokidar watchers
|
|
363
|
+
dependencyGraph = new Map(); // archivo -> dependencias
|
|
364
|
+
reverseDependencyGraph = new Map(); // dependencia -> archivos que la usan
|
|
365
|
+
packageJsonPath = path.join(cwd(), 'package.json');
|
|
366
|
+
nodeModulesPath = path.join(cwd(), 'node_modules');
|
|
367
|
+
isWatchingDependencies = false;
|
|
368
|
+
/**
|
|
369
|
+
* Genera hash SHA-256 del contenido del archivo
|
|
370
|
+
*/ async generateContentHash(filePath) {
|
|
371
|
+
try {
|
|
372
|
+
const content = await readFile(filePath, 'utf8');
|
|
373
|
+
return createHash('sha256').update(content).digest('hex');
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
// Si no se puede leer el archivo, generar hash único basado en la ruta y timestamp
|
|
377
|
+
const fallback = `${filePath}-${Date.now()}`;
|
|
378
|
+
return createHash('sha256').update(fallback).digest('hex');
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Genera hash de la configuración del compilador
|
|
383
|
+
*/
|
|
384
|
+
generateConfigHash() {
|
|
385
|
+
try {
|
|
386
|
+
// Recopilar configuración relevante de variables de entorno
|
|
387
|
+
const config = {
|
|
388
|
+
isPROD: env.isPROD || 'false',
|
|
389
|
+
TAILWIND: env.TAILWIND || 'false',
|
|
390
|
+
ENABLE_LINTER: env.ENABLE_LINTER || 'false',
|
|
391
|
+
PATH_ALIAS: env.PATH_ALIAS || '{}',
|
|
392
|
+
tailwindcss: env.tailwindcss || 'false',
|
|
393
|
+
linter: env.linter || 'false',
|
|
394
|
+
tsconfigFile: env.tsconfigFile || './tsconfig.json',
|
|
395
|
+
};
|
|
396
|
+
const configStr = JSON.stringify(config, Object.keys(config).sort());
|
|
397
|
+
return createHash('sha256')
|
|
398
|
+
.update(configStr)
|
|
399
|
+
.digest('hex')
|
|
400
|
+
.substring(0, 12);
|
|
401
|
+
}
|
|
402
|
+
catch {
|
|
403
|
+
return 'no-config';
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Genera hash de variables de entorno relevantes
|
|
408
|
+
*/
|
|
409
|
+
generateEnvHash() {
|
|
410
|
+
try {
|
|
411
|
+
const envVars = COMPILATION_ENV_VARS.map(key => `${key}=${env[key] || ''}`).join('|');
|
|
412
|
+
return createHash('sha256')
|
|
413
|
+
.update(envVars)
|
|
414
|
+
.digest('hex')
|
|
415
|
+
.substring(0, 12);
|
|
416
|
+
}
|
|
417
|
+
catch {
|
|
418
|
+
return 'no-env';
|
|
419
|
+
}
|
|
420
|
+
} /**
|
|
421
|
+
* ✨ ISSUE #3: Genera hash avanzado de dependencias del proyecto
|
|
422
|
+
* Incluye vigilancia de package.json, node_modules y versiones instaladas
|
|
423
|
+
*/
|
|
424
|
+
async generateDependencyHash() {
|
|
425
|
+
try {
|
|
426
|
+
const hash = createHash('sha256');
|
|
427
|
+
// 1. Hash del package.json con versiones
|
|
428
|
+
const packagePath = path.join(cwd(), 'package.json');
|
|
429
|
+
const packageContent = await readFile(packagePath, 'utf8');
|
|
430
|
+
const pkg = JSON.parse(packageContent);
|
|
431
|
+
const deps = {
|
|
432
|
+
...pkg.dependencies,
|
|
433
|
+
...pkg.devDependencies,
|
|
434
|
+
};
|
|
435
|
+
const depsStr = JSON.stringify(deps, Object.keys(deps).sort());
|
|
436
|
+
hash.update(`package:${depsStr}`);
|
|
437
|
+
// 2. Hash del package-lock.json si existe (versiones exactas instaladas)
|
|
239
438
|
try {
|
|
240
|
-
|
|
439
|
+
const lockPath = path.join(cwd(), 'package-lock.json');
|
|
440
|
+
const lockContent = await readFile(lockPath, 'utf8');
|
|
441
|
+
const lockData = JSON.parse(lockContent);
|
|
442
|
+
// Solo incluir las versiones instaladas, no todo el lockfile
|
|
443
|
+
const installedVersions = {};
|
|
444
|
+
if (lockData.packages) {
|
|
445
|
+
for (const [pkgPath, pkgInfo] of Object.entries(lockData.packages)) {
|
|
446
|
+
if (pkgPath &&
|
|
447
|
+
pkgPath !== '' &&
|
|
448
|
+
typeof pkgInfo === 'object' &&
|
|
449
|
+
pkgInfo !== null) {
|
|
450
|
+
const pkgName = pkgPath.replace('node_modules/', '');
|
|
451
|
+
if (pkgInfo.version) {
|
|
452
|
+
installedVersions[pkgName] = pkgInfo.version;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
hash.update(`lock:${JSON.stringify(installedVersions, Object.keys(installedVersions).sort())}`);
|
|
241
458
|
}
|
|
242
459
|
catch {
|
|
243
|
-
// Ignorar
|
|
460
|
+
// Ignorar si no existe package-lock.json
|
|
244
461
|
}
|
|
462
|
+
// 3. ✨ NUEVO: Hash de timestamps críticos de node_modules
|
|
463
|
+
try {
|
|
464
|
+
const nodeModulesPath = path.join(cwd(), 'node_modules');
|
|
465
|
+
const nodeModulesStat = await stat(nodeModulesPath);
|
|
466
|
+
hash.update(`nmtime:${nodeModulesStat.mtimeMs}`);
|
|
467
|
+
// Verificar timestamps de dependencias críticas instaladas
|
|
468
|
+
const criticalDeps = Object.keys(deps).slice(0, 10); // Top 10 para performance
|
|
469
|
+
for (const dep of criticalDeps) {
|
|
470
|
+
try {
|
|
471
|
+
const depPath = path.join(nodeModulesPath, dep);
|
|
472
|
+
const depStat = await stat(depPath);
|
|
473
|
+
hash.update(`${dep}:${depStat.mtimeMs}`);
|
|
474
|
+
}
|
|
475
|
+
catch {
|
|
476
|
+
// Dependencia no instalada o error
|
|
477
|
+
hash.update(`${dep}:missing`);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
catch {
|
|
482
|
+
// node_modules no existe
|
|
483
|
+
hash.update('nmtime:none');
|
|
484
|
+
}
|
|
485
|
+
return hash.digest('hex').substring(0, 16);
|
|
245
486
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
487
|
+
catch (error) {
|
|
488
|
+
// Incluir información del error en el hash para debugging
|
|
489
|
+
return createHash('sha256')
|
|
490
|
+
.update(`error:${error instanceof Error ? error.message : 'unknown'}`)
|
|
491
|
+
.digest('hex')
|
|
492
|
+
.substring(0, 16);
|
|
250
493
|
}
|
|
251
494
|
}
|
|
252
|
-
|
|
253
|
-
|
|
495
|
+
/**
|
|
496
|
+
* Genera clave de cache granular que incluye todos los factores
|
|
497
|
+
*/
|
|
498
|
+
async generateCacheKey(filePath) {
|
|
499
|
+
const contentHash = await this.generateContentHash(filePath);
|
|
500
|
+
const configHash = this.generateConfigHash();
|
|
501
|
+
const envHash = this.generateEnvHash();
|
|
502
|
+
const dependencyHash = await this.generateDependencyHash();
|
|
503
|
+
// Usar | como separador para evitar problemas con rutas de Windows
|
|
504
|
+
return `${filePath}|${contentHash.substring(0, 12)}|${configHash}|${envHash}|${dependencyHash}`;
|
|
505
|
+
} /**
|
|
506
|
+
* Verifica si una entrada de cache es válida
|
|
507
|
+
*/
|
|
508
|
+
async isValid(filePath) {
|
|
509
|
+
const entry = this.cache.get(filePath);
|
|
510
|
+
if (!entry)
|
|
511
|
+
return false;
|
|
512
|
+
try {
|
|
513
|
+
// Verificar si el archivo de salida existe
|
|
514
|
+
await stat(entry.outputPath);
|
|
515
|
+
// Verificar si el contenido ha cambiado
|
|
516
|
+
const currentContentHash = await this.generateContentHash(filePath);
|
|
517
|
+
if (entry.contentHash !== currentContentHash) {
|
|
518
|
+
this.cache.delete(filePath);
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
// Verificar si la configuración ha cambiado
|
|
522
|
+
const currentConfigHash = this.generateConfigHash();
|
|
523
|
+
if (entry.configHash !== currentConfigHash) {
|
|
524
|
+
this.cache.delete(filePath);
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
// Verificar si las variables de entorno han cambiado
|
|
528
|
+
const currentEnvHash = this.generateEnvHash();
|
|
529
|
+
if (entry.envHash !== currentEnvHash) {
|
|
530
|
+
this.cache.delete(filePath);
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
// Verificar si las dependencias han cambiado
|
|
534
|
+
const currentDependencyHash = await this.generateDependencyHash();
|
|
535
|
+
if (entry.dependencyHash !== currentDependencyHash) {
|
|
536
|
+
this.cache.delete(filePath);
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
// Verificar tiempo de modificación como backup
|
|
540
|
+
const stats = await stat(filePath);
|
|
541
|
+
if (stats.mtimeMs > entry.mtime) {
|
|
542
|
+
this.cache.delete(filePath);
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
// Actualizar tiempo de uso para LRU
|
|
546
|
+
entry.lastUsed = Date.now();
|
|
547
|
+
return true;
|
|
548
|
+
}
|
|
549
|
+
catch {
|
|
550
|
+
// Si hay error verificando, eliminar del cache
|
|
551
|
+
this.cache.delete(filePath);
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
} /**
|
|
555
|
+
* Añade una entrada al cache
|
|
556
|
+
*/
|
|
557
|
+
async set(filePath, outputPath) {
|
|
558
|
+
try {
|
|
559
|
+
const stats = await stat(filePath);
|
|
560
|
+
const contentHash = await this.generateContentHash(filePath);
|
|
561
|
+
const configHash = this.generateConfigHash();
|
|
562
|
+
const envHash = this.generateEnvHash();
|
|
563
|
+
const dependencyHash = await this.generateDependencyHash();
|
|
564
|
+
const entry = {
|
|
565
|
+
contentHash,
|
|
566
|
+
configHash,
|
|
567
|
+
envHash,
|
|
568
|
+
dependencyHash,
|
|
569
|
+
mtime: stats.mtimeMs,
|
|
570
|
+
outputPath,
|
|
571
|
+
lastUsed: Date.now(),
|
|
572
|
+
size: stats.size,
|
|
573
|
+
};
|
|
574
|
+
// Aplicar límites de memoria y entradas antes de agregar
|
|
575
|
+
this.evictIfNeeded(entry.size);
|
|
576
|
+
this.cache.set(filePath, entry);
|
|
577
|
+
this.currentMemoryUsage += entry.size;
|
|
578
|
+
}
|
|
579
|
+
catch (error) {
|
|
580
|
+
// Si hay error, no cachear
|
|
581
|
+
console.warn(`Warning: No se pudo cachear ${filePath}:`, error);
|
|
582
|
+
}
|
|
254
583
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
584
|
+
/**
|
|
585
|
+
* Aplica política LRU para liberar espacio
|
|
586
|
+
*/
|
|
587
|
+
evictIfNeeded(newEntrySize) {
|
|
588
|
+
// Verificar límite de entradas
|
|
589
|
+
while (this.cache.size >= this.maxEntries) {
|
|
590
|
+
this.evictLRU();
|
|
591
|
+
}
|
|
592
|
+
// Verificar límite de memoria
|
|
593
|
+
while (this.currentMemoryUsage + newEntrySize > this.maxMemory &&
|
|
594
|
+
this.cache.size > 0) {
|
|
595
|
+
this.evictLRU();
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Elimina la entrada menos recientemente usada
|
|
600
|
+
*/
|
|
601
|
+
evictLRU() {
|
|
602
|
+
let oldestKey = '';
|
|
603
|
+
let oldestTime = Infinity;
|
|
604
|
+
for (const [key, entry] of this.cache) {
|
|
605
|
+
if (entry.lastUsed < oldestTime) {
|
|
606
|
+
oldestTime = entry.lastUsed;
|
|
607
|
+
oldestKey = key;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
if (oldestKey) {
|
|
611
|
+
const entry = this.cache.get(oldestKey);
|
|
612
|
+
if (entry) {
|
|
613
|
+
this.currentMemoryUsage -= entry.size;
|
|
614
|
+
this.cache.delete(oldestKey);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Carga el cache desde disco
|
|
620
|
+
*/
|
|
621
|
+
async load(cacheFile) {
|
|
622
|
+
try {
|
|
623
|
+
if (env.cleanCache === 'true') {
|
|
624
|
+
this.cache.clear();
|
|
625
|
+
this.currentMemoryUsage = 0;
|
|
626
|
+
try {
|
|
627
|
+
await unlink(cacheFile);
|
|
628
|
+
}
|
|
629
|
+
catch {
|
|
630
|
+
// Ignorar errores al eliminar el archivo
|
|
631
|
+
}
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
const cacheData = await readFile(cacheFile, 'utf-8');
|
|
635
|
+
const parsed = JSON.parse(cacheData);
|
|
636
|
+
// Validar y cargar entradas del cache
|
|
637
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
638
|
+
const entry = value;
|
|
639
|
+
if (entry.contentHash && entry.outputPath && entry.mtime) {
|
|
640
|
+
this.cache.set(key, entry);
|
|
641
|
+
this.currentMemoryUsage += entry.size || 0;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
catch {
|
|
646
|
+
// Cache file doesn't exist or is invalid, start fresh
|
|
647
|
+
this.cache.clear();
|
|
648
|
+
this.currentMemoryUsage = 0;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Guarda el cache a disco
|
|
653
|
+
*/
|
|
654
|
+
async save(cacheFile, cacheDir) {
|
|
655
|
+
try {
|
|
656
|
+
await mkdir(cacheDir, { recursive: true });
|
|
657
|
+
const cacheData = Object.fromEntries(this.cache);
|
|
658
|
+
await writeFile(cacheFile, JSON.stringify(cacheData, null, 2));
|
|
659
|
+
}
|
|
660
|
+
catch (error) {
|
|
661
|
+
console.warn('Warning: No se pudo guardar el cache:', error);
|
|
662
|
+
}
|
|
261
663
|
}
|
|
262
|
-
|
|
263
|
-
|
|
664
|
+
/**
|
|
665
|
+
* Limpia completamente el cache
|
|
666
|
+
*/
|
|
667
|
+
clear() {
|
|
668
|
+
this.cache.clear();
|
|
669
|
+
this.currentMemoryUsage = 0;
|
|
670
|
+
} /**
|
|
671
|
+
* Obtiene la ruta de salida para un archivo cacheado
|
|
672
|
+
*/
|
|
673
|
+
getOutputPath(filePath) {
|
|
674
|
+
const entry = this.cache.get(filePath);
|
|
675
|
+
return entry?.outputPath || '';
|
|
676
|
+
} /**
|
|
677
|
+
* Obtiene estadísticas del cache
|
|
678
|
+
*/
|
|
679
|
+
getStats() {
|
|
680
|
+
return {
|
|
681
|
+
entries: this.cache.size,
|
|
682
|
+
memoryUsage: this.currentMemoryUsage,
|
|
683
|
+
hitRate: 0, // Se calculará externamente
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
// ✨ ISSUE #3: Métodos de vigilancia y invalidación cascada
|
|
687
|
+
/**
|
|
688
|
+
* Inicializa vigilancia de package.json y node_modules
|
|
689
|
+
*/
|
|
690
|
+
async startDependencyWatching() {
|
|
691
|
+
if (this.isWatchingDependencies)
|
|
692
|
+
return;
|
|
693
|
+
try {
|
|
694
|
+
// Lazy load chokidar para evitar problemas de importación
|
|
695
|
+
const chokidar = await import('chokidar');
|
|
696
|
+
// Vigilar package.json
|
|
697
|
+
if (await this.fileExists(this.packageJsonPath)) {
|
|
698
|
+
const packageWatcher = chokidar.watch(this.packageJsonPath, {
|
|
699
|
+
persistent: false, // No mantener el proceso vivo
|
|
700
|
+
ignoreInitial: true,
|
|
701
|
+
});
|
|
702
|
+
packageWatcher.on('change', () => {
|
|
703
|
+
logger.info('📦 package.json modificado - invalidando cache de dependencias');
|
|
704
|
+
this.invalidateByDependencyChange();
|
|
705
|
+
});
|
|
706
|
+
this.fileWatchers.set('package.json', packageWatcher);
|
|
707
|
+
}
|
|
708
|
+
// Vigilar node_modules (solo cambios en el directorio raíz para performance)
|
|
709
|
+
if (await this.fileExists(this.nodeModulesPath)) {
|
|
710
|
+
const nodeModulesWatcher = chokidar.watch(this.nodeModulesPath, {
|
|
711
|
+
persistent: false,
|
|
712
|
+
ignoreInitial: true,
|
|
713
|
+
depth: 1, // Solo primer nivel para performance
|
|
714
|
+
ignored: /(^|[\/\\])\../, // Ignorar archivos ocultos
|
|
715
|
+
});
|
|
716
|
+
nodeModulesWatcher.on('addDir', (path) => {
|
|
717
|
+
logger.info(`📦 Nueva dependencia instalada: ${path.split(/[/\\]/).pop()}`);
|
|
718
|
+
this.invalidateByDependencyChange();
|
|
719
|
+
});
|
|
720
|
+
nodeModulesWatcher.on('unlinkDir', (path) => {
|
|
721
|
+
logger.info(`📦 Dependencia eliminada: ${path.split(/[/\\]/).pop()}`);
|
|
722
|
+
this.invalidateByDependencyChange();
|
|
723
|
+
});
|
|
724
|
+
this.fileWatchers.set('node_modules', nodeModulesWatcher);
|
|
725
|
+
}
|
|
726
|
+
this.isWatchingDependencies = true;
|
|
727
|
+
logger.info('🔍 Vigilancia de dependencias iniciada');
|
|
728
|
+
}
|
|
729
|
+
catch (error) {
|
|
730
|
+
logger.warn('⚠️ No se pudo iniciar vigilancia de dependencias:', error);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Detiene la vigilancia de dependencias
|
|
735
|
+
*/
|
|
736
|
+
async stopDependencyWatching() {
|
|
737
|
+
for (const [name, watcher] of this.fileWatchers) {
|
|
738
|
+
try {
|
|
739
|
+
await watcher.close();
|
|
740
|
+
logger.info(`🛑 Vigilancia detenida: ${name}`);
|
|
741
|
+
}
|
|
742
|
+
catch (error) {
|
|
743
|
+
logger.warn(`⚠️ Error cerrando watcher ${name}:`, error);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
this.fileWatchers.clear();
|
|
747
|
+
this.isWatchingDependencies = false;
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Registra dependencias de un archivo para invalidación cascada
|
|
751
|
+
*/
|
|
752
|
+
registerDependencies(filePath, dependencies) {
|
|
753
|
+
// Limpiar dependencias anteriores
|
|
754
|
+
const oldDeps = this.dependencyGraph.get(filePath);
|
|
755
|
+
if (oldDeps) {
|
|
756
|
+
for (const dep of oldDeps) {
|
|
757
|
+
const reverseDeps = this.reverseDependencyGraph.get(dep);
|
|
758
|
+
if (reverseDeps) {
|
|
759
|
+
reverseDeps.delete(filePath);
|
|
760
|
+
if (reverseDeps.size === 0) {
|
|
761
|
+
this.reverseDependencyGraph.delete(dep);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
// Registrar nuevas dependencias
|
|
767
|
+
const newDeps = new Set(dependencies);
|
|
768
|
+
this.dependencyGraph.set(filePath, newDeps);
|
|
769
|
+
for (const dep of newDeps) {
|
|
770
|
+
if (!this.reverseDependencyGraph.has(dep)) {
|
|
771
|
+
this.reverseDependencyGraph.set(dep, new Set());
|
|
772
|
+
}
|
|
773
|
+
this.reverseDependencyGraph.get(dep).add(filePath);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Invalida cache por cambios en dependencias
|
|
778
|
+
*/
|
|
779
|
+
invalidateByDependencyChange() {
|
|
780
|
+
let invalidatedCount = 0;
|
|
781
|
+
// Invalidar todos los archivos que dependen de dependencias externas
|
|
782
|
+
for (const [filePath] of this.cache) {
|
|
783
|
+
this.cache.delete(filePath);
|
|
784
|
+
invalidatedCount++;
|
|
785
|
+
}
|
|
786
|
+
// Limpiar grafos de dependencias
|
|
787
|
+
this.dependencyGraph.clear();
|
|
788
|
+
this.reverseDependencyGraph.clear();
|
|
789
|
+
this.currentMemoryUsage = 0;
|
|
790
|
+
logger.info(`🗑️ Cache invalidado: ${invalidatedCount} archivos (cambio en dependencias)`);
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Invalida cascada cuando un archivo específico cambia
|
|
794
|
+
*/
|
|
795
|
+
invalidateCascade(changedFile) {
|
|
796
|
+
const invalidated = [];
|
|
797
|
+
const toInvalidate = new Set([changedFile]);
|
|
798
|
+
// BFS para encontrar todos los archivos afectados
|
|
799
|
+
const queue = [changedFile];
|
|
800
|
+
while (queue.length > 0) {
|
|
801
|
+
const current = queue.shift();
|
|
802
|
+
const dependents = this.reverseDependencyGraph.get(current);
|
|
803
|
+
if (dependents) {
|
|
804
|
+
for (const dependent of dependents) {
|
|
805
|
+
if (!toInvalidate.has(dependent)) {
|
|
806
|
+
toInvalidate.add(dependent);
|
|
807
|
+
queue.push(dependent);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
// Invalidar archivos
|
|
813
|
+
for (const filePath of toInvalidate) {
|
|
814
|
+
if (this.cache.has(filePath)) {
|
|
815
|
+
const entry = this.cache.get(filePath);
|
|
816
|
+
this.currentMemoryUsage -= entry.size;
|
|
817
|
+
this.cache.delete(filePath);
|
|
818
|
+
invalidated.push(filePath);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
if (invalidated.length > 0) {
|
|
822
|
+
logger.info(`🔄 Invalidación cascada: ${invalidated.length} archivos afectados por ${changedFile}`);
|
|
823
|
+
}
|
|
824
|
+
return invalidated;
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Verifica si un archivo existe
|
|
828
|
+
*/
|
|
829
|
+
async fileExists(filePath) {
|
|
830
|
+
try {
|
|
831
|
+
await stat(filePath);
|
|
832
|
+
return true;
|
|
833
|
+
}
|
|
834
|
+
catch {
|
|
835
|
+
return false;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Obtiene estadísticas avanzadas del cache
|
|
840
|
+
*/
|
|
841
|
+
getAdvancedStats() {
|
|
842
|
+
return {
|
|
843
|
+
entries: this.cache.size,
|
|
844
|
+
memoryUsage: this.currentMemoryUsage,
|
|
845
|
+
hitRate: 0,
|
|
846
|
+
dependencyNodes: this.dependencyGraph.size,
|
|
847
|
+
watchingDependencies: this.isWatchingDependencies,
|
|
848
|
+
activeWatchers: this.fileWatchers.size,
|
|
849
|
+
};
|
|
264
850
|
}
|
|
265
851
|
}
|
|
852
|
+
// Instancia global del cache inteligente
|
|
853
|
+
const smartCache = new SmartCompilationCache();
|
|
854
|
+
const CACHE_DIR = path.join(path.resolve(env.PATH_PROY || cwd(), 'compiler'), '.cache');
|
|
855
|
+
const CACHE_FILE = path.join(CACHE_DIR, 'versacompile-cache.json');
|
|
856
|
+
async function loadCache() {
|
|
857
|
+
await smartCache.load(CACHE_FILE);
|
|
858
|
+
// ✨ ISSUE #3: Iniciar vigilancia de dependencias en modo watch
|
|
859
|
+
if (env.WATCH_MODE === 'true' ||
|
|
860
|
+
argv.includes('--watch') ||
|
|
861
|
+
argv.includes('-w')) {
|
|
862
|
+
await smartCache.startDependencyWatching();
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
async function saveCache() {
|
|
866
|
+
await smartCache.save(CACHE_FILE, CACHE_DIR);
|
|
867
|
+
}
|
|
266
868
|
// 🎯 Funciones del Sistema Unificado de Manejo de Errores
|
|
267
869
|
/**
|
|
268
870
|
* Registra un error de compilación en el sistema unificado
|
|
@@ -551,10 +1153,10 @@ class WatchModeOptimizer {
|
|
|
551
1153
|
if (cached && cached.mtime >= stats.mtimeMs) {
|
|
552
1154
|
resolve({ success: true, cached: true });
|
|
553
1155
|
return;
|
|
554
|
-
} // Configurar worker para modo watch
|
|
555
|
-
const {
|
|
556
|
-
const
|
|
557
|
-
|
|
1156
|
+
} // Configurar worker pool para modo watch
|
|
1157
|
+
const { TypeScriptWorkerPool } = await import('./typescript-worker-pool.js');
|
|
1158
|
+
const workerPool = TypeScriptWorkerPool.getInstance();
|
|
1159
|
+
workerPool.setMode('watch');
|
|
558
1160
|
const result = await compileFn(filePath);
|
|
559
1161
|
this.fileSystemCache.set(filePath, {
|
|
560
1162
|
mtime: stats.mtimeMs,
|
|
@@ -579,9 +1181,8 @@ async function compileJS(inPath, outPath, mode = 'individual') {
|
|
|
579
1181
|
// Si la ruta ya es absoluta, no la resolvamos de nuevo
|
|
580
1182
|
inPath = path.isAbsolute(inPath)
|
|
581
1183
|
? normalizeRuta(inPath)
|
|
582
|
-
: normalizeRuta(path.resolve(inPath));
|
|
583
|
-
|
|
584
|
-
const moduleManager = ModuleManager.getInstance();
|
|
1184
|
+
: normalizeRuta(path.resolve(inPath)); // 🚀 Usar OptimizedModuleManager para carga optimizada
|
|
1185
|
+
const moduleManager = OptimizedModuleManager.getInstance();
|
|
585
1186
|
// Timing de lectura
|
|
586
1187
|
let start = Date.now();
|
|
587
1188
|
const extension = path.extname(inPath);
|
|
@@ -729,9 +1330,6 @@ async function compileJS(inPath, outPath, mode = 'individual') {
|
|
|
729
1330
|
const destinationDir = path.dirname(outPath);
|
|
730
1331
|
await mkdir(destinationDir, { recursive: true });
|
|
731
1332
|
await writeFile(outPath, code, 'utf-8');
|
|
732
|
-
if (env.VERBOSE === 'true') {
|
|
733
|
-
logger.info(`\n📊 Timings para ${path.basename(inPath)}:`, JSON.stringify(timings));
|
|
734
|
-
}
|
|
735
1333
|
return {
|
|
736
1334
|
error: null,
|
|
737
1335
|
action: 'extension',
|
|
@@ -740,11 +1338,11 @@ async function compileJS(inPath, outPath, mode = 'individual') {
|
|
|
740
1338
|
export async function initCompile(ruta, compileTailwind = true, mode = 'individual') {
|
|
741
1339
|
try {
|
|
742
1340
|
// 🚀 Sistema de Carga Inteligente de Módulos
|
|
743
|
-
const moduleManager =
|
|
1341
|
+
const moduleManager = OptimizedModuleManager.getInstance();
|
|
744
1342
|
const fileExtension = path.extname(ruta);
|
|
745
1343
|
const fileExtensions = new Set([fileExtension]);
|
|
746
1344
|
// Inicializar módulos según el contexto
|
|
747
|
-
await moduleManager.
|
|
1345
|
+
await moduleManager.preloadForContext(mode === 'all' ? 'batch' : mode, fileExtensions);
|
|
748
1346
|
// Generar TailwindCSS si está habilitado
|
|
749
1347
|
if (compileTailwind && Boolean(env.TAILWIND)) {
|
|
750
1348
|
await moduleManager.ensureModuleLoaded('tailwind');
|
|
@@ -950,28 +1548,7 @@ function createProgressBar(current, total, width = 30) {
|
|
|
950
1548
|
}
|
|
951
1549
|
// Función helper para verificar si un archivo debe ser omitido por cache
|
|
952
1550
|
async function shouldSkipFile(filePath) {
|
|
953
|
-
|
|
954
|
-
const stats = await stat(filePath);
|
|
955
|
-
const cacheEntry = compilationCache.get(filePath);
|
|
956
|
-
if (!cacheEntry) {
|
|
957
|
-
return false; // No hay entrada en cache, debe compilarse
|
|
958
|
-
}
|
|
959
|
-
// Verificar si el archivo no ha cambiado desde la última compilación
|
|
960
|
-
if (stats.mtimeMs <= cacheEntry.mtime) {
|
|
961
|
-
// Verificar si el archivo de salida existe
|
|
962
|
-
try {
|
|
963
|
-
await stat(cacheEntry.outputPath);
|
|
964
|
-
return true; // Archivo existe y no ha cambiado, se puede omitir
|
|
965
|
-
}
|
|
966
|
-
catch {
|
|
967
|
-
return false; // Archivo de salida no existe, debe recompilarse
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
return false; // Archivo ha cambiado, debe recompilarse
|
|
971
|
-
}
|
|
972
|
-
catch {
|
|
973
|
-
return false; // Error al verificar, compilar por seguridad
|
|
974
|
-
}
|
|
1551
|
+
return await smartCache.isValid(filePath);
|
|
975
1552
|
}
|
|
976
1553
|
// Función para compilar archivos con límite de concurrencia
|
|
977
1554
|
async function compileWithConcurrencyLimit(files, maxConcurrency = 8) {
|
|
@@ -987,7 +1564,7 @@ async function compileWithConcurrencyLimit(files, maxConcurrency = 8) {
|
|
|
987
1564
|
const progressPercent = Math.round((currentTotal / total) * 100);
|
|
988
1565
|
if (progressPercent > lastProgressUpdate + 1 ||
|
|
989
1566
|
currentTotal === total) {
|
|
990
|
-
|
|
1567
|
+
stdout.write(`\r🚀 ${progressBar} [✅ ${completed} | ⏭️ ${skipped} | ❌ ${failed}]`);
|
|
991
1568
|
lastProgressUpdate = progressPercent;
|
|
992
1569
|
}
|
|
993
1570
|
}
|
|
@@ -1001,18 +1578,12 @@ async function compileWithConcurrencyLimit(files, maxConcurrency = 8) {
|
|
|
1001
1578
|
return {
|
|
1002
1579
|
success: true,
|
|
1003
1580
|
cached: true,
|
|
1004
|
-
output:
|
|
1581
|
+
output: smartCache.getOutputPath(file),
|
|
1005
1582
|
};
|
|
1006
1583
|
}
|
|
1007
|
-
const result = await initCompile(file, false, 'batch');
|
|
1008
|
-
// Actualizar cache si la compilación fue exitosa
|
|
1584
|
+
const result = await initCompile(file, false, 'batch'); // Actualizar cache si la compilación fue exitosa
|
|
1009
1585
|
if (result.success && result.output) {
|
|
1010
|
-
|
|
1011
|
-
compilationCache.set(file, {
|
|
1012
|
-
hash: '', // Se podría implementar hash del contenido si es necesario
|
|
1013
|
-
mtime: stats.mtimeMs,
|
|
1014
|
-
outputPath: result.output,
|
|
1015
|
-
});
|
|
1586
|
+
await smartCache.set(file, result.output);
|
|
1016
1587
|
}
|
|
1017
1588
|
completed++;
|
|
1018
1589
|
showProgress();
|
|
@@ -1094,7 +1665,15 @@ export async function initCompileAll() {
|
|
|
1094
1665
|
else {
|
|
1095
1666
|
maxConcurrency = Math.min(cpuCount * 2, 16);
|
|
1096
1667
|
}
|
|
1097
|
-
logger.info(`🚀 Compilando ${fileCount} archivos con concurrencia optimizada (${maxConcurrency} hilos)...`);
|
|
1668
|
+
logger.info(`🚀 Compilando ${fileCount} archivos con concurrencia optimizada (${maxConcurrency} hilos)...`); // Configurar worker pool para modo batch
|
|
1669
|
+
try {
|
|
1670
|
+
const { TypeScriptWorkerPool } = await import('./typescript-worker-pool.js');
|
|
1671
|
+
const workerPool = TypeScriptWorkerPool.getInstance();
|
|
1672
|
+
workerPool.setMode('batch');
|
|
1673
|
+
}
|
|
1674
|
+
catch {
|
|
1675
|
+
// Error silencioso en configuración del pool
|
|
1676
|
+
}
|
|
1098
1677
|
await compileWithConcurrencyLimit(filesToCompile, maxConcurrency);
|
|
1099
1678
|
// Guardar cache al final
|
|
1100
1679
|
await saveCache();
|