Ir al contenido

Adapters de framework

Un adapter es una capa fina de traducción. Wu Framework solo entiende un contrato — wu.define(name, { mount, unmount, update?, hydrate? }) — y el trabajo de cada adapter es expresar la semántica de montar/desmontar de tu framework en esos términos, más un poco de plomería que de otro modo escribirías a mano: esperar al shell, caer a modo standalone y llevar la contabilidad de las props vivas.

Nunca necesitas un adapter. wu.define() funciona con cualquier framework. El adapter existe para que no escribas las mismas 60 líneas en cada micro-app.

Adapter Subpath 2.º argumento de register() Props vivas (update) SSR (hydrate) Standalone Contenedor por defecto
React wu-framework/adapters/react Component #root
Vue wu-framework/adapters/vue RootComponent sí¹ sí² #app
Angular wu-framework/adapters/angular AppModule / Component no no #root
Svelte wu-framework/adapters/svelte Component sí³ no #app
Preact wu-framework/adapters/preact Component sí⁴ no #app
Solid wu-framework/adapters/solid Component no no #root
Lit wu-framework/adapters/lit ElementClass no #root
Vanilla wu-framework/adapters/vanilla objeto config no #app
Alpine wu-framework/adapters/alpine objeto appConfig no no #app
Qwik wu-framework/adapters/qwik Component no no no
Stencil wu-framework/adapters/stencil string tagName no no #app
HTMX wu-framework/adapters/htmx objeto config no no #app
Stimulus wu-framework/adapters/stimulus objeto config no no #app

¹ Solo cuando Vue.reactive está disponible. ² Solo cuando Vue.createSSRApp está disponible (Vue 3). ³ Solo en register()registerSvelte5() no anuncia update. ⁴ Solo en register()registerCompat() no anuncia update.

La decisión casi siempre es “el que se llama como tu framework”. Los casos interesantes son los solapamientos:

  • Preact: usa register() para una app Preact nativa. Usa registerCompat() solo si el árbol de componentes se escribió contra la API de React e importa de preact/compat.
  • Svelte: register() para Svelte 3/4 (new Component({ target })), registerSvelte5() para Svelte 5 (mount() del paquete svelte).
  • Angular: registerStandalone() es el punto de entrada recomendado. register() usa platformBrowserDynamic + bootstrapModule, que llama a document.querySelector internamente y por tanto no funciona dentro del Shadow DOM — el modo de aislamiento por defecto de Wu.
  • Lit vs Stencil: ambos producen custom elements. Usa el adapter de Lit cuando escribes la clase tú mismo (llama a customElements.define por ti); usa el adapter de Stencil cuando el compilador de Stencil ya definió la etiqueta y solo necesitas que Wu la coloque.
  • Vanilla: no es un plan B. Tiene la superficie más rica de los adapters ligeros (register, registerClass, registerTemplate, createComponent) y es el único construido sobre el contrato compartido createWuAdapter().

Todos los adapters exponen la misma forma: un objeto wuX exportado por defecto, más exports nombrados para tree-shaking.

// Ambas formas funcionan; el import nombrado hace mejor tree-shaking.
import { wuReact } from 'wu-framework/adapters/react';
import { register } from 'wu-framework/adapters/react';
await wuReact.register('cart', CartApp, {
props: { currency: 'EUR' },
standalone: true,
standaloneContainer: '#root',
onMount: (container) => console.log('montado en', container),
onUnmount: (container) => console.log('saliendo de', container),
});

register() siempre es async y siempre resuelve a un boolean:

  • true — registrado con el shell, o montado en modo standalone.
  • false — no se pudo cargar el framework, o Wu no estaba y el modo standalone estaba desactivado o faltaba su contenedor.

Las opciones de arriba (props, standalone, standaloneContainer, onMount, onUnmount) son universales. Todo lo demás es propio de cada adapter y está documentado en su página.

Todos los adapters excepto Qwik implementan el mismo plan B. register() llama a waitForWu(3000); si no aparece ninguna instancia wu en tres segundos y standalone !== false, el adapter monta tu componente directamente en document.querySelector(standaloneContainer)sin Shadow DOM y sin el bus de eventos.

Esto es lo que permite que un mismo bundle sirva para dos propósitos: ejecuta npm run dev y obtienes una app normal en localhost:3001; carga la misma URL desde un shell y obtienes un micro-frontend. Sin flags de build, sin un segundo punto de entrada.

Pon standalone: false cuando una micro-app no tenga sentido por sí sola (por ejemplo, si lee todo de wu.store) — así obtienes un false claro en lugar de una página rota.

import { loadAdapter, ADAPTER_NAMES } from 'wu-framework/adapters';
const wuReact = await loadAdapter('react');
wuReact.register('cart', CartApp);
console.log(ADAPTER_NAMES);
// ['react','vue','angular','svelte','preact','solid','lit',
// 'vanilla','alpine','qwik','stencil','htmx','stimulus']

loadAdapter(name) importa dinámicamente un adapter y resuelve a su objeto wuX. Los resultados se cachean, y las llamadas concurrentes con el mismo nombre comparten una única promesa en vuelo en lugar de competir con dos llamadas a import(). Un nombre desconocido se rechaza con un mensaje que lista los válidos. ADAPTER_NAMES es un array congelado, útil para iterar desde herramientas.

Cada adapter exporta dos helpers, que también están en wu-framework/adapters/shared:

import { getWuInstance, waitForWu } from 'wu-framework/adapters/shared';
const wu = getWuInstance(); // window.wu → parent.wu → top.wu, o null
const ready = await waitForWu(5000); // resuelve cuando aparece wu, si no rechaza

getWuInstance() recorre window, luego window.parent y luego window.top, protegiendo cada lectura cross-origin con un try: leer .wu en un Window cross-origin lanza un SecurityError síncrono, y el encadenamiento opcional no ayuda porque window.parent siempre es un Window.

waitForWu(timeout) escucha los eventos wu:ready y wu:app:ready y consulta cada 200 ms como respaldo. Los adapters lo llaman con 3000; el valor por defecto exportado es 5000.

Consulta Escribir un adapter para createWuAdapter(), escapeHtml() y el contrato completo si quieres dar soporte a un framework que no está en esta lista.