@useavalon/avalon 0.1.0
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 +54 -0
- package/mod.ts +301 -0
- package/package.json +85 -0
- package/src/build/README.md +310 -0
- package/src/build/integration-bundler-plugin.ts +116 -0
- package/src/build/integration-config.ts +168 -0
- package/src/build/integration-detection-plugin.ts +117 -0
- package/src/build/integration-resolver-plugin.ts +90 -0
- package/src/build/island-manifest.ts +269 -0
- package/src/build/island-types-generator.ts +476 -0
- package/src/build/mdx-island-transform.ts +464 -0
- package/src/build/mdx-plugin.ts +98 -0
- package/src/build/page-island-transform.ts +598 -0
- package/src/build/prop-extractors/index.ts +21 -0
- package/src/build/prop-extractors/lit.ts +140 -0
- package/src/build/prop-extractors/qwik.ts +16 -0
- package/src/build/prop-extractors/solid.ts +125 -0
- package/src/build/prop-extractors/svelte.ts +194 -0
- package/src/build/prop-extractors/vue.ts +111 -0
- package/src/build/sidecar-file-manager.ts +104 -0
- package/src/build/sidecar-renderer.ts +30 -0
- package/src/client/adapters/index.ts +13 -0
- package/src/client/adapters/lit-adapter.ts +654 -0
- package/src/client/adapters/preact-adapter.ts +331 -0
- package/src/client/adapters/qwik-adapter.ts +345 -0
- package/src/client/adapters/react-adapter.ts +353 -0
- package/src/client/adapters/solid-adapter.ts +451 -0
- package/src/client/adapters/svelte-adapter.ts +524 -0
- package/src/client/adapters/vue-adapter.ts +467 -0
- package/src/client/components.ts +35 -0
- package/src/client/css-hmr-handler.ts +344 -0
- package/src/client/framework-adapter.ts +462 -0
- package/src/client/hmr-coordinator.ts +396 -0
- package/src/client/hmr-error-overlay.js +533 -0
- package/src/client/main.js +816 -0
- package/src/client/tests/css-hmr-handler.test.ts +360 -0
- package/src/client/tests/framework-adapter.test.ts +519 -0
- package/src/client/tests/hmr-coordinator.test.ts +176 -0
- package/src/client/tests/hydration-option-parsing.test.ts +107 -0
- package/src/client/tests/lit-adapter.test.ts +427 -0
- package/src/client/tests/preact-adapter.test.ts +353 -0
- package/src/client/tests/qwik-adapter.test.ts +343 -0
- package/src/client/tests/react-adapter.test.ts +317 -0
- package/src/client/tests/solid-adapter.test.ts +396 -0
- package/src/client/tests/svelte-adapter.test.ts +387 -0
- package/src/client/tests/vue-adapter.test.ts +407 -0
- package/src/client/types/framework-runtime.d.ts +68 -0
- package/src/client/types/vite-hmr.d.ts +46 -0
- package/src/client/types/vite-virtual-modules.d.ts +60 -0
- package/src/components/Image.tsx +123 -0
- package/src/components/IslandErrorBoundary.tsx +145 -0
- package/src/components/LayoutDataErrorBoundary.tsx +141 -0
- package/src/components/LayoutErrorBoundary.tsx +127 -0
- package/src/components/PersistentIsland.tsx +52 -0
- package/src/components/StreamingErrorBoundary.tsx +233 -0
- package/src/components/StreamingLayout.tsx +538 -0
- package/src/components/tests/component-analyzer.test.ts +96 -0
- package/src/components/tests/component-detection.test.ts +347 -0
- package/src/components/tests/persistent-islands.test.ts +398 -0
- package/src/core/components/component-analyzer.ts +192 -0
- package/src/core/components/component-detection.ts +508 -0
- package/src/core/components/enhanced-framework-detector.ts +500 -0
- package/src/core/components/framework-registry.ts +563 -0
- package/src/core/components/tests/enhanced-framework-detector.test.ts +577 -0
- package/src/core/components/tests/framework-registry.test.ts +465 -0
- package/src/core/content/mdx-processor.ts +46 -0
- package/src/core/integrations/README.md +282 -0
- package/src/core/integrations/index.ts +19 -0
- package/src/core/integrations/loader.ts +125 -0
- package/src/core/integrations/registry.ts +195 -0
- package/src/core/islands/island-persistence.ts +325 -0
- package/src/core/islands/island-state-serializer.ts +258 -0
- package/src/core/islands/persistent-island-context.tsx +80 -0
- package/src/core/islands/use-persistent-state.ts +68 -0
- package/src/core/layout/enhanced-layout-resolver.ts +322 -0
- package/src/core/layout/layout-cache-manager.ts +485 -0
- package/src/core/layout/layout-composer.ts +357 -0
- package/src/core/layout/layout-data-loader.ts +516 -0
- package/src/core/layout/layout-discovery.ts +243 -0
- package/src/core/layout/layout-matcher.ts +299 -0
- package/src/core/layout/layout-types.ts +110 -0
- package/src/core/layout/tests/enhanced-layout-resolver.test.ts +477 -0
- package/src/core/layout/tests/layout-cache-optimization.test.ts +149 -0
- package/src/core/layout/tests/layout-composer.test.ts +486 -0
- package/src/core/layout/tests/layout-data-loader.test.ts +443 -0
- package/src/core/layout/tests/layout-discovery.test.ts +253 -0
- package/src/core/layout/tests/layout-matcher.test.ts +480 -0
- package/src/core/modules/framework-module-resolver.ts +273 -0
- package/src/core/modules/tests/framework-module-resolver.test.ts +263 -0
- package/src/core/modules/tests/module-resolution-integration.test.ts +117 -0
- package/src/islands/component-analysis.ts +213 -0
- package/src/islands/css-utils.ts +565 -0
- package/src/islands/discovery/index.ts +80 -0
- package/src/islands/discovery/registry.ts +340 -0
- package/src/islands/discovery/resolver.ts +477 -0
- package/src/islands/discovery/scanner.ts +386 -0
- package/src/islands/discovery/tests/island-discovery.test.ts +881 -0
- package/src/islands/discovery/types.ts +117 -0
- package/src/islands/discovery/validator.ts +544 -0
- package/src/islands/discovery/watcher.ts +368 -0
- package/src/islands/framework-detection.ts +428 -0
- package/src/islands/integration-loader.ts +490 -0
- package/src/islands/island.tsx +565 -0
- package/src/islands/render-cache.ts +550 -0
- package/src/islands/types.ts +80 -0
- package/src/islands/universal-css-collector.ts +157 -0
- package/src/islands/universal-head-collector.ts +137 -0
- package/src/layout-system.d.ts +592 -0
- package/src/layout-system.ts +218 -0
- package/src/middleware/__tests__/discovery.test.ts +107 -0
- package/src/middleware/discovery.ts +268 -0
- package/src/middleware/executor.ts +315 -0
- package/src/middleware/index.ts +76 -0
- package/src/middleware/types.ts +99 -0
- package/src/nitro/build-config.ts +576 -0
- package/src/nitro/config.ts +483 -0
- package/src/nitro/error-handler.ts +636 -0
- package/src/nitro/index.ts +173 -0
- package/src/nitro/island-manifest.ts +584 -0
- package/src/nitro/middleware-adapter.ts +260 -0
- package/src/nitro/renderer.ts +1458 -0
- package/src/nitro/route-discovery.ts +439 -0
- package/src/nitro/types.ts +321 -0
- package/src/render/collect-css.ts +198 -0
- package/src/render/error-pages.ts +79 -0
- package/src/render/isolated-ssr-renderer.ts +654 -0
- package/src/render/ssr.ts +1030 -0
- package/src/schemas/api.ts +30 -0
- package/src/schemas/core.ts +64 -0
- package/src/schemas/index.ts +212 -0
- package/src/schemas/layout.ts +279 -0
- package/src/schemas/routing/index.ts +38 -0
- package/src/schemas/routing.ts +376 -0
- package/src/types/as-island.ts +20 -0
- package/src/types/image.d.ts +106 -0
- package/src/types/index.d.ts +22 -0
- package/src/types/island-jsx.d.ts +33 -0
- package/src/types/island-prop.d.ts +20 -0
- package/src/types/layout.ts +285 -0
- package/src/types/mdx.d.ts +6 -0
- package/src/types/routing.ts +555 -0
- package/src/types/tests/layout-types.test.ts +197 -0
- package/src/types/types.ts +5 -0
- package/src/types/urlpattern.d.ts +49 -0
- package/src/types/vite-env.d.ts +11 -0
- package/src/utils/dev-logger.ts +299 -0
- package/src/utils/fs.ts +151 -0
- package/src/vite-plugin/auto-discover.ts +551 -0
- package/src/vite-plugin/config.ts +266 -0
- package/src/vite-plugin/errors.ts +127 -0
- package/src/vite-plugin/image-optimization.ts +151 -0
- package/src/vite-plugin/integration-activator.ts +126 -0
- package/src/vite-plugin/island-sidecar-plugin.ts +176 -0
- package/src/vite-plugin/module-discovery.ts +189 -0
- package/src/vite-plugin/nitro-integration.ts +1334 -0
- package/src/vite-plugin/plugin.ts +329 -0
- package/src/vite-plugin/tests/image-optimization.test.ts +54 -0
- package/src/vite-plugin/types.ts +327 -0
- package/src/vite-plugin/validation.ts +228 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React HMR Adapter
|
|
3
|
+
*
|
|
4
|
+
* Provides Hot Module Replacement support for React components using React Fast Refresh.
|
|
5
|
+
* Integrates with @vitejs/plugin-react to preserve hooks state and component tree during updates.
|
|
6
|
+
*
|
|
7
|
+
* Requirements: 2.1
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/// <reference lib="dom" />
|
|
11
|
+
|
|
12
|
+
import { BaseFrameworkAdapter, type StateSnapshot } from '../framework-adapter.ts';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* React component type
|
|
16
|
+
* Can be a function component or class component
|
|
17
|
+
*/
|
|
18
|
+
type ReactComponent<P = Record<string, unknown>> =
|
|
19
|
+
| ((props: P) => ReactElement | null)
|
|
20
|
+
| (new (props: P) => ReactClassComponent);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* React class component interface
|
|
24
|
+
*/
|
|
25
|
+
interface ReactClassComponent {
|
|
26
|
+
render(): ReactElement | null;
|
|
27
|
+
isReactComponent?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* React element type
|
|
32
|
+
*/
|
|
33
|
+
interface ReactElement {
|
|
34
|
+
type: string | ReactComponent;
|
|
35
|
+
props: Record<string, unknown>;
|
|
36
|
+
key: string | number | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* React Root type from react-dom/client
|
|
41
|
+
*/
|
|
42
|
+
interface ReactRoot {
|
|
43
|
+
render(element: ReactElement | null): void;
|
|
44
|
+
unmount(): void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* React module interface
|
|
49
|
+
*/
|
|
50
|
+
interface ReactModule {
|
|
51
|
+
createElement<P = Record<string, unknown>>(
|
|
52
|
+
component: ReactComponent<P>,
|
|
53
|
+
props: P | null,
|
|
54
|
+
...children: unknown[]
|
|
55
|
+
): ReactElement;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* ReactDOM client module interface
|
|
60
|
+
*/
|
|
61
|
+
interface ReactDOMClientModule {
|
|
62
|
+
hydrateRoot(
|
|
63
|
+
container: HTMLElement,
|
|
64
|
+
element: ReactElement,
|
|
65
|
+
options?: {
|
|
66
|
+
onRecoverableError?: (error: Error) => void;
|
|
67
|
+
}
|
|
68
|
+
): ReactRoot;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* React-specific state snapshot
|
|
73
|
+
* Extends base snapshot with React-specific state like hooks
|
|
74
|
+
*/
|
|
75
|
+
interface ReactStateSnapshot extends StateSnapshot {
|
|
76
|
+
framework: 'react';
|
|
77
|
+
data: {
|
|
78
|
+
/**
|
|
79
|
+
* React Fiber tree data (if accessible)
|
|
80
|
+
* Used by React Fast Refresh to preserve hooks state
|
|
81
|
+
*/
|
|
82
|
+
fiberData?: unknown;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Component display name for debugging
|
|
86
|
+
*/
|
|
87
|
+
componentName?: string;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Props at time of state capture
|
|
91
|
+
*/
|
|
92
|
+
capturedProps?: Record<string, unknown>;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* React HMR Adapter
|
|
98
|
+
*
|
|
99
|
+
* Leverages React Fast Refresh (provided by @vitejs/plugin-react) to:
|
|
100
|
+
* - Preserve hooks state across updates
|
|
101
|
+
* - Maintain component tree structure
|
|
102
|
+
* - Handle component updates without full remount
|
|
103
|
+
*
|
|
104
|
+
* React Fast Refresh works by:
|
|
105
|
+
* 1. Detecting when a component module is updated
|
|
106
|
+
* 2. Preserving the React Fiber tree (internal state representation)
|
|
107
|
+
* 3. Re-rendering with the new component definition
|
|
108
|
+
* 4. Restoring hooks state from the preserved Fiber tree
|
|
109
|
+
*/
|
|
110
|
+
export class ReactHMRAdapter extends BaseFrameworkAdapter {
|
|
111
|
+
readonly name = 'react';
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Store React roots for each island to enable proper updates
|
|
115
|
+
*/
|
|
116
|
+
private roots: WeakMap<HTMLElement, ReactRoot> = new WeakMap();
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Check if a component is a React component
|
|
120
|
+
*
|
|
121
|
+
* React components can be:
|
|
122
|
+
* - Function components (including hooks)
|
|
123
|
+
* - Class components (extending React.Component)
|
|
124
|
+
* - Forward refs
|
|
125
|
+
* - Memoized components
|
|
126
|
+
*/
|
|
127
|
+
canHandle(component: unknown): boolean {
|
|
128
|
+
if (!component) return false;
|
|
129
|
+
|
|
130
|
+
// Check if it's a function (function component or class)
|
|
131
|
+
if (typeof component === 'function') {
|
|
132
|
+
// Check for React-specific properties on the function
|
|
133
|
+
// Use unknown first to avoid type errors
|
|
134
|
+
const comp = component as unknown as Record<string, unknown>;
|
|
135
|
+
|
|
136
|
+
// Class components have isReactComponent on prototype
|
|
137
|
+
const proto = (component as { prototype?: Record<string, unknown> }).prototype;
|
|
138
|
+
if (proto && proto.isReactComponent) {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Check for React element symbol (for wrapped components)
|
|
143
|
+
if (comp.$$typeof) {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Assume any function could be a React component
|
|
148
|
+
// React Fast Refresh will handle validation
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Check for React element types
|
|
153
|
+
if (typeof component !== 'object') {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const obj = component as Record<string, unknown>;
|
|
158
|
+
|
|
159
|
+
// Check for React element symbol
|
|
160
|
+
if (obj.$$typeof) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Check for wrapped components (HOCs, memo, forwardRef)
|
|
165
|
+
if (obj.type && typeof obj.type === 'function') {
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Preserve React component state before HMR update
|
|
174
|
+
*
|
|
175
|
+
* React Fast Refresh handles most state preservation automatically through
|
|
176
|
+
* the React Fiber tree. We capture additional DOM state and props for fallback.
|
|
177
|
+
*/
|
|
178
|
+
override preserveState(island: HTMLElement): ReactStateSnapshot | null {
|
|
179
|
+
try {
|
|
180
|
+
// Get base DOM state
|
|
181
|
+
const baseSnapshot = super.preserveState(island);
|
|
182
|
+
if (!baseSnapshot) return null;
|
|
183
|
+
|
|
184
|
+
// Get React-specific data
|
|
185
|
+
const propsAttr = island.getAttribute('data-props');
|
|
186
|
+
const capturedProps = propsAttr ? JSON.parse(propsAttr) : {};
|
|
187
|
+
|
|
188
|
+
// Try to get component name from the island
|
|
189
|
+
const src = island.getAttribute('data-src') || '';
|
|
190
|
+
const componentName = this.extractComponentName(src);
|
|
191
|
+
|
|
192
|
+
const reactSnapshot: ReactStateSnapshot = {
|
|
193
|
+
...baseSnapshot,
|
|
194
|
+
framework: 'react',
|
|
195
|
+
data: {
|
|
196
|
+
componentName,
|
|
197
|
+
capturedProps,
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
return reactSnapshot;
|
|
202
|
+
} catch (error) {
|
|
203
|
+
console.warn('Failed to preserve React state:', error);
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Update React component with HMR
|
|
210
|
+
*
|
|
211
|
+
* This method integrates with React Fast Refresh:
|
|
212
|
+
* 1. React Fast Refresh is automatically enabled by @vitejs/plugin-react
|
|
213
|
+
* 2. When a module updates, Vite sends an HMR event
|
|
214
|
+
* 3. We re-hydrate the component with the new definition
|
|
215
|
+
* 4. React Fast Refresh preserves hooks state automatically
|
|
216
|
+
* 5. The component re-renders with preserved state
|
|
217
|
+
*/
|
|
218
|
+
async update(
|
|
219
|
+
island: HTMLElement,
|
|
220
|
+
newComponent: unknown,
|
|
221
|
+
props: Record<string, unknown>
|
|
222
|
+
): Promise<void> {
|
|
223
|
+
if (!this.canHandle(newComponent)) {
|
|
224
|
+
throw new Error('Component is not a valid React component');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const Component = newComponent as ReactComponent;
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
// Dynamically import React and ReactDOM at runtime
|
|
231
|
+
// These are resolved by Vite in the browser
|
|
232
|
+
const [reactModule, reactDOMModule] = await Promise.all([
|
|
233
|
+
import('react') as Promise<ReactModule>,
|
|
234
|
+
import('react-dom/client') as Promise<ReactDOMClientModule>,
|
|
235
|
+
]);
|
|
236
|
+
|
|
237
|
+
const { createElement } = reactModule;
|
|
238
|
+
const { hydrateRoot } = reactDOMModule;
|
|
239
|
+
|
|
240
|
+
// Check if we have an existing root
|
|
241
|
+
const existingRoot = this.roots.get(island);
|
|
242
|
+
|
|
243
|
+
if (existingRoot) {
|
|
244
|
+
// Update existing root with new component
|
|
245
|
+
// React Fast Refresh will preserve hooks state automatically
|
|
246
|
+
const element = createElement(Component, props);
|
|
247
|
+
existingRoot.render(element);
|
|
248
|
+
} else {
|
|
249
|
+
// Create new root and hydrate
|
|
250
|
+
// This happens on first HMR update or if root was lost
|
|
251
|
+
const element = createElement(Component, props);
|
|
252
|
+
const newRoot = hydrateRoot(island, element, {
|
|
253
|
+
onRecoverableError: (error: Error) => {
|
|
254
|
+
console.warn('React hydration recoverable error during HMR:', error);
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Store root for future updates
|
|
259
|
+
this.roots.set(island, newRoot);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Mark as hydrated
|
|
263
|
+
island.setAttribute('data-hydrated', 'true');
|
|
264
|
+
island.setAttribute('data-hydration-status', 'success');
|
|
265
|
+
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error('React HMR update failed:', error);
|
|
268
|
+
island.setAttribute('data-hydration-status', 'error');
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Restore React component state after HMR update
|
|
275
|
+
*
|
|
276
|
+
* React Fast Refresh handles most state restoration automatically.
|
|
277
|
+
* We restore DOM state (scroll, focus, form values) as a supplement.
|
|
278
|
+
*/
|
|
279
|
+
override restoreState(island: HTMLElement, state: StateSnapshot): void {
|
|
280
|
+
try {
|
|
281
|
+
// Restore DOM state (scroll, focus, form values)
|
|
282
|
+
super.restoreState(island, state);
|
|
283
|
+
|
|
284
|
+
// React Fast Refresh handles hooks state restoration automatically
|
|
285
|
+
// through the React Fiber tree, so we don't need to do anything special here
|
|
286
|
+
|
|
287
|
+
} catch (error) {
|
|
288
|
+
console.warn('Failed to restore React state:', error);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Handle errors during React HMR update
|
|
294
|
+
*
|
|
295
|
+
* Provides React-specific error handling with helpful messages
|
|
296
|
+
*/
|
|
297
|
+
override handleError(island: HTMLElement, error: Error): void {
|
|
298
|
+
console.error('React HMR error:', error);
|
|
299
|
+
|
|
300
|
+
// Use base error handling
|
|
301
|
+
super.handleError(island, error);
|
|
302
|
+
|
|
303
|
+
// Add React-specific error information
|
|
304
|
+
const errorIndicator = island.querySelector('.hmr-error-indicator');
|
|
305
|
+
if (errorIndicator) {
|
|
306
|
+
const errorMessage = error.message;
|
|
307
|
+
|
|
308
|
+
// Provide helpful hints for common React errors
|
|
309
|
+
let hint = '';
|
|
310
|
+
if (errorMessage.includes('hooks')) {
|
|
311
|
+
hint = ' (Hint: Check hooks usage - hooks must be called in the same order)';
|
|
312
|
+
} else if (errorMessage.includes('render')) {
|
|
313
|
+
hint = ' (Hint: Check component render method for errors)';
|
|
314
|
+
} else if (errorMessage.includes('hydration')) {
|
|
315
|
+
hint = ' (Hint: Server and client render must match)';
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
errorIndicator.textContent = `React HMR Error: ${errorMessage}${hint}`;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Extract component name from source path
|
|
324
|
+
* Used for debugging and error messages
|
|
325
|
+
*/
|
|
326
|
+
private extractComponentName(src: string): string {
|
|
327
|
+
const parts = src.split('/');
|
|
328
|
+
const filename = parts[parts.length - 1];
|
|
329
|
+
return filename.replace(/\.(tsx?|jsx?)$/, '');
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Clean up React root when island is removed
|
|
334
|
+
* This should be called when an island is unmounted
|
|
335
|
+
*/
|
|
336
|
+
unmount(island: HTMLElement): void {
|
|
337
|
+
const root = this.roots.get(island);
|
|
338
|
+
if (root) {
|
|
339
|
+
try {
|
|
340
|
+
root.unmount();
|
|
341
|
+
this.roots.delete(island);
|
|
342
|
+
} catch (error) {
|
|
343
|
+
console.warn('Failed to unmount React root:', error);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Create and export a singleton instance of the React HMR adapter
|
|
351
|
+
*/
|
|
352
|
+
export const reactAdapter = new ReactHMRAdapter();
|
|
353
|
+
|