Helpers
Styles
Merge config Tailwind cross-MFE, sanitizzazione CSS, iniezione/rimozione di stili custom nel DOM.
API
function mergeTailwindConfig(
baseConfig: TailwindConfig,
mfeThemeExtension?: Record<string, unknown>,
): TailwindConfig
function sanitizeCss(css: string): void
function injectCustomStyles(mfeName: string, css: string): void
function removeCustomStyles(mfeName: string): void
Helper per comporre stili Tailwind cross-MFE e iniettare CSS custom in modo sicuro.
mergeTailwindConfig
Effettua un deep merge dell'estensione tema Tailwind di un MFE sopra una config base.
import { mergeTailwindConfig } from '@pzeta/mfe-contracts/helpers'
const baseConfig = {
theme: {
extend: {
colors: {
primary: { 500: '#3b82f6' },
},
},
},
}
const mfeExt = {
colors: {
primary: { 600: '#2563eb' }, // aggiunge shade 600
accent: { 500: '#f59e0b' }, // nuova palette
},
spacing: {
'18': '4.5rem',
},
}
const merged = mergeTailwindConfig(baseConfig, mfeExt)
/*
{
theme: {
extend: {
colors: {
primary: {
500: '#3b82f6', // preservato da base
600: '#2563eb', // aggiunto da MFE
},
accent: {
500: '#f59e0b', // nuovo
},
},
spacing: {
'18': '4.5rem',
},
},
},
}
*/
Regole di merge
- Oggetti plain: deep merge ricorsivo (fino a 4 livelli)
- Array: il source sovrascrive il target (no merge array)
- Primitivi: il source sovrascrive il target
- Se
mfeThemeExtensionèundefined: ritornabaseConfiginvariato
Pattern: aggregare più MFE
import { mergeTailwindConfig } from '@pzeta/mfe-contracts/helpers'
let config = baseTailwindConfig
for (const manifest of manifests) {
const ext = manifest.tailwindStyles?.themeExtension
if (ext) {
config = mergeTailwindConfig(config, ext as Record<string, unknown>)
}
}
Build-time o runtime? Tailwind compila le classi al build time. Se vuoi che un MFE possa aggiungere classi dinamicamente, devi:
- includere i path dei MFE nel
content[]del Tailwind base della shell, oppure - usare CSS custom (
tailwindStyles.customClasses) iniettato viainjectCustomStyles, oppure - usare CSS variables per overrides al volo (palette colori).
sanitizeCss
Valida una stringa CSS contro pattern pericolosi noti. Throw se rileva qualcosa di sospetto.
import { sanitizeCss } from '@pzeta/mfe-contracts/helpers'
// OK
sanitizeCss('.btn { background: red; padding: 10px; }')
// Throw: "CSS non sicuro: rilevato pattern pericoloso. url() può esfiltrare dati..."
sanitizeCss('.bg { background: url(https://evil.com/track.gif); }')
// Throw: "@import può caricare stylesheet esterni non controllati"
sanitizeCss('@import "https://evil.com/style.css";')
Pattern bloccati
| Pattern | Rischio |
|---|---|
url( | Esfiltrazione dati via richieste a server esterni |
@import | Caricamento stylesheet esterno non controllato |
expression( | Esecuzione JavaScript in CSS (IE legacy) |
-moz-binding | XBL binding in Firefox (legacy) |
behavior: | Esecuzione HTC in IE (legacy) |
javascript: | Schema non consentito in CSS |
Limitazione: la sanitizzazione opera sulla stringa CSS letterale e non copre CSS unicode escaping (es.
\75rl( decodificato dal browser come url(). Per protezione completa, usa un parser CSS dedicato. Il CSS deve comunque provenire da fonte trusted (manifest del MFE, non input utente).injectCustomStyles
Inietta una stringa CSS nel DOM come tag <style>, sanificandola prima.
import { injectCustomStyles } from '@pzeta/mfe-contracts/helpers'
injectCustomStyles('crm-module', `
.crm-card { border-radius: 0.5rem; padding: 1rem; }
.crm-badge--hot { background: #fee2e2; color: #991b1b; }
`)
// Risultato nel DOM:
// <style id="mfe-styles-crm-module">
// .crm-card { ... }
// .crm-badge--hot { ... }
// </style>
Comportamento
- Chiama
sanitizeCss(css)(throw se pericoloso) - Costruisce
styleId = 'mfe-styles-{mfeName}' - Se esiste già
<style id={styleId}>, lo rimuove - Crea
<style id={styleId}>{css}</style>e lo appende a<head>
Idempotente: chiamarlo più volte con lo stesso mfeName sostituisce gli stili precedenti.
removeCustomStyles
Rimuove gli stili custom precedentemente iniettati.
import { removeCustomStyles } from '@pzeta/mfe-contracts/helpers'
removeCustomStyles('crm-module')
// Rimuove <style id="mfe-styles-crm-module">
Da chiamare in MFEModule.unmount() o quando il MFE viene scaricato dalla shell.
Esempio end-to-end
// Shell — al mount del MFE
import {
injectCustomStyles,
removeCustomStyles,
} from '@pzeta/mfe-contracts/helpers'
async function mountMfe(manifest: MFEManifest, container: HTMLElement) {
// 1. Inietta CSS custom dichiarato nel manifest
if (manifest.tailwindStyles?.customClasses) {
try {
injectCustomStyles(manifest.name, manifest.tailwindStyles.customClasses)
} catch (err) {
console.warn(`CSS custom del MFE "${manifest.name}" rifiutato:`, err.message)
}
}
// 2. Monta il MFE
await mfe.mount(container, context)
}
async function unmountMfe(manifest: MFEManifest) {
await mfe.unmount()
removeCustomStyles(manifest.name)
}
Tipi correlati
MFETailwindStyles— sorgente dithemeExtensionecustomClassesMFEManifest— contienetailwindStyles?: MFETailwindStyles