@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.
Files changed (159) hide show
  1. package/README.md +54 -0
  2. package/mod.ts +301 -0
  3. package/package.json +85 -0
  4. package/src/build/README.md +310 -0
  5. package/src/build/integration-bundler-plugin.ts +116 -0
  6. package/src/build/integration-config.ts +168 -0
  7. package/src/build/integration-detection-plugin.ts +117 -0
  8. package/src/build/integration-resolver-plugin.ts +90 -0
  9. package/src/build/island-manifest.ts +269 -0
  10. package/src/build/island-types-generator.ts +476 -0
  11. package/src/build/mdx-island-transform.ts +464 -0
  12. package/src/build/mdx-plugin.ts +98 -0
  13. package/src/build/page-island-transform.ts +598 -0
  14. package/src/build/prop-extractors/index.ts +21 -0
  15. package/src/build/prop-extractors/lit.ts +140 -0
  16. package/src/build/prop-extractors/qwik.ts +16 -0
  17. package/src/build/prop-extractors/solid.ts +125 -0
  18. package/src/build/prop-extractors/svelte.ts +194 -0
  19. package/src/build/prop-extractors/vue.ts +111 -0
  20. package/src/build/sidecar-file-manager.ts +104 -0
  21. package/src/build/sidecar-renderer.ts +30 -0
  22. package/src/client/adapters/index.ts +13 -0
  23. package/src/client/adapters/lit-adapter.ts +654 -0
  24. package/src/client/adapters/preact-adapter.ts +331 -0
  25. package/src/client/adapters/qwik-adapter.ts +345 -0
  26. package/src/client/adapters/react-adapter.ts +353 -0
  27. package/src/client/adapters/solid-adapter.ts +451 -0
  28. package/src/client/adapters/svelte-adapter.ts +524 -0
  29. package/src/client/adapters/vue-adapter.ts +467 -0
  30. package/src/client/components.ts +35 -0
  31. package/src/client/css-hmr-handler.ts +344 -0
  32. package/src/client/framework-adapter.ts +462 -0
  33. package/src/client/hmr-coordinator.ts +396 -0
  34. package/src/client/hmr-error-overlay.js +533 -0
  35. package/src/client/main.js +816 -0
  36. package/src/client/tests/css-hmr-handler.test.ts +360 -0
  37. package/src/client/tests/framework-adapter.test.ts +519 -0
  38. package/src/client/tests/hmr-coordinator.test.ts +176 -0
  39. package/src/client/tests/hydration-option-parsing.test.ts +107 -0
  40. package/src/client/tests/lit-adapter.test.ts +427 -0
  41. package/src/client/tests/preact-adapter.test.ts +353 -0
  42. package/src/client/tests/qwik-adapter.test.ts +343 -0
  43. package/src/client/tests/react-adapter.test.ts +317 -0
  44. package/src/client/tests/solid-adapter.test.ts +396 -0
  45. package/src/client/tests/svelte-adapter.test.ts +387 -0
  46. package/src/client/tests/vue-adapter.test.ts +407 -0
  47. package/src/client/types/framework-runtime.d.ts +68 -0
  48. package/src/client/types/vite-hmr.d.ts +46 -0
  49. package/src/client/types/vite-virtual-modules.d.ts +60 -0
  50. package/src/components/Image.tsx +123 -0
  51. package/src/components/IslandErrorBoundary.tsx +145 -0
  52. package/src/components/LayoutDataErrorBoundary.tsx +141 -0
  53. package/src/components/LayoutErrorBoundary.tsx +127 -0
  54. package/src/components/PersistentIsland.tsx +52 -0
  55. package/src/components/StreamingErrorBoundary.tsx +233 -0
  56. package/src/components/StreamingLayout.tsx +538 -0
  57. package/src/components/tests/component-analyzer.test.ts +96 -0
  58. package/src/components/tests/component-detection.test.ts +347 -0
  59. package/src/components/tests/persistent-islands.test.ts +398 -0
  60. package/src/core/components/component-analyzer.ts +192 -0
  61. package/src/core/components/component-detection.ts +508 -0
  62. package/src/core/components/enhanced-framework-detector.ts +500 -0
  63. package/src/core/components/framework-registry.ts +563 -0
  64. package/src/core/components/tests/enhanced-framework-detector.test.ts +577 -0
  65. package/src/core/components/tests/framework-registry.test.ts +465 -0
  66. package/src/core/content/mdx-processor.ts +46 -0
  67. package/src/core/integrations/README.md +282 -0
  68. package/src/core/integrations/index.ts +19 -0
  69. package/src/core/integrations/loader.ts +125 -0
  70. package/src/core/integrations/registry.ts +195 -0
  71. package/src/core/islands/island-persistence.ts +325 -0
  72. package/src/core/islands/island-state-serializer.ts +258 -0
  73. package/src/core/islands/persistent-island-context.tsx +80 -0
  74. package/src/core/islands/use-persistent-state.ts +68 -0
  75. package/src/core/layout/enhanced-layout-resolver.ts +322 -0
  76. package/src/core/layout/layout-cache-manager.ts +485 -0
  77. package/src/core/layout/layout-composer.ts +357 -0
  78. package/src/core/layout/layout-data-loader.ts +516 -0
  79. package/src/core/layout/layout-discovery.ts +243 -0
  80. package/src/core/layout/layout-matcher.ts +299 -0
  81. package/src/core/layout/layout-types.ts +110 -0
  82. package/src/core/layout/tests/enhanced-layout-resolver.test.ts +477 -0
  83. package/src/core/layout/tests/layout-cache-optimization.test.ts +149 -0
  84. package/src/core/layout/tests/layout-composer.test.ts +486 -0
  85. package/src/core/layout/tests/layout-data-loader.test.ts +443 -0
  86. package/src/core/layout/tests/layout-discovery.test.ts +253 -0
  87. package/src/core/layout/tests/layout-matcher.test.ts +480 -0
  88. package/src/core/modules/framework-module-resolver.ts +273 -0
  89. package/src/core/modules/tests/framework-module-resolver.test.ts +263 -0
  90. package/src/core/modules/tests/module-resolution-integration.test.ts +117 -0
  91. package/src/islands/component-analysis.ts +213 -0
  92. package/src/islands/css-utils.ts +565 -0
  93. package/src/islands/discovery/index.ts +80 -0
  94. package/src/islands/discovery/registry.ts +340 -0
  95. package/src/islands/discovery/resolver.ts +477 -0
  96. package/src/islands/discovery/scanner.ts +386 -0
  97. package/src/islands/discovery/tests/island-discovery.test.ts +881 -0
  98. package/src/islands/discovery/types.ts +117 -0
  99. package/src/islands/discovery/validator.ts +544 -0
  100. package/src/islands/discovery/watcher.ts +368 -0
  101. package/src/islands/framework-detection.ts +428 -0
  102. package/src/islands/integration-loader.ts +490 -0
  103. package/src/islands/island.tsx +565 -0
  104. package/src/islands/render-cache.ts +550 -0
  105. package/src/islands/types.ts +80 -0
  106. package/src/islands/universal-css-collector.ts +157 -0
  107. package/src/islands/universal-head-collector.ts +137 -0
  108. package/src/layout-system.d.ts +592 -0
  109. package/src/layout-system.ts +218 -0
  110. package/src/middleware/__tests__/discovery.test.ts +107 -0
  111. package/src/middleware/discovery.ts +268 -0
  112. package/src/middleware/executor.ts +315 -0
  113. package/src/middleware/index.ts +76 -0
  114. package/src/middleware/types.ts +99 -0
  115. package/src/nitro/build-config.ts +576 -0
  116. package/src/nitro/config.ts +483 -0
  117. package/src/nitro/error-handler.ts +636 -0
  118. package/src/nitro/index.ts +173 -0
  119. package/src/nitro/island-manifest.ts +584 -0
  120. package/src/nitro/middleware-adapter.ts +260 -0
  121. package/src/nitro/renderer.ts +1458 -0
  122. package/src/nitro/route-discovery.ts +439 -0
  123. package/src/nitro/types.ts +321 -0
  124. package/src/render/collect-css.ts +198 -0
  125. package/src/render/error-pages.ts +79 -0
  126. package/src/render/isolated-ssr-renderer.ts +654 -0
  127. package/src/render/ssr.ts +1030 -0
  128. package/src/schemas/api.ts +30 -0
  129. package/src/schemas/core.ts +64 -0
  130. package/src/schemas/index.ts +212 -0
  131. package/src/schemas/layout.ts +279 -0
  132. package/src/schemas/routing/index.ts +38 -0
  133. package/src/schemas/routing.ts +376 -0
  134. package/src/types/as-island.ts +20 -0
  135. package/src/types/image.d.ts +106 -0
  136. package/src/types/index.d.ts +22 -0
  137. package/src/types/island-jsx.d.ts +33 -0
  138. package/src/types/island-prop.d.ts +20 -0
  139. package/src/types/layout.ts +285 -0
  140. package/src/types/mdx.d.ts +6 -0
  141. package/src/types/routing.ts +555 -0
  142. package/src/types/tests/layout-types.test.ts +197 -0
  143. package/src/types/types.ts +5 -0
  144. package/src/types/urlpattern.d.ts +49 -0
  145. package/src/types/vite-env.d.ts +11 -0
  146. package/src/utils/dev-logger.ts +299 -0
  147. package/src/utils/fs.ts +151 -0
  148. package/src/vite-plugin/auto-discover.ts +551 -0
  149. package/src/vite-plugin/config.ts +266 -0
  150. package/src/vite-plugin/errors.ts +127 -0
  151. package/src/vite-plugin/image-optimization.ts +151 -0
  152. package/src/vite-plugin/integration-activator.ts +126 -0
  153. package/src/vite-plugin/island-sidecar-plugin.ts +176 -0
  154. package/src/vite-plugin/module-discovery.ts +189 -0
  155. package/src/vite-plugin/nitro-integration.ts +1334 -0
  156. package/src/vite-plugin/plugin.ts +329 -0
  157. package/src/vite-plugin/tests/image-optimization.test.ts +54 -0
  158. package/src/vite-plugin/types.ts +327 -0
  159. package/src/vite-plugin/validation.ts +228 -0
