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
|
@@ -4,25 +4,37 @@
|
|
|
4
4
|
* Comparte automáticamente estilos de node_modules entre padre e hijos Shadow DOM
|
|
5
5
|
* Soluciona el problema de aislamiento CSS en microfrontends
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* MODOS DE INYECCIÓN DE ESTILOS:
|
|
8
|
+
* ================================
|
|
9
|
+
*
|
|
10
|
+
* 1. "shared" (default):
|
|
11
|
+
* - Inyecta TODOS los estilos del documento padre en el Shadow DOM
|
|
12
|
+
* - Incluye: librerías (Element Plus, Vue Flow), estilos globales, etc.
|
|
13
|
+
* - Ideal para: Apps que necesitan compartir un design system común
|
|
14
|
+
* - Riesgo de colisiones: ALTO
|
|
15
|
+
*
|
|
16
|
+
* 2. "isolated":
|
|
17
|
+
* - NO inyecta estilos externos
|
|
18
|
+
* - Usa el encapsulamiento NATIVO del Shadow DOM
|
|
19
|
+
* - La app debe incluir sus propios estilos (CSS-in-JS, scoped styles, etc.)
|
|
20
|
+
* - Ideal para: Apps con estilos completamente independientes
|
|
21
|
+
* - Riesgo de colisiones: NINGUNO
|
|
22
|
+
*
|
|
23
|
+
* 3. "fully-isolated":
|
|
24
|
+
* - Inyecta SOLO los estilos propios de la micro-app específica
|
|
25
|
+
* - Detecta estilos por patrón: packages/appName/src/
|
|
26
|
+
* - Usa MutationObserver para HMR de Vite
|
|
27
|
+
* - Ideal para: Apps que necesitan sus estilos pero no los globales
|
|
28
|
+
* - Riesgo de colisiones: NINGUNO
|
|
12
29
|
*/
|
|
13
30
|
|
|
14
31
|
export class WuStyleBridge {
|
|
15
32
|
constructor() {
|
|
16
33
|
this.sharedStyles = new Map();
|
|
17
34
|
this.styleObserver = null;
|
|
18
|
-
this.
|
|
35
|
+
this.fullyIsolatedApps = new Map(); // Mapa de appName -> appUrl para apps con fully-isolated
|
|
19
36
|
this.config = {
|
|
20
|
-
//
|
|
21
|
-
// 'fully-isolated' = aislamiento total incluyendo CSS variables
|
|
22
|
-
// 'shared' = compartir estilos del padre
|
|
23
|
-
// 'auto' = compartir solo librerías específicas
|
|
24
|
-
defaultMode: 'isolated',
|
|
25
|
-
// Librerías que se deben compartir automáticamente (solo en modo 'auto')
|
|
37
|
+
// Librerías que se deben compartir automáticamente
|
|
26
38
|
autoShareLibraries: [
|
|
27
39
|
'element-plus',
|
|
28
40
|
'vue-flow',
|
|
@@ -32,56 +44,94 @@ export class WuStyleBridge {
|
|
|
32
44
|
'normalize.css',
|
|
33
45
|
'reset.css'
|
|
34
46
|
],
|
|
35
|
-
// Patrones de URLs a compartir
|
|
47
|
+
// Patrones de URLs a compartir
|
|
36
48
|
sharePatterns: [
|
|
37
49
|
/\/node_modules\//,
|
|
38
50
|
/\/@vite\/client/,
|
|
39
51
|
/\/dist\/index\.css$/,
|
|
40
52
|
/\/dist\/style\.css$/
|
|
41
53
|
],
|
|
54
|
+
// Modo de compartición
|
|
55
|
+
mode: 'auto', // 'auto' | 'manual' | 'all'
|
|
42
56
|
// Caché de estilos
|
|
43
57
|
cacheEnabled: true
|
|
44
58
|
};
|
|
45
59
|
|
|
46
|
-
console.log('[WuStyleBridge] 🎨 Style sharing system initialized
|
|
60
|
+
console.log('[WuStyleBridge] 🎨 Style sharing system initialized');
|
|
47
61
|
}
|
|
48
62
|
|
|
49
63
|
/**
|
|
50
|
-
* 🛡️
|
|
64
|
+
* 🛡️ REGISTRAR APP FULLY-ISOLATED: Registra una app con fully-isolated para filtrar sus estilos
|
|
51
65
|
* @param {string} appName - Nombre de la app
|
|
52
|
-
* @param {
|
|
53
|
-
* @param {string} appUrl - URL base del MFE (opcional)
|
|
66
|
+
* @param {string} appUrl - URL base de la app
|
|
54
67
|
*/
|
|
55
|
-
|
|
56
|
-
this.
|
|
57
|
-
|
|
58
|
-
this.appUrls = this.appUrls || new Map();
|
|
59
|
-
this.appUrls.set(appName, appUrl);
|
|
60
|
-
}
|
|
61
|
-
console.log(`[WuStyleBridge] 🎨 Style mode for ${appName}: ${mode}${appUrl ? ` (url: ${appUrl})` : ''}`);
|
|
68
|
+
registerFullyIsolatedApp(appName, appUrl) {
|
|
69
|
+
this.fullyIsolatedApps.set(appName, appUrl);
|
|
70
|
+
console.log(`[WuStyleBridge] 🛡️ Registered fully-isolated app: ${appName} (${appUrl})`);
|
|
62
71
|
}
|
|
63
72
|
|
|
64
73
|
/**
|
|
65
|
-
*
|
|
66
|
-
* @param {string}
|
|
67
|
-
* @returns {
|
|
74
|
+
* 🔍 VERIFICAR SI ESTILO ES DE APP FULLY-ISOLATED: Verifica si un estilo proviene de una app con fully-isolated
|
|
75
|
+
* @param {string|Object|HTMLElement} styleUrlOrElement - URL del estilo, objeto de estilo, o elemento DOM
|
|
76
|
+
* @returns {boolean}
|
|
68
77
|
*/
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
78
|
+
isStyleFromFullyIsolatedApp(styleUrlOrElement) {
|
|
79
|
+
let url = '';
|
|
80
|
+
|
|
81
|
+
// Si es un string, usar directamente
|
|
82
|
+
if (typeof styleUrlOrElement === 'string') {
|
|
83
|
+
url = styleUrlOrElement;
|
|
84
|
+
}
|
|
85
|
+
// Si es un elemento DOM (HTMLElement) - verificar si tiene getAttribute (método común de elementos DOM)
|
|
86
|
+
else if (styleUrlOrElement && typeof styleUrlOrElement.getAttribute === 'function') {
|
|
87
|
+
// Obtener data-vite-dev-id o href del elemento
|
|
88
|
+
url = styleUrlOrElement.getAttribute('data-vite-dev-id') || styleUrlOrElement.href || '';
|
|
89
|
+
}
|
|
90
|
+
// Si es un objeto con propiedades
|
|
91
|
+
else if (styleUrlOrElement) {
|
|
92
|
+
if (styleUrlOrElement.href) {
|
|
93
|
+
url = styleUrlOrElement.href;
|
|
94
|
+
} else if (styleUrlOrElement.viteId) {
|
|
95
|
+
url = styleUrlOrElement.viteId;
|
|
96
|
+
} else if (styleUrlOrElement.element) {
|
|
97
|
+
if (typeof styleUrlOrElement.element.getAttribute === 'function') {
|
|
98
|
+
url = styleUrlOrElement.element.getAttribute('data-vite-dev-id') || styleUrlOrElement.element.href || '';
|
|
99
|
+
} else if (styleUrlOrElement.element.href) {
|
|
100
|
+
url = styleUrlOrElement.element.href;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
72
104
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
105
|
+
if (!url || url.trim() === '') return false;
|
|
106
|
+
|
|
107
|
+
// Normalizar la URL para comparación (convertir backslashes a forward slashes)
|
|
108
|
+
const normalizedUrl = url.replace(/\\/g, '/').toLowerCase();
|
|
109
|
+
|
|
110
|
+
// Verificar si la URL pertenece a alguna app con fully-isolated
|
|
111
|
+
for (const [appName, appUrl] of this.fullyIsolatedApps.entries()) {
|
|
112
|
+
const normalizedAppUrl = appUrl.replace(/\\/g, '/').toLowerCase();
|
|
113
|
+
const normalizedAppName = appName.toLowerCase();
|
|
114
|
+
|
|
115
|
+
// Verificar si la URL contiene la URL base de la app (ej: http://localhost:4001)
|
|
116
|
+
if (normalizedAppUrl && normalizedUrl.includes(normalizedAppUrl)) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Verificar si la URL contiene rutas del app en el sistema de archivos
|
|
121
|
+
// Ej: C:/Users/.../header/src/... o /header/src/...
|
|
122
|
+
// Patrón: cualquier ruta que contenga /header/ o \header\
|
|
123
|
+
const appPathPattern = new RegExp(`[/\\\\]${normalizedAppName}[/\\\\]`, 'i');
|
|
124
|
+
if (appPathPattern.test(normalizedUrl)) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return false;
|
|
80
130
|
}
|
|
81
131
|
|
|
82
132
|
/**
|
|
83
133
|
* 🔍 DETECTAR ESTILOS: Escanea todos los estilos del documento
|
|
84
|
-
* @returns {Array} Lista de estilos detectados
|
|
134
|
+
* @returns {Array} Lista de estilos detectados (filtrados para excluir apps con fully-isolated)
|
|
85
135
|
*/
|
|
86
136
|
detectDocumentStyles() {
|
|
87
137
|
const styles = [];
|
|
@@ -89,6 +139,11 @@ export class WuStyleBridge {
|
|
|
89
139
|
// 1. Detectar TODOS los <link> tags de CSS
|
|
90
140
|
const linkTags = document.querySelectorAll('link[rel="stylesheet"]');
|
|
91
141
|
linkTags.forEach((link) => {
|
|
142
|
+
// Filtrar estilos de apps con fully-isolated
|
|
143
|
+
if (this.isStyleFromFullyIsolatedApp(link)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
92
147
|
styles.push({
|
|
93
148
|
type: 'link',
|
|
94
149
|
href: link.href,
|
|
@@ -108,6 +163,12 @@ export class WuStyleBridge {
|
|
|
108
163
|
const viteId = style.getAttribute('data-vite-dev-id');
|
|
109
164
|
const content = style.textContent;
|
|
110
165
|
|
|
166
|
+
// Filtrar estilos de apps con fully-isolated (después de obtener viteId para mejor detección)
|
|
167
|
+
if (this.isStyleFromFullyIsolatedApp(style) || (viteId && this.isStyleFromFullyIsolatedApp(viteId))) {
|
|
168
|
+
console.log(`[WuStyleBridge] 🛡️ Filtered out style from fully-isolated app: ${viteId || 'unknown'}`);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
111
172
|
// Incluir todos los estilos con contenido
|
|
112
173
|
if (content && content.trim().length > 0) {
|
|
113
174
|
styles.push({
|
|
@@ -180,61 +241,42 @@ export class WuStyleBridge {
|
|
|
180
241
|
}
|
|
181
242
|
|
|
182
243
|
/**
|
|
183
|
-
* 🌉 INYECTAR ESTILOS EN SHADOW DOM: Clona estilos al Shadow DOM
|
|
244
|
+
* 🌉 INYECTAR ESTILOS EN SHADOW DOM: Clona estilos al Shadow DOM
|
|
184
245
|
* @param {ShadowRoot} shadowRoot - Shadow DOM donde inyectar
|
|
185
246
|
* @param {string} appName - Nombre de la app
|
|
186
|
-
* @param {string}
|
|
247
|
+
* @param {string} styleMode - Modo de estilos: 'shared', 'isolated', 'fully-isolated'
|
|
187
248
|
* @returns {Promise<number>}
|
|
188
249
|
*/
|
|
189
|
-
async injectStylesIntoShadow(shadowRoot, appName,
|
|
250
|
+
async injectStylesIntoShadow(shadowRoot, appName, styleMode) {
|
|
190
251
|
if (!shadowRoot) {
|
|
191
252
|
console.warn('[WuStyleBridge] ⚠️ No shadow root provided');
|
|
192
253
|
return 0;
|
|
193
254
|
}
|
|
194
255
|
|
|
195
|
-
// 🛡️
|
|
196
|
-
|
|
197
|
-
// Use stored URL if not provided
|
|
198
|
-
const resolvedAppUrl = appUrl || this.getAppUrl(appName);
|
|
199
|
-
console.log(`[WuStyleBridge] 🌉 Injecting styles into ${appName} (mode: ${styleMode}, url: ${resolvedAppUrl || 'none'})...`);
|
|
200
|
-
|
|
201
|
-
// 🔒 FULLY-ISOLATED MODE: Inject CSS variables reset FIRST
|
|
256
|
+
// 🛡️ MODO FULLY-ISOLATED: No inyectar ningún estilo compartido
|
|
257
|
+
// Los estilos propios se manejan en wu-sandbox.js con injectOwnStylesToShadow
|
|
202
258
|
if (styleMode === 'fully-isolated') {
|
|
203
|
-
|
|
259
|
+
console.log(`[WuStyleBridge] 🛡️ Style mode "fully-isolated" for ${appName}, skipping shared style injection`);
|
|
260
|
+
return 0;
|
|
204
261
|
}
|
|
205
262
|
|
|
263
|
+
// 🔒 MODO ISOLATED: No inyectar estilos externos - usar encapsulamiento nativo de Shadow DOM
|
|
264
|
+
// La app debe manejar sus propios estilos (CSS-in-JS, scoped styles, imports directos)
|
|
265
|
+
if (styleMode === 'isolated') {
|
|
266
|
+
console.log(`[WuStyleBridge] 🔒 Style mode "isolated" for ${appName}, using native Shadow DOM encapsulation (no external styles)`);
|
|
267
|
+
return 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// 🌐 MODO SHARED (default): Inyectar todos los estilos compartidos del documento
|
|
271
|
+
console.log(`[WuStyleBridge] 🌐 Style mode "shared" for ${appName}, injecting all shared styles...`);
|
|
272
|
+
|
|
206
273
|
// Detectar estilos del documento
|
|
207
274
|
const styles = this.detectDocumentStyles();
|
|
208
275
|
let injectedCount = 0;
|
|
209
276
|
|
|
210
|
-
// Inyectar cada estilo
|
|
277
|
+
// Inyectar cada estilo
|
|
211
278
|
for (const style of styles) {
|
|
212
279
|
try {
|
|
213
|
-
const styleSource = style.href || style.viteId || '';
|
|
214
|
-
|
|
215
|
-
// 🎯 DETECTAR SI EL ESTILO PERTENECE AL MFE
|
|
216
|
-
const isOwnStyle = this.isStyleFromApp(styleSource, appName, resolvedAppUrl);
|
|
217
|
-
|
|
218
|
-
// 🛡️ En modo 'isolated' o 'fully-isolated': SOLO inyectar estilos propios del MFE
|
|
219
|
-
if (styleMode === 'isolated' || styleMode === 'fully-isolated') {
|
|
220
|
-
if (!isOwnStyle) {
|
|
221
|
-
const shortSource = styleSource.split(/[/\\]/).slice(-2).join('/');
|
|
222
|
-
console.log(`[WuStyleBridge] 🚫 ${appName} skipping foreign style: ${shortSource}`);
|
|
223
|
-
continue; // Skip parent/shell styles
|
|
224
|
-
}
|
|
225
|
-
console.log(`[WuStyleBridge] 🎨 ${appName} own style: ${styleSource.split(/[/\\]/).slice(-2).join('/')}`);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// 🎯 En modo 'auto': compartir librerías + estilos propios
|
|
229
|
-
if (styleMode === 'auto') {
|
|
230
|
-
const shouldShare = this.shouldShareStyle(styleSource);
|
|
231
|
-
if (!shouldShare && !isOwnStyle) {
|
|
232
|
-
continue; // Skip this style
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// 'shared' mode: inject everything
|
|
237
|
-
|
|
238
280
|
switch (style.type) {
|
|
239
281
|
case 'link':
|
|
240
282
|
await this.injectLinkStyle(shadowRoot, style);
|
|
@@ -256,246 +298,10 @@ export class WuStyleBridge {
|
|
|
256
298
|
}
|
|
257
299
|
}
|
|
258
300
|
|
|
259
|
-
console.log(`[WuStyleBridge] ✅ Injected ${injectedCount} styles into ${appName}`);
|
|
301
|
+
console.log(`[WuStyleBridge] ✅ Injected ${injectedCount} shared styles into ${appName}`);
|
|
260
302
|
return injectedCount;
|
|
261
303
|
}
|
|
262
304
|
|
|
263
|
-
/**
|
|
264
|
-
* 🔒 INJECT CSS VARIABLES RESET: Resetea las CSS variables heredadas del padre
|
|
265
|
-
* Este método detecta todas las CSS variables definidas en el documento padre
|
|
266
|
-
* y las resetea a 'initial' dentro del Shadow DOM para lograr aislamiento total.
|
|
267
|
-
*
|
|
268
|
-
* @param {ShadowRoot} shadowRoot - Shadow DOM donde inyectar el reset
|
|
269
|
-
* @param {string} appName - Nombre de la app para logging
|
|
270
|
-
*/
|
|
271
|
-
injectCSSVariablesReset(shadowRoot, appName) {
|
|
272
|
-
// Verificar si ya existe el reset
|
|
273
|
-
const existingReset = shadowRoot.querySelector('style[data-wu-css-reset="true"]');
|
|
274
|
-
if (existingReset) {
|
|
275
|
-
console.log(`[WuStyleBridge] ⏭️ CSS variables reset already exists for ${appName}`);
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// 🔍 Detectar todas las CSS variables del documento padre
|
|
280
|
-
const parentVariables = this.detectParentCSSVariables();
|
|
281
|
-
|
|
282
|
-
if (parentVariables.length === 0) {
|
|
283
|
-
console.log(`[WuStyleBridge] 🔒 No parent CSS variables to reset for ${appName}`);
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// 🎨 Generar CSS reset
|
|
288
|
-
const resetCSS = this.generateCSSVariablesReset(parentVariables);
|
|
289
|
-
|
|
290
|
-
// Crear style tag con el reset
|
|
291
|
-
const resetStyle = document.createElement('style');
|
|
292
|
-
resetStyle.setAttribute('data-wu-css-reset', 'true');
|
|
293
|
-
resetStyle.setAttribute('data-wu-app', appName);
|
|
294
|
-
resetStyle.textContent = resetCSS;
|
|
295
|
-
|
|
296
|
-
// Insertar al PRINCIPIO del shadow root (antes de otros estilos)
|
|
297
|
-
shadowRoot.insertBefore(resetStyle, shadowRoot.firstChild);
|
|
298
|
-
|
|
299
|
-
console.log(`[WuStyleBridge] 🔒 CSS variables reset injected for ${appName} (${parentVariables.length} variables blocked)`);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* 🔍 DETECT PARENT CSS VARIABLES: Detecta todas las CSS variables del documento padre
|
|
304
|
-
* @returns {Array<{name: string, value: string}>} Lista de variables detectadas
|
|
305
|
-
*/
|
|
306
|
-
detectParentCSSVariables() {
|
|
307
|
-
const variables = [];
|
|
308
|
-
const seen = new Set();
|
|
309
|
-
|
|
310
|
-
try {
|
|
311
|
-
// 1. Buscar en :root y html
|
|
312
|
-
const rootStyles = getComputedStyle(document.documentElement);
|
|
313
|
-
|
|
314
|
-
// 2. Buscar en todos los stylesheets del documento
|
|
315
|
-
for (const sheet of document.styleSheets) {
|
|
316
|
-
try {
|
|
317
|
-
// Algunos stylesheets (cross-origin) no permiten acceso
|
|
318
|
-
const rules = sheet.cssRules || sheet.rules;
|
|
319
|
-
if (!rules) continue;
|
|
320
|
-
|
|
321
|
-
for (const rule of rules) {
|
|
322
|
-
// Solo procesar reglas de estilo
|
|
323
|
-
if (rule.type !== CSSRule.STYLE_RULE) continue;
|
|
324
|
-
|
|
325
|
-
// Buscar reglas que definen variables (:root, html, body, *)
|
|
326
|
-
const selector = rule.selectorText?.toLowerCase() || '';
|
|
327
|
-
if (selector === ':root' || selector === 'html' || selector === 'body' || selector === '*') {
|
|
328
|
-
const style = rule.style;
|
|
329
|
-
for (let i = 0; i < style.length; i++) {
|
|
330
|
-
const prop = style[i];
|
|
331
|
-
// Las CSS variables empiezan con --
|
|
332
|
-
if (prop.startsWith('--') && !seen.has(prop)) {
|
|
333
|
-
seen.add(prop);
|
|
334
|
-
variables.push({
|
|
335
|
-
name: prop,
|
|
336
|
-
value: style.getPropertyValue(prop).trim()
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
} catch (e) {
|
|
343
|
-
// Ignorar errores de CORS en stylesheets externos
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// 3. También detectar variables inline en el html element
|
|
348
|
-
const htmlStyle = document.documentElement.getAttribute('style') || '';
|
|
349
|
-
const inlineVarMatches = htmlStyle.matchAll(/--([\w-]+)\s*:\s*([^;]+)/g);
|
|
350
|
-
for (const match of inlineVarMatches) {
|
|
351
|
-
const name = `--${match[1]}`;
|
|
352
|
-
if (!seen.has(name)) {
|
|
353
|
-
seen.add(name);
|
|
354
|
-
variables.push({
|
|
355
|
-
name,
|
|
356
|
-
value: match[2].trim()
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
} catch (error) {
|
|
362
|
-
console.warn('[WuStyleBridge] ⚠️ Error detecting CSS variables:', error);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
return variables;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* 🎨 GENERATE CSS VARIABLES RESET: Genera el CSS que resetea las variables
|
|
370
|
-
* @param {Array<{name: string, value: string}>} variables - Variables a resetear
|
|
371
|
-
* @returns {string} CSS con el reset
|
|
372
|
-
*/
|
|
373
|
-
generateCSSVariablesReset(variables) {
|
|
374
|
-
// 🔒 TÉCNICA: Redefinir cada variable como "unset" dentro del :host
|
|
375
|
-
// Esto rompe la cadena de herencia de CSS custom properties
|
|
376
|
-
//
|
|
377
|
-
// Problema: CSS variables se heredan a través de Shadow DOM por spec
|
|
378
|
-
// Solución: Redefinirlas explícitamente para "sobrescribir" el valor heredado
|
|
379
|
-
//
|
|
380
|
-
// Usamos valores por defecto del navegador para colores comunes
|
|
381
|
-
// y cadena vacía para otros
|
|
382
|
-
|
|
383
|
-
const resetRules = variables.map(v => {
|
|
384
|
-
const name = v.name;
|
|
385
|
-
// Detectar tipo de variable por su nombre y asignar valor neutro
|
|
386
|
-
if (name.includes('color') || name.includes('bg')) {
|
|
387
|
-
return ` ${name}: transparent;`;
|
|
388
|
-
} else if (name.includes('size') || name.includes('width') || name.includes('height')) {
|
|
389
|
-
return ` ${name}: auto;`;
|
|
390
|
-
} else if (name.includes('font')) {
|
|
391
|
-
return ` ${name}: inherit;`;
|
|
392
|
-
} else {
|
|
393
|
-
// Para otras variables, usar valor vacío que las invalida
|
|
394
|
-
return ` ${name}: initial;`;
|
|
395
|
-
}
|
|
396
|
-
}).join('\n');
|
|
397
|
-
|
|
398
|
-
return `
|
|
399
|
-
/* 🔒 WU-FRAMEWORK: CSS Variables Reset (fully-isolated mode)
|
|
400
|
-
* Este estilo BLOQUEA las CSS variables heredadas del padre
|
|
401
|
-
* para lograr un aislamiento visual completo.
|
|
402
|
-
* Variables bloqueadas: ${variables.length}
|
|
403
|
-
*
|
|
404
|
-
* El MFE debe definir sus propias variables CSS si las necesita.
|
|
405
|
-
* Las variables del padre/shell NO afectarán este componente.
|
|
406
|
-
*/
|
|
407
|
-
|
|
408
|
-
:host {
|
|
409
|
-
/* 🛡️ Bloquear herencia de propiedades CSS del padre */
|
|
410
|
-
all: initial;
|
|
411
|
-
display: block;
|
|
412
|
-
|
|
413
|
-
/* 🔒 Redefinir variables del padre como valores neutros */
|
|
414
|
-
${resetRules}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/* Restaurar herencia normal DENTRO del shadow DOM */
|
|
418
|
-
:host * {
|
|
419
|
-
all: revert;
|
|
420
|
-
}
|
|
421
|
-
`.trim();
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
/**
|
|
425
|
-
* 🔗 SET APP FOLDER: Asocia una carpeta del sistema de archivos con un MFE
|
|
426
|
-
* @param {string} appName - Nombre de la app
|
|
427
|
-
* @param {string} folderPath - Ruta de la carpeta (extraída de data-vite-dev-id)
|
|
428
|
-
*/
|
|
429
|
-
setAppFolder(appName, folderPath) {
|
|
430
|
-
this.appFolders = this.appFolders || new Map();
|
|
431
|
-
this.appFolders.set(appName, folderPath.toLowerCase());
|
|
432
|
-
console.log(`[WuStyleBridge] 📁 Folder for ${appName}: ${folderPath}`);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* 🔍 DETECTAR SI UN ESTILO PERTENECE A UN MFE
|
|
437
|
-
* @param {string} styleSource - URL o viteId del estilo
|
|
438
|
-
* @param {string} appName - Nombre de la app
|
|
439
|
-
* @param {string} appUrl - URL base del MFE
|
|
440
|
-
* @returns {boolean}
|
|
441
|
-
*/
|
|
442
|
-
isStyleFromApp(styleSource, appName, appUrl) {
|
|
443
|
-
if (!styleSource) return false;
|
|
444
|
-
|
|
445
|
-
const source = styleSource.toLowerCase();
|
|
446
|
-
|
|
447
|
-
// 🎯 PRIORITY 1: Verificar puerto del MFE en localhost URL
|
|
448
|
-
// Ej: "http://localhost:3001/src/style.css" pertenece a MFE en puerto 3001
|
|
449
|
-
if (appUrl) {
|
|
450
|
-
try {
|
|
451
|
-
const appPort = new URL(appUrl).port;
|
|
452
|
-
if (appPort) {
|
|
453
|
-
// Si el estilo viene de localhost con el puerto del MFE
|
|
454
|
-
if (source.includes(`localhost:${appPort}`) ||
|
|
455
|
-
source.includes(`127.0.0.1:${appPort}`)) {
|
|
456
|
-
console.log(`[WuStyleBridge] ✅ Port match: ${appPort} for ${appName}`);
|
|
457
|
-
return true;
|
|
458
|
-
}
|
|
459
|
-
// Si el estilo viene de localhost pero con OTRO puerto, NO es de este MFE
|
|
460
|
-
const portMatch = source.match(/localhost:(\d+)|127\.0\.0\.1:(\d+)/);
|
|
461
|
-
if (portMatch) {
|
|
462
|
-
const stylePort = portMatch[1] || portMatch[2];
|
|
463
|
-
if (stylePort && stylePort !== appPort) {
|
|
464
|
-
return false;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
} catch (e) {
|
|
469
|
-
// Ignore URL parse errors
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// 🎯 PRIORITY 2: Usar la carpeta registrada del MFE
|
|
474
|
-
// Cuando el MFE carga, detectamos su carpeta y la guardamos
|
|
475
|
-
const appFolder = this.appFolders?.get(appName);
|
|
476
|
-
if (appFolder && source.includes(appFolder)) {
|
|
477
|
-
console.log(`[WuStyleBridge] ✅ Folder match for ${appName}`);
|
|
478
|
-
return true;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
// 🎯 PRIORITY 3: Para file paths de Vite (data-vite-dev-id)
|
|
482
|
-
// Verificar si el path contiene la carpeta registrada del MFE
|
|
483
|
-
// La carpeta ya fue registrada desde wu-core.js con el valor del manifest
|
|
484
|
-
// Solo comparar, NO auto-detectar ni sobrescribir
|
|
485
|
-
if (appFolder) {
|
|
486
|
-
// El path debe contener la carpeta del MFE (e.g., /container/ o /header/)
|
|
487
|
-
// Normalizar la búsqueda para Windows y Unix paths
|
|
488
|
-
const folderPattern = appFolder.replace(/\//g, '[/\\\\]');
|
|
489
|
-
const folderRegex = new RegExp(folderPattern, 'i');
|
|
490
|
-
if (folderRegex.test(source)) {
|
|
491
|
-
console.log(`[WuStyleBridge] ✅ Folder regex match for ${appName}: ${appFolder}`);
|
|
492
|
-
return true;
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
return false;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
305
|
/**
|
|
500
306
|
* 🔗 INYECTAR LINK STYLE: Clona <link> tag al Shadow DOM
|
|
501
307
|
* @param {ShadowRoot} shadowRoot
|