@useavalon/avalon 0.1.11 → 0.1.12

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 (250) hide show
  1. package/README.md +54 -54
  2. package/dist/mod.js +1 -0
  3. package/dist/src/build/integration-bundler-plugin.js +1 -0
  4. package/dist/src/build/integration-config.js +1 -0
  5. package/dist/src/build/integration-detection-plugin.js +1 -0
  6. package/dist/src/build/integration-resolver-plugin.js +1 -0
  7. package/dist/src/build/island-manifest.js +1 -0
  8. package/dist/src/build/island-types-generator.js +5 -0
  9. package/dist/src/build/mdx-island-transform.js +2 -0
  10. package/dist/src/build/mdx-plugin.js +1 -0
  11. package/dist/src/build/page-island-transform.js +3 -0
  12. package/dist/src/build/prop-extractors/index.js +1 -0
  13. package/dist/src/build/prop-extractors/lit.js +1 -0
  14. package/dist/src/build/prop-extractors/qwik.js +1 -0
  15. package/dist/src/build/prop-extractors/solid.js +1 -0
  16. package/dist/src/build/prop-extractors/svelte.js +1 -0
  17. package/dist/src/build/prop-extractors/vue.js +1 -0
  18. package/dist/src/build/sidecar-file-manager.js +1 -0
  19. package/dist/src/build/sidecar-renderer.js +6 -0
  20. package/dist/src/client/adapters/index.js +1 -0
  21. package/dist/src/client/components.js +1 -0
  22. package/dist/src/client/css-hmr-handler.js +1 -0
  23. package/dist/src/client/framework-adapter.js +13 -0
  24. package/dist/src/client/hmr-coordinator.js +1 -0
  25. package/dist/src/client/hmr-error-overlay.js +214 -0
  26. package/dist/src/client/main.js +39 -0
  27. package/{src → dist/src}/client/types/framework-runtime.d.ts +68 -68
  28. package/{src → dist/src}/client/types/vite-hmr.d.ts +46 -46
  29. package/dist/src/client/types/vite-virtual-modules.d.ts +70 -0
  30. package/dist/src/components/Image.js +1 -0
  31. package/dist/src/components/IslandErrorBoundary.js +1 -0
  32. package/dist/src/components/LayoutDataErrorBoundary.js +1 -0
  33. package/dist/src/components/LayoutErrorBoundary.js +1 -0
  34. package/dist/src/components/PersistentIsland.js +1 -0
  35. package/dist/src/components/StreamingErrorBoundary.js +1 -0
  36. package/dist/src/components/StreamingLayout.js +29 -0
  37. package/dist/src/core/components/component-analyzer.js +1 -0
  38. package/dist/src/core/components/component-detection.js +5 -0
  39. package/dist/src/core/components/enhanced-framework-detector.js +1 -0
  40. package/dist/src/core/components/framework-registry.js +1 -0
  41. package/dist/src/core/content/mdx-processor.js +1 -0
  42. package/dist/src/core/integrations/index.js +1 -0
  43. package/dist/src/core/integrations/loader.js +1 -0
  44. package/dist/src/core/integrations/registry.js +1 -0
  45. package/dist/src/core/islands/island-persistence.js +1 -0
  46. package/dist/src/core/islands/island-state-serializer.js +1 -0
  47. package/dist/src/core/islands/persistent-island-context.js +1 -0
  48. package/dist/src/core/islands/use-persistent-state.js +1 -0
  49. package/dist/src/core/layout/enhanced-layout-resolver.js +1 -0
  50. package/dist/src/core/layout/layout-cache-manager.js +1 -0
  51. package/dist/src/core/layout/layout-composer.js +1 -0
  52. package/dist/src/core/layout/layout-data-loader.js +1 -0
  53. package/dist/src/core/layout/layout-discovery.js +1 -0
  54. package/dist/src/core/layout/layout-matcher.js +1 -0
  55. package/dist/src/core/layout/layout-types.js +1 -0
  56. package/dist/src/core/modules/framework-module-resolver.js +1 -0
  57. package/dist/src/islands/component-analysis.js +1 -0
  58. package/dist/src/islands/css-utils.js +17 -0
  59. package/dist/src/islands/discovery/index.js +1 -0
  60. package/dist/src/islands/discovery/registry.js +1 -0
  61. package/dist/src/islands/discovery/resolver.js +2 -0
  62. package/dist/src/islands/discovery/scanner.js +1 -0
  63. package/dist/src/islands/discovery/types.js +1 -0
  64. package/dist/src/islands/discovery/validator.js +18 -0
  65. package/dist/src/islands/discovery/watcher.js +1 -0
  66. package/dist/src/islands/framework-detection.js +1 -0
  67. package/dist/src/islands/integration-loader.js +1 -0
  68. package/dist/src/islands/island.js +1 -0
  69. package/dist/src/islands/render-cache.js +1 -0
  70. package/dist/src/islands/types.js +1 -0
  71. package/dist/src/islands/universal-css-collector.js +5 -0
  72. package/dist/src/islands/universal-head-collector.js +2 -0
  73. package/{src → dist/src}/layout-system.d.ts +592 -592
  74. package/dist/src/layout-system.js +1 -0
  75. package/dist/src/middleware/discovery.js +1 -0
  76. package/dist/src/middleware/executor.js +1 -0
  77. package/dist/src/middleware/index.js +1 -0
  78. package/dist/src/middleware/types.js +1 -0
  79. package/dist/src/nitro/build-config.js +1 -0
  80. package/dist/src/nitro/config.js +1 -0
  81. package/dist/src/nitro/error-handler.js +198 -0
  82. package/dist/src/nitro/index.js +1 -0
  83. package/dist/src/nitro/island-manifest.js +2 -0
  84. package/dist/src/nitro/middleware-adapter.js +1 -0
  85. package/dist/src/nitro/renderer.js +183 -0
  86. package/dist/src/nitro/route-discovery.js +1 -0
  87. package/dist/src/nitro/types.js +1 -0
  88. package/dist/src/render/collect-css.js +3 -0
  89. package/{src/render/error-pages.ts → dist/src/render/error-pages.js} +7 -38
  90. package/dist/src/render/isolated-ssr-renderer.js +1 -0
  91. package/dist/src/render/ssr.js +90 -0
  92. package/dist/src/schemas/api.js +1 -0
  93. package/dist/src/schemas/core.js +1 -0
  94. package/dist/src/schemas/index.js +1 -0
  95. package/dist/src/schemas/layout.js +1 -0
  96. package/dist/src/schemas/routing/index.js +1 -0
  97. package/dist/src/schemas/routing.js +1 -0
  98. package/dist/src/types/as-island.js +1 -0
  99. package/{src → dist/src}/types/image.d.ts +106 -106
  100. package/{src → dist/src}/types/index.d.ts +22 -22
  101. package/{src → dist/src}/types/island-jsx.d.ts +33 -33
  102. package/{src → dist/src}/types/island-prop.d.ts +20 -20
  103. package/dist/src/types/layout.js +1 -0
  104. package/{src → dist/src}/types/mdx.d.ts +6 -6
  105. package/dist/src/types/routing.js +1 -0
  106. package/dist/src/types/types.js +1 -0
  107. package/{src → dist/src}/types/urlpattern.d.ts +49 -49
  108. package/{src → dist/src}/types/vite-env.d.ts +11 -11
  109. package/dist/src/utils/dev-logger.js +12 -0
  110. package/dist/src/utils/fs.js +1 -0
  111. package/dist/src/vite-plugin/auto-discover.js +1 -0
  112. package/dist/src/vite-plugin/config.js +1 -0
  113. package/dist/src/vite-plugin/errors.js +1 -0
  114. package/dist/src/vite-plugin/image-optimization.js +45 -0
  115. package/dist/src/vite-plugin/integration-activator.js +1 -0
  116. package/dist/src/vite-plugin/island-sidecar-plugin.js +1 -0
  117. package/dist/src/vite-plugin/module-discovery.js +1 -0
  118. package/dist/src/vite-plugin/nitro-integration.js +42 -0
  119. package/dist/src/vite-plugin/plugin.js +1 -0
  120. package/dist/src/vite-plugin/types.js +1 -0
  121. package/dist/src/vite-plugin/validation.js +2 -0
  122. package/package.json +57 -26
  123. package/mod.ts +0 -302
  124. package/src/build/integration-bundler-plugin.ts +0 -116
  125. package/src/build/integration-config.ts +0 -168
  126. package/src/build/integration-detection-plugin.ts +0 -117
  127. package/src/build/integration-resolver-plugin.ts +0 -90
  128. package/src/build/island-manifest.ts +0 -269
  129. package/src/build/island-types-generator.ts +0 -476
  130. package/src/build/mdx-island-transform.ts +0 -464
  131. package/src/build/mdx-plugin.ts +0 -98
  132. package/src/build/page-island-transform.ts +0 -598
  133. package/src/build/prop-extractors/index.ts +0 -21
  134. package/src/build/prop-extractors/lit.ts +0 -140
  135. package/src/build/prop-extractors/qwik.ts +0 -16
  136. package/src/build/prop-extractors/solid.ts +0 -125
  137. package/src/build/prop-extractors/svelte.ts +0 -194
  138. package/src/build/prop-extractors/vue.ts +0 -111
  139. package/src/build/sidecar-file-manager.ts +0 -104
  140. package/src/build/sidecar-renderer.ts +0 -30
  141. package/src/client/adapters/index.js +0 -12
  142. package/src/client/adapters/index.ts +0 -13
  143. package/src/client/adapters/lit-adapter.js +0 -467
  144. package/src/client/adapters/lit-adapter.ts +0 -654
  145. package/src/client/adapters/preact-adapter.js +0 -223
  146. package/src/client/adapters/preact-adapter.ts +0 -331
  147. package/src/client/adapters/qwik-adapter.js +0 -259
  148. package/src/client/adapters/qwik-adapter.ts +0 -345
  149. package/src/client/adapters/react-adapter.js +0 -220
  150. package/src/client/adapters/react-adapter.ts +0 -353
  151. package/src/client/adapters/solid-adapter.js +0 -295
  152. package/src/client/adapters/solid-adapter.ts +0 -451
  153. package/src/client/adapters/svelte-adapter.js +0 -368
  154. package/src/client/adapters/svelte-adapter.ts +0 -524
  155. package/src/client/adapters/vue-adapter.js +0 -278
  156. package/src/client/adapters/vue-adapter.ts +0 -467
  157. package/src/client/components.js +0 -23
  158. package/src/client/components.ts +0 -35
  159. package/src/client/css-hmr-handler.js +0 -263
  160. package/src/client/css-hmr-handler.ts +0 -344
  161. package/src/client/framework-adapter.js +0 -283
  162. package/src/client/framework-adapter.ts +0 -462
  163. package/src/client/hmr-coordinator.js +0 -274
  164. package/src/client/hmr-coordinator.ts +0 -396
  165. package/src/client/hmr-error-overlay.js +0 -533
  166. package/src/client/main.js +0 -816
  167. package/src/client/types/vite-virtual-modules.d.ts +0 -60
  168. package/src/components/Image.tsx +0 -123
  169. package/src/components/IslandErrorBoundary.tsx +0 -145
  170. package/src/components/LayoutDataErrorBoundary.tsx +0 -141
  171. package/src/components/LayoutErrorBoundary.tsx +0 -127
  172. package/src/components/PersistentIsland.tsx +0 -52
  173. package/src/components/StreamingErrorBoundary.tsx +0 -233
  174. package/src/components/StreamingLayout.tsx +0 -538
  175. package/src/core/components/component-analyzer.ts +0 -192
  176. package/src/core/components/component-detection.ts +0 -508
  177. package/src/core/components/enhanced-framework-detector.ts +0 -500
  178. package/src/core/components/framework-registry.ts +0 -563
  179. package/src/core/content/mdx-processor.ts +0 -46
  180. package/src/core/integrations/index.ts +0 -19
  181. package/src/core/integrations/loader.ts +0 -125
  182. package/src/core/integrations/registry.ts +0 -175
  183. package/src/core/islands/island-persistence.ts +0 -325
  184. package/src/core/islands/island-state-serializer.ts +0 -258
  185. package/src/core/islands/persistent-island-context.tsx +0 -80
  186. package/src/core/islands/use-persistent-state.ts +0 -68
  187. package/src/core/layout/enhanced-layout-resolver.ts +0 -322
  188. package/src/core/layout/layout-cache-manager.ts +0 -485
  189. package/src/core/layout/layout-composer.ts +0 -357
  190. package/src/core/layout/layout-data-loader.ts +0 -516
  191. package/src/core/layout/layout-discovery.ts +0 -243
  192. package/src/core/layout/layout-matcher.ts +0 -299
  193. package/src/core/layout/layout-types.ts +0 -110
  194. package/src/core/modules/framework-module-resolver.ts +0 -273
  195. package/src/islands/component-analysis.ts +0 -213
  196. package/src/islands/css-utils.ts +0 -565
  197. package/src/islands/discovery/index.ts +0 -80
  198. package/src/islands/discovery/registry.ts +0 -340
  199. package/src/islands/discovery/resolver.ts +0 -477
  200. package/src/islands/discovery/scanner.ts +0 -386
  201. package/src/islands/discovery/types.ts +0 -117
  202. package/src/islands/discovery/validator.ts +0 -544
  203. package/src/islands/discovery/watcher.ts +0 -368
  204. package/src/islands/framework-detection.ts +0 -428
  205. package/src/islands/integration-loader.ts +0 -490
  206. package/src/islands/island.tsx +0 -565
  207. package/src/islands/render-cache.ts +0 -550
  208. package/src/islands/types.ts +0 -80
  209. package/src/islands/universal-css-collector.ts +0 -157
  210. package/src/islands/universal-head-collector.ts +0 -137
  211. package/src/layout-system.ts +0 -218
  212. package/src/middleware/discovery.ts +0 -268
  213. package/src/middleware/executor.ts +0 -315
  214. package/src/middleware/index.ts +0 -76
  215. package/src/middleware/types.ts +0 -99
  216. package/src/nitro/build-config.ts +0 -576
  217. package/src/nitro/config.ts +0 -483
  218. package/src/nitro/error-handler.ts +0 -636
  219. package/src/nitro/index.ts +0 -173
  220. package/src/nitro/island-manifest.ts +0 -584
  221. package/src/nitro/middleware-adapter.ts +0 -260
  222. package/src/nitro/renderer.ts +0 -1471
  223. package/src/nitro/route-discovery.ts +0 -439
  224. package/src/nitro/types.ts +0 -321
  225. package/src/render/collect-css.ts +0 -198
  226. package/src/render/isolated-ssr-renderer.ts +0 -654
  227. package/src/render/ssr.ts +0 -1030
  228. package/src/schemas/api.ts +0 -30
  229. package/src/schemas/core.ts +0 -64
  230. package/src/schemas/index.ts +0 -212
  231. package/src/schemas/layout.ts +0 -279
  232. package/src/schemas/routing/index.ts +0 -38
  233. package/src/schemas/routing.ts +0 -376
  234. package/src/types/as-island.ts +0 -20
  235. package/src/types/layout.ts +0 -285
  236. package/src/types/routing.ts +0 -555
  237. package/src/types/types.ts +0 -5
  238. package/src/utils/dev-logger.ts +0 -299
  239. package/src/utils/fs.ts +0 -151
  240. package/src/vite-plugin/auto-discover.ts +0 -551
  241. package/src/vite-plugin/config.ts +0 -266
  242. package/src/vite-plugin/errors.ts +0 -127
  243. package/src/vite-plugin/image-optimization.ts +0 -156
  244. package/src/vite-plugin/integration-activator.ts +0 -126
  245. package/src/vite-plugin/island-sidecar-plugin.ts +0 -176
  246. package/src/vite-plugin/module-discovery.ts +0 -189
  247. package/src/vite-plugin/nitro-integration.ts +0 -1354
  248. package/src/vite-plugin/plugin.ts +0 -409
  249. package/src/vite-plugin/types.ts +0 -327
  250. package/src/vite-plugin/validation.ts +0 -228