@@ -0,0 +1,524 @@
1
+ /**
2
+ * Svelte HMR Adapter
3
+ *
4
+ * Provides Hot Module Replacement support for Svelte 5 components.
5
+ * Integrates with @sveltejs/vite-plugin-svelte for HMR updates.
6
+ *
7
+ * IMPORTANT: Svelte 5 HMR Behavior
8
+ * - HMR is controlled via compilerOptions.hmr in the Vite plugin config
9
+ * - Local state is NOT preserved during HMR (by design in Svelte 5)
10
+ * - CSS-only changes DO preserve state (100% preserved)
11
+ * - Store subscriptions are maintained across updates
12
+ * - The component is remounted with fresh state on JS changes
13
+ *
14
+ * Requirements: 2.4
15
+ */
16
+
17
+ /// <reference lib="dom" />
18
+
19
+ import { BaseFrameworkAdapter, type StateSnapshot } from '../framework-adapter.ts';
20
+
21
+ /**
22
+ * Svelte component class type
23
+ * Svelte components are compiled to classes with specific methods
24
+ */
25
+ interface SvelteComponent {
26
+ /**
27
+ * Create a new component instance
28
+ */
29
+ new (options: SvelteComponentOptions): SvelteComponentInstance;
30
+
31
+ /**
32
+ * Svelte component marker
33
+ */
34
+ $render?: unknown;
35
+ }
36
+
37
+ /**
38
+ * Svelte component constructor options
39
+ */
40
+ interface SvelteComponentOptions {
41
+ /** Target DOM element */
42
+ target: HTMLElement;
43
+ /** Component props */
44
+ props?: Record<string, unknown>;
45
+ /** Hydration mode */
46
+ hydrate?: boolean;
47
+ /** Intro animations */
48
+ intro?: boolean;
49
+ /** Anchor element for insertion */
50
+ anchor?: Element | null;
51
+ /** Context for component */
52
+ context?: Map<unknown, unknown>;
53
+ }
54
+
55
+ /**
56
+ * Svelte component instance interface
57
+ */
58
+ interface SvelteComponentInstance {
59
+ /** Update component props */
60
+ $set(props: Record<string, unknown>): void;
61
+ /** Destroy component instance */
62
+ $destroy(): void;
63
+ /** Subscribe to component events */
64
+ $on?(event: string, handler: (...args: unknown[]) => void): () => void;
65
+ /** Access to component state (internal) */
66
+ $$?: {
67
+ ctx?: unknown[];
68
+ props?: Record<string, unknown>;
69
+ bound?: Record<string, unknown>;
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Svelte store interface
75
+ */
76
+ interface SvelteStore<T = unknown> {
77
+ subscribe(subscriber: (value: T) => void): () => void;
78
+ set?(value: T): void;
79
+ update?(updater: (value: T) => T): void;
80
+ }
81
+
82
+ /**
83
+ * Svelte-specific state snapshot
84
+ * Extends base snapshot with Svelte-specific state like local state and stores
85
+ */
86
+ interface SvelteStateSnapshot extends StateSnapshot {
87
+ framework: 'svelte';
88
+ data: {
89
+ /**
90
+ * Svelte component local state
91
+ * Captured from component instance
92
+ */
93
+ localState?: Record<string, unknown>;
94
+
95
+ /**
96
+ * Store subscriptions and values
97
+ * Captured from active stores
98
+ */
99
+ storeValues?: Record<string, unknown>;
100
+
101
+ /**
102
+ * Component display name for debugging
103
+ */
104
+ componentName?: string;
105
+
106
+ /**
107
+ * Props at time of state capture
108
+ */
109
+ capturedProps?: Record<string, unknown>;
110
+
111
+ /**
112
+ * Reactive statement dependencies
113
+ */
114
+ reactiveDependencies?: string[];
115
+ };
116
+ }
117
+
118
+ /**
119
+ * Svelte HMR Adapter
120
+ *
121
+ * Handles HMR for Svelte 5 components in the Avalon islands architecture.
122
+ *
123
+ * Svelte 5 HMR Behavior:
124
+ * - HMR is controlled via compilerOptions.hmr in the Vite plugin config
125
+ * - Local state is NOT preserved during HMR (by design in Svelte 5)
126
+ * - CSS-only changes DO preserve state (100% preserved)
127
+ * - Store subscriptions are maintained across updates
128
+ * - The component is remounted with fresh state on JS changes
129
+ *
130
+ * How it works:
131
+ * 1. When a component module is updated, Vite sends an HMR event
132
+ * 2. Svelte's compiler-integrated HMR handles the update
133
+ * 3. We clean up the old instance and mount the new component
134
+ * 4. DOM state (scroll, focus, form values) is preserved where possible
135
+ * 5. The component re-renders with fresh state
136
+ */
137
+ export class SvelteHMRAdapter extends BaseFrameworkAdapter {
138
+ readonly name = 'svelte';
139
+
140
+ /**
141
+ * Store Svelte component instances for each island to enable proper cleanup
142
+ */
143
+ private readonly instances: WeakMap<HTMLElement, SvelteComponentInstance> = new WeakMap();
144
+
145
+ /**
146
+ * Store component IDs for tracking
147
+ */
148
+ private readonly componentIds: WeakMap<HTMLElement, string> = new WeakMap();
149
+
150
+ /**
151
+ * Store active store subscriptions for cleanup
152
+ */
153
+ private readonly storeSubscriptions: WeakMap<HTMLElement, Array<() => void>> = new WeakMap();
154
+
155
+ /**
156
+ * Check if a function/class has Svelte component markers
157
+ */
158
+ private isSvelteFunction(component: Function): boolean {
159
+ const comp = component as unknown as Record<string, unknown>;
160
+
161
+ if (comp.$$render) {
162
+ return true;
163
+ }
164
+
165
+ const proto = (component as { prototype?: Record<string, unknown> }).prototype;
166
+ if (proto && ((proto.$set && proto.$destroy) || proto.$$)) {
167
+ return true;
168
+ }
169
+
170
+ try {
171
+ const funcStr = component.toString();
172
+ if (funcStr.includes('$set') || funcStr.includes('$destroy') || funcStr.includes('$$')) {
173
+ return true;
174
+ }
175
+ } catch {
176
+ // Ignore errors from toString()
177
+ }
178
+
179
+ return false;
180
+ }
181
+
182
+ /**
183
+ * Check if a component is a Svelte component
184
+ *
185
+ * Svelte components are compiled to classes with specific markers:
186
+ * - Constructor function
187
+ * - $$render method (SSR marker)
188
+ * - Prototype with $set, $destroy methods
189
+ */
190
+ canHandle(component: unknown): boolean {
191
+ if (!component) return false;
192
+
193
+ if (typeof component === 'function') {
194
+ return this.isSvelteFunction(component);
195
+ }
196
+
197
+ if (typeof component !== 'object') {
198
+ return false;
199
+ }
200
+
201
+ const obj = component as Record<string, unknown>;
202
+
203
+ if (obj.default && typeof obj.default === 'function') {
204
+ return this.canHandle(obj.default);
205
+ }
206
+
207
+ return obj.$$render !== undefined;
208
+ }
209
+
210
+ /**
211
+ * Preserve Svelte component state before HMR update
212
+ *
213
+ * Note: In Svelte 5, local state is NOT preserved during HMR (by design).
214
+ * We capture DOM state (scroll, focus, form values) which CAN be restored.
215
+ */
216
+ override preserveState(island: HTMLElement): SvelteStateSnapshot | null {
217
+ try {
218
+ // Get base DOM state
219
+ const baseSnapshot = super.preserveState(island);
220
+ if (!baseSnapshot) return null;
221
+
222
+ // Get Svelte-specific data
223
+ const propsAttr = island.dataset.props;
224
+ const capturedProps = propsAttr ? JSON.parse(propsAttr) : {};
225
+
226
+ // Try to get component name from the island
227
+ const src = island.dataset.src || '';
228
+ const componentName = this.extractComponentName(src);
229
+
230
+ // Note: In Svelte 5, local state is not preserved during HMR
231
+ // We still capture it for debugging purposes, but it won't be restored
232
+ const localState = this.captureLocalState(island);
233
+
234
+ // Store values are maintained by Svelte's reactivity system
235
+ const storeValues = this.captureStoreValues(island);
236
+
237
+ const svelteSnapshot: SvelteStateSnapshot = {
238
+ ...baseSnapshot,
239
+ framework: 'svelte',
240
+ data: {
241
+ componentName,
242
+ capturedProps,
243
+ localState,
244
+ storeValues,
245
+ },
246
+ };
247
+
248
+ return svelteSnapshot;
249
+ } catch (error) {
250
+ console.warn('Failed to preserve Svelte state:', error);
251
+ return null;
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Extract the Svelte component class from a module or function
257
+ */
258
+ private extractComponent(newComponent: unknown): SvelteComponent {
259
+ if (typeof newComponent === 'object' && newComponent !== null) {
260
+ const obj = newComponent as Record<string, unknown>;
261
+ if (obj.default && typeof obj.default === 'function') {
262
+ return obj.default as SvelteComponent;
263
+ }
264
+ throw new Error('Svelte component object must have a default export');
265
+ }
266
+ if (typeof newComponent === 'function') {
267
+ return newComponent as SvelteComponent;
268
+ }
269
+ throw new TypeError('Invalid Svelte component type');
270
+ }
271
+
272
+ /**
273
+ * Clean up an existing Svelte component instance
274
+ */
275
+ private async cleanupInstance(island: HTMLElement, instance: SvelteComponentInstance): Promise<void> {
276
+ try {
277
+ const subscriptions = this.storeSubscriptions.get(island);
278
+ if (subscriptions) {
279
+ subscriptions.forEach(unsubscribe => unsubscribe());
280
+ this.storeSubscriptions.delete(island);
281
+ }
282
+
283
+ const svelteModule = await import('svelte') as Record<string, unknown>;
284
+ const svelteUnmount = svelteModule.unmount as ((component: unknown) => void) | undefined;
285
+ if (svelteUnmount) {
286
+ svelteUnmount(instance);
287
+ } else if (instance.$destroy) {
288
+ instance.$destroy();
289
+ }
290
+ } catch {
291
+ if (instance.$destroy) {
292
+ instance.$destroy();
293
+ }
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Mount a Svelte component, trying Svelte 5 API first then falling back to constructor
299
+ */
300
+ private async mountComponent(
301
+ Component: SvelteComponent,
302
+ island: HTMLElement,
303
+ props: Record<string, unknown>
304
+ ): Promise<SvelteComponentInstance> {
305
+ try {
306
+ const svelteModule = await import('svelte') as Record<string, unknown>;
307
+ const svelteMount = svelteModule.mount as ((component: unknown, options: { target: HTMLElement; props: Record<string, unknown> }) => unknown) | undefined;
308
+
309
+ if (svelteMount) {
310
+ return svelteMount(Component as any, { target: island, props }) as SvelteComponentInstance;
311
+ }
312
+
313
+ return new Component({ target: island, props, hydrate: false, intro: false });
314
+ } catch (svelte5Error) {
315
+ console.debug('Svelte 5 API not available, using constructor API:', svelte5Error);
316
+ return new Component({ target: island, props, hydrate: false, intro: false });
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Update Svelte component with HMR
322
+ *
323
+ * Svelte 5 HMR Behavior:
324
+ * - HMR is handled by the Svelte compiler via compilerOptions.hmr
325
+ * - Local state is NOT preserved (by design)
326
+ * - CSS-only changes preserve state 100%
327
+ * - We clean up the old instance and mount the new component
328
+ */
329
+ async update(
330
+ island: HTMLElement,
331
+ newComponent: unknown,
332
+ props: Record<string, unknown>
333
+ ): Promise<void> {
334
+ if (!this.canHandle(newComponent)) {
335
+ throw new Error('Component is not a valid Svelte component');
336
+ }
337
+
338
+ const Component = this.extractComponent(newComponent);
339
+
340
+ try {
341
+ const existingInstance = this.instances.get(island);
342
+ if (existingInstance) {
343
+ await this.cleanupInstance(island, existingInstance).catch(error => {
344
+ console.warn('Failed to destroy existing Svelte instance:', error);
345
+ });
346
+ }
347
+
348
+ island.innerHTML = '';
349
+
350
+ const instance = await this.mountComponent(Component, island, props);
351
+
352
+ this.instances.set(island, instance);
353
+
354
+ const src = island.dataset.src || '';
355
+ this.componentIds.set(island, this.generateComponentId(src));
356
+
357
+ island.dataset.hydrated = 'true';
358
+ island.dataset.hydrationStatus = 'success';
359
+ } catch (error) {
360
+ console.error('Svelte HMR update failed:', error);
361
+ island.dataset.hydrationStatus = 'error';
362
+ throw error;
363
+ }
364
+ }
365
+
366
+ /**
367
+ * Restore Svelte component state after HMR update
368
+ *
369
+ * Note: In Svelte 5, local state is NOT preserved during HMR (by design).
370
+ * We only restore DOM state (scroll, focus, form values).
371
+ */
372
+ override restoreState(island: HTMLElement, state: StateSnapshot): void {
373
+ try {
374
+ // Restore DOM state (scroll, focus, form values)
375
+ super.restoreState(island, state);
376
+
377
+ // Note: Svelte 5 does NOT preserve local state during HMR
378
+ // This is by design - see https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/faq.md
379
+ // CSS-only changes DO preserve state 100%
380
+
381
+ } catch (error) {
382
+ console.warn('Failed to restore Svelte state:', error);
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Handle errors during Svelte HMR update
388
+ *
389
+ * Provides Svelte-specific error handling with helpful messages
390
+ */
391
+ override handleError(island: HTMLElement, error: Error): void {
392
+ console.error('Svelte HMR error:', error);
393
+
394
+ // Use base error handling
395
+ super.handleError(island, error);
396
+
397
+ // Add Svelte-specific error information
398
+ const errorIndicator = island.querySelector('.hmr-error-indicator');
399
+ if (errorIndicator) {
400
+ const errorMessage = error.message;
401
+
402
+ // Provide helpful hints for common Svelte errors
403
+ let hint = '';
404
+ if (errorMessage.includes('$:') || errorMessage.includes('reactive')) {
405
+ hint = ' (Hint: Check reactive statements ($:) - they must be at component top level)';
406
+ } else if (errorMessage.includes('store')) {
407
+ hint = ' (Hint: Check store usage - stores must be imported and subscribed correctly)';
408
+ } else if (errorMessage.includes('hydration') || errorMessage.includes('hydrate')) {
409
+ hint = ' (Hint: Server and client render must match)';
410
+ } else if (errorMessage.includes('target')) {
411
+ hint = ' (Hint: Check component target - it must be a valid DOM element)';
412
+ } else if (errorMessage.includes('props')) {
413
+ hint = ' (Hint: Check component props - they must match the component definition)';
414
+ }
415
+
416
+ errorIndicator.textContent = `Svelte HMR Error: ${errorMessage}${hint}`;
417
+ }
418
+ }
419
+
420
+ /**
421
+ * Extract component name from source path
422
+ * Used for debugging and error messages
423
+ */
424
+ private extractComponentName(src: string): string {
425
+ const parts = src.split('/');
426
+ const filename = parts.at(-1) ?? '';
427
+ return filename.replace(/\.svelte$/, '');
428
+ }
429
+
430
+ /**
431
+ * Detect if an island has existing SSR content vs being an empty container
432
+ *
433
+ * @param island - Island element to check
434
+ * @returns True if island has SSR content
435
+ */
436
+ private detectSSRContent(island: HTMLElement): boolean {
437
+ // Check if element has any meaningful content
438
+ const hasTextContent = island.textContent && island.textContent.trim().length > 0;
439
+ const hasChildElements = island.children && island.children.length > 0;
440
+ const hasAttributes = island.dataset.ssrContent !== undefined || island.dataset.svelteRendered !== undefined;
441
+
442
+ // Consider it SSR content if it has text, child elements, or explicit markers
443
+ return hasTextContent || hasChildElements || hasAttributes;
444
+ }
445
+
446
+ /**
447
+ * Generate a unique component ID for HMR runtime
448
+ */
449
+ private generateComponentId(src: string): string {
450
+ // Use the source path as the component ID
451
+ // This ensures consistency across HMR updates
452
+ return src.replaceAll(/[^a-zA-Z0-9]/g, '_');
453
+ }
454
+
455
+ /**
456
+ * Attempt to capture local state from Svelte instance
457
+ * This is best-effort and may not work in all cases
458
+ */
459
+ private captureLocalState(island: HTMLElement): Record<string, unknown> | undefined {
460
+ try {
461
+ const instance = this.instances.get(island);
462
+ if (!instance) return undefined;
463
+
464
+ // Try to access Svelte's internal state
465
+ // This is internal API and may change, so we wrap in try-catch
466
+ const internalState = instance.$$;
467
+
468
+ if (internalState?.ctx) {
469
+ // ctx is an array of component state values
470
+ // We can't easily map these back to variable names,
471
+ // so we just store the raw values
472
+ return {
473
+ ctx: internalState.ctx,
474
+ props: internalState.props,
475
+ bound: internalState.bound,
476
+ };
477
+ }
478
+
479
+ return undefined;
480
+ } catch (error) {
481
+ // Silently fail - this is best-effort
482
+ console.debug('Could not capture Svelte local state:', error);
483
+ return undefined;
484
+ }
485
+ }
486
+
487
+ /**
488
+ * Attempt to capture store values
489
+ * This is best-effort and may not work in all cases
490
+ */
491
+ private captureStoreValues(_island: HTMLElement): Record<string, unknown> | undefined {
492
+ // Svelte stores are typically imported at module level,
493
+ // so we can't easily access them from the component instance
494
+ // The HMR system should handle store subscriptions automatically
495
+
496
+ // We could potentially track stores if we intercept store.subscribe calls,
497
+ // but that would require modifying the Svelte runtime, which is not feasible
498
+
499
+ // For now, we rely on Svelte's HMR to preserve store subscriptions
500
+ return undefined;
501
+ }
502
+
503
+ /**
504
+ * Clean up Svelte component when island is removed
505
+ * This should be called when an island is unmounted
506
+ */
507
+ async unmount(_island: HTMLElement): Promise<void> {
508
+ const instance = this.instances.get(_island);
509
+ if (instance) {
510
+ try {
511
+ await this.cleanupInstance(_island, instance);
512
+ this.instances.delete(_island);
513
+ this.componentIds.delete(_island);
514
+ } catch (error) {
515
+ console.warn('Failed to unmount Svelte component:', error);
516
+ }
517
+ }
518
+ }
519
+ }
520
+
521
+ /**
522
+ * Create and export a singleton instance of the Svelte HMR adapter
523
+ */
524
+ export const svelteAdapter = new SvelteHMRAdapter();