@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,223 +0,0 @@
1
- /**
2
- * Preact HMR Adapter
3
- *
4
- * Provides Hot Module Replacement support for Preact components.
5
- * Integrates with @preact/preset-vite to preserve hooks state and component tree during updates.
6
- *
7
- * Requirements: 2.2
8
- */
9
- /// <reference lib="dom" />
10
- import { BaseFrameworkAdapter } from "../framework-adapter.js";
11
- /**
12
- * Preact HMR Adapter
13
- *
14
- * Leverages Preact's HMR capabilities (provided by @preact/preset-vite) to:
15
- * - Preserve hooks state across updates
16
- * - Maintain component tree structure
17
- * - Handle component updates without full remount
18
- *
19
- * Preact HMR works similarly to React Fast Refresh:
20
- * 1. Detecting when a component module is updated
21
- * 2. Preserving the component tree (internal state representation)
22
- * 3. Re-rendering with the new component definition
23
- * 4. Restoring hooks state from the preserved tree
24
- */
25
- export class PreactHMRAdapter extends BaseFrameworkAdapter {
26
- name = "preact";
27
- /**
28
- * Store Preact component instances for each island to enable proper updates
29
- */
30
- instances = new WeakMap();
31
- /**
32
- * Check if a component is a Preact component
33
- *
34
- * Preact components can be:
35
- * - Function components (including hooks)
36
- * - Class components (extending Preact Component)
37
- * - Forward refs
38
- * - Memoized components
39
- *
40
- * Preact is API-compatible with React, so detection is similar
41
- */
42
- canHandle(component) {
43
- if (!component) return false;
44
- // Check if it's a function (function component or class)
45
- if (typeof component === "function") {
46
- // Check for Preact/React-specific properties on the function
47
- const comp = component;
48
- // Class components have isReactComponent on prototype
49
- // (Preact uses the same marker for compatibility)
50
- const proto = component.prototype;
51
- if (proto && proto.isReactComponent) {
52
- return true;
53
- }
54
- // Check for Preact element symbol (for wrapped components)
55
- if (comp.$typeof) {
56
- return true;
57
- }
58
- // Assume any function could be a Preact component
59
- // Preact HMR will handle validation
60
- return true;
61
- }
62
- // Check for Preact VNode types
63
- if (typeof component !== "object") {
64
- return false;
65
- }
66
- const obj = component;
67
- // Check for Preact VNode symbol
68
- if (obj.$typeof) {
69
- return true;
70
- }
71
- // Check for wrapped components (HOCs, memo, forwardRef)
72
- if (obj.type && typeof obj.type === "function") {
73
- return true;
74
- }
75
- return false;
76
- }
77
- /**
78
- * Preserve Preact component state before HMR update
79
- *
80
- * Preact HMR handles most state preservation automatically through
81
- * the component tree. We capture additional DOM state and props for fallback.
82
- */
83
- preserveState(island) {
84
- try {
85
- // Get base DOM state
86
- const baseSnapshot = super.preserveState(island);
87
- if (!baseSnapshot) return null;
88
- // Get Preact-specific data
89
- const propsAttr = island.getAttribute("data-props");
90
- const capturedProps = propsAttr ? JSON.parse(propsAttr) : {};
91
- // Try to get component name from the island
92
- const src = island.getAttribute("data-src") || "";
93
- const componentName = this.extractComponentName(src);
94
- const preactSnapshot = {
95
- ...baseSnapshot,
96
- framework: "preact",
97
- data: {
98
- componentName,
99
- capturedProps
100
- }
101
- };
102
- return preactSnapshot;
103
- } catch (error) {
104
- console.warn("Failed to preserve Preact state:", error);
105
- return null;
106
- }
107
- }
108
- /**
109
- * Update Preact component with HMR
110
- *
111
- * This method integrates with Preact HMR:
112
- * 1. Preact HMR is automatically enabled by @preact/preset-vite
113
- * 2. When a module updates, Vite sends an HMR event
114
- * 3. We re-hydrate the component with the new definition
115
- * 4. Preact HMR preserves hooks state automatically
116
- * 5. The component re-renders with preserved state
117
- */
118
- async update(island, newComponent, props) {
119
- if (!this.canHandle(newComponent)) {
120
- throw new Error("Component is not a valid Preact component");
121
- }
122
- const Component = newComponent;
123
- try {
124
- // Dynamically import Preact at runtime
125
- // This is resolved by Vite in the browser
126
- const preactModule = await import("preact");
127
- const { h, hydrate } = preactModule;
128
- // Check if we have an existing instance
129
- const existingInstance = this.instances.get(island);
130
- if (existingInstance) {
131
- // Update existing component
132
- // Preact HMR will preserve hooks state automatically
133
- const vnode = h(Component, props);
134
- hydrate(vnode, island);
135
- } else {
136
- // Create new instance and hydrate
137
- // This happens on first HMR update or if instance was lost
138
- const vnode = h(Component, props);
139
- hydrate(vnode, island);
140
- // Store instance for future updates
141
- this.instances.set(island, Component);
142
- }
143
- // Mark as hydrated
144
- island.setAttribute("data-hydrated", "true");
145
- island.setAttribute("data-hydration-status", "success");
146
- } catch (error) {
147
- console.error("Preact HMR update failed:", error);
148
- island.setAttribute("data-hydration-status", "error");
149
- throw error;
150
- }
151
- }
152
- /**
153
- * Restore Preact component state after HMR update
154
- *
155
- * Preact HMR handles most state restoration automatically.
156
- * We restore DOM state (scroll, focus, form values) as a supplement.
157
- */
158
- restoreState(island, state) {
159
- try {
160
- // Restore DOM state (scroll, focus, form values)
161
- super.restoreState(island, state);
162
- } catch (error) {
163
- console.warn("Failed to restore Preact state:", error);
164
- }
165
- }
166
- /**
167
- * Handle errors during Preact HMR update
168
- *
169
- * Provides Preact-specific error handling with helpful messages
170
- */
171
- handleError(island, error) {
172
- console.error("Preact HMR error:", error);
173
- // Use base error handling
174
- super.handleError(island, error);
175
- // Add Preact-specific error information
176
- const errorIndicator = island.querySelector(".hmr-error-indicator");
177
- if (errorIndicator) {
178
- const errorMessage = error.message;
179
- // Provide helpful hints for common Preact errors
180
- let hint = "";
181
- if (errorMessage.includes("hooks")) {
182
- hint = " (Hint: Check hooks usage - hooks must be called in the same order)";
183
- } else if (errorMessage.includes("render")) {
184
- hint = " (Hint: Check component render method for errors)";
185
- } else if (errorMessage.includes("hydration") || errorMessage.includes("hydrate")) {
186
- hint = " (Hint: Server and client render must match)";
187
- }
188
- errorIndicator.textContent = `Preact HMR Error: ${errorMessage}${hint}`;
189
- }
190
- }
191
- /**
192
- * Extract component name from source path
193
- * Used for debugging and error messages
194
- */
195
- extractComponentName(src) {
196
- const parts = src.split("/");
197
- const filename = parts[parts.length - 1];
198
- return filename.replace(/\.(tsx?|jsx?)$/, "");
199
- }
200
- /**
201
- * Clean up Preact component when island is removed
202
- * This should be called when an island is unmounted
203
- */
204
- unmount(island) {
205
- const instance = this.instances.get(island);
206
- if (instance) {
207
- try {
208
- // Preact doesn't have an explicit unmount API like React
209
- // We just clear the instance reference
210
- this.instances.delete(island);
211
- // Clear the island content to trigger cleanup
212
- // Preact will handle component lifecycle cleanup
213
- island.innerHTML = "";
214
- } catch (error) {
215
- console.warn("Failed to unmount Preact component:", error);
216
- }
217
- }
218
- }
219
- }
220
- /**
221
- * Create and export a singleton instance of the Preact HMR adapter
222
- */
223
- export const preactAdapter = new PreactHMRAdapter();
@@ -1,331 +0,0 @@
1
- /**
2
- * Preact HMR Adapter
3
- *
4
- * Provides Hot Module Replacement support for Preact components.
5
- * Integrates with @preact/preset-vite to preserve hooks state and component tree during updates.
6
- *
7
- * Requirements: 2.2
8
- */
9
-
10
- /// <reference lib="dom" />
11
-
12
- import { BaseFrameworkAdapter, type StateSnapshot } from '../framework-adapter.ts';
13
-
14
- /**
15
- * Preact component type
16
- * Can be a function component or class component
17
- */
18
- type PreactComponent<P = Record<string, unknown>> =
19
- | ((props: P) => PreactVNode | null)
20
- | (new (props: P) => PreactClassComponent);
21
-
22
- /**
23
- * Preact class component interface
24
- */
25
- interface PreactClassComponent {
26
- render(): PreactVNode | null;
27
- isReactComponent?: boolean; // Preact uses same marker as React
28
- }
29
-
30
- /**
31
- * Preact VNode (Virtual Node) type
32
- */
33
- interface PreactVNode {
34
- type: string | PreactComponent;
35
- props: Record<string, unknown>;
36
- key: string | number | null;
37
- }
38
-
39
- /**
40
- * Preact module interface
41
- */
42
- interface PreactModule {
43
- h<P = Record<string, unknown>>(
44
- component: PreactComponent<P> | string,
45
- props: P | null,
46
- ...children: unknown[]
47
- ): PreactVNode;
48
- render(vnode: PreactVNode, container: HTMLElement): void;
49
- hydrate(vnode: PreactVNode, container: HTMLElement): void;
50
- }
51
-
52
- /**
53
- * Preact-specific state snapshot
54
- * Extends base snapshot with Preact-specific state like hooks
55
- */
56
- interface PreactStateSnapshot extends StateSnapshot {
57
- framework: 'preact';
58
- data: {
59
- /**
60
- * Preact component tree data (if accessible)
61
- * Used by Preact HMR to preserve hooks state
62
- */
63
- componentData?: unknown;
64
-
65
- /**
66
- * Component display name for debugging
67
- */
68
- componentName?: string;
69
-
70
- /**
71
- * Props at time of state capture
72
- */
73
- capturedProps?: Record<string, unknown>;
74
- };
75
- }
76
-
77
- /**
78
- * Preact HMR Adapter
79
- *
80
- * Leverages Preact's HMR capabilities (provided by @preact/preset-vite) to:
81
- * - Preserve hooks state across updates
82
- * - Maintain component tree structure
83
- * - Handle component updates without full remount
84
- *
85
- * Preact HMR works similarly to React Fast Refresh:
86
- * 1. Detecting when a component module is updated
87
- * 2. Preserving the component tree (internal state representation)
88
- * 3. Re-rendering with the new component definition
89
- * 4. Restoring hooks state from the preserved tree
90
- */
91
- export class PreactHMRAdapter extends BaseFrameworkAdapter {
92
- readonly name = 'preact';
93
-
94
- /**
95
- * Store Preact component instances for each island to enable proper updates
96
- */
97
- private instances: WeakMap<HTMLElement, unknown> = new WeakMap();
98
-
99
- /**
100
- * Check if a component is a Preact component
101
- *
102
- * Preact components can be:
103
- * - Function components (including hooks)
104
- * - Class components (extending Preact Component)
105
- * - Forward refs
106
- * - Memoized components
107
- *
108
- * Preact is API-compatible with React, so detection is similar
109
- */
110
- canHandle(component: unknown): boolean {
111
- if (!component) return false;
112
-
113
- // Check if it's a function (function component or class)
114
- if (typeof component === 'function') {
115
- // Check for Preact/React-specific properties on the function
116
- const comp = component as unknown as Record<string, unknown>;
117
-
118
- // Class components have isReactComponent on prototype
119
- // (Preact uses the same marker for compatibility)
120
- const proto = (component as { prototype?: Record<string, unknown> }).prototype;
121
- if (proto && proto.isReactComponent) {
122
- return true;
123
- }
124
-
125
- // Check for Preact element symbol (for wrapped components)
126
- if (comp.$typeof) {
127
- return true;
128
- }
129
-
130
- // Assume any function could be a Preact component
131
- // Preact HMR will handle validation
132
- return true;
133
- }
134
-
135
- // Check for Preact VNode types
136
- if (typeof component !== 'object') {
137
- return false;
138
- }
139
-
140
- const obj = component as Record<string, unknown>;
141
-
142
- // Check for Preact VNode symbol
143
- if (obj.$typeof) {
144
- return true;
145
- }
146
-
147
- // Check for wrapped components (HOCs, memo, forwardRef)
148
- if (obj.type && typeof obj.type === 'function') {
149
- return true;
150
- }
151
-
152
- return false;
153
- }
154
-
155
- /**
156
- * Preserve Preact component state before HMR update
157
- *
158
- * Preact HMR handles most state preservation automatically through
159
- * the component tree. We capture additional DOM state and props for fallback.
160
- */
161
- override preserveState(island: HTMLElement): PreactStateSnapshot | null {
162
- try {
163
- // Get base DOM state
164
- const baseSnapshot = super.preserveState(island);
165
- if (!baseSnapshot) return null;
166
-
167
- // Get Preact-specific data
168
- const propsAttr = island.getAttribute('data-props');
169
- const capturedProps = propsAttr ? JSON.parse(propsAttr) : {};
170
-
171
- // Try to get component name from the island
172
- const src = island.getAttribute('data-src') || '';
173
- const componentName = this.extractComponentName(src);
174
-
175
- const preactSnapshot: PreactStateSnapshot = {
176
- ...baseSnapshot,
177
- framework: 'preact',
178
- data: {
179
- componentName,
180
- capturedProps,
181
- },
182
- };
183
-
184
- return preactSnapshot;
185
- } catch (error) {
186
- console.warn('Failed to preserve Preact state:', error);
187
- return null;
188
- }
189
- }
190
-
191
- /**
192
- * Update Preact component with HMR
193
- *
194
- * This method integrates with Preact HMR:
195
- * 1. Preact HMR is automatically enabled by @preact/preset-vite
196
- * 2. When a module updates, Vite sends an HMR event
197
- * 3. We re-hydrate the component with the new definition
198
- * 4. Preact HMR preserves hooks state automatically
199
- * 5. The component re-renders with preserved state
200
- */
201
- async update(
202
- island: HTMLElement,
203
- newComponent: unknown,
204
- props: Record<string, unknown>
205
- ): Promise<void> {
206
- if (!this.canHandle(newComponent)) {
207
- throw new Error('Component is not a valid Preact component');
208
- }
209
-
210
- const Component = newComponent as PreactComponent;
211
-
212
- try {
213
- // Dynamically import Preact at runtime
214
- // This is resolved by Vite in the browser
215
- const preactModule = await import('preact') as PreactModule;
216
- const { h, hydrate } = preactModule;
217
-
218
- // Check if we have an existing instance
219
- const existingInstance = this.instances.get(island);
220
-
221
- if (existingInstance) {
222
- // Update existing component
223
- // Preact HMR will preserve hooks state automatically
224
- const vnode = h(Component, props);
225
- hydrate(vnode, island);
226
- } else {
227
- // Create new instance and hydrate
228
- // This happens on first HMR update or if instance was lost
229
- const vnode = h(Component, props);
230
- hydrate(vnode, island);
231
-
232
- // Store instance for future updates
233
- this.instances.set(island, Component);
234
- }
235
-
236
- // Mark as hydrated
237
- island.setAttribute('data-hydrated', 'true');
238
- island.setAttribute('data-hydration-status', 'success');
239
-
240
- } catch (error) {
241
- console.error('Preact HMR update failed:', error);
242
- island.setAttribute('data-hydration-status', 'error');
243
- throw error;
244
- }
245
- }
246
-
247
- /**
248
- * Restore Preact component state after HMR update
249
- *
250
- * Preact HMR handles most state restoration automatically.
251
- * We restore DOM state (scroll, focus, form values) as a supplement.
252
- */
253
- override restoreState(island: HTMLElement, state: StateSnapshot): void {
254
- try {
255
- // Restore DOM state (scroll, focus, form values)
256
- super.restoreState(island, state);
257
-
258
- // Preact HMR handles hooks state restoration automatically
259
- // through the component tree, so we don't need to do anything special here
260
-
261
- } catch (error) {
262
- console.warn('Failed to restore Preact state:', error);
263
- }
264
- }
265
-
266
- /**
267
- * Handle errors during Preact HMR update
268
- *
269
- * Provides Preact-specific error handling with helpful messages
270
- */
271
- override handleError(island: HTMLElement, error: Error): void {
272
- console.error('Preact HMR error:', error);
273
-
274
- // Use base error handling
275
- super.handleError(island, error);
276
-
277
- // Add Preact-specific error information
278
- const errorIndicator = island.querySelector('.hmr-error-indicator');
279
- if (errorIndicator) {
280
- const errorMessage = error.message;
281
-
282
- // Provide helpful hints for common Preact errors
283
- let hint = '';
284
- if (errorMessage.includes('hooks')) {
285
- hint = ' (Hint: Check hooks usage - hooks must be called in the same order)';
286
- } else if (errorMessage.includes('render')) {
287
- hint = ' (Hint: Check component render method for errors)';
288
- } else if (errorMessage.includes('hydration') || errorMessage.includes('hydrate')) {
289
- hint = ' (Hint: Server and client render must match)';
290
- }
291
-
292
- errorIndicator.textContent = `Preact HMR Error: ${errorMessage}${hint}`;
293
- }
294
- }
295
-
296
- /**
297
- * Extract component name from source path
298
- * Used for debugging and error messages
299
- */
300
- private extractComponentName(src: string): string {
301
- const parts = src.split('/');
302
- const filename = parts[parts.length - 1];
303
- return filename.replace(/\.(tsx?|jsx?)$/, '');
304
- }
305
-
306
- /**
307
- * Clean up Preact component when island is removed
308
- * This should be called when an island is unmounted
309
- */
310
- unmount(island: HTMLElement): void {
311
- const instance = this.instances.get(island);
312
- if (instance) {
313
- try {
314
- // Preact doesn't have an explicit unmount API like React
315
- // We just clear the instance reference
316
- this.instances.delete(island);
317
-
318
- // Clear the island content to trigger cleanup
319
- // Preact will handle component lifecycle cleanup
320
- island.innerHTML = '';
321
- } catch (error) {
322
- console.warn('Failed to unmount Preact component:', error);
323
- }
324
- }
325
- }
326
- }
327
-
328
- /**
329
- * Create and export a singleton instance of the Preact HMR adapter
330
- */
331
- export const preactAdapter = new PreactHMRAdapter();