@@ -1,816 +0,0 @@
1
- // Main client entry point for Vite
2
- // Integration-based island hydration system
3
- //
4
- // NOTE: This file contains imports to Vite virtual modules (/@useavalon/*/client)
5
- // that will show as errors in the IDE. These are resolved by Vite at runtime
6
- // and work correctly in the browser. The errors can be safely ignored.
7
-
8
- if (document.readyState === 'loading') {
9
- document.addEventListener('DOMContentLoaded', initializeHydration);
10
- } else {
11
- initializeHydration();
12
- }
13
-
14
- /**
15
- * Initialize hydration for all islands on the page
16
- * Discovers islands by data-framework attribute and routes to appropriate integration
17
- */
18
- function initializeHydration() {
19
- const islands = document.querySelectorAll('[data-framework]');
20
-
21
- if (islands.length === 0) {
22
- return;
23
- }
24
-
25
- islands.forEach(island => {
26
- try {
27
- const framework = island.dataset.framework;
28
- const condition = island.dataset.condition || 'on:client';
29
- const renderStrategy = island.dataset.renderStrategy;
30
-
31
- if (renderStrategy === 'ssr-only') {
32
- return;
33
- }
34
-
35
- if (!shouldHydrate(island, condition)) {
36
- return;
37
- }
38
-
39
- if (condition === 'on:client') {
40
- hydrateIsland(island, framework);
41
- } else if (condition === 'on:visible') {
42
- setupIntersectionObserver(island, framework);
43
- } else if (condition === 'on:interaction') {
44
- setupInteractionObserver(island, framework);
45
- } else if (condition === 'on:idle') {
46
- setupIdleCallback(island, framework);
47
- } else if (condition.startsWith('media:')) {
48
- const mediaQuery = condition.slice(6);
49
- setupMediaQuery(island, framework, mediaQuery);
50
- } else {
51
- hydrateIsland(island, framework);
52
- }
53
- } catch (error) {
54
- console.error('Error processing island:', error);
55
- handleHydrationError(island, island.dataset.framework || 'unknown', island.dataset.src || 'unknown', error);
56
- }
57
- });
58
- }
59
-
60
- /**
61
- * Determine if an island should hydrate based on its condition
62
- *
63
- * @param {HTMLElement} island - The island element
64
- * @param {string} condition - The hydration condition
65
- * @returns {boolean} Whether the island should hydrate
66
- */
67
- function shouldHydrate(island, condition) {
68
- if (!condition || condition === 'on:client') {
69
- return true;
70
- }
71
-
72
- if (condition.startsWith('media:')) {
73
- const mediaQuery = condition.slice(6);
74
- try {
75
- return globalThis.matchMedia(mediaQuery).matches;
76
- } catch (error) {
77
- console.error('Invalid media query:', mediaQuery, error);
78
- return true;
79
- }
80
- }
81
-
82
- if (condition === 'on:visible' || condition === 'on:interaction' || condition === 'on:idle') {
83
- return true;
84
- }
85
-
86
- console.warn('Unknown hydration condition:', condition);
87
- return true;
88
- }
89
-
90
- /**
91
- * Setup Intersection Observer for "on:visible" hydration
92
- *
93
- * @param {HTMLElement} island - The island element
94
- * @param {string} framework - The framework name
95
- */
96
- function setupIntersectionObserver(island, framework) {
97
- try {
98
- const observer = new IntersectionObserver(
99
- entries => {
100
- const entry = entries[0];
101
- if (entry.isIntersecting) {
102
- hydrateIsland(island, framework);
103
- observer.disconnect();
104
- }
105
- },
106
- {
107
- rootMargin: '50px',
108
- threshold: 0,
109
- },
110
- );
111
-
112
- observer.observe(island);
113
- } catch (error) {
114
- console.error('Failed to setup intersection observer:', error);
115
- hydrateIsland(island, framework);
116
- }
117
- }
118
-
119
- /**
120
- * Setup interaction observer for "on:interaction" hydration
121
- *
122
- * @param {HTMLElement} island - The island element
123
- * @param {string} framework - The framework name
124
- */
125
- function setupInteractionObserver(island, framework) {
126
- const events = ['click', 'touchstart', 'mouseenter', 'focusin'];
127
- let hydrated = false;
128
-
129
- const handleInteraction = () => {
130
- if (hydrated) return;
131
- hydrated = true;
132
-
133
- events.forEach(eventType => {
134
- island.removeEventListener(eventType, handleInteraction);
135
- });
136
-
137
- hydrateIsland(island, framework);
138
- };
139
-
140
- try {
141
- events.forEach(eventType => {
142
- island.addEventListener(eventType, handleInteraction, { once: true, passive: true });
143
- });
144
- } catch (error) {
145
- console.error('Failed to setup interaction observer:', error);
146
- hydrateIsland(island, framework);
147
- }
148
- }
149
-
150
- /**
151
- * Setup idle callback for "on:idle" hydration
152
- *
153
- * @param {HTMLElement} island - The island element
154
- * @param {string} framework - The framework name
155
- */
156
- function setupIdleCallback(island, framework) {
157
- try {
158
- if ('requestIdleCallback' in globalThis) {
159
- globalThis.requestIdleCallback(
160
- () => {
161
- hydrateIsland(island, framework);
162
- },
163
- { timeout: 5000 },
164
- );
165
- } else if (document.readyState === 'complete') {
166
- setTimeout(() => {
167
- hydrateIsland(island, framework);
168
- }, 200);
169
- } else {
170
- globalThis.addEventListener(
171
- 'load',
172
- () => {
173
- setTimeout(() => {
174
- hydrateIsland(island, framework);
175
- }, 200);
176
- },
177
- { once: true },
178
- );
179
- }
180
- } catch (error) {
181
- console.error('Failed to setup idle callback:', error);
182
- hydrateIsland(island, framework);
183
- }
184
- }
185
-
186
- /**
187
- * Setup media query listener for "media:" hydration
188
- *
189
- * @param {HTMLElement} island - The island element
190
- * @param {string} framework - The framework name
191
- * @param {string} mediaQuery - The media query string
192
- */
193
- function setupMediaQuery(island, framework, mediaQuery) {
194
- try {
195
- const mql = globalThis.matchMedia(mediaQuery);
196
-
197
- if (mql.matches) {
198
- hydrateIsland(island, framework);
199
- return;
200
- }
201
-
202
- const handleChange = event => {
203
- if (event.matches) {
204
- hydrateIsland(island, framework);
205
- mql.removeEventListener('change', handleChange);
206
- }
207
- };
208
-
209
- mql.addEventListener('change', handleChange);
210
- } catch (error) {
211
- console.error('Failed to setup media query:', mediaQuery, error);
212
- hydrateIsland(island, framework);
213
- }
214
- }
215
-
216
- /**
217
- * Load the integration module for a given framework
218
- *
219
- * @param {string} framework - The framework name
220
- * @returns {Promise<object>} The integration module
221
- */
222
- async function loadIntegrationModule(framework) {
223
- switch (framework) {
224
- case 'preact':
225
- // @ts-ignore - Vite resolves this at runtime
226
- return import('/@useavalon/preact/client');
227
- case 'react':
228
- // @ts-ignore - Vite resolves this at runtime
229
- return import('/@useavalon/react/client');
230
- case 'vue':
231
- // @ts-ignore - Vite resolves this at runtime
232
- return import('/@useavalon/vue/client');
233
- case 'svelte':
234
- // @ts-ignore - Vite resolves this at runtime
235
- return import('/@useavalon/svelte/client');
236
- case 'solid':
237
- // @ts-ignore - Vite resolves this at runtime
238
- return import('/@useavalon/solid/client');
239
- case 'lit':
240
- // @ts-ignore - Vite resolves this at runtime
241
- return import('/@useavalon/lit/client');
242
- case 'qwik':
243
- // @ts-ignore - Vite resolves this at runtime
244
- return import('/@useavalon/qwik/client');
245
- default:
246
- throw new Error(`Unknown framework: ${framework}`);
247
- }
248
- }
249
-
250
- /**
251
- * Resolve the component from a module, trying default export then named exports
252
- *
253
- * @param {object} componentModule - The imported module
254
- * @param {string} src - The component source path (for error messages)
255
- * @returns {object} The resolved component
256
- */
257
- function resolveComponent(componentModule, src) {
258
- let Component = componentModule.default;
259
-
260
- if (!Component) {
261
- const exports = Object.keys(componentModule).filter(key => key !== 'default');
262
- for (const exportName of exports) {
263
- const exportValue = componentModule[exportName];
264
- if (typeof exportValue === 'function' && exportValue.prototype) {
265
- Component = exportValue;
266
- break;
267
- }
268
- }
269
-
270
- if (!Component) {
271
- Component = componentModule;
272
- }
273
- }
274
-
275
- if (!Component) {
276
- throw new Error(`Component ${src} has no default export`);
277
- }
278
-
279
- return Component;
280
- }
281
-
282
- /**
283
- * Hydrate an island using the integration system
284
- *
285
- * @param {HTMLElement} island - The island element
286
- * @param {string} framework - The framework name
287
- */
288
- async function hydrateIsland(island, framework) {
289
- if (island.dataset.hydrated) {
290
- return;
291
- }
292
-
293
- const src = island.dataset.src;
294
- const propsAttr = island.dataset.props;
295
-
296
- if (!src) {
297
- console.warn('Island missing data-src attribute');
298
- return;
299
- }
300
-
301
- try {
302
- const props = propsAttr ? JSON.parse(propsAttr) : {};
303
-
304
- // CRITICAL: For Lit components, load hydration support BEFORE importing the component
305
- // This ensures our patch is applied before @customElement decorator runs
306
- if (framework === 'lit') {
307
- // @ts-ignore - Vite resolves this virtual module at runtime
308
- await import('/@useavalon/lit/client');
309
- }
310
-
311
- const componentModule = await import(/* @vite-ignore */ src);
312
- const Component = resolveComponent(componentModule, src);
313
-
314
- try {
315
- const integrationModule = await loadIntegrationModule(framework);
316
-
317
- if (!integrationModule.hydrate || typeof integrationModule.hydrate !== 'function') {
318
- throw new Error(`Integration ${framework} does not export a hydrate function`);
319
- }
320
-
321
- integrationModule.hydrate(island, Component, props);
322
- island.dataset.hydrated = 'true';
323
- } catch (integrationError) {
324
- if (import.meta.env?.DEV) {
325
- console.error(`Integration hydration failed for ${framework}: ${src}`, integrationError);
326
- }
327
-
328
- island.dataset.hydrationStatus = 'failed';
329
- island.dataset.hydrationError = integrationError.message;
330
-
331
- island.dispatchEvent(
332
- new CustomEvent('hydration-error', {
333
- detail: {
334
- framework,
335
- src,
336
- error: integrationError.message,
337
- timestamp: Date.now(),
338
- hydrationType: 'integration-level',
339
- },
340
- bubbles: true,
341
- }),
342
- );
343
- }
344
- } catch (error) {
345
- console.error(`❌ Critical error hydrating ${framework} island ${src}:`, error);
346
- handleHydrationError(island, framework, src, error);
347
- }
348
- }
349
-
350
- /**
351
- * Handle hydration errors with graceful degradation
352
- *
353
- * @param {HTMLElement} island - The island element
354
- * @param {string} framework - The framework name
355
- * @param {string} src - The component source path
356
- * @param {Error} error - The error that occurred
357
- */
358
- function handleHydrationError(island, framework, src, error) {
359
- console.error(`Hydration error for ${framework} island:`, {
360
- src,
361
- error: error.message,
362
- stack: error.stack,
363
- });
364
-
365
- island.dataset.hydrationStatus = 'failed';
366
- island.dataset.renderStrategy = 'ssr-only';
367
- island.classList.add('hydration-failed');
368
-
369
- island.dispatchEvent(
370
- new CustomEvent('hydration-error', {
371
- detail: {
372
- framework,
373
- src,
374
- error: error.message,
375
- timestamp: Date.now(),
376
- },
377
- bubbles: true,
378
- }),
379
- );
380
-
381
- if (isDevelopment()) {
382
- addErrorIndicator(island, framework, src, error);
383
- }
384
- }
385
-
386
- /**
387
- * Add visual error indicator in development mode
388
- *
389
- * @param {HTMLElement} island - The island element
390
- * @param {string} framework - The framework name
391
- * @param {string} src - The component source path
392
- * @param {Error} error - The error that occurred
393
- */
394
- function addErrorIndicator(island, framework, src, error) {
395
- const indicator = document.createElement('div');
396
- indicator.className = 'hydration-error-indicator';
397
- indicator.style.cssText = `
398
- position: absolute;
399
- top: 0;
400
- right: 0;
401
- background: #ff4444;
402
- color: white;
403
- padding: 4px 8px;
404
- font-size: 11px;
405
- font-family: monospace;
406
- border-radius: 0 0 0 4px;
407
- z-index: 9999;
408
- cursor: pointer;
409
- box-shadow: 0 2px 4px rgba(0,0,0,0.2);
410
- `;
411
- indicator.textContent = `❌ ${framework}`;
412
- indicator.title = `Hydration failed: ${src}\n${error.message}\nClick for details`;
413
-
414
- indicator.addEventListener('click', () => {
415
- alert(
416
- `Hydration Error\n\nFramework: ${framework}\nComponent: ${src}\n\nError: ${error.message}\n\nStack:\n${error.stack}`,
417
- );
418
- });
419
-
420
- const computedStyle = globalThis.getComputedStyle(island);
421
- if (computedStyle.position === 'static') {
422
- island.style.position = 'relative';
423
- }
424
-
425
- island.appendChild(indicator);
426
- }
427
-
428
- /**
429
- * Check if running in development mode
430
- *
431
- * @returns {boolean} True if in development
432
- */
433
- function isDevelopment() {
434
- return (
435
- import.meta.env?.DEV ||
436
- import.meta.env?.MODE === 'development' ||
437
- globalThis.location?.hostname === 'localhost' ||
438
- globalThis.location?.hostname === '127.0.0.1'
439
- );
440
- }
441
-
442
- /**
443
- * Store island state before HMR update
444
- * @param {HTMLElement} island - The island element
445
- * @returns {object|null} The preserved state
446
- */
447
- function preserveIslandState(island) {
448
- const framework = island.dataset.framework;
449
- const src = island.dataset.src;
450
-
451
- if (!src) return null;
452
-
453
- const state = {
454
- framework,
455
- src,
456
- props: island.dataset.props,
457
- scrollPosition: {
458
- x: globalThis.scrollX,
459
- y: globalThis.scrollY,
460
- },
461
- focusedElement: document.activeElement?.id || null,
462
- };
463
-
464
- try {
465
- if (framework === 'vue' && island.__vue__) {
466
- state.vueData = structuredClone(island.__vue__.$data || {});
467
- } else if (framework === 'svelte' && island.__svelte__) {
468
- state.svelteState = island.__svelte__;
469
- } else if (framework === 'lit' && island.tagName?.includes('-')) {
470
- const litElement = island.querySelector('[data-lit-element]') || island;
471
- if (litElement._$litElement$) {
472
- state.litProperties = {};
473
- }
474
- }
475
- } catch (error) {
476
- console.warn('Failed to preserve island state:', error);
477
- }
478
-
479
- return state;
480
- }
481
-
482
- /**
483
- * Restore island state after HMR update
484
- * @param {HTMLElement} island - The island element
485
- * @param {object} state - The preserved state
486
- */
487
- function restoreIslandState(island, state) {
488
- if (!state) return;
489
-
490
- try {
491
- if (state.scrollPosition) {
492
- globalThis.scrollTo(state.scrollPosition.x, state.scrollPosition.y);
493
- }
494
-
495
- if (state.focusedElement) {
496
- const element = document.getElementById(state.focusedElement);
497
- if (element) {
498
- element.focus();
499
- }
500
- }
501
-
502
- if (island.dataset.framework === 'vue' && state.vueData && island.__vue__) {
503
- Object.assign(island.__vue__.$data, state.vueData);
504
- }
505
- } catch (error) {
506
- console.warn('Failed to restore island state:', error);
507
- }
508
- }
509
-
510
- /**
511
- * Hydrate an island with a fresh module (cache-busted)
512
- * @param {HTMLElement} island - The island element
513
- * @param {string} framework - The framework name
514
- * @param {string} freshSrc - The cache-busted source path
515
- * @param {string} originalSrc - The original source path
516
- */
517
- async function hydrateIslandWithFreshModule(island, framework, freshSrc, originalSrc) {
518
- const propsAttr = island.dataset.props;
519
- const props = propsAttr ? JSON.parse(propsAttr) : {};
520
-
521
- if (framework === 'lit') {
522
- // @ts-ignore - Vite resolves this at runtime
523
- await import('/@useavalon/lit/client');
524
- }
525
-
526
- const componentModule = await import(/* @vite-ignore */ freshSrc);
527
- const Component = resolveComponent(componentModule, originalSrc);
528
-
529
- const integrationModule = await loadIntegrationModule(framework);
530
-
531
- if (!integrationModule.hydrate || typeof integrationModule.hydrate !== 'function') {
532
- throw new Error(`Integration ${framework} does not export a hydrate function`);
533
- }
534
-
535
- integrationModule.hydrate(island, Component, props);
536
- island.dataset.hydrated = 'true';
537
- }
538
-
539
- /**
540
- * Show inline HMR error indicator
541
- * @param {HTMLElement} island - The island element
542
- * @param {string} framework - The framework name
543
- * @param {string} src - The component source path
544
- * @param {Error} error - The error that occurred
545
- */
546
- function showInlineHMRError(island, framework, src, error) {
547
- const existing = island.querySelector('.hmr-error-indicator');
548
- if (existing) {
549
- existing.remove();
550
- }
551
-
552
- const indicator = document.createElement('div');
553
- indicator.className = 'hmr-error-indicator';
554
- indicator.style.cssText = `
555
- position: absolute;
556
- top: 0;
557
- left: 0;
558
- right: 0;
559
- background: linear-gradient(135deg, #ff6b6b, #ee5a5a);
560
- color: white;
561
- padding: 8px 12px;
562
- font-size: 12px;
563
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, monospace;
564
- z-index: 10000;
565
- box-shadow: 0 2px 8px rgba(0,0,0,0.3);
566
- display: flex;
567
- align-items: center;
568
- gap: 8px;
569
- `;
570
-
571
- const icon = document.createElement('span');
572
- icon.textContent = '⚠️';
573
- icon.style.fontSize = '14px';
574
-
575
- const message = document.createElement('span');
576
- message.style.flex = '1';
577
- message.innerHTML = `<strong>HMR Failed:</strong> ${error.message.slice(0, 100)}${error.message.length > 100 ? '...' : ''}`;
578
-
579
- const dismissBtn = document.createElement('button');
580
- dismissBtn.textContent = '×';
581
- dismissBtn.style.cssText = `
582
- background: rgba(255,255,255,0.2);
583
- border: none;
584
- color: white;
585
- width: 20px;
586
- height: 20px;
587
- border-radius: 50%;
588
- cursor: pointer;
589
- font-size: 14px;
590
- line-height: 1;
591
- `;
592
- dismissBtn.onclick = () => indicator.remove();
593
-
594
- indicator.appendChild(icon);
595
- indicator.appendChild(message);
596
- indicator.appendChild(dismissBtn);
597
-
598
- const computedStyle = globalThis.getComputedStyle(island);
599
- if (computedStyle.position === 'static') {
600
- island.style.position = 'relative';
601
- }
602
-
603
- island.insertBefore(indicator, island.firstChild);
604
- }
605
-
606
- // HMR support for development
607
- if (import.meta.hot) {
608
- import.meta.hot.accept();
609
-
610
- // Lazy HMR adapter registration - only load adapters for frameworks used on the page
611
- import('./hmr-coordinator.js')
612
- .then(async ({ initializeHMR, getHMRCoordinator }) => {
613
- initializeHMR();
614
-
615
- const coordinator = getHMRCoordinator();
616
-
617
- // Discover which frameworks are actually used on this page
618
- const usedFrameworks = new Set();
619
- document.querySelectorAll('[data-framework]').forEach(island => {
620
- const framework = island.dataset.framework;
621
- if (framework) usedFrameworks.add(framework);
622
- });
623
-
624
- // Only register adapters for frameworks that are used
625
- const adapterLoaders = {
626
- react: () => import('./adapters/react-adapter.js').then(m => m.reactAdapter),
627
- preact: () => import('./adapters/preact-adapter.js').then(m => m.preactAdapter),
628
- vue: () => import('./adapters/vue-adapter.js').then(m => m.vueAdapter),
629
- svelte: () => import('./adapters/svelte-adapter.js').then(m => m.svelteAdapter),
630
- solid: () => import('./adapters/solid-adapter.js').then(m => m.solidAdapter),
631
- lit: () => import('./adapters/lit-adapter.js').then(m => m.litAdapter),
632
- qwik: () => import('./adapters/qwik-adapter.js').then(m => m.qwikAdapter),
633
- };
634
-
635
- for (const framework of usedFrameworks) {
636
- const loader = adapterLoaders[framework];
637
- if (loader) {
638
- try {
639
- const adapter = await loader();
640
- coordinator.registerAdapter(framework, adapter);
641
- } catch (error) {
642
- console.warn(`[HMR] Failed to load adapter for ${framework}:`, error);
643
- }
644
- }
645
- }
646
- })
647
- .catch(error => {
648
- console.error('[HMR] Failed to initialize:', error);
649
- });
650
-
651
- // Enhanced HMR support for nested islands
652
- setupNestedIslandHMR();
653
- }
654
-
655
- /**
656
- * Show HMR error feedback on the island
657
- * @param {HTMLElement} island - The island element
658
- * @param {string} hmrFramework - The framework name
659
- * @param {string} hmrSrc - The component source path
660
- * @param {Error} error - The error that occurred
661
- */
662
- async function showHMRError(island, hmrFramework, hmrSrc, error) {
663
- try {
664
- const { showHMRErrorOverlay } = await import('./hmr-error-overlay.js');
665
- showHMRErrorOverlay({
666
- framework: hmrFramework,
667
- src: hmrSrc,
668
- error,
669
- filePath: hmrSrc,
670
- });
671
- } catch {
672
- showInlineHMRError(island, hmrFramework, hmrSrc, error);
673
- }
674
- }
675
-
676
- /**
677
- * Setup HMR support for nested island directories.
678
- * Handles hot module replacement for islands in any discovered directory,
679
- * including nested paths like /src/modules/[module]/islands/.
680
- */
681
- function setupNestedIslandHMR() {
682
- if (!import.meta.hot) return;
683
-
684
- const hydratedIslands = new Map();
685
-
686
- async function handleIslandHMR(modulePath) {
687
- const normalizedPath = modulePath.replaceAll('\\', '/');
688
-
689
- const islands = document.querySelectorAll(`[data-src*="${normalizedPath}"], [data-src$="${normalizedPath}"]`);
690
-
691
- if (islands.length === 0) {
692
- const allIslands = document.querySelectorAll('[data-src]');
693
- for (const island of allIslands) {
694
- const src = island.dataset.src;
695
- if (src && (src.includes(normalizedPath) || normalizedPath.includes(src.replace(/^\//, '')))) {
696
- await rehydrateIsland(island);
697
- }
698
- }
699
- return;
700
- }
701
-
702
- for (const island of islands) {
703
- await rehydrateIsland(island);
704
- }
705
- }
706
-
707
- async function rehydrateIsland(island) {
708
- const framework = island.dataset.framework;
709
- const src = island.dataset.src;
710
-
711
- if (!src || !framework) return;
712
-
713
- try {
714
- const state = preserveIslandState(island);
715
- hydratedIslands.set(src, state);
716
-
717
- delete island.dataset.hydrated;
718
- delete island.dataset.hydrationStatus;
719
-
720
- const errorIndicator = island.querySelector('.hydration-error-indicator');
721
- if (errorIndicator) {
722
- errorIndicator.remove();
723
- }
724
-
725
- const timestamp = Date.now();
726
- const freshSrc = src.includes('?') ? `${src}&t=${timestamp}` : `${src}?t=${timestamp}`;
727
-
728
- await hydrateIslandWithFreshModule(island, framework, freshSrc, src);
729
-
730
- const preservedState = hydratedIslands.get(src);
731
- if (preservedState) {
732
- restoreIslandState(island, preservedState);
733
- hydratedIslands.delete(src);
734
- }
735
-
736
- island.dispatchEvent(
737
- new CustomEvent('hmr-update', {
738
- detail: {
739
- framework,
740
- src,
741
- timestamp: Date.now(),
742
- success: true,
743
- },
744
- bubbles: true,
745
- }),
746
- );
747
- } catch (error) {
748
- console.error(`[HMR] Failed for ${framework} island ${src}:`, error);
749
-
750
- island.dispatchEvent(
751
- new CustomEvent('hmr-error', {
752
- detail: {
753
- framework,
754
- src,
755
- error: error.message,
756
- timestamp: Date.now(),
757
- },
758
- bubbles: true,
759
- }),
760
- );
761
-
762
- if (isDevelopment()) {
763
- showHMRError(island, framework, src, error);
764
- }
765
- }
766
- }
767
-
768
- // Listen for Vite HMR events
769
- import.meta.hot.on('vite:beforeUpdate', payload => {
770
- for (const update of payload.updates || []) {
771
- const path = update.path || update.acceptedPath;
772
- if (path && (path.includes('/islands/') || path.includes('\\islands\\'))) {
773
- handleIslandHMR(path);
774
- }
775
- }
776
- });
777
-
778
- // Handle full page reloads for islands
779
- import.meta.hot.on('vite:beforeFullReload', () => {
780
- const islands = document.querySelectorAll('[data-hydrated="true"]');
781
- const states = {};
782
-
783
- for (const island of islands) {
784
- const src = island.dataset.src;
785
- if (src) {
786
- states[src] = preserveIslandState(island);
787
- }
788
- }
789
-
790
- try {
791
- sessionStorage.setItem('__avalon_hmr_states__', JSON.stringify(states));
792
- } catch {
793
- // sessionStorage might not be available
794
- }
795
- });
796
-
797
- // Restore states after page load (for full reloads)
798
- try {
799
- const savedStates = sessionStorage.getItem('__avalon_hmr_states__');
800
- if (savedStates) {
801
- const states = JSON.parse(savedStates);
802
- sessionStorage.removeItem('__avalon_hmr_states__');
803
-
804
- setTimeout(() => {
805
- for (const [src, state] of Object.entries(states)) {
806
- const island = document.querySelector(`[data-src="${src}"]`);
807
- if (island && state) {
808
- restoreIslandState(island, state);
809
- }
810
- }
811
- }, 100);
812
- }
813
- } catch {
814
- // Ignore errors - state restoration is best-effort
815
- }
816
- }