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