versacompiler 2.0.0 → 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 +345 -69
- package/dist/compiler/compile.js +427 -185
- package/dist/compiler/minify.js +199 -10
- package/dist/compiler/module-resolution-optimizer.js +844 -0
- package/dist/compiler/parser.js +179 -6
- package/dist/compiler/performance-monitor.js +192 -0
- package/dist/compiler/transform-optimizer.js +287 -0
- package/dist/compiler/transforms.js +121 -87
- 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 +479 -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 +316 -0
- package/dist/servicios/logger.js +1 -0
- package/dist/servicios/readConfig.js +1 -0
- package/dist/utils/module-resolver.js +8 -19
- package/dist/utils/promptUser.js +1 -1
- package/dist/utils/resolve-bin.js +28 -9
- package/package.json +7 -7
- package/dist/servicios/chokidar.js +0 -178
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript Worker Pool - Pool de workers para compilación paralela
|
|
3
|
+
* Reemplaza el worker único con múltiples workers para aprovecha la concurrencia real
|
|
4
|
+
*/
|
|
5
|
+
import * as os from 'node:os';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import * as process from 'node:process';
|
|
8
|
+
import { Worker } from 'node:worker_threads';
|
|
9
|
+
import { validateTypesWithLanguageService } from './typescript-sync-validator.js';
|
|
10
|
+
/**
|
|
11
|
+
* Pool de workers para compilación TypeScript paralela
|
|
12
|
+
* Distribuye las tareas entre múltiples workers para mayor rendimiento
|
|
13
|
+
*/
|
|
14
|
+
export class TypeScriptWorkerPool {
|
|
15
|
+
static instance;
|
|
16
|
+
workers = [];
|
|
17
|
+
poolSize;
|
|
18
|
+
workerPath;
|
|
19
|
+
initPromise = null;
|
|
20
|
+
isInitialized = false; // Configuración optimizada con reciclaje de workers
|
|
21
|
+
TASK_TIMEOUT = 10000; // 10 segundos por tarea
|
|
22
|
+
WORKER_INIT_TIMEOUT = 5000; // 5 segundos para inicializar
|
|
23
|
+
MAX_TASKS_PER_WORKER = 50; // Máximo de tareas por worker antes de reciclaje
|
|
24
|
+
WORKER_MEMORY_CHECK_INTERVAL = 100; // Verificar cada 100 tareas
|
|
25
|
+
// Métricas de rendimiento
|
|
26
|
+
totalTasks = 0;
|
|
27
|
+
completedTasks = 0;
|
|
28
|
+
failedTasks = 0;
|
|
29
|
+
constructor() {
|
|
30
|
+
// Determinar tamaño óptimo del pool
|
|
31
|
+
const cpuCount = os.cpus().length;
|
|
32
|
+
this.poolSize = Math.min(Math.max(cpuCount - 1, 2), 8); // Entre 2 y 8 workers
|
|
33
|
+
this.workerPath = path.join(process.env.PATH_PROY || path.join(process.cwd(), 'src'), 'compiler', 'typescript-worker-thread.cjs');
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Obtiene la instancia singleton del Worker Pool
|
|
37
|
+
*/
|
|
38
|
+
static getInstance() {
|
|
39
|
+
if (!TypeScriptWorkerPool.instance) {
|
|
40
|
+
TypeScriptWorkerPool.instance = new TypeScriptWorkerPool();
|
|
41
|
+
}
|
|
42
|
+
return TypeScriptWorkerPool.instance;
|
|
43
|
+
} /**
|
|
44
|
+
* Configura el modo de operación del pool
|
|
45
|
+
*/
|
|
46
|
+
setMode(mode) {
|
|
47
|
+
// Ajustar configuración según el modo
|
|
48
|
+
switch (mode) {
|
|
49
|
+
case 'batch':
|
|
50
|
+
// Para modo batch, optimizar para throughput
|
|
51
|
+
this.poolSize = Math.min(os.cpus().length, 12);
|
|
52
|
+
break;
|
|
53
|
+
case 'watch':
|
|
54
|
+
// Para modo watch, menos workers pero más responsivos
|
|
55
|
+
this.poolSize = Math.min(Math.max(os.cpus().length / 2, 2), 6);
|
|
56
|
+
break;
|
|
57
|
+
case 'individual':
|
|
58
|
+
// Para individual, pool pequeño
|
|
59
|
+
this.poolSize = Math.min(4, os.cpus().length);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Inicializa el pool de workers
|
|
65
|
+
*/
|
|
66
|
+
async initializePool() {
|
|
67
|
+
if (this.initPromise) {
|
|
68
|
+
return this.initPromise;
|
|
69
|
+
}
|
|
70
|
+
this.initPromise = this._performPoolInitialization();
|
|
71
|
+
return this.initPromise;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Realiza la inicialización del pool de workers
|
|
75
|
+
*/ async _performPoolInitialization() {
|
|
76
|
+
try {
|
|
77
|
+
// Verificar que el archivo del worker existe
|
|
78
|
+
const fs = await import('node:fs');
|
|
79
|
+
const exists = fs.existsSync(this.workerPath);
|
|
80
|
+
if (!exists) {
|
|
81
|
+
throw new Error(`Worker thread file not found: ${this.workerPath}`);
|
|
82
|
+
}
|
|
83
|
+
// Crear workers en paralelo
|
|
84
|
+
const workerPromises = Array.from({ length: this.poolSize }, (_, index) => this.createWorker(index));
|
|
85
|
+
this.workers = await Promise.all(workerPromises);
|
|
86
|
+
this.isInitialized = true;
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
this.isInitialized = false;
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Crea un worker individual
|
|
95
|
+
*/ async createWorker(workerId) {
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
try {
|
|
98
|
+
const worker = new Worker(this.workerPath, {
|
|
99
|
+
env: {
|
|
100
|
+
...process.env,
|
|
101
|
+
NODE_OPTIONS: '',
|
|
102
|
+
WORKER_ID: workerId.toString(),
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
const poolWorker = {
|
|
106
|
+
worker,
|
|
107
|
+
id: workerId,
|
|
108
|
+
busy: false,
|
|
109
|
+
pendingTasks: new Map(),
|
|
110
|
+
taskCounter: 0,
|
|
111
|
+
};
|
|
112
|
+
// Configurar listeners
|
|
113
|
+
this.setupWorkerListeners(poolWorker);
|
|
114
|
+
// Timeout para inicialización
|
|
115
|
+
const initTimeout = setTimeout(() => {
|
|
116
|
+
reject(new Error(`Worker ${workerId} initialization timeout`));
|
|
117
|
+
}, this.WORKER_INIT_TIMEOUT);
|
|
118
|
+
// Esperar que el worker esté listo
|
|
119
|
+
const checkReady = () => {
|
|
120
|
+
worker.postMessage({ type: 'ping' });
|
|
121
|
+
};
|
|
122
|
+
worker.on('message', function readyHandler(response) {
|
|
123
|
+
if (response.id === 'worker-ready' ||
|
|
124
|
+
response.message === 'pong') {
|
|
125
|
+
clearTimeout(initTimeout);
|
|
126
|
+
worker.off('message', readyHandler);
|
|
127
|
+
resolve(poolWorker);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
worker.on('error', error => {
|
|
131
|
+
clearTimeout(initTimeout);
|
|
132
|
+
reject(error);
|
|
133
|
+
});
|
|
134
|
+
// Intentar conectar
|
|
135
|
+
checkReady();
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
reject(error);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Configura los listeners para un worker individual
|
|
144
|
+
*/ setupWorkerListeners(poolWorker) {
|
|
145
|
+
const { worker } = poolWorker;
|
|
146
|
+
worker.on('message', (response) => {
|
|
147
|
+
try {
|
|
148
|
+
// Ignorar mensajes de control
|
|
149
|
+
if (response.id === 'worker-ready' ||
|
|
150
|
+
response.message === 'pong') {
|
|
151
|
+
return;
|
|
152
|
+
} // Buscar la tarea pendiente
|
|
153
|
+
const pendingTask = poolWorker.pendingTasks.get(response.id);
|
|
154
|
+
if (!pendingTask) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
// Limpiar timeout y eliminar tarea
|
|
158
|
+
clearTimeout(pendingTask.timeout);
|
|
159
|
+
poolWorker.pendingTasks.delete(response.id);
|
|
160
|
+
// Marcar worker como disponible si no tiene más tareas
|
|
161
|
+
if (poolWorker.pendingTasks.size === 0) {
|
|
162
|
+
poolWorker.busy = false;
|
|
163
|
+
}
|
|
164
|
+
// Procesar respuesta
|
|
165
|
+
if (response.success &&
|
|
166
|
+
response.diagnostics !== undefined &&
|
|
167
|
+
response.hasErrors !== undefined) {
|
|
168
|
+
this.completedTasks++;
|
|
169
|
+
pendingTask.resolve({
|
|
170
|
+
diagnostics: response.diagnostics,
|
|
171
|
+
hasErrors: response.hasErrors,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
this.failedTasks++;
|
|
176
|
+
const errorMessage = response.error || 'Error desconocido del worker';
|
|
177
|
+
pendingTask.reject(new Error(errorMessage));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// Error silencioso - no imprimir cada error
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
worker.on('error', async (error) => {
|
|
185
|
+
await this.handleWorkerError(poolWorker, error);
|
|
186
|
+
});
|
|
187
|
+
worker.on('exit', async (code) => {
|
|
188
|
+
await this.handleWorkerExit(poolWorker, code);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Maneja errores de un worker específico con cleanup completo
|
|
193
|
+
*/ async handleWorkerError(poolWorker, error) {
|
|
194
|
+
console.warn(`[WorkerPool] Manejando error del worker ${poolWorker.id}:`, error.message);
|
|
195
|
+
// 1. Rechazar todas las tareas pendientes con cleanup de timeouts
|
|
196
|
+
poolWorker.pendingTasks.forEach(task => {
|
|
197
|
+
clearTimeout(task.timeout);
|
|
198
|
+
this.failedTasks++;
|
|
199
|
+
task.reject(new Error(`Worker ${poolWorker.id} failed: ${error.message}`));
|
|
200
|
+
});
|
|
201
|
+
poolWorker.pendingTasks.clear();
|
|
202
|
+
// 2. Terminar worker correctamente para evitar memory leaks
|
|
203
|
+
try {
|
|
204
|
+
poolWorker.worker.removeAllListeners();
|
|
205
|
+
await poolWorker.worker.terminate();
|
|
206
|
+
}
|
|
207
|
+
catch (terminateError) {
|
|
208
|
+
console.error(`[WorkerPool] Error terminando worker ${poolWorker.id}:`, terminateError);
|
|
209
|
+
}
|
|
210
|
+
// 3. Marcar como no disponible
|
|
211
|
+
poolWorker.busy = false;
|
|
212
|
+
// 4. Recrear worker si el pool está activo
|
|
213
|
+
if (this.isInitialized && this.workers.length > 0) {
|
|
214
|
+
try {
|
|
215
|
+
const newWorker = await this.createWorker(poolWorker.id);
|
|
216
|
+
const index = this.workers.findIndex(w => w.id === poolWorker.id);
|
|
217
|
+
if (index !== -1) {
|
|
218
|
+
this.workers[index] = newWorker;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch (recreateError) {
|
|
222
|
+
console.error(`[WorkerPool] No se pudo recrear worker ${poolWorker.id}:`, recreateError);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
} /**
|
|
226
|
+
* Maneja la salida inesperada de un worker con cleanup completo
|
|
227
|
+
*/
|
|
228
|
+
async handleWorkerExit(poolWorker, code) {
|
|
229
|
+
console.warn(`[WorkerPool] Worker ${poolWorker.id} salió con código ${code}`);
|
|
230
|
+
// 1. Rechazar tareas pendientes con cleanup
|
|
231
|
+
poolWorker.pendingTasks.forEach(task => {
|
|
232
|
+
clearTimeout(task.timeout);
|
|
233
|
+
this.failedTasks++;
|
|
234
|
+
task.reject(new Error(`Worker ${poolWorker.id} exited with code ${code}`));
|
|
235
|
+
});
|
|
236
|
+
poolWorker.pendingTasks.clear();
|
|
237
|
+
poolWorker.busy = false;
|
|
238
|
+
// 2. Limpiar listeners para evitar memory leaks
|
|
239
|
+
try {
|
|
240
|
+
poolWorker.worker.removeAllListeners();
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
// Error silencioso en cleanup
|
|
244
|
+
}
|
|
245
|
+
// 3. Recrear worker si es necesario y el pool está activo
|
|
246
|
+
if (this.isInitialized && this.workers.length > 0) {
|
|
247
|
+
try {
|
|
248
|
+
await this.recreateWorker(poolWorker);
|
|
249
|
+
}
|
|
250
|
+
catch (recreateError) {
|
|
251
|
+
console.error(`[WorkerPool] Error recreando worker ${poolWorker.id}:`, recreateError);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Recrea un worker que falló
|
|
257
|
+
*/ async recreateWorker(failedWorker) {
|
|
258
|
+
try {
|
|
259
|
+
const newWorker = await this.createWorker(failedWorker.id);
|
|
260
|
+
// Reemplazar en el array
|
|
261
|
+
const index = this.workers.findIndex(w => w.id === failedWorker.id);
|
|
262
|
+
if (index !== -1) {
|
|
263
|
+
this.workers[index] = newWorker;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
// Error silencioso en recreación
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Recicla un worker para prevenir memory leaks
|
|
272
|
+
*/
|
|
273
|
+
async recycleWorker(poolWorker) {
|
|
274
|
+
try {
|
|
275
|
+
console.log(`[WorkerPool] Reciclando worker ${poolWorker.id} después de ${poolWorker.taskCounter} tareas`); // 1. Esperar a que termine tareas pendientes (timeout corto)
|
|
276
|
+
const maxWait = 2000; // 2 segundos máximo
|
|
277
|
+
const startTime = Date.now();
|
|
278
|
+
await new Promise(resolve => {
|
|
279
|
+
const checkPending = () => {
|
|
280
|
+
if (poolWorker.pendingTasks.size === 0 ||
|
|
281
|
+
Date.now() - startTime >= maxWait) {
|
|
282
|
+
resolve();
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
setTimeout(checkPending, 100);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
checkPending();
|
|
289
|
+
});
|
|
290
|
+
// 2. Si aún hay tareas pendientes, rechazarlas
|
|
291
|
+
if (poolWorker.pendingTasks.size > 0) {
|
|
292
|
+
poolWorker.pendingTasks.forEach(task => {
|
|
293
|
+
clearTimeout(task.timeout);
|
|
294
|
+
task.reject(new Error('Worker being recycled'));
|
|
295
|
+
});
|
|
296
|
+
poolWorker.pendingTasks.clear();
|
|
297
|
+
}
|
|
298
|
+
// 3. Terminar worker actual
|
|
299
|
+
poolWorker.worker.removeAllListeners();
|
|
300
|
+
await poolWorker.worker.terminate();
|
|
301
|
+
// 4. Crear nuevo worker
|
|
302
|
+
const newWorker = await this.createWorker(poolWorker.id);
|
|
303
|
+
// 5. Reemplazar en el array
|
|
304
|
+
const index = this.workers.findIndex(w => w.id === poolWorker.id);
|
|
305
|
+
if (index !== -1) {
|
|
306
|
+
this.workers[index] = newWorker;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
console.error(`[WorkerPool] Error reciclando worker ${poolWorker.id}:`, error);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Encuentra el worker menos ocupado
|
|
315
|
+
*/
|
|
316
|
+
findAvailableWorker() {
|
|
317
|
+
// Buscar worker completamente libre
|
|
318
|
+
const freeWorker = this.workers.find(w => !w.busy && w.pendingTasks.size === 0);
|
|
319
|
+
if (freeWorker) {
|
|
320
|
+
return freeWorker;
|
|
321
|
+
}
|
|
322
|
+
// Si no hay workers libres, buscar el menos ocupado
|
|
323
|
+
const leastBusyWorker = this.workers.reduce((least, current) => {
|
|
324
|
+
if (current.pendingTasks.size < least.pendingTasks.size) {
|
|
325
|
+
return current;
|
|
326
|
+
}
|
|
327
|
+
return least;
|
|
328
|
+
});
|
|
329
|
+
// Solo devolver si no está demasiado ocupado
|
|
330
|
+
if (leastBusyWorker.pendingTasks.size < 5) {
|
|
331
|
+
return leastBusyWorker;
|
|
332
|
+
}
|
|
333
|
+
return null; // Todos los workers están muy ocupados
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Realiza type checking usando el pool de workers
|
|
337
|
+
*/
|
|
338
|
+
async typeCheck(fileName, content, compilerOptions) {
|
|
339
|
+
// Asegurar que el pool esté inicializado
|
|
340
|
+
await this.initializePool();
|
|
341
|
+
if (!this.isInitialized) {
|
|
342
|
+
return this.typeCheckWithSyncFallback(fileName, content, compilerOptions);
|
|
343
|
+
}
|
|
344
|
+
// Buscar worker disponible
|
|
345
|
+
const availableWorker = this.findAvailableWorker();
|
|
346
|
+
if (!availableWorker) {
|
|
347
|
+
return this.typeCheckWithSyncFallback(fileName, content, compilerOptions);
|
|
348
|
+
}
|
|
349
|
+
try {
|
|
350
|
+
this.totalTasks++;
|
|
351
|
+
return await this.typeCheckWithWorker(availableWorker, fileName, content, compilerOptions);
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
return this.typeCheckWithSyncFallback(fileName, content, compilerOptions);
|
|
355
|
+
}
|
|
356
|
+
} /**
|
|
357
|
+
* Realiza type checking usando un worker específico con reciclaje automático
|
|
358
|
+
*/
|
|
359
|
+
async typeCheckWithWorker(poolWorker, fileName, content, compilerOptions) {
|
|
360
|
+
// Verificar si el worker necesita reciclaje por número de tareas
|
|
361
|
+
if (poolWorker.taskCounter >= this.MAX_TASKS_PER_WORKER) {
|
|
362
|
+
await this.recycleWorker(poolWorker);
|
|
363
|
+
}
|
|
364
|
+
return new Promise((resolve, reject) => {
|
|
365
|
+
const taskId = `task-${poolWorker.id}-${++poolWorker.taskCounter}-${Date.now()}`;
|
|
366
|
+
// Marcar worker como ocupado
|
|
367
|
+
poolWorker.busy = true;
|
|
368
|
+
// Configurar timeout para la tarea
|
|
369
|
+
const timeout = setTimeout(() => {
|
|
370
|
+
poolWorker.pendingTasks.delete(taskId);
|
|
371
|
+
if (poolWorker.pendingTasks.size === 0) {
|
|
372
|
+
poolWorker.busy = false;
|
|
373
|
+
}
|
|
374
|
+
reject(new Error(`Timeout en type checking para ${fileName}`));
|
|
375
|
+
}, this.TASK_TIMEOUT);
|
|
376
|
+
// Agregar tarea a la lista de pendientes del worker
|
|
377
|
+
poolWorker.pendingTasks.set(taskId, {
|
|
378
|
+
resolve,
|
|
379
|
+
reject,
|
|
380
|
+
timeout,
|
|
381
|
+
fileName,
|
|
382
|
+
startTime: Date.now(),
|
|
383
|
+
});
|
|
384
|
+
try {
|
|
385
|
+
// Crear mensaje para el worker
|
|
386
|
+
const message = {
|
|
387
|
+
id: taskId,
|
|
388
|
+
fileName,
|
|
389
|
+
content,
|
|
390
|
+
compilerOptions,
|
|
391
|
+
};
|
|
392
|
+
// Enviar mensaje al worker
|
|
393
|
+
poolWorker.worker.postMessage(message);
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
// Limpiar en caso de error
|
|
397
|
+
clearTimeout(timeout);
|
|
398
|
+
poolWorker.pendingTasks.delete(taskId);
|
|
399
|
+
if (poolWorker.pendingTasks.size === 0) {
|
|
400
|
+
poolWorker.busy = false;
|
|
401
|
+
}
|
|
402
|
+
reject(error);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Fallback síncrono para type checking
|
|
408
|
+
*/
|
|
409
|
+
typeCheckWithSyncFallback(fileName, content, compilerOptions) {
|
|
410
|
+
try {
|
|
411
|
+
return validateTypesWithLanguageService(fileName, content, compilerOptions);
|
|
412
|
+
}
|
|
413
|
+
catch {
|
|
414
|
+
return {
|
|
415
|
+
diagnostics: [],
|
|
416
|
+
hasErrors: false,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Cierra todos los workers del pool con cleanup completo
|
|
422
|
+
*/ async terminate() {
|
|
423
|
+
console.log('[WorkerPool] Cerrando pool de workers...');
|
|
424
|
+
// 1. Rechazar todas las tareas pendientes con cleanup
|
|
425
|
+
let totalPendingTasks = 0;
|
|
426
|
+
for (const poolWorker of this.workers) {
|
|
427
|
+
totalPendingTasks += poolWorker.pendingTasks.size;
|
|
428
|
+
poolWorker.pendingTasks.forEach(task => {
|
|
429
|
+
clearTimeout(task.timeout);
|
|
430
|
+
task.reject(new Error('Worker pool cerrado'));
|
|
431
|
+
});
|
|
432
|
+
poolWorker.pendingTasks.clear();
|
|
433
|
+
// Limpiar listeners para evitar memory leaks
|
|
434
|
+
try {
|
|
435
|
+
poolWorker.worker.removeAllListeners();
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
// Error silencioso en cleanup
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (totalPendingTasks > 0) {
|
|
442
|
+
console.log(`[WorkerPool] Se cancelaron ${totalPendingTasks} tareas pendientes`);
|
|
443
|
+
} // 2. Cerrar todos los workers con manejo de errores
|
|
444
|
+
const terminatePromises = this.workers.map(async (poolWorker) => {
|
|
445
|
+
try {
|
|
446
|
+
await poolWorker.worker.terminate();
|
|
447
|
+
}
|
|
448
|
+
catch (error) {
|
|
449
|
+
console.warn(`[WorkerPool] Error terminando worker ${poolWorker.id}:`, error);
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
await Promise.allSettled(terminatePromises);
|
|
453
|
+
// 3. Limpiar estado
|
|
454
|
+
this.workers = [];
|
|
455
|
+
this.isInitialized = false;
|
|
456
|
+
this.initPromise = null;
|
|
457
|
+
console.log(`[WorkerPool] Pool cerrado. Estadísticas finales: ${this.completedTasks} completadas, ${this.failedTasks} fallidas`);
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Obtiene estadísticas del pool
|
|
461
|
+
*/
|
|
462
|
+
getStats() {
|
|
463
|
+
const busyWorkers = this.workers.filter(w => w.busy).length;
|
|
464
|
+
const totalPendingTasks = this.workers.reduce((sum, w) => sum + w.pendingTasks.size, 0);
|
|
465
|
+
const successRate = this.totalTasks > 0
|
|
466
|
+
? Math.round((this.completedTasks / this.totalTasks) * 100)
|
|
467
|
+
: 0;
|
|
468
|
+
return {
|
|
469
|
+
poolSize: this.workers.length,
|
|
470
|
+
busyWorkers,
|
|
471
|
+
totalPendingTasks,
|
|
472
|
+
totalTasks: this.totalTasks,
|
|
473
|
+
completedTasks: this.completedTasks,
|
|
474
|
+
failedTasks: this.failedTasks,
|
|
475
|
+
successRate,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
//# sourceMappingURL=typescript-worker-pool.js.map
|
|
@@ -16,10 +16,10 @@ export class TypeScriptWorkerManager {
|
|
|
16
16
|
pendingTasks = new Map();
|
|
17
17
|
taskCounter = 0;
|
|
18
18
|
workerReady = false;
|
|
19
|
-
initPromise = null;
|
|
20
|
-
// Configuración del worker
|
|
21
|
-
WORKER_TIMEOUT = 30000; // 30 segundos timeout (incrementado)
|
|
19
|
+
initPromise = null; // Configuración del worker private readonly WORKER_TIMEOUT = 45000; // 45 segundos timeout (incrementado para manejar concurrencia)
|
|
22
20
|
MAX_RETRY_ATTEMPTS = 2;
|
|
21
|
+
MAX_CONCURRENT_TASKS = 20; // Aumentar límite de tareas concurrentes
|
|
22
|
+
TASK_TIMEOUT = 15000; // 15 segundos timeout por tarea individual
|
|
23
23
|
// NUEVOS: Gestión de modo y estado para optimización
|
|
24
24
|
currentMode = null;
|
|
25
25
|
constructor() { }
|
|
@@ -50,29 +50,38 @@ export class TypeScriptWorkerManager {
|
|
|
50
50
|
}
|
|
51
51
|
this.initPromise = this._performWorkerInitialization();
|
|
52
52
|
return this.initPromise;
|
|
53
|
-
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
54
55
|
* Realiza la inicialización del worker thread
|
|
55
|
-
*/
|
|
56
|
-
async _performWorkerInitialization() {
|
|
56
|
+
*/ async _performWorkerInitialization() {
|
|
57
57
|
try {
|
|
58
|
-
// Obtener ruta al worker thread (compatible con ES modules y Windows)
|
|
59
|
-
const workerPath = path.join(process.env.PATH_PROY || process.cwd(), 'compiler', 'typescript-worker-thread.cjs');
|
|
60
|
-
|
|
58
|
+
console.log('[WorkerManager] 🚀 Iniciando proceso de inicialización del worker...'); // Obtener ruta al worker thread (compatible con ES modules y Windows)
|
|
59
|
+
const workerPath = path.join(process.env.PATH_PROY || path.join(process.cwd(), 'src'), 'compiler', 'typescript-worker-thread.cjs');
|
|
60
|
+
console.log('[WorkerManager] 📂 Ruta del worker:', workerPath);
|
|
61
|
+
console.log('[WorkerManager] 🌍 PATH_PROY:', process.env.PATH_PROY);
|
|
62
|
+
console.log('[WorkerManager] 📁 CWD:', process.cwd());
|
|
63
|
+
// Verificar que el archivo existe
|
|
64
|
+
const fs = await import('node:fs');
|
|
65
|
+
const exists = fs.existsSync(workerPath);
|
|
66
|
+
console.log('[WorkerManager] 📋 Worker file exists:', exists);
|
|
61
67
|
// Crear el worker thread sin tsx para evitar dependencias externas
|
|
68
|
+
console.log('[WorkerManager] 🔧 Creando Worker...');
|
|
62
69
|
this.worker = new Worker(workerPath, {
|
|
63
70
|
env: {
|
|
64
71
|
...process.env,
|
|
65
72
|
NODE_OPTIONS: '', // Limpiar NODE_OPTIONS para evitar conflictos con tsx
|
|
66
73
|
},
|
|
67
74
|
});
|
|
75
|
+
console.log('[WorkerManager] ✅ Worker creado, configurando listeners...');
|
|
68
76
|
// Configurar listeners del worker
|
|
69
77
|
this.setupWorkerListeners();
|
|
78
|
+
console.log('[WorkerManager] ⏳ Esperando que el worker esté listo...');
|
|
70
79
|
// Esperar a que el worker esté listo
|
|
71
80
|
await this.waitForWorkerReady();
|
|
72
|
-
|
|
81
|
+
console.log('[WorkerManager] ✅ Worker inicializado exitosamente');
|
|
73
82
|
}
|
|
74
83
|
catch (error) {
|
|
75
|
-
console.error('[WorkerManager] Error inicializando worker:', error);
|
|
84
|
+
console.error('[WorkerManager] ❌ Error inicializando worker:', error);
|
|
76
85
|
this.worker = null;
|
|
77
86
|
this.workerReady = false;
|
|
78
87
|
throw error;
|
|
@@ -178,33 +187,55 @@ export class TypeScriptWorkerManager {
|
|
|
178
187
|
*/
|
|
179
188
|
generateTaskId() {
|
|
180
189
|
return `task-${++this.taskCounter}-${Date.now()}`;
|
|
181
|
-
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
182
192
|
* Realiza type checking usando el worker thread (con fallback síncrono)
|
|
183
193
|
* @param fileName - Nombre del archivo TypeScript
|
|
184
194
|
* @param content - Contenido del archivo
|
|
185
195
|
* @param compilerOptions - Opciones del compilador TypeScript
|
|
186
196
|
* @returns Resultado de la validación de tipos
|
|
187
|
-
*/
|
|
188
|
-
|
|
197
|
+
*/ async typeCheck(fileName, content, compilerOptions) {
|
|
198
|
+
// Limitar tareas concurrentes para evitar saturación
|
|
199
|
+
if (this.pendingTasks.size >= this.MAX_CONCURRENT_TASKS) {
|
|
200
|
+
return this.typeCheckWithSyncFallback(fileName, content, compilerOptions);
|
|
201
|
+
}
|
|
189
202
|
// En modo de testing o si hay problemas de inicialización, usar fallback directo
|
|
190
203
|
if (process.env.NODE_ENV === 'test' || !this.worker) {
|
|
204
|
+
if (!this.worker) {
|
|
205
|
+
try {
|
|
206
|
+
await this.initializeWorker();
|
|
207
|
+
if (this.worker && this.workerReady) {
|
|
208
|
+
console.log('[WorkerManager] ✅ Worker inicializado exitosamente, reintentando...');
|
|
209
|
+
return this.typeCheckWithWorker(fileName, content, compilerOptions);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
console.error('[WorkerManager] ❌ Error inicializando worker:', error);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
console.log('[WorkerManager] 🔄 Usando fallback síncrono (test mode o worker no disponible)');
|
|
191
217
|
return this.typeCheckWithSyncFallback(fileName, content, compilerOptions);
|
|
192
218
|
}
|
|
193
219
|
try {
|
|
194
|
-
|
|
220
|
+
console.log('[WorkerManager] 🚀 Intentando usar worker thread...');
|
|
221
|
+
// Intentar usar el worker thread con timeout más realista
|
|
195
222
|
const workerPromise = this.typeCheckWithWorker(fileName, content, compilerOptions);
|
|
196
223
|
const timeoutPromise = new Promise((resolve, reject) => {
|
|
197
224
|
setTimeout(() => {
|
|
225
|
+
console.log('[WorkerManager] ⏰ Worker timeout, usando fallback');
|
|
198
226
|
reject(new Error('Worker timeout - usando fallback'));
|
|
199
|
-
},
|
|
227
|
+
}, this.TASK_TIMEOUT); // Usar timeout por tarea más realista
|
|
200
228
|
});
|
|
201
|
-
|
|
229
|
+
console.log('[WorkerManager] ⏳ Esperando respuesta del worker...');
|
|
230
|
+
const result = await Promise.race([workerPromise, timeoutPromise]);
|
|
231
|
+
console.log('[WorkerManager] ✅ Worker completado exitosamente');
|
|
232
|
+
return result;
|
|
202
233
|
}
|
|
203
234
|
catch (workerError) {
|
|
204
235
|
const errorMessage = workerError instanceof Error
|
|
205
236
|
? workerError.message
|
|
206
237
|
: String(workerError);
|
|
207
|
-
console.warn('[WorkerManager] Error en worker, usando fallback síncrono:', errorMessage);
|
|
238
|
+
console.warn('[WorkerManager] ❌ Error en worker, usando fallback síncrono:', errorMessage);
|
|
208
239
|
// Fallback a validación síncrona
|
|
209
240
|
return this.typeCheckWithSyncFallback(fileName, content, compilerOptions);
|
|
210
241
|
}
|
|
@@ -219,12 +250,11 @@ export class TypeScriptWorkerManager {
|
|
|
219
250
|
throw new Error('Worker no disponible');
|
|
220
251
|
}
|
|
221
252
|
return new Promise((resolve, reject) => {
|
|
222
|
-
const taskId = this.generateTaskId();
|
|
223
|
-
// Configurar timeout para la tarea
|
|
253
|
+
const taskId = this.generateTaskId(); // Configurar timeout para la tarea
|
|
224
254
|
const timeout = setTimeout(() => {
|
|
225
255
|
this.pendingTasks.delete(taskId);
|
|
226
256
|
reject(new Error(`Timeout en type checking para ${fileName}`));
|
|
227
|
-
}, this.
|
|
257
|
+
}, this.TASK_TIMEOUT);
|
|
228
258
|
// Agregar tarea a la lista de pendientes
|
|
229
259
|
this.pendingTasks.set(taskId, {
|
|
230
260
|
resolve,
|