versacompiler 2.5.0 → 2.6.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/dist/compiler/compile-worker-pool.js +12 -0
- package/dist/compiler/compile.js +97 -17
- package/dist/compiler/error-reporter.js +12 -0
- package/dist/compiler/integrity-validator.js +12 -0
- package/dist/compiler/linter.js +12 -0
- package/dist/compiler/minify.js +12 -0
- package/dist/compiler/minifyTemplate.js +12 -0
- package/dist/compiler/module-resolution-optimizer.js +13 -1
- package/dist/compiler/parser.js +12 -0
- package/dist/compiler/performance-monitor.js +12 -0
- package/dist/compiler/pipeline/build-pipeline.js +12 -0
- package/dist/compiler/pipeline/core-plugins.js +12 -0
- package/dist/compiler/pipeline/module-graph.js +12 -0
- package/dist/compiler/pipeline/plugin-driver.js +12 -0
- package/dist/compiler/pipeline/types.js +12 -0
- package/dist/compiler/tailwindcss.js +12 -0
- package/dist/compiler/transform-optimizer.js +12 -0
- package/dist/compiler/transformTStoJS.js +38 -5
- package/dist/compiler/transforms.js +12 -0
- package/dist/compiler/typescript-compiler.js +12 -0
- package/dist/compiler/typescript-error-parser.js +12 -0
- package/dist/compiler/typescript-manager.js +12 -0
- package/dist/compiler/typescript-sync-validator.js +12 -0
- package/dist/compiler/typescript-worker-pool.js +12 -0
- package/dist/compiler/typescript-worker.js +12 -0
- package/dist/compiler/vuejs.js +41 -15
- package/dist/config.js +12 -0
- package/dist/hrm/VueHRM.js +132 -7
- package/dist/hrm/errorScreen.js +12 -0
- package/dist/hrm/getInstanciaVue.js +12 -0
- package/dist/hrm/initHRM.js +117 -9
- package/dist/hrm/versaHMR.js +317 -0
- package/dist/main.js +21 -2
- package/dist/servicios/browserSync.js +119 -4
- package/dist/servicios/file-watcher.js +104 -15
- package/dist/servicios/logger.js +12 -0
- package/dist/servicios/readConfig.js +13 -1
- package/dist/servicios/versacompile.config.types.js +12 -0
- package/dist/utils/excluded-modules.js +12 -0
- package/dist/utils/module-resolver.js +12 -0
- package/dist/utils/promptUser.js +12 -0
- package/dist/utils/proxyValidator.js +12 -0
- package/dist/utils/resolve-bin.js +12 -0
- package/dist/utils/utils.js +12 -0
- package/dist/utils/vue-types-setup.js +14 -2
- package/dist/wrappers/eslint-node.js +12 -0
- package/dist/wrappers/oxlint-node.js +12 -0
- package/dist/wrappers/tailwind-node.js +12 -0
- package/package.json +4 -3
package/dist/compiler/vuejs.js
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/* VersaCompiler HMR shim [dev] */
|
|
2
|
+
if (typeof window !== 'undefined' && window.__versaHMR) {
|
|
3
|
+
(() => {
|
|
4
|
+
const _id = new URL(import.meta.url).pathname;
|
|
5
|
+
import.meta.hot = {
|
|
6
|
+
accept(cb) { window.__versaHMR.accept(_id, typeof cb === 'function' ? cb : () => {}); },
|
|
7
|
+
invalidate() { window.__versaHMR._invalidate?.(_id); },
|
|
8
|
+
dispose(cb) { window.__versaHMR._onDispose?.(_id, cb); },
|
|
9
|
+
get data() { return window.__versaHMR._getHotData?.(_id) ?? {}; },
|
|
10
|
+
};
|
|
11
|
+
})();
|
|
12
|
+
}
|
|
1
13
|
import { createHash } from 'node:crypto';
|
|
2
14
|
import path from 'node:path';
|
|
3
15
|
import * as vCompiler from 'vue/compiler-sfc';
|
|
@@ -63,19 +75,30 @@ class VueHMRInjectionCache {
|
|
|
63
75
|
else {
|
|
64
76
|
injectedData = originalData.replace(/(<script.*?>)/, `$1${varContent}`);
|
|
65
77
|
}
|
|
66
|
-
// Inyectar :key en el template
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
// Inyectar :key en el primer elemento hijo del template.
|
|
79
|
+
// Dos pasos para evitar backtracking catastrófico (ReDoS) que ocurría con
|
|
80
|
+
// el pattern combinado /(<template[^>]*>[\s\S]*?)(<(\w+)([^>]*?))(\/?>)/
|
|
81
|
+
const templateTagMatch = /(<template[^>]*>)/.exec(injectedData);
|
|
82
|
+
if (templateTagMatch) {
|
|
83
|
+
const templateEnd = templateTagMatch.index + templateTagMatch[0].length;
|
|
84
|
+
const afterTemplate = injectedData.slice(templateEnd);
|
|
85
|
+
// Buscar el primer tag de elemento (ignora espacios/comentarios) justo después del <template>
|
|
86
|
+
const firstChildMatch = /^(\s*)(<(\w+)([^>]*?)(\/?>))/.exec(afterTemplate);
|
|
87
|
+
if (firstChildMatch) {
|
|
88
|
+
const [, whitespace = '', fullTag = '', tagName, attrs = '', closing,] = firstChildMatch;
|
|
89
|
+
if (!attrs.includes(':key=') && !attrs.includes('key=')) {
|
|
90
|
+
const isSelfClosing = closing === '/>';
|
|
91
|
+
const newTag = isSelfClosing
|
|
92
|
+
? `<${tagName}${attrs} :key="versaComponentKey" />`
|
|
93
|
+
: `<${tagName}${attrs} :key="versaComponentKey">`;
|
|
94
|
+
injectedData =
|
|
95
|
+
injectedData.slice(0, templateEnd) +
|
|
96
|
+
whitespace +
|
|
97
|
+
newTag +
|
|
98
|
+
afterTemplate.slice(whitespace.length + fullTag.length);
|
|
99
|
+
}
|
|
77
100
|
}
|
|
78
|
-
}
|
|
101
|
+
}
|
|
79
102
|
// Cachear resultado
|
|
80
103
|
this.cache.set(cacheKey, {
|
|
81
104
|
contentHash,
|
|
@@ -167,8 +190,6 @@ export const preCompileVue = async (data, source, isProd = false) => {
|
|
|
167
190
|
scriptInfo: undefined,
|
|
168
191
|
};
|
|
169
192
|
}
|
|
170
|
-
// Guardar el código original antes de inyectar HMR
|
|
171
|
-
const originalData = data;
|
|
172
193
|
if (!isProd) {
|
|
173
194
|
const { injectedData } = hmrInjectionCache.getOrGenerateHMRInjection(data, fileName);
|
|
174
195
|
data = injectedData;
|
|
@@ -312,10 +333,14 @@ export const preCompileVue = async (data, source, isProd = false) => {
|
|
|
312
333
|
filename: `${fileName}.vue`,
|
|
313
334
|
});
|
|
314
335
|
});
|
|
336
|
+
// data-versa-hmr-component permite a VueHRM.js eliminar style tags
|
|
337
|
+
// de ciclos HMR anteriores para evitar acumulación de estilos duplicados.
|
|
338
|
+
const sanitizedForAttr = fileName.replace(/[^a-zA-Z0-9_-]/g, '');
|
|
315
339
|
const insertStyles = compiledStyles.length
|
|
316
340
|
? `(function(){
|
|
317
341
|
let styleTag = document.createElement('style');
|
|
318
342
|
styleTag.setAttribute('data-v-${id}', '');
|
|
343
|
+
styleTag.setAttribute('data-versa-hmr-component', '${sanitizedForAttr}');
|
|
319
344
|
styleTag.innerHTML = \`${compiledStyles.map((s) => s.code).join('\n')}\`;
|
|
320
345
|
document.head.appendChild(styleTag);
|
|
321
346
|
})();`
|
|
@@ -380,6 +405,8 @@ export const preCompileVue = async (data, source, isProd = false) => {
|
|
|
380
405
|
export default ${componentName}; `;
|
|
381
406
|
output = `${output}\n${finishComponent}`;
|
|
382
407
|
// 🚀 OPTIMIZACIÓN CRÍTICA: Evitar crear scriptInfo si no hay script
|
|
408
|
+
// originalData NO se incluye en scriptInfo para evitar retener la cadena completa
|
|
409
|
+
// del .vue en memoria. Se pasa por separado como sourceCode en parseTypeScriptErrors.
|
|
383
410
|
const result = {
|
|
384
411
|
lang: finalCompiledScript.lang,
|
|
385
412
|
error: null,
|
|
@@ -391,7 +418,6 @@ export const preCompileVue = async (data, source, isProd = false) => {
|
|
|
391
418
|
startLine: (descriptor.script || descriptor.scriptSetup).loc?.start
|
|
392
419
|
.line || 1,
|
|
393
420
|
content: (descriptor.script || descriptor.scriptSetup).content,
|
|
394
|
-
originalData: originalData, // String directa, no closure
|
|
395
421
|
};
|
|
396
422
|
}
|
|
397
423
|
return result;
|
package/dist/config.js
CHANGED
|
@@ -1,2 +1,14 @@
|
|
|
1
|
+
/* VersaCompiler HMR shim [dev] */
|
|
2
|
+
if (typeof window !== 'undefined' && window.__versaHMR) {
|
|
3
|
+
(() => {
|
|
4
|
+
const _id = new URL(import.meta.url).pathname;
|
|
5
|
+
import.meta.hot = {
|
|
6
|
+
accept(cb) { window.__versaHMR.accept(_id, typeof cb === 'function' ? cb : () => {}); },
|
|
7
|
+
invalidate() { window.__versaHMR._invalidate?.(_id); },
|
|
8
|
+
dispose(cb) { window.__versaHMR._onDispose?.(_id, cb); },
|
|
9
|
+
get data() { return window.__versaHMR._getHotData?.(_id) ?? {}; },
|
|
10
|
+
};
|
|
11
|
+
})();
|
|
12
|
+
}
|
|
1
13
|
export { defineConfig, } from './servicios/versacompile.config.types';
|
|
2
14
|
//# sourceMappingURL=config.js.map
|
package/dist/hrm/VueHRM.js
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/* VersaCompiler HMR shim [dev] */
|
|
2
|
+
if (typeof window !== 'undefined' && window.__versaHMR) {
|
|
3
|
+
(() => {
|
|
4
|
+
const _id = new URL(import.meta.url).pathname;
|
|
5
|
+
import.meta.hot = {
|
|
6
|
+
accept(cb) { window.__versaHMR.accept(_id, typeof cb === 'function' ? cb : () => {}); },
|
|
7
|
+
invalidate() { window.__versaHMR._invalidate?.(_id); },
|
|
8
|
+
dispose(cb) { window.__versaHMR._onDispose?.(_id, cb); },
|
|
9
|
+
get data() { return window.__versaHMR._getHotData?.(_id) ?? {}; },
|
|
10
|
+
};
|
|
11
|
+
})();
|
|
12
|
+
}
|
|
1
13
|
/**
|
|
2
14
|
* @typedef {Object} TreeNode
|
|
3
15
|
* @property {string} name - Nombre del componente
|
|
@@ -222,7 +234,80 @@ function tryForceUpdate(instance) {
|
|
|
222
234
|
}
|
|
223
235
|
|
|
224
236
|
/**
|
|
225
|
-
*
|
|
237
|
+
* Limpia los caches internos de Vue para una definición de componente.
|
|
238
|
+
* Necesario para que Vue detecte el cambio de props/emits/options.
|
|
239
|
+
* @param {Object} appContext - Contexto de la app Vue (instance.appContext)
|
|
240
|
+
* @param {Object} componentDef - Definición del componente
|
|
241
|
+
*/
|
|
242
|
+
function clearVueCaches(appContext, componentDef) {
|
|
243
|
+
if (!appContext || !componentDef) return;
|
|
244
|
+
try {
|
|
245
|
+
appContext.propsCache?.delete(componentDef);
|
|
246
|
+
appContext.emitsCache?.delete(componentDef);
|
|
247
|
+
appContext.optionsCache?.delete(componentDef);
|
|
248
|
+
} catch {
|
|
249
|
+
// Los caches pueden no existir en todas las versiones de Vue
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Actualiza una instancia de componente Vue en-place con la nueva definición.
|
|
255
|
+
* Muta el objeto `instance.type` directamente para que TODAS las referencias
|
|
256
|
+
* a la definición (incluidas las capturadas en closures de render functions
|
|
257
|
+
* de componentes padre que hacen import estático) vean la nueva versión.
|
|
258
|
+
*
|
|
259
|
+
* @param {Object} instance - Instancia Vue del componente a actualizar
|
|
260
|
+
* @param {Object} newComponentDef - Nueva definición del componente
|
|
261
|
+
* @returns {boolean} true si la actualización fue exitosa
|
|
262
|
+
*/
|
|
263
|
+
function updateInstanceInPlace(instance, newComponentDef) {
|
|
264
|
+
if (!instance || !newComponentDef) return false;
|
|
265
|
+
|
|
266
|
+
const oldDef = instance.type;
|
|
267
|
+
if (!oldDef || typeof oldDef !== 'object') return false;
|
|
268
|
+
|
|
269
|
+
// 1. Mutar la definición existente en-place.
|
|
270
|
+
// Object.assign copia propiedades enumerables; copiamos render/setup
|
|
271
|
+
// explícitamente porque pueden no ser enumerables en algunos builds.
|
|
272
|
+
Object.assign(oldDef, newComponentDef);
|
|
273
|
+
if (newComponentDef.render) oldDef.render = newComponentDef.render;
|
|
274
|
+
if (newComponentDef.setup) oldDef.setup = newComponentDef.setup;
|
|
275
|
+
if (newComponentDef.ssrRender) oldDef.ssrRender = newComponentDef.ssrRender;
|
|
276
|
+
|
|
277
|
+
// 2. Actualizar instance.render directamente.
|
|
278
|
+
// Vue almacena la referencia a la render function en instance.render durante
|
|
279
|
+
// el mount (handleSetupResult), y es ESA la que llama en cada patch.
|
|
280
|
+
// Cambiar solo instance.type.render NO es suficiente — hay que actualizar
|
|
281
|
+
// también instance.render para que el próximo update use la nueva template.
|
|
282
|
+
if (newComponentDef.render && typeof instance.render !== 'undefined') {
|
|
283
|
+
instance.render = newComponentDef.render;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// 3. Limpiar caches internos de Vue para que re-evalúe props/emits/options.
|
|
287
|
+
clearVueCaches(instance.appContext, oldDef);
|
|
288
|
+
|
|
289
|
+
// 4. Forzar actualización de ESTA instancia directamente (no solo del padre).
|
|
290
|
+
// Incrementar versaComponentKey para triggear el :key del template.
|
|
291
|
+
if (instance.ctx?._.setupState?.versaComponentKey !== undefined) {
|
|
292
|
+
instance.ctx._.setupState.versaComponentKey++;
|
|
293
|
+
}
|
|
294
|
+
if (typeof instance.update === 'function') {
|
|
295
|
+
instance.update();
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
if (instance.proxy && typeof instance.proxy.$forceUpdate === 'function') {
|
|
299
|
+
instance.proxy.$forceUpdate();
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Intenta actualizar un componente en el camino del árbol.
|
|
308
|
+
* Ahora usa mutación in-place de la definición + limpieza de caches Vue,
|
|
309
|
+
* en lugar de reemplazar la referencia en el mapa de components del padre.
|
|
310
|
+
*
|
|
226
311
|
* @param {TreeNode[]} path - Camino de nodos desde el componente hasta la raíz
|
|
227
312
|
* @param {Object} newComponent - Nuevo componente a usar
|
|
228
313
|
* @param {string} componentName - Nombre del componente
|
|
@@ -235,7 +320,25 @@ function tryUpdateComponentPath(path, newComponent, componentName, App) {
|
|
|
235
320
|
return false;
|
|
236
321
|
}
|
|
237
322
|
|
|
238
|
-
//
|
|
323
|
+
// path[0] es el nodo del propio componente a actualizar.
|
|
324
|
+
// Intentar actualización directa en la instancia del componente.
|
|
325
|
+
const targetNode = path[0];
|
|
326
|
+
if (targetNode?.instancia) {
|
|
327
|
+
const updated = updateInstanceInPlace(
|
|
328
|
+
targetNode.instancia,
|
|
329
|
+
newComponent,
|
|
330
|
+
);
|
|
331
|
+
if (updated) {
|
|
332
|
+
// También forzar actualización del padre para que el vdom se reconcilie.
|
|
333
|
+
const parentNode = path[1];
|
|
334
|
+
if (parentNode?.instancia && !parentNode.isRoot) {
|
|
335
|
+
tryForceUpdate(parentNode.instancia);
|
|
336
|
+
}
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Fallback: recorrer hacia el padre si el nodo propio no es accesible.
|
|
239
342
|
for (let i = 1; i < path.length; i++) {
|
|
240
343
|
const parent = path[i];
|
|
241
344
|
|
|
@@ -244,19 +347,29 @@ function tryUpdateComponentPath(path, newComponent, componentName, App) {
|
|
|
244
347
|
return true;
|
|
245
348
|
}
|
|
246
349
|
|
|
247
|
-
if (!parent
|
|
350
|
+
if (!parent?.instancia) {
|
|
248
351
|
console.error('❌ Nodo padre no válido en el camino:', parent);
|
|
249
|
-
continue;
|
|
352
|
+
continue;
|
|
250
353
|
}
|
|
251
354
|
|
|
252
|
-
// Actualizar la instancia del componente
|
|
253
355
|
const componentsDefinition =
|
|
254
356
|
parent.instancia?.type?.components || parent.instancia?.components;
|
|
255
357
|
|
|
256
358
|
if (componentsDefinition && componentsDefinition[componentName]) {
|
|
257
|
-
|
|
359
|
+
// Mutar la definición existente en el mapa del padre también,
|
|
360
|
+
// para que nuevas instancias del componente creadas después se usen
|
|
361
|
+
// con la definición actualizada.
|
|
362
|
+
const existingDef = componentsDefinition[componentName];
|
|
363
|
+
if (existingDef && typeof existingDef === 'object') {
|
|
364
|
+
Object.assign(existingDef, newComponent);
|
|
365
|
+
if (newComponent.render)
|
|
366
|
+
existingDef.render = newComponent.render;
|
|
367
|
+
if (newComponent.setup) existingDef.setup = newComponent.setup;
|
|
368
|
+
clearVueCaches(parent.instancia.appContext, existingDef);
|
|
369
|
+
} else {
|
|
370
|
+
componentsDefinition[componentName] = newComponent;
|
|
371
|
+
}
|
|
258
372
|
|
|
259
|
-
// Forzar actualización de la instancia padre
|
|
260
373
|
return (
|
|
261
374
|
tryForceUpdate(parent.instancia) ||
|
|
262
375
|
tryForceUpdate(parent.instancia.proxy)
|
|
@@ -297,6 +410,18 @@ export async function reloadComponent(App, Component) {
|
|
|
297
410
|
const timestamp = Date.now();
|
|
298
411
|
const moduleUrl = `${urlOrigin}?t=${timestamp}`;
|
|
299
412
|
|
|
413
|
+
// Eliminar style tags del ciclo HMR anterior para este componente
|
|
414
|
+
// para evitar acumulación de estilos duplicados en el documento.
|
|
415
|
+
const componentName_clean = componentName.replace(
|
|
416
|
+
/[^a-zA-Z0-9_-]/g,
|
|
417
|
+
'',
|
|
418
|
+
);
|
|
419
|
+
document
|
|
420
|
+
.querySelectorAll(
|
|
421
|
+
`[data-versa-hmr-component="${componentName_clean}"]`,
|
|
422
|
+
)
|
|
423
|
+
.forEach(el => el.remove());
|
|
424
|
+
|
|
300
425
|
const module = await import(moduleUrl);
|
|
301
426
|
|
|
302
427
|
if (!module.default) {
|
package/dist/hrm/errorScreen.js
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/* VersaCompiler HMR shim [dev] */
|
|
2
|
+
if (typeof window !== 'undefined' && window.__versaHMR) {
|
|
3
|
+
(() => {
|
|
4
|
+
const _id = new URL(import.meta.url).pathname;
|
|
5
|
+
import.meta.hot = {
|
|
6
|
+
accept(cb) { window.__versaHMR.accept(_id, typeof cb === 'function' ? cb : () => {}); },
|
|
7
|
+
invalidate() { window.__versaHMR._invalidate?.(_id); },
|
|
8
|
+
dispose(cb) { window.__versaHMR._onDispose?.(_id, cb); },
|
|
9
|
+
get data() { return window.__versaHMR._getHotData?.(_id) ?? {}; },
|
|
10
|
+
};
|
|
11
|
+
})();
|
|
12
|
+
}
|
|
1
13
|
/**
|
|
2
14
|
* Variable global que mantiene la referencia al overlay de error actual
|
|
3
15
|
* @type {HTMLElement|null}
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/* VersaCompiler HMR shim [dev] */
|
|
2
|
+
if (typeof window !== 'undefined' && window.__versaHMR) {
|
|
3
|
+
(() => {
|
|
4
|
+
const _id = new URL(import.meta.url).pathname;
|
|
5
|
+
import.meta.hot = {
|
|
6
|
+
accept(cb) { window.__versaHMR.accept(_id, typeof cb === 'function' ? cb : () => {}); },
|
|
7
|
+
invalidate() { window.__versaHMR._invalidate?.(_id); },
|
|
8
|
+
dispose(cb) { window.__versaHMR._onDispose?.(_id, cb); },
|
|
9
|
+
get data() { return window.__versaHMR._getHotData?.(_id) ?? {}; },
|
|
10
|
+
};
|
|
11
|
+
})();
|
|
12
|
+
}
|
|
1
13
|
/**
|
|
2
14
|
* Script para obtener la instancia de Vue usando solo JavaScript
|
|
3
15
|
* Compatible con Vue 2 y Vue 3
|
package/dist/hrm/initHRM.js
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/* VersaCompiler HMR shim [dev] */
|
|
2
|
+
if (typeof window !== 'undefined' && window.__versaHMR) {
|
|
3
|
+
(() => {
|
|
4
|
+
const _id = new URL(import.meta.url).pathname;
|
|
5
|
+
import.meta.hot = {
|
|
6
|
+
accept(cb) { window.__versaHMR.accept(_id, typeof cb === 'function' ? cb : () => {}); },
|
|
7
|
+
invalidate() { window.__versaHMR._invalidate?.(_id); },
|
|
8
|
+
dispose(cb) { window.__versaHMR._onDispose?.(_id, cb); },
|
|
9
|
+
get data() { return window.__versaHMR._getHotData?.(_id) ?? {}; },
|
|
10
|
+
};
|
|
11
|
+
})();
|
|
12
|
+
}
|
|
1
13
|
/**
|
|
2
14
|
* @fileoverview Inicialización del sistema Hot Module Replacement (HMR) para VersaCompiler
|
|
3
15
|
* Este archivo maneja la conexión con BrowserSync y configura los listeners para HMR de Vue
|
|
@@ -11,6 +23,9 @@
|
|
|
11
23
|
|
|
12
24
|
import { hideErrorOverlay, showErrorOverlay } from './errorScreen.js';
|
|
13
25
|
import { obtenerInstanciaVue } from './getInstanciaVue.js';
|
|
26
|
+
|
|
27
|
+
// oxlint-disable-next-line import/no-unassigned-import -- side-effect: inicializa window.__versaHMR
|
|
28
|
+
import './versaHMR.js';
|
|
14
29
|
import { reloadComponent } from './VueHRM.js';
|
|
15
30
|
|
|
16
31
|
/**
|
|
@@ -343,6 +358,27 @@ async function initSocket(retries = 0) {
|
|
|
343
358
|
socket.on('reloadFull', () => window.location.reload()); // Obtener la instancia de Vue con toda la lógica integrada
|
|
344
359
|
let vueInstance = await obtenerInstanciaVue();
|
|
345
360
|
|
|
361
|
+
// Exponer función de recarga Vue por path para que los shims de módulos
|
|
362
|
+
// puedan disparar una actualización de instancia Vue cuando una dependencia
|
|
363
|
+
// (sampleFile.js, etc.) se actualiza vía HMR propagation.
|
|
364
|
+
if (window.__versaHMR) {
|
|
365
|
+
window.__versaHMR._reloadVueByPath = async path => {
|
|
366
|
+
try {
|
|
367
|
+
vueInstance = window.__VUE_APP__ || vueInstance;
|
|
368
|
+
// normalizedPath sin slash inicial (reloadComponent usa origin + '/' + path)
|
|
369
|
+
const normalizedPath = path.replace(/^\//, '');
|
|
370
|
+
const nameFile =
|
|
371
|
+
path.split('/').pop()?.replace('.js', '') ?? '';
|
|
372
|
+
await reloadComponent(vueInstance, {
|
|
373
|
+
normalizedPath,
|
|
374
|
+
nameFile,
|
|
375
|
+
});
|
|
376
|
+
} catch (err) {
|
|
377
|
+
console.error('❌ _reloadVueByPath failed:', err);
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
346
382
|
// Configurar listener para HMR de componentes Vue
|
|
347
383
|
socket.on('HRMVue', async (/** @type {ComponentInfo} */ data) => {
|
|
348
384
|
try {
|
|
@@ -372,12 +408,31 @@ async function initSocket(retries = 0) {
|
|
|
372
408
|
console.log('🔄 HRMHelper recibido:', data);
|
|
373
409
|
console.log('📋 Archivo modificado:', data.filePath);
|
|
374
410
|
|
|
411
|
+
// moduleId: identificador canónico del módulo en el registry
|
|
412
|
+
// El servidor lo envía como el path del output (ej: /public/utils/math.js)
|
|
413
|
+
const moduleId = data.moduleId || data.filePath;
|
|
414
|
+
|
|
375
415
|
if (data?.strategy) {
|
|
376
416
|
switch (data.strategy) {
|
|
377
417
|
case 'self-accept': {
|
|
418
|
+
// El módulo acepta su propio reemplazo (import.meta.hot.accept)
|
|
378
419
|
try {
|
|
379
420
|
const timestamp = Date.now();
|
|
380
|
-
|
|
421
|
+
// Usar moduleId (path absoluto con /) en lugar de data.filePath (relativo)
|
|
422
|
+
// data.filePath relativo resuelve mal desde /__versa/initHRM.js
|
|
423
|
+
const newModule = await import(
|
|
424
|
+
`${moduleId}?t=${timestamp}`
|
|
425
|
+
);
|
|
426
|
+
// También notificar al registry por si hay consumers adicionales
|
|
427
|
+
const registry = window.__versaHMR;
|
|
428
|
+
if (registry && registry.hasObservers(moduleId)) {
|
|
429
|
+
const exportedValue =
|
|
430
|
+
newModule.default !== undefined
|
|
431
|
+
? newModule.default
|
|
432
|
+
: newModule;
|
|
433
|
+
registry.notifyUpdate(moduleId, exportedValue);
|
|
434
|
+
}
|
|
435
|
+
console.log(`✅ Self-accept HMR: ${moduleId}`);
|
|
381
436
|
return;
|
|
382
437
|
} catch (error) {
|
|
383
438
|
reportErrorToServer(
|
|
@@ -391,9 +446,48 @@ async function initSocket(retries = 0) {
|
|
|
391
446
|
break;
|
|
392
447
|
}
|
|
393
448
|
case 'propagate': {
|
|
449
|
+
// Re-importar el módulo con cache-bust y notificar al registry
|
|
394
450
|
try {
|
|
395
451
|
const timestamp = Date.now();
|
|
396
|
-
|
|
452
|
+
// Usar moduleId (path absoluto con /) en lugar de data.filePath (relativo)
|
|
453
|
+
// data.filePath relativo resuelve mal desde /__versa/initHRM.js
|
|
454
|
+
const newModule = await import(
|
|
455
|
+
`${moduleId}?t=${timestamp}`
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
// Extraer el valor exportado (default o namespace)
|
|
459
|
+
const exportedValue =
|
|
460
|
+
newModule.default !== undefined
|
|
461
|
+
? newModule.default
|
|
462
|
+
: newModule;
|
|
463
|
+
|
|
464
|
+
// Notificar al registry de VersaHMR si hay observers registrados
|
|
465
|
+
const registry = window.__versaHMR;
|
|
466
|
+
if (registry && registry.hasObservers(moduleId)) {
|
|
467
|
+
const notified = registry.notifyUpdate(
|
|
468
|
+
moduleId,
|
|
469
|
+
exportedValue,
|
|
470
|
+
);
|
|
471
|
+
if (notified) {
|
|
472
|
+
console.log(
|
|
473
|
+
`✅ HMR sin recarga: ${moduleId} actualizado via registry`,
|
|
474
|
+
);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
// Si los callbacks fallaron, hacer full-reload como safety net
|
|
478
|
+
console.warn(
|
|
479
|
+
`⚠️ Callbacks del registry fallaron para ${moduleId}, haciendo full-reload`,
|
|
480
|
+
);
|
|
481
|
+
window.location.reload();
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Sin observers en registry → la re-importación ya actualizó
|
|
486
|
+
// el módulo en el scope de ES modules del browser.
|
|
487
|
+
// Los consumers que hicieron import dinámico obtendrán la nueva versión.
|
|
488
|
+
console.log(
|
|
489
|
+
`✅ HMR propagado: ${moduleId} (sin observers en registry, re-import completado)`,
|
|
490
|
+
);
|
|
397
491
|
return;
|
|
398
492
|
} catch (error) {
|
|
399
493
|
reportErrorToServer(
|
|
@@ -403,8 +497,10 @@ async function initSocket(retries = 0) {
|
|
|
403
497
|
: new Error(String(error)),
|
|
404
498
|
data,
|
|
405
499
|
);
|
|
500
|
+
// Fallback a full-reload en caso de error
|
|
501
|
+
window.location.reload();
|
|
502
|
+
return;
|
|
406
503
|
}
|
|
407
|
-
break;
|
|
408
504
|
}
|
|
409
505
|
case 'full-reload':
|
|
410
506
|
default: {
|
|
@@ -453,18 +549,30 @@ async function initSocket(retries = 0) {
|
|
|
453
549
|
break;
|
|
454
550
|
|
|
455
551
|
case 'propagate':
|
|
456
|
-
// Propagar la actualización
|
|
552
|
+
// Propagar la actualización: re-importar y notificar al registry
|
|
457
553
|
console.log(
|
|
458
554
|
'🔄 Propagando actualización a importadores',
|
|
459
555
|
);
|
|
460
556
|
try {
|
|
461
|
-
// Invalidar el módulo en el cache del navegador
|
|
462
|
-
// y dejar que los importadores se actualicen
|
|
463
557
|
const timestamp = Date.now();
|
|
464
|
-
await import(
|
|
465
|
-
|
|
466
|
-
'✅ Actualización propagada exitosamente',
|
|
558
|
+
const newModule = await import(
|
|
559
|
+
`${data.filePath}?t=${timestamp}`
|
|
467
560
|
);
|
|
561
|
+
const exportedValue =
|
|
562
|
+
newModule.default !== undefined
|
|
563
|
+
? newModule.default
|
|
564
|
+
: newModule;
|
|
565
|
+
const registry = window.__versaHMR;
|
|
566
|
+
if (registry && registry.hasObservers(moduleId)) {
|
|
567
|
+
registry.notifyUpdate(moduleId, exportedValue);
|
|
568
|
+
console.log(
|
|
569
|
+
'✅ Actualización propagada via registry',
|
|
570
|
+
);
|
|
571
|
+
} else {
|
|
572
|
+
console.log(
|
|
573
|
+
'✅ Re-import completado (sin observers en registry)',
|
|
574
|
+
);
|
|
575
|
+
}
|
|
468
576
|
return;
|
|
469
577
|
} catch (error) {
|
|
470
578
|
console.error(
|