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.
@@ -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
- // console.log('[WorkerManager] Inicializando worker en:', workerPath);
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
- // console.log('[WorkerManager] Worker inicializado exitosamente');
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
- async typeCheck(fileName, content, compilerOptions) {
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
- // Intentar usar el worker thread con timeout más corto
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
- }, 5000); // 5 segundos max para worker
227
+ }, this.TASK_TIMEOUT); // Usar timeout por tarea más realista
200
228
  });
201
- return await Promise.race([workerPromise, timeoutPromise]);
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.WORKER_TIMEOUT);
257
+ }, this.TASK_TIMEOUT);
228
258
  // Agregar tarea a la lista de pendientes
229
259
  this.pendingTasks.set(taskId, {
230
260
  resolve,
@@ -1,3 +1,4 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import path from 'node:path';
2
3
  import * as vCompiler from 'vue/compiler-sfc';
3
4
  import { logger } from '../servicios/logger.js';
@@ -10,6 +11,123 @@ async function loadChalk() {
10
11
  }
11
12
  return chalk;
12
13
  }
14
+ class VueHMRInjectionCache {
15
+ static instance;
16
+ cache = new Map();
17
+ MAX_CACHE_SIZE = 100;
18
+ CACHE_TTL = 5 * 60 * 1000; // 5 minutos
19
+ static getInstance() {
20
+ if (!VueHMRInjectionCache.instance) {
21
+ VueHMRInjectionCache.instance = new VueHMRInjectionCache();
22
+ }
23
+ return VueHMRInjectionCache.instance;
24
+ }
25
+ /**
26
+ * Genera un hash del contenido original para detectar cambios
27
+ */
28
+ generateContentHash(data) {
29
+ return createHash('md5').update(data).digest('hex');
30
+ }
31
+ /**
32
+ * Obtiene código HMR inyectado desde cache o lo genera
33
+ */
34
+ getOrGenerateHMRInjection(originalData, fileName) {
35
+ const contentHash = this.generateContentHash(originalData);
36
+ const cacheKey = `${fileName}:${contentHash}`;
37
+ // Verificar cache
38
+ const cached = this.cache.get(cacheKey);
39
+ if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {
40
+ return {
41
+ injectedData: cached.injectedCode,
42
+ cached: true,
43
+ };
44
+ }
45
+ // Generar nueva inyección HMR
46
+ const vueImportPattern = /import\s*\{[^}]*\bref\b[^}]*\}\s*from\s*['"]vue['"]/;
47
+ const hasRefImport = vueImportPattern.test(originalData);
48
+ const varContent = `
49
+ ${hasRefImport ? '' : 'import { ref } from "/node_modules/vue/dist/vue.esm-browser.js";'}
50
+ const versaComponentKey = ref(0);
51
+ `;
52
+ let injectedData;
53
+ const ifExistScript = originalData.includes('<script');
54
+ if (!ifExistScript) {
55
+ injectedData =
56
+ `<script setup lang="ts">${varContent}</script>/n` +
57
+ originalData;
58
+ }
59
+ else {
60
+ injectedData = originalData.replace(/(<script.*?>)/, `$1${varContent}`);
61
+ }
62
+ // Inyectar :key en el template
63
+ injectedData = injectedData.replace(/(<template[^>]*>[\s\S]*?)(<(\w+)([^>]*?))(\/?>)/, (match, p1, p2, p3, p4, p5) => {
64
+ if (p4.includes(':key=') || p4.includes('key=')) {
65
+ return match;
66
+ }
67
+ const isSelfClosing = p5 === '/>';
68
+ if (isSelfClosing) {
69
+ return `${p1}<${p3}${p4} :key="versaComponentKey" />`;
70
+ }
71
+ else {
72
+ return `${p1}<${p3}${p4} :key="versaComponentKey">`;
73
+ }
74
+ });
75
+ // Cachear resultado
76
+ this.cache.set(cacheKey, {
77
+ contentHash,
78
+ injectedCode: injectedData,
79
+ hasRefImport,
80
+ timestamp: Date.now(),
81
+ });
82
+ // Limpiar cache si es necesario
83
+ this.evictIfNeeded();
84
+ return {
85
+ injectedData,
86
+ cached: false,
87
+ };
88
+ }
89
+ /**
90
+ * Limpia entradas de cache cuando se excede el límite
91
+ */
92
+ evictIfNeeded() {
93
+ if (this.cache.size <= this.MAX_CACHE_SIZE)
94
+ return;
95
+ const entries = Array.from(this.cache.entries());
96
+ entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
97
+ // Eliminar las entradas más antiguas
98
+ const toDelete = entries.slice(0, entries.length - this.MAX_CACHE_SIZE);
99
+ toDelete.forEach(([key]) => this.cache.delete(key));
100
+ }
101
+ /**
102
+ * Limpia entradas expiradas
103
+ */
104
+ cleanExpired() {
105
+ const now = Date.now();
106
+ for (const [key, entry] of this.cache.entries()) {
107
+ if (now - entry.timestamp > this.CACHE_TTL) {
108
+ this.cache.delete(key);
109
+ }
110
+ }
111
+ }
112
+ /**
113
+ * Obtiene estadísticas del cache
114
+ */
115
+ getStats() {
116
+ return {
117
+ size: this.cache.size,
118
+ maxSize: this.MAX_CACHE_SIZE,
119
+ ttl: this.CACHE_TTL,
120
+ };
121
+ }
122
+ /**
123
+ * Limpia todo el cache
124
+ */
125
+ clear() {
126
+ this.cache.clear();
127
+ }
128
+ }
129
+ // Instancia global del cache HMR
130
+ const hmrInjectionCache = VueHMRInjectionCache.getInstance();
13
131
  const getComponentsVueMap = async (ast) => {
14
132
  let components = [];
15
133
  const importsStatic = ast?.module?.staticImports;
@@ -22,12 +140,6 @@ const getComponentsVueMap = async (ast) => {
22
140
  }
23
141
  return components;
24
142
  };
