wu-framework 1.1.3 → 1.1.5
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 +1115 -395
- package/dist/wu-framework.protected.js +24 -0
- package/package.json +32 -6
- package/scripts/build-protected.js +366 -0
- package/scripts/build.js +212 -0
- package/scripts/rollup-plugin-hex.js +143 -0
- package/src/adapters/angular.js +171 -0
- package/src/adapters/vue.js +41 -0
- package/src/core/wu-core.js +14 -32
- package/src/core/wu-manifest.js +13 -31
- package/src/core/wu-sandbox.js +259 -18
- package/src/core/wu-style-bridge.js +118 -312
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🔐 ROLLUP PLUGIN: HEX ENCODER
|
|
3
|
+
*
|
|
4
|
+
* Convierte el código JavaScript a formato hexadecimal
|
|
5
|
+
* para máxima ofuscación y protección del código fuente.
|
|
6
|
+
*
|
|
7
|
+
* El código resultante se decodifica y ejecuta en runtime.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Convierte string a hexadecimal
|
|
12
|
+
* @param {string} str - String a convertir
|
|
13
|
+
* @returns {string} String en hexadecimal
|
|
14
|
+
*/
|
|
15
|
+
function stringToHex(str) {
|
|
16
|
+
let hex = '';
|
|
17
|
+
for (let i = 0; i < str.length; i++) {
|
|
18
|
+
const charCode = str.charCodeAt(i);
|
|
19
|
+
hex += charCode.toString(16).padStart(2, '0');
|
|
20
|
+
}
|
|
21
|
+
return hex;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Genera el loader que decodifica y ejecuta el código hex
|
|
26
|
+
* @param {string} hexCode - Código en hexadecimal
|
|
27
|
+
* @param {boolean} isModule - Si es módulo ES
|
|
28
|
+
* @returns {string} Código JavaScript con loader
|
|
29
|
+
*/
|
|
30
|
+
function generateLoader(hexCode, isModule = true) {
|
|
31
|
+
// Loader minificado que decodifica hex y ejecuta
|
|
32
|
+
const loader = `
|
|
33
|
+
(function(){
|
|
34
|
+
var h="${hexCode}";
|
|
35
|
+
var d=function(x){
|
|
36
|
+
for(var s="",i=0;i<x.length;i+=2)
|
|
37
|
+
s+=String.fromCharCode(parseInt(x.substr(i,2),16));
|
|
38
|
+
return s;
|
|
39
|
+
};
|
|
40
|
+
var c=d(h);
|
|
41
|
+
${isModule ?
|
|
42
|
+
'var b=new Blob([c],{type:"text/javascript"});var u=URL.createObjectURL(b);import(u).then(function(m){URL.revokeObjectURL(u);Object.assign(window.__wu_exports__=window.__wu_exports__||{},m);});' :
|
|
43
|
+
'var s=document.createElement("script");s.text=c;document.head.appendChild(s);'
|
|
44
|
+
}
|
|
45
|
+
})();
|
|
46
|
+
`.trim().replace(/\s+/g, ' ');
|
|
47
|
+
|
|
48
|
+
return loader;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Genera loader asíncrono para módulos ES
|
|
53
|
+
* @param {string} hexCode - Código hexadecimal
|
|
54
|
+
* @returns {string} Código con loader async
|
|
55
|
+
*/
|
|
56
|
+
function generateAsyncLoader(hexCode) {
|
|
57
|
+
return `/*!
|
|
58
|
+
* Wu Framework - Hex Encoded Build
|
|
59
|
+
* Decode and execute at runtime
|
|
60
|
+
*/
|
|
61
|
+
const __wu_hex__="${hexCode}";
|
|
62
|
+
const __wu_decode__=(h)=>{let s="";for(let i=0;i<h.length;i+=2)s+=String.fromCharCode(parseInt(h.substr(i,2),16));return s;};
|
|
63
|
+
const __wu_code__=__wu_decode__(__wu_hex__);
|
|
64
|
+
const __wu_blob__=new Blob([__wu_code__],{type:"text/javascript"});
|
|
65
|
+
const __wu_url__=URL.createObjectURL(__wu_blob__);
|
|
66
|
+
const __wu_module__=await import(__wu_url__);
|
|
67
|
+
URL.revokeObjectURL(__wu_url__);
|
|
68
|
+
export const WuCore=__wu_module__.WuCore;
|
|
69
|
+
export const WuLoader=__wu_module__.WuLoader;
|
|
70
|
+
export const WuSandbox=__wu_module__.WuSandbox;
|
|
71
|
+
export const WuManifest=__wu_module__.WuManifest;
|
|
72
|
+
export const WuEventBus=__wu_module__.WuEventBus;
|
|
73
|
+
export const WuStore=__wu_module__.WuStore;
|
|
74
|
+
export const WuCache=__wu_module__.WuCache;
|
|
75
|
+
export const WuPlugin=__wu_module__.WuPlugin;
|
|
76
|
+
export const WuHooks=__wu_module__.WuHooks;
|
|
77
|
+
export const WuPerformance=__wu_module__.WuPerformance;
|
|
78
|
+
export const adapters=__wu_module__.adapters;
|
|
79
|
+
export default __wu_module__.default||__wu_module__.WuCore;
|
|
80
|
+
`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Plugin de Rollup para codificación hexadecimal
|
|
85
|
+
* @param {Object} options - Opciones del plugin
|
|
86
|
+
* @returns {Object} Plugin de Rollup
|
|
87
|
+
*/
|
|
88
|
+
export default function hexEncoder(options = {}) {
|
|
89
|
+
const {
|
|
90
|
+
encodeStrings = true,
|
|
91
|
+
encodeIdentifiers = true,
|
|
92
|
+
addLoader = true,
|
|
93
|
+
asyncLoader = true,
|
|
94
|
+
chunkSize = 0 // 0 = sin chunks, >0 = dividir en chunks
|
|
95
|
+
} = options;
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
name: 'hex-encoder',
|
|
99
|
+
|
|
100
|
+
renderChunk(code, chunk, outputOptions) {
|
|
101
|
+
console.log(`[HexEncoder] Processing chunk: ${chunk.fileName}`);
|
|
102
|
+
|
|
103
|
+
// Convertir código a hexadecimal
|
|
104
|
+
const hexCode = stringToHex(code);
|
|
105
|
+
|
|
106
|
+
console.log(`[HexEncoder] Original size: ${code.length} bytes`);
|
|
107
|
+
console.log(`[HexEncoder] Hex size: ${hexCode.length} bytes`);
|
|
108
|
+
|
|
109
|
+
if (!addLoader) {
|
|
110
|
+
// Solo devolver el hex sin loader
|
|
111
|
+
return {
|
|
112
|
+
code: `// Wu Framework - Hex Encoded\n// Decode with: hexToString("${hexCode}")\nexport default "${hexCode}";`,
|
|
113
|
+
map: null
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Generar código con loader
|
|
118
|
+
let finalCode;
|
|
119
|
+
|
|
120
|
+
if (asyncLoader) {
|
|
121
|
+
finalCode = generateAsyncLoader(hexCode);
|
|
122
|
+
} else {
|
|
123
|
+
finalCode = generateLoader(hexCode, outputOptions.format === 'es');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
console.log(`[HexEncoder] Final size: ${finalCode.length} bytes`);
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
code: finalCode,
|
|
130
|
+
map: null
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
generateBundle(outputOptions, bundle) {
|
|
135
|
+
console.log(`[HexEncoder] Build complete!`);
|
|
136
|
+
console.log(`[HexEncoder] Output format: ${outputOptions.format}`);
|
|
137
|
+
console.log(`[HexEncoder] Files generated: ${Object.keys(bundle).join(', ')}`);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Exportar utilidades para uso standalone
|
|
143
|
+
export { stringToHex, generateLoader, generateAsyncLoader };
|
package/src/adapters/angular.js
CHANGED
|
@@ -387,6 +387,175 @@ async function registerStandalone(appName, RootComponent, options = {}) {
|
|
|
387
387
|
}
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
+
/**
|
|
391
|
+
* Registra un componente Angular Elements (Web Component) como microfrontend
|
|
392
|
+
*
|
|
393
|
+
* @param {string} appName - Nombre único del microfrontend
|
|
394
|
+
* @param {Type<any>} Component - Componente Angular standalone
|
|
395
|
+
* @param {Object} options - Opciones adicionales
|
|
396
|
+
* @param {string} options.elementTag - Tag del custom element (default: basado en appName)
|
|
397
|
+
* @param {ApplicationConfig} options.appConfig - Configuración de la aplicación Angular
|
|
398
|
+
* @param {Function} options.onMount - Callback después de montar
|
|
399
|
+
* @param {Function} options.onUnmount - Callback antes de desmontar
|
|
400
|
+
* @param {boolean} options.standalone - Permitir ejecución standalone (default: true)
|
|
401
|
+
* @param {string} options.standaloneContainer - Selector para modo standalone (default: '#root')
|
|
402
|
+
*
|
|
403
|
+
* @example
|
|
404
|
+
* // Angular Elements (Web Components)
|
|
405
|
+
* import { App } from './app/app';
|
|
406
|
+
* import { appConfig } from './app/app.config';
|
|
407
|
+
*
|
|
408
|
+
* wuAngular.registerElement('mfe-angular', App, {
|
|
409
|
+
* elementTag: 'mfe-angular-content',
|
|
410
|
+
* appConfig
|
|
411
|
+
* });
|
|
412
|
+
*/
|
|
413
|
+
async function registerElement(appName, Component, options = {}) {
|
|
414
|
+
const {
|
|
415
|
+
elementTag = `${appName}-element`,
|
|
416
|
+
appConfig = {},
|
|
417
|
+
onMount = null,
|
|
418
|
+
onUnmount = null,
|
|
419
|
+
standalone = true,
|
|
420
|
+
standaloneContainer = '#root'
|
|
421
|
+
} = options;
|
|
422
|
+
|
|
423
|
+
let customElementRegistered = false;
|
|
424
|
+
|
|
425
|
+
// Función para inicializar Angular Elements
|
|
426
|
+
const initializeElement = async () => {
|
|
427
|
+
if (customElementRegistered) return true;
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
// Import dinámico de Angular
|
|
431
|
+
const [{ createApplication }, { createCustomElement }] = await Promise.all([
|
|
432
|
+
import('@angular/platform-browser'),
|
|
433
|
+
import('@angular/elements')
|
|
434
|
+
]);
|
|
435
|
+
|
|
436
|
+
// Crear aplicación Angular
|
|
437
|
+
const app = await createApplication(appConfig);
|
|
438
|
+
|
|
439
|
+
// Crear y registrar el custom element
|
|
440
|
+
const CustomElement = createCustomElement(Component, { injector: app.injector });
|
|
441
|
+
|
|
442
|
+
if (!customElements.get(elementTag)) {
|
|
443
|
+
customElements.define(elementTag, CustomElement);
|
|
444
|
+
console.log(`[WuAngular] ✅ Custom element registered: ${elementTag}`);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
customElementRegistered = true;
|
|
448
|
+
|
|
449
|
+
// Guardar referencia
|
|
450
|
+
adapterState.apps.set(`${appName}:element`, {
|
|
451
|
+
app,
|
|
452
|
+
elementTag,
|
|
453
|
+
CustomElement
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
return true;
|
|
457
|
+
} catch (error) {
|
|
458
|
+
console.error(`[WuAngular] Failed to initialize Angular Element:`, error);
|
|
459
|
+
throw error;
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
// Función de mount
|
|
464
|
+
const mountApp = async (container) => {
|
|
465
|
+
if (!container) {
|
|
466
|
+
console.error(`[WuAngular] Mount failed for ${appName}: container is null`);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
try {
|
|
471
|
+
// Asegurar que el elemento está registrado
|
|
472
|
+
await initializeElement();
|
|
473
|
+
|
|
474
|
+
// Crear el elemento custom
|
|
475
|
+
const element = document.createElement(elementTag);
|
|
476
|
+
element.setAttribute('data-wu-angular-element', appName);
|
|
477
|
+
|
|
478
|
+
// Limpiar y agregar al container
|
|
479
|
+
container.innerHTML = '';
|
|
480
|
+
container.appendChild(element);
|
|
481
|
+
|
|
482
|
+
// Guardar referencia del mount
|
|
483
|
+
adapterState.apps.set(appName, {
|
|
484
|
+
element,
|
|
485
|
+
container,
|
|
486
|
+
elementTag
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
console.log(`[WuAngular] ✅ ${appName} (element) mounted successfully`);
|
|
490
|
+
|
|
491
|
+
if (onMount) {
|
|
492
|
+
onMount(container, element);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return element;
|
|
496
|
+
} catch (error) {
|
|
497
|
+
console.error(`[WuAngular] Mount error for ${appName}:`, error);
|
|
498
|
+
throw error;
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
// Función de unmount
|
|
503
|
+
const unmountApp = async (container) => {
|
|
504
|
+
const instance = adapterState.apps.get(appName);
|
|
505
|
+
|
|
506
|
+
if (instance) {
|
|
507
|
+
try {
|
|
508
|
+
if (onUnmount) {
|
|
509
|
+
onUnmount(instance.container, instance.element);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Remover elemento del DOM
|
|
513
|
+
if (instance.element && instance.element.parentNode) {
|
|
514
|
+
instance.element.remove();
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
adapterState.apps.delete(appName);
|
|
518
|
+
|
|
519
|
+
console.log(`[WuAngular] ✅ ${appName} (element) unmounted successfully`);
|
|
520
|
+
} catch (error) {
|
|
521
|
+
console.error(`[WuAngular] Unmount error for ${appName}:`, error);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (container) {
|
|
526
|
+
container.innerHTML = '';
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
// Intentar registrar con Wu Framework
|
|
531
|
+
try {
|
|
532
|
+
const wu = await waitForWu(3000);
|
|
533
|
+
|
|
534
|
+
wu.define(appName, {
|
|
535
|
+
mount: mountApp,
|
|
536
|
+
unmount: unmountApp
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
console.log(`[WuAngular] ✅ ${appName} (element) registered with Wu Framework`);
|
|
540
|
+
return true;
|
|
541
|
+
|
|
542
|
+
} catch (error) {
|
|
543
|
+
console.warn(`[WuAngular] Wu Framework not available for ${appName}`);
|
|
544
|
+
|
|
545
|
+
if (standalone) {
|
|
546
|
+
const containerElement = document.querySelector(standaloneContainer);
|
|
547
|
+
|
|
548
|
+
if (containerElement) {
|
|
549
|
+
console.log(`[WuAngular] Running ${appName} in standalone mode`);
|
|
550
|
+
await mountApp(containerElement);
|
|
551
|
+
return true;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
390
559
|
/**
|
|
391
560
|
* Servicio Injectable para usar Wu Framework en Angular
|
|
392
561
|
*
|
|
@@ -621,6 +790,7 @@ function getWuSlotModuleConfig() {
|
|
|
621
790
|
export const wuAngular = {
|
|
622
791
|
register,
|
|
623
792
|
registerStandalone,
|
|
793
|
+
registerElement,
|
|
624
794
|
createWuService,
|
|
625
795
|
createWuSlotComponent,
|
|
626
796
|
getWuSlotModuleConfig,
|
|
@@ -632,6 +802,7 @@ export const wuAngular = {
|
|
|
632
802
|
export {
|
|
633
803
|
register,
|
|
634
804
|
registerStandalone,
|
|
805
|
+
registerElement,
|
|
635
806
|
createWuService,
|
|
636
807
|
createWuSlotComponent,
|
|
637
808
|
getWuSlotModuleConfig,
|
package/src/adapters/vue.js
CHANGED
|
@@ -169,6 +169,17 @@ async function register(appName, RootComponent, options = {}) {
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
try {
|
|
172
|
+
// Detectar si el container está dentro de un Shadow DOM
|
|
173
|
+
let shadowRoot = null;
|
|
174
|
+
let element = container;
|
|
175
|
+
while (element && element !== document.body) {
|
|
176
|
+
if (element.getRootNode && element.getRootNode() instanceof ShadowRoot) {
|
|
177
|
+
shadowRoot = element.getRootNode();
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
element = element.parentElement || element.host;
|
|
181
|
+
}
|
|
182
|
+
|
|
172
183
|
// Crear la aplicación Vue
|
|
173
184
|
const app = createApp(RootComponent, props);
|
|
174
185
|
|
|
@@ -184,6 +195,36 @@ async function register(appName, RootComponent, options = {}) {
|
|
|
184
195
|
// Montar
|
|
185
196
|
app.mount(container);
|
|
186
197
|
|
|
198
|
+
// Si está en Shadow DOM, copiar estilos de Vue al Shadow DOM
|
|
199
|
+
if (shadowRoot) {
|
|
200
|
+
// Esperar un poco para que Vue inyecte los estilos en el head
|
|
201
|
+
setTimeout(() => {
|
|
202
|
+
const vueStyles = document.querySelectorAll('style[data-vite-dev-id*="/' + appName + '/"], style[data-vite-dev-id*="\\' + appName + '\\"]');
|
|
203
|
+
vueStyles.forEach(style => {
|
|
204
|
+
// Verificar que no esté ya en el Shadow DOM
|
|
205
|
+
const viteId = style.getAttribute('data-vite-dev-id');
|
|
206
|
+
if (viteId && !shadowRoot.querySelector(`style[data-vite-dev-id="${viteId}"]`)) {
|
|
207
|
+
const clonedStyle = style.cloneNode(true);
|
|
208
|
+
shadowRoot.insertBefore(clonedStyle, shadowRoot.firstChild);
|
|
209
|
+
console.log(`[WuVue] ✅ Injected style into Shadow DOM: ${viteId}`);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// También copiar estilos que contengan rutas del app en el viteId
|
|
214
|
+
const allStyles = document.querySelectorAll('style[data-vite-dev-id]');
|
|
215
|
+
allStyles.forEach(style => {
|
|
216
|
+
const viteId = style.getAttribute('data-vite-dev-id');
|
|
217
|
+
if (viteId && (viteId.includes(`/${appName}/`) || viteId.includes(`\\${appName}\\`))) {
|
|
218
|
+
if (!shadowRoot.querySelector(`style[data-vite-dev-id="${viteId}"]`)) {
|
|
219
|
+
const clonedStyle = style.cloneNode(true);
|
|
220
|
+
shadowRoot.insertBefore(clonedStyle, shadowRoot.firstChild);
|
|
221
|
+
console.log(`[WuVue] ✅ Injected app style into Shadow DOM: ${viteId}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
}, 100);
|
|
226
|
+
}
|
|
227
|
+
|
|
187
228
|
// Guardar referencia
|
|
188
229
|
adapterState.apps.set(appName, { app, container });
|
|
189
230
|
|
package/src/core/wu-core.js
CHANGED
|
@@ -587,25 +587,12 @@ export class WuCore {
|
|
|
587
587
|
// 🏊 Acquire sandbox from pool (if configured)
|
|
588
588
|
const poolSandbox = this.sandboxPool.acquire(appName);
|
|
589
589
|
|
|
590
|
-
//
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
if (this.sandbox.styleBridge) {
|
|
597
|
-
// Pass app URL so StyleBridge can identify MFE's own styles
|
|
598
|
-
this.sandbox.styleBridge.setAppStyleMode(appName, styleMode, app.url);
|
|
599
|
-
console.log(`[Wu] 🎨 Style mode for ${appName}: ${styleMode}`);
|
|
600
|
-
|
|
601
|
-
// 📁 If folder is specified in manifest, register it for style detection
|
|
602
|
-
// This is needed when folder name differs from app name (e.g., folder="container", name="content")
|
|
603
|
-
const folderName = manifest?.folder || appName;
|
|
604
|
-
this.sandbox.styleBridge.setAppFolder(appName, `/${folderName}/`);
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
// 🛡️ Quantum sandbox creation
|
|
608
|
-
const sandbox = this.sandbox.create(appName, container);
|
|
590
|
+
// 🛡️ Quantum sandbox creation - pasar manifest con styleMode y URL de la app
|
|
591
|
+
const sandbox = this.sandbox.create(appName, container, {
|
|
592
|
+
manifest: app.manifest,
|
|
593
|
+
styleMode: app.manifest?.styleMode,
|
|
594
|
+
appUrl: app.url // Pasar URL de la app para filtrar estilos de apps fully-isolated
|
|
595
|
+
});
|
|
609
596
|
|
|
610
597
|
// 🪝 Execute afterLoad hooks
|
|
611
598
|
await this.hooks.execute('afterLoad', { appName, containerSelector, sandbox });
|
|
@@ -620,14 +607,6 @@ export class WuCore {
|
|
|
620
607
|
if (!lifecycle) {
|
|
621
608
|
throw new Error(`App ${appName} did not register with wu.define()`);
|
|
622
609
|
}
|
|
623
|
-
|
|
624
|
-
// 🎨 RE-INJECT STYLES: After remote module loads, Vite may have injected new styles
|
|
625
|
-
// We need to re-scan and inject any new styles into the Shadow DOM
|
|
626
|
-
if (sandbox.shadowRoot && this.sandbox.styleBridge) {
|
|
627
|
-
console.log(`[Wu] 🎨 Re-injecting styles after module load for ${appName}...`);
|
|
628
|
-
// Pass app URL so StyleBridge can identify MFE's own styles
|
|
629
|
-
await this.sandbox.styleBridge.injectStylesIntoShadow(sandbox.shadowRoot, appName, app.url);
|
|
630
|
-
}
|
|
631
610
|
}
|
|
632
611
|
|
|
633
612
|
// 🪝 Execute beforeMount hooks
|
|
@@ -1029,12 +1008,15 @@ export class WuCore {
|
|
|
1029
1008
|
const content = await response.text();
|
|
1030
1009
|
|
|
1031
1010
|
// 🚫 DETECT HTML FALLBACK: Check if server returned HTML instead of JS
|
|
1011
|
+
// Only check if content STARTS with HTML markers (trimmed), not if it contains them anywhere
|
|
1012
|
+
// This avoids false positives for Angular/React bundles that contain template strings
|
|
1013
|
+
const trimmedContent = content.trim().toLowerCase();
|
|
1032
1014
|
const isHtmlFallback =
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1015
|
+
trimmedContent.startsWith('<!doctype') ||
|
|
1016
|
+
trimmedContent.startsWith('<html') ||
|
|
1017
|
+
trimmedContent.startsWith('<head') ||
|
|
1018
|
+
trimmedContent.startsWith('<body') ||
|
|
1019
|
+
trimmedContent.startsWith('<!-');
|
|
1038
1020
|
|
|
1039
1021
|
if (isHtmlFallback) {
|
|
1040
1022
|
console.log(`[Wu] ❌ Path validation failed - Server returned HTML fallback page: ${url}`);
|
package/src/core/wu-manifest.js
CHANGED
|
@@ -43,19 +43,14 @@ export class WuManifest {
|
|
|
43
43
|
defineSchema() {
|
|
44
44
|
this.schemas.set('wu.json', {
|
|
45
45
|
required: ['name', 'entry'],
|
|
46
|
-
optional: ['wu'
|
|
46
|
+
optional: ['wu'],
|
|
47
47
|
wu: {
|
|
48
48
|
optional: ['exports', 'imports', 'routes', 'permissions'],
|
|
49
49
|
exports: 'object',
|
|
50
50
|
imports: 'array',
|
|
51
51
|
routes: 'array',
|
|
52
52
|
permissions: 'array'
|
|
53
|
-
}
|
|
54
|
-
// 🎨 styleMode: 'isolated' | 'shared' | 'auto'
|
|
55
|
-
// - isolated: No comparte estilos del padre (default, máximo aislamiento)
|
|
56
|
-
// - shared: Comparte todos los estilos del padre
|
|
57
|
-
// - auto: Solo comparte estilos de librerías específicas (element-plus, etc.)
|
|
58
|
-
styleMode: 'string'
|
|
53
|
+
}
|
|
59
54
|
});
|
|
60
55
|
}
|
|
61
56
|
|
|
@@ -317,11 +312,7 @@ export class WuManifest {
|
|
|
317
312
|
normalize(manifest) {
|
|
318
313
|
const normalized = {
|
|
319
314
|
name: manifest.name.trim(),
|
|
320
|
-
// 📁 folder: carpeta física del MFE (si difiere del name)
|
|
321
|
-
folder: manifest.folder?.trim() || manifest.name.trim(),
|
|
322
315
|
entry: this.normalizeEntry(manifest.entry),
|
|
323
|
-
// 🎨 styleMode: 'isolated' (default) | 'shared' | 'auto'
|
|
324
|
-
styleMode: this.normalizeStyleMode(manifest.styleMode),
|
|
325
316
|
wu: {
|
|
326
317
|
exports: manifest.wu?.exports || {},
|
|
327
318
|
imports: manifest.wu?.imports || [],
|
|
@@ -330,6 +321,17 @@ export class WuManifest {
|
|
|
330
321
|
}
|
|
331
322
|
};
|
|
332
323
|
|
|
324
|
+
// Preservar campos opcionales del manifest (styleMode, version, folder, etc.)
|
|
325
|
+
if (manifest.styleMode) {
|
|
326
|
+
normalized.styleMode = manifest.styleMode;
|
|
327
|
+
}
|
|
328
|
+
if (manifest.version) {
|
|
329
|
+
normalized.version = manifest.version;
|
|
330
|
+
}
|
|
331
|
+
if (manifest.folder) {
|
|
332
|
+
normalized.folder = manifest.folder;
|
|
333
|
+
}
|
|
334
|
+
|
|
333
335
|
// Normalizar exports
|
|
334
336
|
if (normalized.wu.exports) {
|
|
335
337
|
const normalizedExports = {};
|
|
@@ -351,26 +353,6 @@ export class WuManifest {
|
|
|
351
353
|
return normalized;
|
|
352
354
|
}
|
|
353
355
|
|
|
354
|
-
/**
|
|
355
|
-
* 🎨 Normalizar styleMode
|
|
356
|
-
* @param {string} styleMode - Modo de estilos
|
|
357
|
-
* @returns {'isolated' | 'fully-isolated' | 'shared' | 'auto'} styleMode normalizado
|
|
358
|
-
*
|
|
359
|
-
* Modos disponibles:
|
|
360
|
-
* - 'isolated': MFE solo tiene sus propios estilos (CSS variables SÍ se heredan)
|
|
361
|
-
* - 'fully-isolated': Aislamiento TOTAL - resetea CSS variables del padre (ÚNICO en wu-framework!)
|
|
362
|
-
* - 'shared': Comparte todos los estilos del padre
|
|
363
|
-
* - 'auto': Comparte solo librerías específicas (element-plus, etc.)
|
|
364
|
-
*/
|
|
365
|
-
normalizeStyleMode(styleMode) {
|
|
366
|
-
const validModes = ['isolated', 'fully-isolated', 'shared', 'auto'];
|
|
367
|
-
if (styleMode && validModes.includes(styleMode)) {
|
|
368
|
-
return styleMode;
|
|
369
|
-
}
|
|
370
|
-
// Default: isolated (máximo aislamiento sin romper CSS variables)
|
|
371
|
-
return 'isolated';
|
|
372
|
-
}
|
|
373
|
-
|
|
374
356
|
/**
|
|
375
357
|
* Normalizar entry path
|
|
376
358
|
* @param {string} entry - Entry path
|