versacompiler 2.0.1 → 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/dist/compiler/compile.js +344 -7
- package/dist/compiler/module-resolution-optimizer.js +14 -36
- package/dist/compiler/transform-optimizer.js +111 -6
- package/dist/compiler/transforms.js +3 -31
- package/dist/compiler/typescript-worker-pool.js +387 -21
- package/dist/servicios/file-watcher.js +69 -18
- package/dist/utils/excluded-modules.js +36 -0
- package/dist/utils/module-resolver.js +1 -29
- package/package.json +2 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { env } from 'node:process';
|
|
3
3
|
import { logger } from '../servicios/logger.js';
|
|
4
|
+
import { EXCLUDED_MODULES } from '../utils/excluded-modules.js';
|
|
4
5
|
import { getModuleSubPath } from '../utils/module-resolver.js';
|
|
5
6
|
import { analyzeAndFormatMultipleErrors } from './error-reporter.js';
|
|
6
7
|
import { getOptimizedAliasPath, getOptimizedModulePath, } from './module-resolution-optimizer.js';
|
|
@@ -70,21 +71,6 @@ function isExternalModule(moduleRequest, pathAlias) {
|
|
|
70
71
|
} // NUEVA LÓGICA: Verificar PRIMERO si es un módulo excluido antes de verificar alias
|
|
71
72
|
// Esto es importante porque algunos módulos excluidos pueden tener nombres que
|
|
72
73
|
// coinciden con patrones de alias (como @vue/compiler-sfc con @/*)
|
|
73
|
-
const EXCLUDED_MODULES = new Set([
|
|
74
|
-
'vue/compiler-sfc',
|
|
75
|
-
'vue/dist/vue.runtime.esm-bundler',
|
|
76
|
-
'@vue/compiler-sfc',
|
|
77
|
-
'@vue/compiler-dom',
|
|
78
|
-
'@vue/runtime-core',
|
|
79
|
-
'@vue/runtime-dom',
|
|
80
|
-
'oxc-parser',
|
|
81
|
-
'oxc-parser/wasm',
|
|
82
|
-
'oxc-minify',
|
|
83
|
-
'oxc-minify/browser',
|
|
84
|
-
'@oxc-parser/binding-wasm32-wasi',
|
|
85
|
-
'@oxc-minify/binding-wasm32-wasi',
|
|
86
|
-
'typescript/lib/typescript',
|
|
87
|
-
]);
|
|
88
74
|
if (EXCLUDED_MODULES.has(moduleRequest)) {
|
|
89
75
|
return true;
|
|
90
76
|
} // Descartar alias conocidos
|
|
@@ -429,26 +415,12 @@ async function replaceAliasInStrings(code) {
|
|
|
429
415
|
// IMPORTANTE: Verificar si es un módulo excluido antes de transformar
|
|
430
416
|
if (isExternalModule(stringContent, pathAlias)) {
|
|
431
417
|
// Para strings que parecen ser módulos externos, verificar si están excluidos
|
|
432
|
-
const EXCLUDED_MODULES = new Set([
|
|
433
|
-
'vue/compiler-sfc',
|
|
434
|
-
'vue/dist/vue.runtime.esm-bundler',
|
|
435
|
-
'@vue/compiler-sfc',
|
|
436
|
-
'@vue/compiler-dom',
|
|
437
|
-
'@vue/runtime-core',
|
|
438
|
-
'@vue/runtime-dom',
|
|
439
|
-
'oxc-parser',
|
|
440
|
-
'oxc-parser/wasm',
|
|
441
|
-
'oxc-minify',
|
|
442
|
-
'oxc-minify/browser',
|
|
443
|
-
'@oxc-parser/binding-wasm32-wasi',
|
|
444
|
-
'@oxc-minify/binding-wasm32-wasi',
|
|
445
|
-
'typescript/lib/typescript',
|
|
446
|
-
]);
|
|
447
418
|
if (EXCLUDED_MODULES.has(stringContent)) {
|
|
448
419
|
// Es un módulo excluido, no transformar
|
|
449
420
|
continue;
|
|
450
421
|
}
|
|
451
|
-
}
|
|
422
|
+
}
|
|
423
|
+
// Reemplazar el alias con la ruta del target
|
|
452
424
|
const relativePath = stringContent.replace(aliasPattern, '');
|
|
453
425
|
// Construir la nueva ruta basada en la configuración del target
|
|
454
426
|
let newPath;
|
|
@@ -31,6 +31,131 @@ export class TypeScriptWorkerPool {
|
|
|
31
31
|
const cpuCount = os.cpus().length;
|
|
32
32
|
this.poolSize = Math.min(Math.max(cpuCount - 1, 2), 8); // Entre 2 y 8 workers
|
|
33
33
|
this.workerPath = path.join(process.env.PATH_PROY || path.join(process.cwd(), 'src'), 'compiler', 'typescript-worker-thread.cjs');
|
|
34
|
+
// ✨ ISSUE #4: Configurar monitoreo de memoria automático
|
|
35
|
+
this.startMemoryMonitoring();
|
|
36
|
+
}
|
|
37
|
+
// ✨ ISSUE #4: Métodos de control de memoria y timeouts
|
|
38
|
+
/**
|
|
39
|
+
* Inicia el monitoreo automático de memoria de workers
|
|
40
|
+
*/
|
|
41
|
+
startMemoryMonitoring() {
|
|
42
|
+
// Monitoreo cada 30 segundos
|
|
43
|
+
setInterval(() => {
|
|
44
|
+
this.checkWorkersMemory();
|
|
45
|
+
}, 30000);
|
|
46
|
+
// Limpieza de workers inactivos cada 5 minutos
|
|
47
|
+
setInterval(() => {
|
|
48
|
+
this.cleanupInactiveWorkers();
|
|
49
|
+
}, 300000);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Verifica el uso de memoria de todos los workers con medición real
|
|
53
|
+
*/
|
|
54
|
+
async checkWorkersMemory() {
|
|
55
|
+
const now = Date.now();
|
|
56
|
+
for (const poolWorker of this.workers) {
|
|
57
|
+
// Actualizar tiempo de última verificación
|
|
58
|
+
poolWorker.lastMemoryCheck = now;
|
|
59
|
+
try {
|
|
60
|
+
// ✨ ISSUE #4: Obtener memoria real del worker
|
|
61
|
+
const memoryInfo = await this.getWorkerMemoryUsage(poolWorker);
|
|
62
|
+
poolWorker.memoryUsage = memoryInfo.heapUsed;
|
|
63
|
+
// Verificar límites de memoria y reciclaje automático
|
|
64
|
+
if (this.shouldRecycleWorker(poolWorker)) {
|
|
65
|
+
const reason = this.getRecycleReason(poolWorker);
|
|
66
|
+
console.warn(`[WorkerPool] Worker ${poolWorker.id} requiere reciclaje: ${reason}`);
|
|
67
|
+
await this.recycleWorker(poolWorker);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
console.warn(`[WorkerPool] Error verificando memoria del worker ${poolWorker.id}:`, error);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Obtiene el uso real de memoria de un worker
|
|
77
|
+
*/
|
|
78
|
+
async getWorkerMemoryUsage(poolWorker) {
|
|
79
|
+
return new Promise(resolve => {
|
|
80
|
+
const timeout = setTimeout(() => {
|
|
81
|
+
// Fallback con estimación si no hay respuesta
|
|
82
|
+
resolve({
|
|
83
|
+
heapUsed: poolWorker.tasksProcessed * 2048, // 2KB por tarea
|
|
84
|
+
heapTotal: poolWorker.tasksProcessed * 3072, // 3KB total estimado
|
|
85
|
+
rss: poolWorker.tasksProcessed * 4096, // 4KB RSS estimado
|
|
86
|
+
});
|
|
87
|
+
}, 1000);
|
|
88
|
+
// Solicitar memoria real del worker
|
|
89
|
+
const memoryRequestId = `memory-${poolWorker.id}-${Date.now()}`;
|
|
90
|
+
const handler = (response) => {
|
|
91
|
+
if (response.id === memoryRequestId &&
|
|
92
|
+
response.type === 'memory-usage') {
|
|
93
|
+
clearTimeout(timeout);
|
|
94
|
+
poolWorker.worker.off('message', handler);
|
|
95
|
+
resolve({
|
|
96
|
+
heapUsed: response.memoryUsage?.heapUsed ||
|
|
97
|
+
poolWorker.tasksProcessed * 2048,
|
|
98
|
+
heapTotal: response.memoryUsage?.heapTotal ||
|
|
99
|
+
poolWorker.tasksProcessed * 3072,
|
|
100
|
+
rss: response.memoryUsage?.rss ||
|
|
101
|
+
poolWorker.tasksProcessed * 4096,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
poolWorker.worker.on('message', handler);
|
|
106
|
+
poolWorker.worker.postMessage({
|
|
107
|
+
type: 'get-memory-usage',
|
|
108
|
+
id: memoryRequestId,
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Obtiene la razón por la cual un worker debe ser reciclado
|
|
114
|
+
*/
|
|
115
|
+
getRecycleReason(poolWorker) {
|
|
116
|
+
const now = Date.now();
|
|
117
|
+
const MEMORY_LIMIT = 50 * 1024 * 1024; // 50MB
|
|
118
|
+
const TIME_LIMIT = 30 * 60 * 1000; // 30 minutos
|
|
119
|
+
const TASK_LIMIT = this.MAX_TASKS_PER_WORKER;
|
|
120
|
+
const reasons = [];
|
|
121
|
+
if (poolWorker.memoryUsage > MEMORY_LIMIT) {
|
|
122
|
+
reasons.push(`memoria excede ${Math.round(MEMORY_LIMIT / 1024 / 1024)}MB (actual: ${Math.round(poolWorker.memoryUsage / 1024 / 1024)}MB)`);
|
|
123
|
+
}
|
|
124
|
+
if (now - poolWorker.creationTime > TIME_LIMIT) {
|
|
125
|
+
reasons.push(`tiempo de vida excede ${Math.round(TIME_LIMIT / 60000)} minutos`);
|
|
126
|
+
}
|
|
127
|
+
if (poolWorker.tasksProcessed >= TASK_LIMIT) {
|
|
128
|
+
reasons.push(`tareas procesadas exceden ${TASK_LIMIT} (actual: ${poolWorker.tasksProcessed})`);
|
|
129
|
+
}
|
|
130
|
+
return reasons.join(', ');
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Limpia workers que han estado inactivos por mucho tiempo
|
|
134
|
+
*/
|
|
135
|
+
async cleanupInactiveWorkers() {
|
|
136
|
+
const now = Date.now();
|
|
137
|
+
const INACTIVE_TIMEOUT = 10 * 60 * 1000; // 10 minutos
|
|
138
|
+
for (const poolWorker of this.workers) {
|
|
139
|
+
const timeSinceLastActivity = now - poolWorker.lastActivityTime;
|
|
140
|
+
if (timeSinceLastActivity > INACTIVE_TIMEOUT &&
|
|
141
|
+
!poolWorker.busy &&
|
|
142
|
+
poolWorker.pendingTasks.size === 0) {
|
|
143
|
+
console.info(`[WorkerPool] Worker ${poolWorker.id} inactivo por ${Math.round(timeSinceLastActivity / 60000)} minutos, reciclando...`);
|
|
144
|
+
await this.recycleWorker(poolWorker);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Verifica si un worker debe ser reciclado por límites de memoria/tiempo
|
|
150
|
+
*/
|
|
151
|
+
shouldRecycleWorker(poolWorker) {
|
|
152
|
+
const now = Date.now();
|
|
153
|
+
const MEMORY_LIMIT = 50 * 1024 * 1024; // 50MB
|
|
154
|
+
const TIME_LIMIT = 30 * 60 * 1000; // 30 minutos
|
|
155
|
+
const TASK_LIMIT = this.MAX_TASKS_PER_WORKER;
|
|
156
|
+
return (poolWorker.memoryUsage > MEMORY_LIMIT ||
|
|
157
|
+
now - poolWorker.creationTime > TIME_LIMIT ||
|
|
158
|
+
poolWorker.tasksProcessed >= TASK_LIMIT);
|
|
34
159
|
}
|
|
35
160
|
/**
|
|
36
161
|
* Obtiene la instancia singleton del Worker Pool
|
|
@@ -40,7 +165,8 @@ export class TypeScriptWorkerPool {
|
|
|
40
165
|
TypeScriptWorkerPool.instance = new TypeScriptWorkerPool();
|
|
41
166
|
}
|
|
42
167
|
return TypeScriptWorkerPool.instance;
|
|
43
|
-
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
44
170
|
* Configura el modo de operación del pool
|
|
45
171
|
*/
|
|
46
172
|
setMode(mode) {
|
|
@@ -72,7 +198,8 @@ export class TypeScriptWorkerPool {
|
|
|
72
198
|
}
|
|
73
199
|
/**
|
|
74
200
|
* Realiza la inicialización del pool de workers
|
|
75
|
-
*/
|
|
201
|
+
*/
|
|
202
|
+
async _performPoolInitialization() {
|
|
76
203
|
try {
|
|
77
204
|
// Verificar que el archivo del worker existe
|
|
78
205
|
const fs = await import('node:fs');
|
|
@@ -92,7 +219,8 @@ export class TypeScriptWorkerPool {
|
|
|
92
219
|
}
|
|
93
220
|
/**
|
|
94
221
|
* Crea un worker individual
|
|
95
|
-
*/
|
|
222
|
+
*/
|
|
223
|
+
async createWorker(workerId) {
|
|
96
224
|
return new Promise((resolve, reject) => {
|
|
97
225
|
try {
|
|
98
226
|
const worker = new Worker(this.workerPath, {
|
|
@@ -108,6 +236,12 @@ export class TypeScriptWorkerPool {
|
|
|
108
236
|
busy: false,
|
|
109
237
|
pendingTasks: new Map(),
|
|
110
238
|
taskCounter: 0,
|
|
239
|
+
// ✨ ISSUE #4: Inicializar controles de memoria
|
|
240
|
+
memoryUsage: 0,
|
|
241
|
+
lastMemoryCheck: Date.now(),
|
|
242
|
+
tasksProcessed: 0,
|
|
243
|
+
creationTime: Date.now(),
|
|
244
|
+
lastActivityTime: Date.now(),
|
|
111
245
|
};
|
|
112
246
|
// Configurar listeners
|
|
113
247
|
this.setupWorkerListeners(poolWorker);
|
|
@@ -141,7 +275,8 @@ export class TypeScriptWorkerPool {
|
|
|
141
275
|
}
|
|
142
276
|
/**
|
|
143
277
|
* Configura los listeners para un worker individual
|
|
144
|
-
*/
|
|
278
|
+
*/
|
|
279
|
+
setupWorkerListeners(poolWorker) {
|
|
145
280
|
const { worker } = poolWorker;
|
|
146
281
|
worker.on('message', (response) => {
|
|
147
282
|
try {
|
|
@@ -149,7 +284,12 @@ export class TypeScriptWorkerPool {
|
|
|
149
284
|
if (response.id === 'worker-ready' ||
|
|
150
285
|
response.message === 'pong') {
|
|
151
286
|
return;
|
|
152
|
-
}
|
|
287
|
+
}
|
|
288
|
+
// ✨ ISSUE #4: Manejar reportes de memoria del worker
|
|
289
|
+
if (response.type === 'memory-usage') {
|
|
290
|
+
return; // Ya manejado en getWorkerMemoryUsage
|
|
291
|
+
}
|
|
292
|
+
// Buscar la tarea pendiente
|
|
153
293
|
const pendingTask = poolWorker.pendingTasks.get(response.id);
|
|
154
294
|
if (!pendingTask) {
|
|
155
295
|
return;
|
|
@@ -161,20 +301,26 @@ export class TypeScriptWorkerPool {
|
|
|
161
301
|
if (poolWorker.pendingTasks.size === 0) {
|
|
162
302
|
poolWorker.busy = false;
|
|
163
303
|
}
|
|
164
|
-
//
|
|
304
|
+
// Actualizar actividad del worker
|
|
305
|
+
poolWorker.lastActivityTime = Date.now();
|
|
306
|
+
// ✨ ISSUE #4: Manejo mejorado de errores de TypeScript
|
|
165
307
|
if (response.success &&
|
|
166
308
|
response.diagnostics !== undefined &&
|
|
167
309
|
response.hasErrors !== undefined) {
|
|
168
310
|
this.completedTasks++;
|
|
169
|
-
|
|
311
|
+
// Analizar y categorizar errores de TypeScript
|
|
312
|
+
const categorizedResult = this.categorizeTypeScriptErrors({
|
|
170
313
|
diagnostics: response.diagnostics,
|
|
171
314
|
hasErrors: response.hasErrors,
|
|
172
|
-
});
|
|
315
|
+
}, pendingTask.fileName);
|
|
316
|
+
pendingTask.resolve(categorizedResult);
|
|
173
317
|
}
|
|
174
318
|
else {
|
|
175
319
|
this.failedTasks++;
|
|
176
320
|
const errorMessage = response.error || 'Error desconocido del worker';
|
|
177
|
-
|
|
321
|
+
// ✨ Crear error categorizado
|
|
322
|
+
const categorizedError = this.createCategorizedError(errorMessage, pendingTask.fileName, response);
|
|
323
|
+
pendingTask.reject(categorizedError);
|
|
178
324
|
}
|
|
179
325
|
}
|
|
180
326
|
catch {
|
|
@@ -190,7 +336,8 @@ export class TypeScriptWorkerPool {
|
|
|
190
336
|
}
|
|
191
337
|
/**
|
|
192
338
|
* Maneja errores de un worker específico con cleanup completo
|
|
193
|
-
*/
|
|
339
|
+
*/
|
|
340
|
+
async handleWorkerError(poolWorker, error) {
|
|
194
341
|
console.warn(`[WorkerPool] Manejando error del worker ${poolWorker.id}:`, error.message);
|
|
195
342
|
// 1. Rechazar todas las tareas pendientes con cleanup de timeouts
|
|
196
343
|
poolWorker.pendingTasks.forEach(task => {
|
|
@@ -222,7 +369,8 @@ export class TypeScriptWorkerPool {
|
|
|
222
369
|
console.error(`[WorkerPool] No se pudo recrear worker ${poolWorker.id}:`, recreateError);
|
|
223
370
|
}
|
|
224
371
|
}
|
|
225
|
-
}
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
226
374
|
* Maneja la salida inesperada de un worker con cleanup completo
|
|
227
375
|
*/
|
|
228
376
|
async handleWorkerExit(poolWorker, code) {
|
|
@@ -254,7 +402,8 @@ export class TypeScriptWorkerPool {
|
|
|
254
402
|
}
|
|
255
403
|
/**
|
|
256
404
|
* Recrea un worker que falló
|
|
257
|
-
*/
|
|
405
|
+
*/
|
|
406
|
+
async recreateWorker(failedWorker) {
|
|
258
407
|
try {
|
|
259
408
|
const newWorker = await this.createWorker(failedWorker.id);
|
|
260
409
|
// Reemplazar en el array
|
|
@@ -272,7 +421,8 @@ export class TypeScriptWorkerPool {
|
|
|
272
421
|
*/
|
|
273
422
|
async recycleWorker(poolWorker) {
|
|
274
423
|
try {
|
|
275
|
-
console.log(`[WorkerPool] Reciclando worker ${poolWorker.id} después de ${poolWorker.taskCounter} tareas`);
|
|
424
|
+
console.log(`[WorkerPool] Reciclando worker ${poolWorker.id} después de ${poolWorker.taskCounter} tareas`);
|
|
425
|
+
// 1. Esperar a que termine tareas pendientes (timeout corto)
|
|
276
426
|
const maxWait = 2000; // 2 segundos máximo
|
|
277
427
|
const startTime = Date.now();
|
|
278
428
|
await new Promise(resolve => {
|
|
@@ -353,7 +503,8 @@ export class TypeScriptWorkerPool {
|
|
|
353
503
|
catch {
|
|
354
504
|
return this.typeCheckWithSyncFallback(fileName, content, compilerOptions);
|
|
355
505
|
}
|
|
356
|
-
}
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
357
508
|
* Realiza type checking usando un worker específico con reciclaje automático
|
|
358
509
|
*/
|
|
359
510
|
async typeCheckWithWorker(poolWorker, fileName, content, compilerOptions) {
|
|
@@ -363,16 +514,20 @@ export class TypeScriptWorkerPool {
|
|
|
363
514
|
}
|
|
364
515
|
return new Promise((resolve, reject) => {
|
|
365
516
|
const taskId = `task-${poolWorker.id}-${++poolWorker.taskCounter}-${Date.now()}`;
|
|
366
|
-
// Marcar worker como ocupado
|
|
517
|
+
// Marcar worker como ocupado y actualizar actividad
|
|
367
518
|
poolWorker.busy = true;
|
|
519
|
+
poolWorker.lastActivityTime = Date.now();
|
|
520
|
+
poolWorker.tasksProcessed++;
|
|
521
|
+
// ✨ ISSUE #4: Timeout dinámico basado en complejidad del archivo
|
|
522
|
+
const dynamicTimeout = this.calculateDynamicTimeout(fileName, content, compilerOptions);
|
|
368
523
|
// Configurar timeout para la tarea
|
|
369
524
|
const timeout = setTimeout(() => {
|
|
370
525
|
poolWorker.pendingTasks.delete(taskId);
|
|
371
526
|
if (poolWorker.pendingTasks.size === 0) {
|
|
372
527
|
poolWorker.busy = false;
|
|
373
528
|
}
|
|
374
|
-
reject(new Error(`Timeout en type checking para ${fileName}`));
|
|
375
|
-
},
|
|
529
|
+
reject(new Error(`Timeout (${dynamicTimeout}ms) en type checking para ${fileName} - archivo complejo detectado`));
|
|
530
|
+
}, dynamicTimeout);
|
|
376
531
|
// Agregar tarea a la lista de pendientes del worker
|
|
377
532
|
poolWorker.pendingTasks.set(taskId, {
|
|
378
533
|
resolve,
|
|
@@ -382,12 +537,16 @@ export class TypeScriptWorkerPool {
|
|
|
382
537
|
startTime: Date.now(),
|
|
383
538
|
});
|
|
384
539
|
try {
|
|
385
|
-
// Crear mensaje para el worker
|
|
540
|
+
// Crear mensaje para el worker con configuración de timeout
|
|
386
541
|
const message = {
|
|
387
542
|
id: taskId,
|
|
388
543
|
fileName,
|
|
389
544
|
content,
|
|
390
|
-
compilerOptions
|
|
545
|
+
compilerOptions: {
|
|
546
|
+
...compilerOptions,
|
|
547
|
+
// ✨ Añadir timeout al worker para manejo interno
|
|
548
|
+
_workerTimeout: dynamicTimeout - 1000, // 1 segundo menos para cleanup interno
|
|
549
|
+
},
|
|
391
550
|
};
|
|
392
551
|
// Enviar mensaje al worker
|
|
393
552
|
poolWorker.worker.postMessage(message);
|
|
@@ -403,6 +562,59 @@ export class TypeScriptWorkerPool {
|
|
|
403
562
|
}
|
|
404
563
|
});
|
|
405
564
|
}
|
|
565
|
+
/**
|
|
566
|
+
* Calcula un timeout dinámico basado en la complejidad del archivo TypeScript
|
|
567
|
+
*/
|
|
568
|
+
calculateDynamicTimeout(fileName, content, compilerOptions) {
|
|
569
|
+
const baseTimeout = this.TASK_TIMEOUT; // 10 segundos base
|
|
570
|
+
let complexityMultiplier = 1;
|
|
571
|
+
// Factor 1: Tamaño del archivo
|
|
572
|
+
const contentLength = content.length;
|
|
573
|
+
if (contentLength > 100000) {
|
|
574
|
+
// Archivos > 100KB
|
|
575
|
+
complexityMultiplier += 1.5;
|
|
576
|
+
}
|
|
577
|
+
else if (contentLength > 50000) {
|
|
578
|
+
// Archivos > 50KB
|
|
579
|
+
complexityMultiplier += 1;
|
|
580
|
+
}
|
|
581
|
+
else if (contentLength > 20000) {
|
|
582
|
+
// Archivos > 20KB
|
|
583
|
+
complexityMultiplier += 0.5;
|
|
584
|
+
}
|
|
585
|
+
// Factor 2: Complejidad sintáctica
|
|
586
|
+
const importCount = (content.match(/^import\s+/gm) || []).length;
|
|
587
|
+
const typeCount = (content.match(/\btype\s+\w+/g) || []).length;
|
|
588
|
+
const interfaceCount = (content.match(/\binterface\s+\w+/g) || [])
|
|
589
|
+
.length;
|
|
590
|
+
const genericCount = (content.match(/<[^>]*>/g) || []).length;
|
|
591
|
+
const complexConstructs = importCount + typeCount + interfaceCount + genericCount * 0.5;
|
|
592
|
+
if (complexConstructs > 100) {
|
|
593
|
+
complexityMultiplier += 2;
|
|
594
|
+
}
|
|
595
|
+
else if (complexConstructs > 50) {
|
|
596
|
+
complexityMultiplier += 1;
|
|
597
|
+
}
|
|
598
|
+
else if (complexConstructs > 20) {
|
|
599
|
+
complexityMultiplier += 0.5;
|
|
600
|
+
}
|
|
601
|
+
// Factor 3: Configuración de TypeScript estricta
|
|
602
|
+
if (compilerOptions?.strict || compilerOptions?.noImplicitAny) {
|
|
603
|
+
complexityMultiplier += 0.3;
|
|
604
|
+
}
|
|
605
|
+
// Factor 4: Extensión de archivo compleja (.d.ts, .vue.ts, etc.)
|
|
606
|
+
if (fileName.includes('.d.ts')) {
|
|
607
|
+
complexityMultiplier += 1; // Los archivos de definición son más complejos
|
|
608
|
+
}
|
|
609
|
+
else if (fileName.includes('.vue')) {
|
|
610
|
+
complexityMultiplier += 0.5; // Los archivos Vue requieren procesamiento adicional
|
|
611
|
+
}
|
|
612
|
+
// Aplicar límites razonables
|
|
613
|
+
complexityMultiplier = Math.min(complexityMultiplier, 5); // Máximo 5x el timeout base
|
|
614
|
+
complexityMultiplier = Math.max(complexityMultiplier, 0.5); // Mínimo 0.5x el timeout base
|
|
615
|
+
const finalTimeout = Math.round(baseTimeout * complexityMultiplier);
|
|
616
|
+
return Math.min(finalTimeout, 60000); // Máximo absoluto de 60 segundos
|
|
617
|
+
}
|
|
406
618
|
/**
|
|
407
619
|
* Fallback síncrono para type checking
|
|
408
620
|
*/
|
|
@@ -419,7 +631,8 @@ export class TypeScriptWorkerPool {
|
|
|
419
631
|
}
|
|
420
632
|
/**
|
|
421
633
|
* Cierra todos los workers del pool con cleanup completo
|
|
422
|
-
*/
|
|
634
|
+
*/
|
|
635
|
+
async terminate() {
|
|
423
636
|
console.log('[WorkerPool] Cerrando pool de workers...');
|
|
424
637
|
// 1. Rechazar todas las tareas pendientes con cleanup
|
|
425
638
|
let totalPendingTasks = 0;
|
|
@@ -440,7 +653,8 @@ export class TypeScriptWorkerPool {
|
|
|
440
653
|
}
|
|
441
654
|
if (totalPendingTasks > 0) {
|
|
442
655
|
console.log(`[WorkerPool] Se cancelaron ${totalPendingTasks} tareas pendientes`);
|
|
443
|
-
}
|
|
656
|
+
}
|
|
657
|
+
// 2. Cerrar todos los workers con manejo de errores
|
|
444
658
|
const terminatePromises = this.workers.map(async (poolWorker) => {
|
|
445
659
|
try {
|
|
446
660
|
await poolWorker.worker.terminate();
|
|
@@ -475,5 +689,157 @@ export class TypeScriptWorkerPool {
|
|
|
475
689
|
successRate,
|
|
476
690
|
};
|
|
477
691
|
}
|
|
692
|
+
// ✨ ISSUE #4: Métodos de categorización de errores de TypeScript
|
|
693
|
+
/**
|
|
694
|
+
* Categoriza y mejora los errores de TypeScript para mejor debugging
|
|
695
|
+
*/
|
|
696
|
+
categorizeTypeScriptErrors(result, fileName) {
|
|
697
|
+
if (!result.hasErrors || !result.diagnostics?.length) {
|
|
698
|
+
return result;
|
|
699
|
+
}
|
|
700
|
+
const categorizedDiagnostics = result.diagnostics.map(diagnostic => {
|
|
701
|
+
// Añadir metadatos útiles para debugging
|
|
702
|
+
const enhanced = {
|
|
703
|
+
...diagnostic,
|
|
704
|
+
_category: this.getErrorCategory(diagnostic),
|
|
705
|
+
_severity: this.getErrorSeverity(diagnostic),
|
|
706
|
+
_fileName: fileName,
|
|
707
|
+
_timestamp: Date.now(),
|
|
708
|
+
};
|
|
709
|
+
return enhanced;
|
|
710
|
+
});
|
|
711
|
+
return {
|
|
712
|
+
...result,
|
|
713
|
+
diagnostics: categorizedDiagnostics,
|
|
714
|
+
// Añadir estadísticas de errores
|
|
715
|
+
_errorStats: this.calculateErrorStats(categorizedDiagnostics),
|
|
716
|
+
};
|
|
717
|
+
} /**
|
|
718
|
+
* Determina la categoría de un error de TypeScript
|
|
719
|
+
*/
|
|
720
|
+
getErrorCategory(diagnostic) {
|
|
721
|
+
const code = diagnostic.code;
|
|
722
|
+
// Categorización basada en códigos de error comunes
|
|
723
|
+
if ([2304, 2339, 2346].includes(code)) {
|
|
724
|
+
return 'MISSING_DECLARATION'; // No puede encontrar nombre/propiedad
|
|
725
|
+
}
|
|
726
|
+
else if ([2322, 2322, 2345].includes(code)) {
|
|
727
|
+
return 'TYPE_MISMATCH'; // Error de tipos
|
|
728
|
+
}
|
|
729
|
+
else if ([2307, 2317].includes(code)) {
|
|
730
|
+
return 'MODULE_RESOLUTION'; // Error de resolución de módulos
|
|
731
|
+
}
|
|
732
|
+
else if ([2552, 2551].includes(code)) {
|
|
733
|
+
return 'PROPERTY_ACCESS'; // Error de acceso a propiedades
|
|
734
|
+
}
|
|
735
|
+
else if (code >= 1000 && code < 2000) {
|
|
736
|
+
return 'SYNTAX_ERROR'; // Errores de sintaxis
|
|
737
|
+
}
|
|
738
|
+
else if (code >= 2000 && code < 3000) {
|
|
739
|
+
return 'SEMANTIC_ERROR'; // Errores semánticos
|
|
740
|
+
}
|
|
741
|
+
else if (code >= 4000) {
|
|
742
|
+
return 'CONFIG_ERROR'; // Errores de configuración
|
|
743
|
+
}
|
|
744
|
+
return 'OTHER';
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Determina la severidad de un error de TypeScript
|
|
748
|
+
*/
|
|
749
|
+
getErrorSeverity(diagnostic) {
|
|
750
|
+
const category = diagnostic.category;
|
|
751
|
+
switch (category) {
|
|
752
|
+
case 1:
|
|
753
|
+
return 'ERROR'; // typescript.DiagnosticCategory.Error
|
|
754
|
+
case 2:
|
|
755
|
+
return 'WARNING'; // typescript.DiagnosticCategory.Warning
|
|
756
|
+
case 3:
|
|
757
|
+
return 'INFO'; // typescript.DiagnosticCategory.Message
|
|
758
|
+
default:
|
|
759
|
+
return 'ERROR';
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Calcula estadísticas de errores para análisis
|
|
764
|
+
*/
|
|
765
|
+
calculateErrorStats(diagnostics) {
|
|
766
|
+
const stats = {
|
|
767
|
+
totalErrors: diagnostics.length,
|
|
768
|
+
errorsByCategory: {},
|
|
769
|
+
errorsBySeverity: {},
|
|
770
|
+
mostCommonError: null,
|
|
771
|
+
};
|
|
772
|
+
// Contar por categoría y severidad
|
|
773
|
+
diagnostics.forEach(diag => {
|
|
774
|
+
const category = diag._category || 'OTHER';
|
|
775
|
+
const severity = diag._severity || 'ERROR';
|
|
776
|
+
stats.errorsByCategory[category] =
|
|
777
|
+
(stats.errorsByCategory[category] || 0) + 1;
|
|
778
|
+
stats.errorsBySeverity[severity] =
|
|
779
|
+
(stats.errorsBySeverity[severity] || 0) + 1;
|
|
780
|
+
}); // Encontrar el error más común
|
|
781
|
+
const errorCounts = {};
|
|
782
|
+
diagnostics.forEach(diag => {
|
|
783
|
+
const code = String(diag.code);
|
|
784
|
+
errorCounts[code] = (errorCounts[code] || 0) + 1;
|
|
785
|
+
});
|
|
786
|
+
const errorCountKeys = Object.keys(errorCounts);
|
|
787
|
+
if (errorCountKeys.length > 0) {
|
|
788
|
+
const mostCommonCode = errorCountKeys.reduce((a, b) => (errorCounts[a] || 0) > (errorCounts[b] || 0) ? a : b);
|
|
789
|
+
stats.mostCommonError = {
|
|
790
|
+
code: mostCommonCode,
|
|
791
|
+
count: errorCounts[mostCommonCode],
|
|
792
|
+
message: diagnostics.find(d => String(d.code) === mostCommonCode)?.messageText,
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
return stats;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Crea un error categorizado con información de contexto
|
|
799
|
+
*/
|
|
800
|
+
createCategorizedError(errorMessage, fileName, response) {
|
|
801
|
+
const error = new Error(errorMessage);
|
|
802
|
+
// Añadir metadatos del error
|
|
803
|
+
error.fileName = fileName;
|
|
804
|
+
error.timestamp = Date.now();
|
|
805
|
+
error.workerResponse = response;
|
|
806
|
+
error.category = this.categorizeGenericError(errorMessage);
|
|
807
|
+
error.isRecoverable = this.isRecoverableError(errorMessage);
|
|
808
|
+
return error;
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Categoriza errores genéricos del worker
|
|
812
|
+
*/
|
|
813
|
+
categorizeGenericError(errorMessage) {
|
|
814
|
+
if (errorMessage.includes('timeout') ||
|
|
815
|
+
errorMessage.includes('Timeout')) {
|
|
816
|
+
return 'TIMEOUT';
|
|
817
|
+
}
|
|
818
|
+
else if (errorMessage.includes('memory') ||
|
|
819
|
+
errorMessage.includes('Memory')) {
|
|
820
|
+
return 'MEMORY';
|
|
821
|
+
}
|
|
822
|
+
else if (errorMessage.includes('Worker') &&
|
|
823
|
+
errorMessage.includes('exited')) {
|
|
824
|
+
return 'WORKER_CRASH';
|
|
825
|
+
}
|
|
826
|
+
else if (errorMessage.includes('initialization') ||
|
|
827
|
+
errorMessage.includes('init')) {
|
|
828
|
+
return 'INITIALIZATION';
|
|
829
|
+
}
|
|
830
|
+
return 'UNKNOWN';
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Determina si un error es recuperable
|
|
834
|
+
*/
|
|
835
|
+
isRecoverableError(errorMessage) {
|
|
836
|
+
const recoverablePatterns = [
|
|
837
|
+
'timeout',
|
|
838
|
+
'Worker being recycled',
|
|
839
|
+
'Worker pool cerrado',
|
|
840
|
+
'temporary',
|
|
841
|
+
];
|
|
842
|
+
return recoverablePatterns.some(pattern => errorMessage.toLowerCase().includes(pattern.toLowerCase()));
|
|
843
|
+
}
|
|
478
844
|
}
|
|
479
845
|
//# sourceMappingURL=typescript-worker-pool.js.map
|