25
- /**
26
- * Compila un bloque personalizado.
27
- * @param {Object} block - El bloque personalizado a compilar.
28
- * @param {string} source - La fuente del bloque.
29
- */
30
- const _compileCustomBlock = async (_block, _source) => { };
31
143
  /**
32
144
  * Precompila un componente Vue.
33
145
  * @param {string} data - El código del componente Vue.
@@ -45,36 +157,8 @@ export const preCompileVue = async (data, source, isProd = false) => {
45
157
  };
46
158
  }
47
159
  if (!isProd) {
48
- // Verificar si ya existe una importación de ref desde vue de manera más precisa
49
- const vueImportPattern = /import\s*\{[^}]*\bref\b[^}]*\}\s*from\s*['"]vue['"]/;
50
- const hasRefImport = vueImportPattern.test(data);
51
- // esto es para HMR re re forzado
52
- const varContent = `
53
- ${hasRefImport ? '' : 'import { ref } from "/node_modules/vue/dist/vue.esm-browser.js";'}
54
- const versaComponentKey = ref(0);
55
- `;
56
- const ifExistScript = data.includes('<script');
57
- if (!ifExistScript) {
58
- data =
59
- `<script setup lang="ts">${varContent}</script>/n` + data;
60
- }
61
- else {
62
- data = data.replace(/(<script.*?>)/, `$1${varContent}`);
63
- }
64
- data = data.replace(/(<template[^>]*>[\s\S]*?)(<(\w+)([^>]*?))(\/?>)/, (match, p1, p2, p3, p4, p5) => {
65
- // Si ya tiene :key, no agregarlo de nuevo
66
- if (p4.includes(':key=') || p4.includes('key=')) {
67
- return match;
68
- }
69
- // Si es self-closing (termina con '/>'), manejar diferente
70
- const isSelfClosing = p5 === '/>';
71
- if (isSelfClosing) {
72
- return `${p1}<${p3}${p4} :key="versaComponentKey" />`;
73
- }
74
- else {
75
- return `${p1}<${p3}${p4} :key="versaComponentKey">`;
76
- }
77
- });
160
+ const { injectedData } = hmrInjectionCache.getOrGenerateHMRInjection(data, fileName);
161
+ data = injectedData;
78
162
  }
79
163
  const { descriptor, errors } = vCompiler.parse(data, {
80
164
  filename: fileName,
@@ -132,7 +216,7 @@ export const preCompileVue = async (data, source, isProd = false) => {
132
216
  const ast = await parser(`temp.${scriptLang}`, scriptContent, scriptLang);
133
217
  if (ast?.errors.length > 0) {
134
218
  throw new Error(`Error al analizar el script del componente Vue ${source}:\n${ast.errors
135
- .map(e => e.message)
219
+ .map((e) => e.message)
136
220
  .join('\n')}`);
137
221
  }
138
222
  const components = await getComponentsVueMap(ast);
@@ -293,4 +377,14 @@ export const preCompileVue = async (data, source, isProd = false) => {
293
377
  };
294
378
  }
295
379
  };
380
+ // ✨ NUEVA FUNCIÓN: Exportar funcionalidades del cache HMR para uso externo
381
+ export const getVueHMRCacheStats = () => {
382
+ return hmrInjectionCache.getStats();
383
+ };
384
+ export const clearVueHMRCache = () => {
385
+ hmrInjectionCache.clear();
386
+ };
387
+ export const cleanExpiredVueHMRCache = () => {
388
+ hmrInjectionCache.cleanExpired();
389
+ };
296
390
  //# sourceMappingURL=vuejs.js.map
package/dist/main.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import path from 'node:path'; // Importar el módulo path
3
- import { env } from 'node:process';
3
+ import process, { env } from 'node:process';
4
4
  // Lazy loading optimizations - Only import lightweight modules synchronously
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { logger } from './servicios/logger.js';
@@ -35,7 +35,7 @@ async function loadBrowserSyncModule() {
35
35
  return await import('./servicios/browserSync.js');
36
36
  }
37
37
  async function loadChokidarModule() {
38
- return await import('./servicios/chokidar.js');
38
+ return await import('./servicios/file-watcher.js');
39
39
  }
40
40
  async function loadConfigModule() {
41
41
  return await import('./servicios/readConfig.js');
@@ -100,7 +100,7 @@ async function main() {
100
100
  .alias('y', 'yes')
101
101
  .option('typeCheck', {
102
102
  type: 'boolean',
103
- description: 'Habilitar/Deshabilitar la verificación de tipos. Por defecto --typeCheck=true',
103
+ description: 'Habilitar/Deshabilitar la verificación de tipos. Por defecto --typeCheck=false',
104
104
  default: false,
105
105
  })
106
106
  .alias('t', 'typeCheck');
@@ -199,8 +199,7 @@ async function main() {
199
199
  }
200
200
  if (argv.file) {
201
201
  // Compilar archivo individual
202
- logger.info(chalk.yellow(`📄 Compilando archivo: ${argv.file}`));
203
- // Verificar si el archivo existe
202
+ logger.info(chalk.yellow(`📄 Compilando archivo: ${argv.file}`)); // Verificar si el archivo existe
204
203
  const fs = await import('node:fs/promises');
205
204
  const { compileFile } = await loadCompilerModule();
206
205
  let absolutePathFile;
@@ -212,7 +211,7 @@ async function main() {
212
211
  logger.error(chalk.red(`❌ Error: El archivo '${argv.file}' no existe.`));
213
212
  process.exit(1);
214
213
  }
215
- // Compilar el archivo
214
+ // Compilar el archivo (absolutePathFile está garantizado aquí)
216
215
  const result = await compileFile(absolutePathFile);
217
216
  if (result.success) {
218
217
  logger.info(chalk.green(`✅ Archivo compilado exitosamente: ${result.output}`));