wu-framework 1.1.17 → 1.1.19
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/LICENSE +39 -39
- package/README.md +460 -440
- package/integrations/astro/README.md +127 -127
- package/integrations/astro/WuApp.astro +63 -63
- package/integrations/astro/WuShell.astro +39 -39
- package/integrations/astro/index.js +68 -68
- package/integrations/astro/package.json +38 -38
- package/integrations/astro/types.d.ts +53 -53
- package/package.json +45 -2
- package/src/adapters/alpine/index.d.ts +60 -0
- package/src/adapters/alpine/index.js +231 -0
- package/src/adapters/alpine.d.ts +3 -0
- package/src/adapters/alpine.js +3 -0
- package/src/adapters/htmx/index.d.ts +60 -0
- package/src/adapters/htmx/index.js +242 -0
- package/src/adapters/htmx.d.ts +3 -0
- package/src/adapters/htmx.js +3 -0
- package/src/adapters/index.js +60 -3
- package/src/adapters/qwik/index.d.ts +52 -0
- package/src/adapters/qwik/index.js +50 -0
- package/src/adapters/qwik.d.ts +3 -0
- package/src/adapters/qwik.js +3 -0
- package/src/adapters/react/ai.js +135 -135
- package/src/adapters/react/index.d.ts +246 -246
- package/src/adapters/react/index.js +695 -695
- package/src/adapters/stencil/index.d.ts +54 -0
- package/src/adapters/stencil/index.js +228 -0
- package/src/adapters/stencil.d.ts +3 -0
- package/src/adapters/stencil.js +3 -0
- package/src/adapters/stimulus/index.d.ts +60 -0
- package/src/adapters/stimulus/index.js +255 -0
- package/src/adapters/stimulus.d.ts +3 -0
- package/src/adapters/stimulus.js +3 -0
- package/src/adapters/vue/index.js +8 -0
- package/dist/wu-framework.cjs.js +0 -3
- package/dist/wu-framework.cjs.js.map +0 -1
- package/dist/wu-framework.dev.js +0 -15667
- package/dist/wu-framework.dev.js.map +0 -1
- package/dist/wu-framework.esm.js +0 -3
- package/dist/wu-framework.esm.js.map +0 -1
- package/dist/wu-framework.umd.js +0 -3
- package/dist/wu-framework.umd.js.map +0 -1
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WU-FRAMEWORK STENCIL ADAPTER - TypeScript Declarations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { WuCore } from '../../core/wu-core';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Stencil adapter registration options
|
|
9
|
+
*/
|
|
10
|
+
export interface StencilRegisterOptions {
|
|
11
|
+
/** Properties to set on the custom element (rich data: objects, arrays, functions) */
|
|
12
|
+
props?: Record<string, any>;
|
|
13
|
+
/** HTML attributes to set on the custom element (string values) */
|
|
14
|
+
attributes?: Record<string, string>;
|
|
15
|
+
/** Callback after mounting */
|
|
16
|
+
onMount?: (container: HTMLElement) => void;
|
|
17
|
+
/** Callback before unmounting */
|
|
18
|
+
onUnmount?: (container: HTMLElement) => void;
|
|
19
|
+
/** Allow standalone execution when Wu Framework is not available */
|
|
20
|
+
standalone?: boolean;
|
|
21
|
+
/** CSS selector for standalone mode container */
|
|
22
|
+
standaloneContainer?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Register a Stencil-compiled Web Component as a Wu microfrontend
|
|
27
|
+
*/
|
|
28
|
+
export function register(
|
|
29
|
+
appName: string,
|
|
30
|
+
tagName: string,
|
|
31
|
+
options?: StencilRegisterOptions
|
|
32
|
+
): Promise<boolean>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get the current Wu Framework instance
|
|
36
|
+
*/
|
|
37
|
+
export function getWuInstance(): WuCore | null;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Wait for Wu Framework to become available
|
|
41
|
+
*/
|
|
42
|
+
export function waitForWu(timeout?: number): Promise<WuCore>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Stencil adapter interface
|
|
46
|
+
*/
|
|
47
|
+
export interface WuStencilAdapter {
|
|
48
|
+
register: typeof register;
|
|
49
|
+
getWuInstance: typeof getWuInstance;
|
|
50
|
+
waitForWu: typeof waitForWu;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const wuStencil: WuStencilAdapter;
|
|
54
|
+
export default wuStencil;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WU-FRAMEWORK STENCIL ADAPTER
|
|
3
|
+
*
|
|
4
|
+
* Integrates Stencil-compiled Web Components into Wu Framework.
|
|
5
|
+
* Stencil outputs standards-compliant Custom Elements, so this
|
|
6
|
+
* adapter's job is thin by design: create the element, append it,
|
|
7
|
+
* and let the browser's native lifecycle (connectedCallback /
|
|
8
|
+
* disconnectedCallback) do the heavy lifting. The adapter adds
|
|
9
|
+
* Wu's orchestration layer on top -- coordinated mount/unmount
|
|
10
|
+
* across the microfrontend graph, standalone fallback, and
|
|
11
|
+
* property forwarding.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* import { wuStencil } from 'wu-framework/adapters/stencil';
|
|
15
|
+
*
|
|
16
|
+
* wuStencil.register('my-component', 'my-stencil-component');
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* // With properties
|
|
20
|
+
* wuStencil.register('user-profile', 'user-profile-card', {
|
|
21
|
+
* props: { userId: 42, theme: 'dark' },
|
|
22
|
+
* onMount: (container) => console.log('Stencil component connected')
|
|
23
|
+
* });
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
// Adapter-scoped state
|
|
27
|
+
const adapterState = {
|
|
28
|
+
apps: new Map()
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Obtiene la instancia de Wu Framework
|
|
33
|
+
*/
|
|
34
|
+
function getWuInstance() {
|
|
35
|
+
if (typeof window === 'undefined') return null;
|
|
36
|
+
|
|
37
|
+
return window.wu
|
|
38
|
+
|| window.parent?.wu
|
|
39
|
+
|| window.top?.wu
|
|
40
|
+
|| null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Espera a que Wu Framework este disponible
|
|
45
|
+
*/
|
|
46
|
+
function waitForWu(timeout = 5000) {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
const wu = getWuInstance();
|
|
49
|
+
if (wu) {
|
|
50
|
+
resolve(wu);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const startTime = Date.now();
|
|
55
|
+
|
|
56
|
+
const handleWuReady = () => {
|
|
57
|
+
cleanup();
|
|
58
|
+
resolve(getWuInstance());
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
window.addEventListener('wu:ready', handleWuReady);
|
|
62
|
+
window.addEventListener('wu:app:ready', handleWuReady);
|
|
63
|
+
|
|
64
|
+
const checkInterval = setInterval(() => {
|
|
65
|
+
const wu = getWuInstance();
|
|
66
|
+
if (wu) {
|
|
67
|
+
cleanup();
|
|
68
|
+
resolve(wu);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (Date.now() - startTime > timeout) {
|
|
73
|
+
cleanup();
|
|
74
|
+
reject(new Error(`Wu Framework not found after ${timeout}ms`));
|
|
75
|
+
}
|
|
76
|
+
}, 200);
|
|
77
|
+
|
|
78
|
+
function cleanup() {
|
|
79
|
+
clearInterval(checkInterval);
|
|
80
|
+
window.removeEventListener('wu:ready', handleWuReady);
|
|
81
|
+
window.removeEventListener('wu:app:ready', handleWuReady);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Registra un Web Component compilado con Stencil como microfrontend
|
|
88
|
+
*
|
|
89
|
+
* @param {string} appName - Nombre unico del microfrontend
|
|
90
|
+
* @param {string} tagName - Tag del Custom Element generado por Stencil
|
|
91
|
+
* @param {Object} [options] - Opciones adicionales
|
|
92
|
+
* @param {Object} [options.props={}] - Propiedades a setear en el elemento
|
|
93
|
+
* @param {Object} [options.attributes={}] - Atributos HTML a setear en el elemento
|
|
94
|
+
* @param {Function} [options.onMount] - Callback despues de montar
|
|
95
|
+
* @param {Function} [options.onUnmount] - Callback antes de desmontar
|
|
96
|
+
* @param {boolean} [options.standalone=true] - Permitir ejecucion standalone
|
|
97
|
+
* @param {string} [options.standaloneContainer='#app'] - Selector para modo standalone
|
|
98
|
+
* @returns {Promise<boolean>} true si el registro fue exitoso
|
|
99
|
+
*/
|
|
100
|
+
async function register(appName, tagName, options = {}) {
|
|
101
|
+
const {
|
|
102
|
+
props = {},
|
|
103
|
+
attributes = {},
|
|
104
|
+
onMount = null,
|
|
105
|
+
onUnmount = null,
|
|
106
|
+
standalone = true,
|
|
107
|
+
standaloneContainer = '#app'
|
|
108
|
+
} = options;
|
|
109
|
+
|
|
110
|
+
if (!tagName || typeof tagName !== 'string') {
|
|
111
|
+
throw new Error(`[WuStencil] tagName (custom element tag) is required for ${appName}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Mount function
|
|
115
|
+
const mountApp = (container) => {
|
|
116
|
+
if (!container) {
|
|
117
|
+
console.error(`[WuStencil] Mount failed for ${appName}: container is null`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Prevent double-mount
|
|
122
|
+
if (adapterState.apps.has(appName)) {
|
|
123
|
+
console.warn(`[WuStencil] ${appName} already mounted, unmounting first`);
|
|
124
|
+
unmountApp();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
container.innerHTML = '';
|
|
129
|
+
|
|
130
|
+
// Create the Stencil web component element
|
|
131
|
+
const element = document.createElement(tagName);
|
|
132
|
+
|
|
133
|
+
// Forward properties (rich data: objects, arrays, functions)
|
|
134
|
+
for (const [key, value] of Object.entries(props)) {
|
|
135
|
+
element[key] = value;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Set HTML attributes (strings only)
|
|
139
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
140
|
+
element.setAttribute(key, String(value));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
container.appendChild(element);
|
|
144
|
+
|
|
145
|
+
adapterState.apps.set(appName, {
|
|
146
|
+
container,
|
|
147
|
+
element,
|
|
148
|
+
tagName
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
console.log(`[WuStencil] ${appName} (<${tagName}>) mounted successfully`);
|
|
152
|
+
|
|
153
|
+
if (onMount) {
|
|
154
|
+
onMount(container);
|
|
155
|
+
}
|
|
156
|
+
} catch (error) {
|
|
157
|
+
console.error(`[WuStencil] Mount error for ${appName}:`, error);
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// Unmount function -- Stencil components clean up via disconnectedCallback
|
|
163
|
+
const unmountApp = (container) => {
|
|
164
|
+
const appData = adapterState.apps.get(appName);
|
|
165
|
+
|
|
166
|
+
if (appData) {
|
|
167
|
+
try {
|
|
168
|
+
if (onUnmount) {
|
|
169
|
+
onUnmount(appData.container);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Removing the element triggers Stencil's disconnectedCallback
|
|
173
|
+
if (appData.element && appData.element.parentNode) {
|
|
174
|
+
appData.element.remove();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
appData.container.innerHTML = '';
|
|
178
|
+
adapterState.apps.delete(appName);
|
|
179
|
+
|
|
180
|
+
console.log(`[WuStencil] ${appName} (<${tagName}>) unmounted successfully`);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.error(`[WuStencil] Unmount error for ${appName}:`, error);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (container) {
|
|
187
|
+
container.innerHTML = '';
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Register with Wu Framework or fall back to standalone
|
|
192
|
+
try {
|
|
193
|
+
const wu = await waitForWu(3000);
|
|
194
|
+
|
|
195
|
+
wu.define(appName, {
|
|
196
|
+
mount: mountApp,
|
|
197
|
+
unmount: unmountApp
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
console.log(`[WuStencil] ${appName} registered with Wu Framework`);
|
|
201
|
+
return true;
|
|
202
|
+
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.warn(`[WuStencil] Wu Framework not available for ${appName}`);
|
|
205
|
+
|
|
206
|
+
if (standalone) {
|
|
207
|
+
const containerElement = document.querySelector(standaloneContainer);
|
|
208
|
+
|
|
209
|
+
if (containerElement) {
|
|
210
|
+
console.log(`[WuStencil] Running ${appName} in standalone mode`);
|
|
211
|
+
mountApp(containerElement);
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Public API
|
|
221
|
+
export const wuStencil = {
|
|
222
|
+
register,
|
|
223
|
+
getWuInstance,
|
|
224
|
+
waitForWu
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export { register, getWuInstance, waitForWu };
|
|
228
|
+
export default wuStencil;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WU-FRAMEWORK STIMULUS ADAPTER - TypeScript Declarations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { WuCore } from '../../core/wu-core';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Stimulus app configuration
|
|
9
|
+
*/
|
|
10
|
+
export interface StimulusAppConfig {
|
|
11
|
+
/** Map of controller name to Controller class */
|
|
12
|
+
controllers: Record<string, any>;
|
|
13
|
+
/** HTML template string containing data-controller and data-action attributes */
|
|
14
|
+
template: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Stimulus adapter registration options
|
|
19
|
+
*/
|
|
20
|
+
export interface StimulusRegisterOptions {
|
|
21
|
+
/** Callback after mounting */
|
|
22
|
+
onMount?: (container: HTMLElement) => void;
|
|
23
|
+
/** Callback before unmounting */
|
|
24
|
+
onUnmount?: (container: HTMLElement) => void;
|
|
25
|
+
/** Allow standalone execution when Wu Framework is not available */
|
|
26
|
+
standalone?: boolean;
|
|
27
|
+
/** CSS selector for standalone mode container */
|
|
28
|
+
standaloneContainer?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Register a Stimulus app as a Wu microfrontend
|
|
33
|
+
*/
|
|
34
|
+
export function register(
|
|
35
|
+
appName: string,
|
|
36
|
+
config: StimulusAppConfig,
|
|
37
|
+
options?: StimulusRegisterOptions
|
|
38
|
+
): Promise<boolean>;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get the current Wu Framework instance
|
|
42
|
+
*/
|
|
43
|
+
export function getWuInstance(): WuCore | null;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Wait for Wu Framework to become available
|
|
47
|
+
*/
|
|
48
|
+
export function waitForWu(timeout?: number): Promise<WuCore>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Stimulus adapter interface
|
|
52
|
+
*/
|
|
53
|
+
export interface WuStimulusAdapter {
|
|
54
|
+
register: typeof register;
|
|
55
|
+
getWuInstance: typeof getWuInstance;
|
|
56
|
+
waitForWu: typeof waitForWu;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const wuStimulus: WuStimulusAdapter;
|
|
60
|
+
export default wuStimulus;
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WU-FRAMEWORK STIMULUS ADAPTER
|
|
3
|
+
*
|
|
4
|
+
* Integrates Hotwire Stimulus controllers into Wu Framework's
|
|
5
|
+
* microfrontend orchestration. Stimulus is the "modest JavaScript
|
|
6
|
+
* framework" -- it enhances server-rendered HTML with controllers
|
|
7
|
+
* that respond to DOM mutations via data-controller attributes.
|
|
8
|
+
*
|
|
9
|
+
* This adapter creates a scoped Stimulus Application instance per
|
|
10
|
+
* microfrontend, registers the provided controllers, injects the
|
|
11
|
+
* HTML template, and tears everything down cleanly on unmount.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* import { wuStimulus } from 'wu-framework/adapters/stimulus';
|
|
15
|
+
* import { Controller } from '@hotwired/stimulus';
|
|
16
|
+
*
|
|
17
|
+
* class GreetingController extends Controller {
|
|
18
|
+
* static targets = ['output'];
|
|
19
|
+
* greet() { this.outputTarget.textContent = 'Hello!'; }
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* wuStimulus.register('greeting-app', {
|
|
23
|
+
* controllers: { greeting: GreetingController },
|
|
24
|
+
* template: `
|
|
25
|
+
* <div data-controller="greeting">
|
|
26
|
+
* <button data-action="click->greeting#greet">Greet</button>
|
|
27
|
+
* <span data-greeting-target="output"></span>
|
|
28
|
+
* </div>
|
|
29
|
+
* `
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* // Multiple controllers
|
|
34
|
+
* wuStimulus.register('dashboard', {
|
|
35
|
+
* controllers: {
|
|
36
|
+
* tabs: TabsController,
|
|
37
|
+
* chart: ChartController,
|
|
38
|
+
* filter: FilterController
|
|
39
|
+
* },
|
|
40
|
+
* template: dashboardHTML
|
|
41
|
+
* }, {
|
|
42
|
+
* onMount: (container) => console.log('Stimulus controllers active')
|
|
43
|
+
* });
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
// Adapter-scoped state
|
|
47
|
+
const adapterState = {
|
|
48
|
+
apps: new Map()
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Obtiene la instancia de Wu Framework
|
|
53
|
+
*/
|
|
54
|
+
function getWuInstance() {
|
|
55
|
+
if (typeof window === 'undefined') return null;
|
|
56
|
+
|
|
57
|
+
return window.wu
|
|
58
|
+
|| window.parent?.wu
|
|
59
|
+
|| window.top?.wu
|
|
60
|
+
|| null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Espera a que Wu Framework este disponible
|
|
65
|
+
*/
|
|
66
|
+
function waitForWu(timeout = 5000) {
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
const wu = getWuInstance();
|
|
69
|
+
if (wu) {
|
|
70
|
+
resolve(wu);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const startTime = Date.now();
|
|
75
|
+
|
|
76
|
+
const handleWuReady = () => {
|
|
77
|
+
cleanup();
|
|
78
|
+
resolve(getWuInstance());
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
window.addEventListener('wu:ready', handleWuReady);
|
|
82
|
+
window.addEventListener('wu:app:ready', handleWuReady);
|
|
83
|
+
|
|
84
|
+
const checkInterval = setInterval(() => {
|
|
85
|
+
const wu = getWuInstance();
|
|
86
|
+
if (wu) {
|
|
87
|
+
cleanup();
|
|
88
|
+
resolve(wu);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (Date.now() - startTime > timeout) {
|
|
93
|
+
cleanup();
|
|
94
|
+
reject(new Error(`Wu Framework not found after ${timeout}ms`));
|
|
95
|
+
}
|
|
96
|
+
}, 200);
|
|
97
|
+
|
|
98
|
+
function cleanup() {
|
|
99
|
+
clearInterval(checkInterval);
|
|
100
|
+
window.removeEventListener('wu:ready', handleWuReady);
|
|
101
|
+
window.removeEventListener('wu:app:ready', handleWuReady);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Registra una app Stimulus como microfrontend
|
|
108
|
+
*
|
|
109
|
+
* @param {string} appName - Nombre unico del microfrontend
|
|
110
|
+
* @param {Object} config - Configuracion de la app Stimulus
|
|
111
|
+
* @param {Object} config.controllers - Map de nombre -> Controller class
|
|
112
|
+
* @param {string} config.template - HTML string con data-controller / data-action attributes
|
|
113
|
+
* @param {Object} [options] - Opciones adicionales
|
|
114
|
+
* @param {Function} [options.onMount] - Callback despues de montar
|
|
115
|
+
* @param {Function} [options.onUnmount] - Callback antes de desmontar
|
|
116
|
+
* @param {boolean} [options.standalone=true] - Permitir ejecucion standalone
|
|
117
|
+
* @param {string} [options.standaloneContainer='#app'] - Selector para modo standalone
|
|
118
|
+
* @returns {Promise<boolean>} true si el registro fue exitoso
|
|
119
|
+
*/
|
|
120
|
+
async function register(appName, config, options = {}) {
|
|
121
|
+
const { controllers = {}, template } = config;
|
|
122
|
+
|
|
123
|
+
const {
|
|
124
|
+
onMount = null,
|
|
125
|
+
onUnmount = null,
|
|
126
|
+
standalone = true,
|
|
127
|
+
standaloneContainer = '#app'
|
|
128
|
+
} = options;
|
|
129
|
+
|
|
130
|
+
if (!template || typeof template !== 'string') {
|
|
131
|
+
throw new Error(`[WuStimulus] template (HTML string) is required for ${appName}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!controllers || typeof controllers !== 'object' || Object.keys(controllers).length === 0) {
|
|
135
|
+
throw new Error(`[WuStimulus] controllers (object of name -> Controller class) is required for ${appName}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Mount function
|
|
139
|
+
const mountApp = (container) => {
|
|
140
|
+
if (!container) {
|
|
141
|
+
console.error(`[WuStimulus] Mount failed for ${appName}: container is null`);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Prevent double-mount
|
|
146
|
+
if (adapterState.apps.has(appName)) {
|
|
147
|
+
console.warn(`[WuStimulus] ${appName} already mounted, unmounting first`);
|
|
148
|
+
unmountApp();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
container.innerHTML = '';
|
|
153
|
+
|
|
154
|
+
// Dynamic import keeps Stimulus out of the critical path
|
|
155
|
+
import('@hotwired/stimulus').then(({ Application }) => {
|
|
156
|
+
// Create a scoped Stimulus Application for this microfrontend
|
|
157
|
+
const application = Application.start(container);
|
|
158
|
+
|
|
159
|
+
// Register all controllers
|
|
160
|
+
for (const [name, ControllerClass] of Object.entries(controllers)) {
|
|
161
|
+
application.register(name, ControllerClass);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Inject template -- Stimulus will auto-connect controllers
|
|
165
|
+
// via MutationObserver watching data-controller attributes
|
|
166
|
+
container.innerHTML = template;
|
|
167
|
+
|
|
168
|
+
adapterState.apps.set(appName, {
|
|
169
|
+
container,
|
|
170
|
+
config,
|
|
171
|
+
application
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
console.log(`[WuStimulus] ${appName} mounted successfully (${Object.keys(controllers).length} controllers)`);
|
|
175
|
+
|
|
176
|
+
if (onMount) {
|
|
177
|
+
onMount(container);
|
|
178
|
+
}
|
|
179
|
+
}).catch((err) => {
|
|
180
|
+
console.error(`[WuStimulus] Failed to import @hotwired/stimulus for ${appName}:`, err);
|
|
181
|
+
container.innerHTML = `<div style="color:#721c24;padding:1rem;">Failed to load Stimulus runtime</div>`;
|
|
182
|
+
});
|
|
183
|
+
} catch (error) {
|
|
184
|
+
console.error(`[WuStimulus] Mount error for ${appName}:`, error);
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// Unmount function
|
|
190
|
+
const unmountApp = (container) => {
|
|
191
|
+
const appData = adapterState.apps.get(appName);
|
|
192
|
+
|
|
193
|
+
if (appData) {
|
|
194
|
+
try {
|
|
195
|
+
if (onUnmount) {
|
|
196
|
+
onUnmount(appData.container);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Stop the scoped Stimulus Application
|
|
200
|
+
if (appData.application && typeof appData.application.stop === 'function') {
|
|
201
|
+
appData.application.stop();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
appData.container.innerHTML = '';
|
|
205
|
+
adapterState.apps.delete(appName);
|
|
206
|
+
|
|
207
|
+
console.log(`[WuStimulus] ${appName} unmounted successfully`);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error(`[WuStimulus] Unmount error for ${appName}:`, error);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (container) {
|
|
214
|
+
container.innerHTML = '';
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// Register with Wu Framework or fall back to standalone
|
|
219
|
+
try {
|
|
220
|
+
const wu = await waitForWu(3000);
|
|
221
|
+
|
|
222
|
+
wu.define(appName, {
|
|
223
|
+
mount: mountApp,
|
|
224
|
+
unmount: unmountApp
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
console.log(`[WuStimulus] ${appName} registered with Wu Framework`);
|
|
228
|
+
return true;
|
|
229
|
+
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.warn(`[WuStimulus] Wu Framework not available for ${appName}`);
|
|
232
|
+
|
|
233
|
+
if (standalone) {
|
|
234
|
+
const containerElement = document.querySelector(standaloneContainer);
|
|
235
|
+
|
|
236
|
+
if (containerElement) {
|
|
237
|
+
console.log(`[WuStimulus] Running ${appName} in standalone mode`);
|
|
238
|
+
mountApp(containerElement);
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Public API
|
|
248
|
+
export const wuStimulus = {
|
|
249
|
+
register,
|
|
250
|
+
getWuInstance,
|
|
251
|
+
waitForWu
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
export { register, getWuInstance, waitForWu };
|
|
255
|
+
export default wuStimulus;
|
|
@@ -190,6 +190,14 @@ async function register(appName, RootComponent, options = {}) {
|
|
|
190
190
|
setup(app);
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
// Error handlers for debugging
|
|
194
|
+
app.config.errorHandler = (err, instance, info) => {
|
|
195
|
+
console.error(`[WuVue] ${appName} error in ${info}:`, err);
|
|
196
|
+
};
|
|
197
|
+
app.config.warnHandler = (msg, instance, trace) => {
|
|
198
|
+
console.warn(`[WuVue] ${appName} warn:`, msg);
|
|
199
|
+
};
|
|
200
|
+
|
|
193
201
|
// Proveer información del contexto Wu
|
|
194
202
|
app.provide('wuAppName', appName);
|
|
195
203
|
app.provide('wuInstance', getWuInstance());
|