versacompiler 2.1.0 → 2.2.0
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 +1 -1
- package/dist/compiler/compile.js +2520 -25
- package/dist/compiler/error-reporter.js +467 -38
- package/dist/compiler/linter.js +72 -1
- package/dist/compiler/minify.js +272 -1
- package/dist/compiler/minifyTemplate.js +230 -1
- package/dist/compiler/module-resolution-optimizer.js +844 -1
- package/dist/compiler/parser.js +336 -1
- package/dist/compiler/performance-monitor.js +204 -56
- package/dist/compiler/tailwindcss.js +39 -1
- package/dist/compiler/transform-optimizer.js +392 -1
- package/dist/compiler/transformTStoJS.js +16 -1
- package/dist/compiler/transforms.js +554 -1
- package/dist/compiler/typescript-compiler.js +172 -2
- package/dist/compiler/typescript-error-parser.js +281 -10
- package/dist/compiler/typescript-manager.js +304 -2
- package/dist/compiler/typescript-sync-validator.js +295 -31
- package/dist/compiler/typescript-worker-pool.js +936 -1
- package/dist/compiler/typescript-worker-thread.cjs +466 -22
- package/dist/compiler/typescript-worker.js +339 -1
- package/dist/compiler/vuejs.js +396 -37
- package/dist/hrm/VueHRM.js +359 -1
- package/dist/hrm/errorScreen.js +83 -1
- package/dist/hrm/getInstanciaVue.js +313 -1
- package/dist/hrm/initHRM.js +586 -1
- package/dist/main.js +353 -7
- package/dist/servicios/browserSync.js +589 -2
- package/dist/servicios/file-watcher.js +425 -4
- package/dist/servicios/logger.js +63 -3
- package/dist/servicios/readConfig.js +399 -53
- package/dist/utils/excluded-modules.js +37 -1
- package/dist/utils/module-resolver.js +466 -1
- package/dist/utils/promptUser.js +48 -2
- package/dist/utils/proxyValidator.js +68 -1
- package/dist/utils/resolve-bin.js +58 -1
- package/dist/utils/utils.js +21 -1
- package/dist/utils/vue-types-setup.js +435 -241
- package/dist/wrappers/eslint-node.js +1 -1
- package/dist/wrappers/oxlint-node.js +122 -1
- package/dist/wrappers/tailwind-node.js +94 -1
- package/package.json +106 -103
package/dist/hrm/initHRM.js
CHANGED
|
@@ -1 +1,586 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Inicialización del sistema Hot Module Replacement (HMR) para VersaCompiler
|
|
3
|
+
* Este archivo maneja la conexión con BrowserSync y configura los listeners para HMR de Vue
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {Object} ComponentInfo
|
|
8
|
+
* @property {string} normalizedPath - Ruta normalizada del componente
|
|
9
|
+
* @property {string} nameFile - Nombre del archivo del componente
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { hideErrorOverlay, showErrorOverlay } from './errorScreen.js';
|
|
13
|
+
import { obtenerInstanciaVue } from './getInstanciaVue.js';
|
|
14
|
+
import { reloadComponent } from './VueHRM.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Analiza el contenido de un módulo para extraer sus imports/exports
|
|
18
|
+
* @param {string} content - Contenido del archivo JavaScript/TypeScript
|
|
19
|
+
* @returns {{imports: string[], exports: string[], hasHMRAccept: boolean}}
|
|
20
|
+
*/
|
|
21
|
+
function analyzeModuleContent(content) {
|
|
22
|
+
const imports = [];
|
|
23
|
+
const exports = [];
|
|
24
|
+
let hasHMRAccept = false;
|
|
25
|
+
|
|
26
|
+
// Detectar imports (ESM y CommonJS)
|
|
27
|
+
const importMatches = content.matchAll(
|
|
28
|
+
/import\s+(?:{[^}]*}|[^\s]+|\*\s+as\s+\w+)\s+from\s+['"]([^'"]+)['"]/g,
|
|
29
|
+
);
|
|
30
|
+
const requireMatches = content.matchAll(/require\(['"]([^'"]+)['"]\)/g);
|
|
31
|
+
|
|
32
|
+
for (const match of importMatches) {
|
|
33
|
+
if (match[1]) imports.push(match[1]);
|
|
34
|
+
}
|
|
35
|
+
for (const match of requireMatches) {
|
|
36
|
+
if (match[1]) imports.push(match[1]);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Detectar exports
|
|
40
|
+
const exportMatches = content.matchAll(
|
|
41
|
+
/export\s+(?:default|const|let|var|function|class|{[^}]*})\s+(\w+)/g,
|
|
42
|
+
);
|
|
43
|
+
for (const match of exportMatches) {
|
|
44
|
+
if (match[1]) exports.push(match[1]);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Detectar si el módulo acepta HMR (import.meta.hot.accept)
|
|
48
|
+
hasHMRAccept = /import\.meta\.hot\.accept/.test(content);
|
|
49
|
+
|
|
50
|
+
return { imports, exports, hasHMRAccept };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Detecta automáticamente la estrategia de HMR para un archivo modificado
|
|
55
|
+
* @param {string} filePath - Ruta del archivo modificado
|
|
56
|
+
* @param {string|null} content - Contenido del archivo (si está disponible)
|
|
57
|
+
* @returns {Promise<{strategy: 'self-accept'|'propagate'|'full-reload', boundary?: string}>}
|
|
58
|
+
*/
|
|
59
|
+
async function detectHMRStrategy(filePath, content = null) {
|
|
60
|
+
try {
|
|
61
|
+
// Si no tenemos el contenido, intentar leerlo
|
|
62
|
+
if (!content && typeof fetch !== 'undefined') {
|
|
63
|
+
try {
|
|
64
|
+
const response = await fetch(filePath);
|
|
65
|
+
content = await response.text();
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.warn(
|
|
68
|
+
'⚠️ No se pudo obtener contenido del módulo:',
|
|
69
|
+
error,
|
|
70
|
+
);
|
|
71
|
+
return { strategy: 'full-reload' };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!content) {
|
|
76
|
+
return { strategy: 'full-reload' };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const analysis = analyzeModuleContent(content);
|
|
80
|
+
|
|
81
|
+
// Estrategia 1: Self-accept (el módulo declara que puede reemplazarse a sí mismo)
|
|
82
|
+
if (analysis.hasHMRAccept) {
|
|
83
|
+
console.log('✨ Módulo con self-accept HMR:', filePath);
|
|
84
|
+
return { strategy: 'self-accept', boundary: filePath };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Estrategia 2: Propagate (buscar el primer importador que acepte HMR)
|
|
88
|
+
// En el browser no tenemos acceso al módulo graph del servidor,
|
|
89
|
+
// así que usamos heurísticas basadas en el contenido
|
|
90
|
+
|
|
91
|
+
// Si solo exporta funciones/constantes simples, probablemente sea seguro recargar
|
|
92
|
+
const hasOnlySimpleExports =
|
|
93
|
+
analysis.exports.length > 0 &&
|
|
94
|
+
!content.includes('class ') &&
|
|
95
|
+
!content.includes('new ');
|
|
96
|
+
|
|
97
|
+
if (hasOnlySimpleExports) {
|
|
98
|
+
console.log(
|
|
99
|
+
'✨ Módulo con exports simples, propagando HMR:',
|
|
100
|
+
filePath,
|
|
101
|
+
);
|
|
102
|
+
return { strategy: 'propagate', boundary: filePath };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Estrategia 3: Full reload (no es seguro hacer HMR)
|
|
106
|
+
console.log('⚠️ Módulo requiere recarga completa:', filePath);
|
|
107
|
+
return { strategy: 'full-reload' };
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error('❌ Error detectando estrategia HMR:', error);
|
|
110
|
+
return { strategy: 'full-reload' };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Envía un error del cliente al servidor para debugging
|
|
116
|
+
* @param {string} type - Tipo de error (hmr, reload, vue, etc)
|
|
117
|
+
* @param {Error|string} error - El error a reportar
|
|
118
|
+
* @param {Object} [context] - Contexto adicional del error
|
|
119
|
+
*/
|
|
120
|
+
function reportErrorToServer(type, error, context = {}) {
|
|
121
|
+
try {
|
|
122
|
+
// Verificar que estamos en un navegador
|
|
123
|
+
if (typeof window === 'undefined') {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!window.___browserSync___ || !window.___browserSync___.socket) {
|
|
128
|
+
console.warn(
|
|
129
|
+
'⚠️ BrowserSync socket no disponible para reportar error',
|
|
130
|
+
);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const errorData = {
|
|
135
|
+
type,
|
|
136
|
+
timestamp: new Date().toISOString(),
|
|
137
|
+
userAgent: navigator.userAgent,
|
|
138
|
+
url: window.location.href,
|
|
139
|
+
context,
|
|
140
|
+
error: {
|
|
141
|
+
message: error?.message || String(error),
|
|
142
|
+
stack: error?.stack || '',
|
|
143
|
+
name: error?.name || 'Error',
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// Enviar error al servidor
|
|
148
|
+
window.___browserSync___.socket.emit('client:error', errorData);
|
|
149
|
+
|
|
150
|
+
console.error(`📤 Error reportado al servidor [${type}]:`, errorData);
|
|
151
|
+
} catch (err) {
|
|
152
|
+
console.error('❌ Error al reportar error al servidor:', err);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Maneja el hot reload de librerías sin recarga completa de página
|
|
158
|
+
* @param {Object} data - Datos del evento HRMHelper
|
|
159
|
+
* @param {string} data.libraryName - Nombre de la librería a actualizar
|
|
160
|
+
* @param {string} data.libraryPath - Ruta de la nueva librería
|
|
161
|
+
* @param {string} [data.globalName] - Nombre global de la librería (ej: 'Vue', 'React')
|
|
162
|
+
* @param {Function} [importFn] - Función import para cargar módulos (inyectable para tests)
|
|
163
|
+
* @returns {Promise<boolean>} True si el hot reload fue exitoso
|
|
164
|
+
*/
|
|
165
|
+
export async function handleLibraryHotReload(
|
|
166
|
+
data,
|
|
167
|
+
importFn = url => import(url),
|
|
168
|
+
) {
|
|
169
|
+
const { libraryName, libraryPath, globalName } = data;
|
|
170
|
+
|
|
171
|
+
if (!libraryName || !libraryPath) {
|
|
172
|
+
console.error(
|
|
173
|
+
'❌ HRMHelper: Datos incompletos para hot reload de librería',
|
|
174
|
+
);
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 2. Determinar el nombre global de la librería
|
|
179
|
+
const targetGlobalName = globalName || libraryName;
|
|
180
|
+
|
|
181
|
+
// 3. Backup de la versión anterior (antes de cargar la nueva)
|
|
182
|
+
let oldLibraryVersion;
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
console.log(`🔄 Iniciando hot reload de librería: ${libraryName}`);
|
|
186
|
+
|
|
187
|
+
oldLibraryVersion = window[targetGlobalName];
|
|
188
|
+
|
|
189
|
+
// 4. Cargar la nueva versión de la librería
|
|
190
|
+
const timestamp = Date.now();
|
|
191
|
+
const moduleUrl = `${libraryPath}?t=${timestamp}`;
|
|
192
|
+
|
|
193
|
+
console.log(`📦 Cargando nueva versión desde: ${moduleUrl}`);
|
|
194
|
+
const module = await importFn(moduleUrl);
|
|
195
|
+
|
|
196
|
+
if (!module.default && !module[libraryName]) {
|
|
197
|
+
console.error(
|
|
198
|
+
'❌ HRMHelper: La nueva versión no tiene export válido',
|
|
199
|
+
);
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const newLibraryVersion = module.default || module[libraryName];
|
|
204
|
+
|
|
205
|
+
// 3. Reemplazar la librería en el contexto global
|
|
206
|
+
console.log(`🔄 Reemplazando ${targetGlobalName} en contexto global`);
|
|
207
|
+
window[targetGlobalName] = newLibraryVersion;
|
|
208
|
+
|
|
209
|
+
// 4. Limpiar caches si existen
|
|
210
|
+
if (
|
|
211
|
+
typeof newLibraryVersion === 'object' &&
|
|
212
|
+
newLibraryVersion.clearCache
|
|
213
|
+
) {
|
|
214
|
+
try {
|
|
215
|
+
newLibraryVersion.clearCache();
|
|
216
|
+
} catch (_e) {
|
|
217
|
+
// Ignorar errores de clearCache, no es crítico
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// 5. Re-inicializar aplicación si es necesario
|
|
222
|
+
if (targetGlobalName === 'Vue' || libraryName.includes('vue')) {
|
|
223
|
+
console.log(
|
|
224
|
+
'🔄 Librería Vue actualizada, se recomienda recarga completa',
|
|
225
|
+
);
|
|
226
|
+
// Para Vue, es más seguro hacer recarga completa
|
|
227
|
+
window.location.reload();
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// 6. Intentar limpiar caches si existen
|
|
232
|
+
try {
|
|
233
|
+
// Limpiar cualquier cache que pueda existir
|
|
234
|
+
if (
|
|
235
|
+
typeof window !== 'undefined' &&
|
|
236
|
+
window.performance &&
|
|
237
|
+
window.performance.clearResourceTimings
|
|
238
|
+
) {
|
|
239
|
+
window.performance.clearResourceTimings();
|
|
240
|
+
}
|
|
241
|
+
} catch (_e) {
|
|
242
|
+
// Ignorar errores de limpieza de cache
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
console.log(`✅ Hot reload exitoso de librería: ${libraryName}`);
|
|
246
|
+
return true;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error(
|
|
249
|
+
`❌ Error en hot reload de librería ${libraryName}:`,
|
|
250
|
+
error,
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
// Reportar error al servidor
|
|
254
|
+
reportErrorToServer('library-hotreload', error, {
|
|
255
|
+
libraryName,
|
|
256
|
+
libraryPath,
|
|
257
|
+
globalName,
|
|
258
|
+
hadOldVersion: oldLibraryVersion !== undefined,
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Intentar rollback si es posible
|
|
262
|
+
if (targetGlobalName && oldLibraryVersion !== undefined) {
|
|
263
|
+
console.log('🔄 Intentando rollback de librería...');
|
|
264
|
+
window[targetGlobalName] = oldLibraryVersion;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Variable para controlar si ya se está inicializando
|
|
272
|
+
let isInitializing = false;
|
|
273
|
+
let initializationTimeout = null;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Inicializa la conexión socket con BrowserSync y configura los listeners para HMR
|
|
277
|
+
* @param {number} [retries=0] - Número de reintentos realizados
|
|
278
|
+
* @returns {Promise<void>} Promise que se resuelve cuando la conexión está configurada
|
|
279
|
+
*/
|
|
280
|
+
async function initSocket(retries = 0) {
|
|
281
|
+
// Evitar inicializaciones concurrentes
|
|
282
|
+
if (isInitializing && retries > 0) {
|
|
283
|
+
console.log(
|
|
284
|
+
'⏳ Versa HMR: Ya hay una inicialización en curso, saltando...',
|
|
285
|
+
);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
isInitializing = true;
|
|
290
|
+
|
|
291
|
+
const maxRetries = 10;
|
|
292
|
+
const retryDelay = Math.min(2000 * (retries + 1), 10000); // Backoff exponencial hasta 10s
|
|
293
|
+
|
|
294
|
+
// Verificar si BrowserSync está disponible y tiene socket
|
|
295
|
+
if (window.___browserSync___ && window.___browserSync___.socket) {
|
|
296
|
+
const socket = window.___browserSync___.socket;
|
|
297
|
+
let connected = socket.connected; // Verificar estado inicial de conexión
|
|
298
|
+
|
|
299
|
+
// Limpiar listeners previos para evitar duplicados
|
|
300
|
+
socket.off('connect');
|
|
301
|
+
socket.off('disconnect');
|
|
302
|
+
socket.off('reloadFull');
|
|
303
|
+
socket.off('HRMVue');
|
|
304
|
+
socket.off('HRMHelper');
|
|
305
|
+
socket.off('error');
|
|
306
|
+
|
|
307
|
+
// Limpiar timeout previo si existe
|
|
308
|
+
if (initializationTimeout) {
|
|
309
|
+
clearTimeout(initializationTimeout);
|
|
310
|
+
initializationTimeout = null;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Configurar listener para eventos de conexión
|
|
314
|
+
socket.on('connect', async () => {
|
|
315
|
+
connected = true;
|
|
316
|
+
isInitializing = false;
|
|
317
|
+
hideErrorOverlay();
|
|
318
|
+
console.log('✔️ Versa HMR: Socket conectado');
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// Configurar listener para eventos de desconexión
|
|
322
|
+
socket.on('disconnect', () => {
|
|
323
|
+
connected = false;
|
|
324
|
+
isInitializing = false;
|
|
325
|
+
console.log('❌ Versa HMR: Socket desconectado, reintentando...');
|
|
326
|
+
// Lógica de reintentos para desconexión
|
|
327
|
+
initializationTimeout = setTimeout(() => {
|
|
328
|
+
if (!socket.connected && retries < maxRetries) {
|
|
329
|
+
initSocket(retries + 1);
|
|
330
|
+
} else if (!socket.connected) {
|
|
331
|
+
console.error(
|
|
332
|
+
`❌ Versa HMR: Socket no conectado después de ${maxRetries} reintentos tras desconexión.`,
|
|
333
|
+
);
|
|
334
|
+
showErrorOverlay(
|
|
335
|
+
'HMR Desconectado',
|
|
336
|
+
'No se pudo reconectar a BrowserSync después de múltiples reintentos.',
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
}, retryDelay);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// Configurar listener para recarga completa
|
|
343
|
+
socket.on('reloadFull', () => window.location.reload()); // Obtener la instancia de Vue con toda la lógica integrada
|
|
344
|
+
let vueInstance = await obtenerInstanciaVue();
|
|
345
|
+
|
|
346
|
+
// Configurar listener para HMR de componentes Vue
|
|
347
|
+
socket.on('HRMVue', async (/** @type {ComponentInfo} */ data) => {
|
|
348
|
+
try {
|
|
349
|
+
hideErrorOverlay();
|
|
350
|
+
vueInstance = window.__VUE_APP__ || vueInstance;
|
|
351
|
+
if (vueInstance) {
|
|
352
|
+
console.log('🔥 Preparando HMR para Vue...');
|
|
353
|
+
await reloadComponent(vueInstance, data);
|
|
354
|
+
} else {
|
|
355
|
+
console.log('🔄 Usando método fallback:', vueInstance);
|
|
356
|
+
}
|
|
357
|
+
} catch (error) {
|
|
358
|
+
console.error('❌ Error en HMR de Vue:', error);
|
|
359
|
+
reportErrorToServer('vue-hmr', error, {
|
|
360
|
+
component: data?.nameFile,
|
|
361
|
+
path: data?.normalizedPath,
|
|
362
|
+
});
|
|
363
|
+
showErrorOverlay(
|
|
364
|
+
'Error en HMR de Vue',
|
|
365
|
+
error.message || String(error),
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// Configurar listener para datos auxiliares de HMR
|
|
371
|
+
socket.on('HRMHelper', async data => {
|
|
372
|
+
console.log('🔄 HRMHelper recibido:', data);
|
|
373
|
+
console.log('📋 Archivo modificado:', data.filePath);
|
|
374
|
+
|
|
375
|
+
// Sistema inteligente de detección automática (como Vite/esbuild)
|
|
376
|
+
if (data.filePath && !data.libraryName && !data.libraryPath) {
|
|
377
|
+
console.log('🔍 Analizando estrategia HMR automática...');
|
|
378
|
+
|
|
379
|
+
// Detectar la estrategia de HMR apropiada
|
|
380
|
+
const hmrStrategy = await detectHMRStrategy(
|
|
381
|
+
data.filePath,
|
|
382
|
+
data.content,
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
console.log('📊 Estrategia detectada:', hmrStrategy.strategy);
|
|
386
|
+
|
|
387
|
+
// Ejecutar la estrategia apropiada
|
|
388
|
+
switch (hmrStrategy.strategy) {
|
|
389
|
+
case 'self-accept':
|
|
390
|
+
// El módulo puede reemplazarse a sí mismo
|
|
391
|
+
console.log('✨ Aplicando self-accept HMR');
|
|
392
|
+
try {
|
|
393
|
+
// Reimportar el módulo con cache busting
|
|
394
|
+
const timestamp = Date.now();
|
|
395
|
+
await import(`${data.filePath}?t=${timestamp}`);
|
|
396
|
+
console.log('✅ Módulo recargado exitosamente');
|
|
397
|
+
return;
|
|
398
|
+
} catch (error) {
|
|
399
|
+
console.error(
|
|
400
|
+
'❌ Error en self-accept HMR:',
|
|
401
|
+
error,
|
|
402
|
+
);
|
|
403
|
+
reportErrorToServer(
|
|
404
|
+
'hmr-self-accept-failed',
|
|
405
|
+
error instanceof Error
|
|
406
|
+
? error
|
|
407
|
+
: new Error(String(error)),
|
|
408
|
+
data,
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
break;
|
|
412
|
+
|
|
413
|
+
case 'propagate':
|
|
414
|
+
// Propagar la actualización a los importadores
|
|
415
|
+
console.log(
|
|
416
|
+
'🔄 Propagando actualización a importadores',
|
|
417
|
+
);
|
|
418
|
+
try {
|
|
419
|
+
// Invalidar el módulo en el cache del navegador
|
|
420
|
+
// y dejar que los importadores se actualicen
|
|
421
|
+
const timestamp = Date.now();
|
|
422
|
+
await import(`${data.filePath}?t=${timestamp}`);
|
|
423
|
+
console.log(
|
|
424
|
+
'✅ Actualización propagada exitosamente',
|
|
425
|
+
);
|
|
426
|
+
return;
|
|
427
|
+
} catch (error) {
|
|
428
|
+
console.error(
|
|
429
|
+
'❌ Error propagando actualización:',
|
|
430
|
+
error,
|
|
431
|
+
);
|
|
432
|
+
reportErrorToServer(
|
|
433
|
+
'hmr-propagate-failed',
|
|
434
|
+
error instanceof Error
|
|
435
|
+
? error
|
|
436
|
+
: new Error(String(error)),
|
|
437
|
+
data,
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
break;
|
|
441
|
+
|
|
442
|
+
case 'full-reload':
|
|
443
|
+
default:
|
|
444
|
+
// Recarga completa necesaria
|
|
445
|
+
console.log(
|
|
446
|
+
'🔄 Recarga completa requerida - módulo no soporta HMR',
|
|
447
|
+
);
|
|
448
|
+
reportErrorToServer(
|
|
449
|
+
'hmr-full-reload',
|
|
450
|
+
new Error(
|
|
451
|
+
'Módulo requiere recarga completa (no self-accept)',
|
|
452
|
+
),
|
|
453
|
+
{
|
|
454
|
+
...data,
|
|
455
|
+
strategy: hmrStrategy.strategy,
|
|
456
|
+
hint: 'Considera agregar import.meta.hot.accept() al módulo para HMR sin recarga',
|
|
457
|
+
},
|
|
458
|
+
);
|
|
459
|
+
window.location.reload();
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
try {
|
|
465
|
+
// Intentar hacer hot reload de librería sin recarga completa
|
|
466
|
+
const success = await handleLibraryHotReload(data);
|
|
467
|
+
if (!success) {
|
|
468
|
+
console.warn(
|
|
469
|
+
'⚠️ Hot reload de librería falló, haciendo recarga completa',
|
|
470
|
+
);
|
|
471
|
+
reportErrorToServer(
|
|
472
|
+
'hmr-helper-failed',
|
|
473
|
+
new Error('Hot reload returned false'),
|
|
474
|
+
data,
|
|
475
|
+
);
|
|
476
|
+
window.location.reload();
|
|
477
|
+
}
|
|
478
|
+
} catch (error) {
|
|
479
|
+
console.error('❌ Error en HRMHelper:', error);
|
|
480
|
+
reportErrorToServer(
|
|
481
|
+
'hmr-helper-exception',
|
|
482
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
483
|
+
data,
|
|
484
|
+
);
|
|
485
|
+
window.location.reload();
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
// Configurar listener para errores de socket
|
|
490
|
+
socket.on('error', err => {
|
|
491
|
+
console.error('❌ Versa HMR: Error en el socket:', err);
|
|
492
|
+
showErrorOverlay(
|
|
493
|
+
'Error de Socket',
|
|
494
|
+
'Se produjo un error en la conexión de BrowserSync.',
|
|
495
|
+
);
|
|
496
|
+
}); // Watchdog para verificar conexión inicial si el socket existe pero no está conectado
|
|
497
|
+
if (!connected) {
|
|
498
|
+
console.log(
|
|
499
|
+
`Versa HMR: Objeto socket encontrado, intentando conexión (Intento ${
|
|
500
|
+
retries + 1
|
|
501
|
+
}/${maxRetries})`,
|
|
502
|
+
);
|
|
503
|
+
initializationTimeout = setTimeout(() => {
|
|
504
|
+
if (!socket.connected && retries <= maxRetries) {
|
|
505
|
+
console.warn(
|
|
506
|
+
'Versa HMR: Sin conexión de socket después del tiempo de espera inicial, reintentando initSocket...',
|
|
507
|
+
);
|
|
508
|
+
initSocket(retries + 1);
|
|
509
|
+
} else if (!socket.connected) {
|
|
510
|
+
isInitializing = false;
|
|
511
|
+
console.error(
|
|
512
|
+
`❌ Versa HMR: Socket aún no conectado después de ${maxRetries} intentos iniciales.`,
|
|
513
|
+
);
|
|
514
|
+
showErrorOverlay(
|
|
515
|
+
'Falló HMR de BrowserSync',
|
|
516
|
+
'No se pudo conectar al socket de BrowserSync después de intentos iniciales.',
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
}, 5000); // Timeout de 5s para el watchdog inicial
|
|
520
|
+
} else {
|
|
521
|
+
isInitializing = false;
|
|
522
|
+
}
|
|
523
|
+
} else {
|
|
524
|
+
// BrowserSync no está disponible, intentar reinicializar
|
|
525
|
+
console.warn(
|
|
526
|
+
`[HMR] Socket de BrowserSync no encontrado o BrowserSync no completamente inicializado. Reintentando initSocket... (${
|
|
527
|
+
retries + 1
|
|
528
|
+
}/${maxRetries})`,
|
|
529
|
+
);
|
|
530
|
+
if (retries < maxRetries) {
|
|
531
|
+
initializationTimeout = setTimeout(
|
|
532
|
+
() => initSocket(retries + 1),
|
|
533
|
+
retryDelay,
|
|
534
|
+
);
|
|
535
|
+
} else {
|
|
536
|
+
isInitializing = false;
|
|
537
|
+
console.error(
|
|
538
|
+
`❌ Versa HMR: Socket de BrowserSync no encontrado después de ${maxRetries} reintentos.`,
|
|
539
|
+
);
|
|
540
|
+
showErrorOverlay(
|
|
541
|
+
'Falló HMR de BrowserSync',
|
|
542
|
+
'Socket o cliente de BrowserSync no encontrado después de múltiples reintentos.',
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Solo ejecutar en ambiente de navegador (no en tests de Node.js)
|
|
549
|
+
if (
|
|
550
|
+
typeof window !== 'undefined' &&
|
|
551
|
+
typeof window.addEventListener === 'function'
|
|
552
|
+
) {
|
|
553
|
+
// Capturar errores globales no manejados
|
|
554
|
+
window.addEventListener('error', event => {
|
|
555
|
+
if (
|
|
556
|
+
event.filename &&
|
|
557
|
+
(event.filename.includes('/hrm/') || event.filename.includes('HRM'))
|
|
558
|
+
) {
|
|
559
|
+
reportErrorToServer(
|
|
560
|
+
'uncaught-error',
|
|
561
|
+
event.error || new Error(event.message),
|
|
562
|
+
{
|
|
563
|
+
filename: event.filename,
|
|
564
|
+
lineno: event.lineno,
|
|
565
|
+
colno: event.colno,
|
|
566
|
+
},
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
// Capturar promesas rechazadas no manejadas
|
|
572
|
+
window.addEventListener('unhandledrejection', event => {
|
|
573
|
+
const error = event.reason;
|
|
574
|
+
if (
|
|
575
|
+
error &&
|
|
576
|
+
(error.stack?.includes('/hrm/') || error.message?.includes('HRM'))
|
|
577
|
+
) {
|
|
578
|
+
reportErrorToServer('unhandled-rejection', error, {
|
|
579
|
+
promise: String(event.promise),
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
// Inicializar el sistema HMR al cargar el script
|
|
585
|
+
initSocket();
|
|
586
|
+
}
|