@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,259 +0,0 @@
1
- /**
2
- * Qwik HMR Adapter
3
- *
4
- * Provides Hot Module Replacement support for Qwik components.
5
- * Qwik is fundamentally different from other frameworks — it uses resumability
6
- * instead of hydration. Components are serialized on the server and resumed
7
- * on the client without replaying application logic.
8
- *
9
- * Key Qwik concepts:
10
- * - Resumability: No hydration step — the app resumes from serialized state
11
- * - Lazy loading via $: Code is split at $ boundaries and loaded on demand
12
- * - Containers: Qwik apps run inside container elements with q:container attribute
13
- * - Qwikloader: A tiny (~1KB) script that sets up global event listeners
14
- *
15
- * Requirements: 2.1
16
- */
17
- /// <reference lib="dom" />
18
- import { BaseFrameworkAdapter } from "../framework-adapter.js";
19
- /**
20
- * Qwik HMR Adapter
21
- *
22
- * Unlike other frameworks, Qwik doesn't hydrate — it resumes. This adapter
23
- * handles HMR by re-rendering the container rather than performing traditional
24
- * hydration-based hot updates.
25
- *
26
- * During development, Qwik's optimizer (via vite-plugin-qwik) handles most
27
- * of the HMR heavy lifting. This adapter provides the island-level integration
28
- * for Avalon's HMR coordinator.
29
- */
30
- export class QwikHMRAdapter extends BaseFrameworkAdapter {
31
- name = "qwik";
32
- /**
33
- * Track container elements for cleanup
34
- */
35
- containers = new WeakMap();
36
- /**
37
- * Check if a component is a Qwik component
38
- *
39
- * Qwik components are created with component$() and have distinctive markers:
40
- * - __brand or __qrl property from the Qwik optimizer
41
- * - $ suffix convention in source
42
- * - QRL (Qwik Resource Locator) references
43
- */
44
- canHandle(component) {
45
- if (!component) return false;
46
- // Check if it's a function (Qwik components are functions)
47
- if (typeof component === "function") {
48
- const comp = component;
49
- // Check for Qwik-specific markers set by the optimizer
50
- if (comp.__brand === "QwikComponent" || comp.__qrl) {
51
- return true;
52
- }
53
- // Check for QRL wrapper pattern
54
- if (comp.getSymbol || comp.getHash) {
55
- return true;
56
- }
57
- try {
58
- const funcStr = component.toString();
59
- // Look for Qwik-specific compiled patterns
60
- if (funcStr.includes("component$") || funcStr.includes("qrl") || funcStr.includes("useSignal") || funcStr.includes("useStore") || funcStr.includes("useTask$") || funcStr.includes("useVisibleTask$") || funcStr.includes("_qrl") || funcStr.includes("qwik")) {
61
- return true;
62
- }
63
- } catch {}
64
- }
65
- // Check if it's a Qwik component object (wrapped or exported)
66
- if (typeof component === "object" && component !== null) {
67
- const obj = component;
68
- // Check for default export pattern
69
- if (obj.default && typeof obj.default === "function") {
70
- return this.canHandle(obj.default);
71
- }
72
- // Check for Qwik QRL markers
73
- if (obj.__qrl || obj.__brand === "QwikComponent") {
74
- return true;
75
- }
76
- }
77
- return false;
78
- }
79
- /**
80
- * Preserve Qwik component state before HMR update
81
- *
82
- * Qwik serializes state into the DOM via q:container attributes and
83
- * inline <script type="qwik/json"> blocks. We capture this serialized
84
- * state so it can be used during re-rendering.
85
- */
86
- preserveState(island) {
87
- try {
88
- const baseSnapshot = super.preserveState(island);
89
- if (!baseSnapshot) return null;
90
- // Get props from the island
91
- const propsAttr = island.getAttribute("data-props");
92
- const capturedProps = propsAttr ? JSON.parse(propsAttr) : {};
93
- const src = island.getAttribute("data-src") || "";
94
- const componentName = this.extractComponentName(src);
95
- // Capture Qwik container state — serialized in the DOM
96
- const containerEl = island.closest("[q\\:container]") || island;
97
- const containerState = containerEl.querySelector("script[type=\"qwik/json\"]")?.textContent || null;
98
- // Capture q: attributes that hold container metadata
99
- const qContainerAttrs = {};
100
- const attrs = containerEl.attributes;
101
- if (attrs) {
102
- for (let i = 0; i < attrs.length; i++) {
103
- const attr = attrs[i];
104
- if (attr.name.startsWith("q:")) {
105
- qContainerAttrs[attr.name] = attr.value;
106
- }
107
- }
108
- }
109
- return {
110
- ...baseSnapshot,
111
- framework: "qwik",
112
- data: {
113
- componentName,
114
- capturedProps,
115
- containerState,
116
- qContainerAttrs
117
- }
118
- };
119
- } catch (error) {
120
- console.warn("Failed to preserve Qwik state:", error);
121
- return null;
122
- }
123
- }
124
- /**
125
- * Update Qwik component with HMR
126
- *
127
- * Qwik's resumability model means we don't hydrate in the traditional sense.
128
- * Instead, during HMR:
129
- * 1. The Qwik optimizer (vite-plugin-qwik) invalidates the affected QRLs
130
- * 2. We re-render the component into the island container
131
- * 3. Qwikloader picks up the new event bindings automatically
132
- * 4. Serialized state is restored from the container
133
- */
134
- async update(island, newComponent, props) {
135
- if (!this.canHandle(newComponent)) {
136
- throw new Error("Component is not a valid Qwik component");
137
- }
138
- // Extract the actual component function
139
- let Component;
140
- if (typeof newComponent === "object" && newComponent !== null) {
141
- const obj = newComponent;
142
- if (obj.default && typeof obj.default === "function") {
143
- Component = obj.default;
144
- } else {
145
- throw new Error("Qwik component object must have a default export");
146
- }
147
- } else if (typeof newComponent === "function") {
148
- Component = newComponent;
149
- } else {
150
- throw new Error("Invalid Qwik component type");
151
- }
152
- try {
153
- // Clean up existing container tracking
154
- const existing = this.containers.get(island);
155
- if (existing?.cleanup) {
156
- try {
157
- existing.cleanup();
158
- } catch (error) {
159
- console.warn("Failed to clean up existing Qwik container:", error);
160
- }
161
- }
162
- // Dynamically import Qwik's client render API
163
- // In development, vite-plugin-qwik makes this available
164
- // Use a variable to prevent Vite's static import analysis from resolving this at build time
165
- const qwikId = "@builder.io/qwik";
166
- const qwikModule = await import(
167
- /* @vite-ignore */
168
- qwikId
169
- );
170
- if (qwikModule.render) {
171
- // Use Qwik's client-side render to mount the component
172
- // Qwik's render handles setting up the container and resumability
173
- const jsxNode = typeof qwikModule.jsx === "function" ? qwikModule.jsx(Component, props) : Component(props);
174
- await qwikModule.render(island, jsxNode);
175
- } else {
176
- // Fallback: replace innerHTML and let Qwikloader resume
177
- // This works because Qwik's event listeners are declarative in the DOM
178
- console.warn("Qwik render API not available, using innerHTML fallback");
179
- const result = Component(props);
180
- if (typeof result === "string") {
181
- island.innerHTML = result;
182
- }
183
- }
184
- // Track the container for future cleanup
185
- this.containers.set(island, {});
186
- // Mark as hydrated (resumed, in Qwik terms)
187
- island.setAttribute("data-hydrated", "true");
188
- island.setAttribute("data-hydration-status", "success");
189
- } catch (error) {
190
- console.error("Qwik HMR update failed:", error);
191
- island.setAttribute("data-hydration-status", "error");
192
- throw error;
193
- }
194
- }
195
- /**
196
- * Restore Qwik component state after HMR update
197
- *
198
- * Qwik's serialized state lives in the DOM, so restoring the container
199
- * state script block and q: attributes is sufficient for the framework
200
- * to resume correctly.
201
- */
202
- restoreState(island, state) {
203
- try {
204
- // Restore DOM state (scroll, focus, form values)
205
- super.restoreState(island, state);
206
- } catch (error) {
207
- console.warn("Failed to restore Qwik state:", error);
208
- }
209
- }
210
- /**
211
- * Handle errors during Qwik HMR update
212
- */
213
- handleError(island, error) {
214
- console.error("Qwik HMR error:", error);
215
- super.handleError(island, error);
216
- const errorIndicator = island.querySelector(".hmr-error-indicator");
217
- if (errorIndicator) {
218
- const errorMessage = error.message;
219
- let hint = "";
220
- if (errorMessage.includes("component$") || errorMessage.includes("component\\$")) {
221
- hint = " (Hint: Ensure component is wrapped with component$())";
222
- } else if (errorMessage.includes("useSignal") || errorMessage.includes("useStore")) {
223
- hint = " (Hint: Qwik hooks must be called inside component$() body)";
224
- } else if (errorMessage.includes("QRL") || errorMessage.includes("qrl")) {
225
- hint = " (Hint: Check that lazy-loaded boundaries use $ correctly)";
226
- } else if (errorMessage.includes("serialize") || errorMessage.includes("container")) {
227
- hint = " (Hint: Ensure all state is serializable — Qwik serializes state to the DOM)";
228
- } else if (errorMessage.includes("resumable") || errorMessage.includes("resume")) {
229
- hint = " (Hint: Server and client container state must match for resumability)";
230
- }
231
- errorIndicator.textContent = `Qwik HMR Error: ${errorMessage}${hint}`;
232
- }
233
- }
234
- /**
235
- * Extract component name from source path
236
- */
237
- extractComponentName(src) {
238
- const parts = src.split("/");
239
- const filename = parts[parts.length - 1];
240
- return filename.replace(/\.qwik\.(tsx?|jsx?)$/, "").replace(/\.(tsx?|jsx?)$/, "");
241
- }
242
- /**
243
- * Clean up Qwik component when island is removed
244
- */
245
- unmount(island) {
246
- const container = this.containers.get(island);
247
- if (container) {
248
- try {
249
- if (container.cleanup) {
250
- container.cleanup();
251
- }
252
- this.containers.delete(island);
253
- } catch (error) {
254
- console.warn("Failed to unmount Qwik component:", error);
255
- }
256
- }
257
- }
258
- }
259
- export const qwikAdapter = new QwikHMRAdapter();
@@ -1,345 +0,0 @@
1
- /**
2
- * Qwik HMR Adapter
3
- *
4
- * Provides Hot Module Replacement support for Qwik components.
5
- * Qwik is fundamentally different from other frameworks — it uses resumability
6
- * instead of hydration. Components are serialized on the server and resumed
7
- * on the client without replaying application logic.
8
- *
9
- * Key Qwik concepts:
10
- * - Resumability: No hydration step — the app resumes from serialized state
11
- * - Lazy loading via $: Code is split at $ boundaries and loaded on demand
12
- * - Containers: Qwik apps run inside container elements with q:container attribute
13
- * - Qwikloader: A tiny (~1KB) script that sets up global event listeners
14
- *
15
- * Requirements: 2.1
16
- */
17
-
18
- /// <reference lib="dom" />
19
-
20
- import { BaseFrameworkAdapter, type StateSnapshot } from '../framework-adapter.ts';
21
-
22
- /**
23
- * Qwik component type
24
- * Qwik components are created with component$() which returns a QRL-wrapped component
25
- */
26
- type QwikComponent<P = Record<string, unknown>> = {
27
- (props: P): unknown;
28
- __brand?: 'QwikComponent';
29
- };
30
-
31
- /**
32
- * Qwik module interface for SSR/client rendering
33
- */
34
- interface QwikModule {
35
- renderToString?: (opts: Record<string, unknown>) => Promise<string>;
36
- render?: (parent: Element, jsxNode: unknown) => Promise<void>;
37
- }
38
-
39
- /**
40
- * Qwik state snapshot extending base state
41
- */
42
- interface QwikStateSnapshot extends StateSnapshot {
43
- framework: 'qwik';
44
- data: {
45
- componentName: string;
46
- capturedProps: Record<string, unknown>;
47
- containerState: string | null;
48
- qContainerAttrs: Record<string, string>;
49
- };
50
- }
51
-
52
- /**
53
- * Qwik HMR Adapter
54
- *
55
- * Unlike other frameworks, Qwik doesn't hydrate — it resumes. This adapter
56
- * handles HMR by re-rendering the container rather than performing traditional
57
- * hydration-based hot updates.
58
- *
59
- * During development, Qwik's optimizer (via vite-plugin-qwik) handles most
60
- * of the HMR heavy lifting. This adapter provides the island-level integration
61
- * for Avalon's HMR coordinator.
62
- */
63
- export class QwikHMRAdapter extends BaseFrameworkAdapter {
64
- readonly name = 'qwik';
65
-
66
- /**
67
- * Track container elements for cleanup
68
- */
69
- private containers: WeakMap<HTMLElement, { cleanup?: () => void }> = new WeakMap();
70
-
71
- /**
72
- * Check if a component is a Qwik component
73
- *
74
- * Qwik components are created with component$() and have distinctive markers:
75
- * - __brand or __qrl property from the Qwik optimizer
76
- * - $ suffix convention in source
77
- * - QRL (Qwik Resource Locator) references
78
- */
79
- canHandle(component: unknown): boolean {
80
- if (!component) return false;
81
-
82
- // Check if it's a function (Qwik components are functions)
83
- if (typeof component === 'function') {
84
- const comp = component as unknown as Record<string, unknown>;
85
-
86
- // Check for Qwik-specific markers set by the optimizer
87
- if (comp.__brand === 'QwikComponent' || comp.__qrl) {
88
- return true;
89
- }
90
-
91
- // Check for QRL wrapper pattern
92
- if (comp.getSymbol || comp.getHash) {
93
- return true;
94
- }
95
-
96
- try {
97
- const funcStr = component.toString();
98
-
99
- // Look for Qwik-specific compiled patterns
100
- if (
101
- funcStr.includes('component$') ||
102
- funcStr.includes('qrl') ||
103
- funcStr.includes('useSignal') ||
104
- funcStr.includes('useStore') ||
105
- funcStr.includes('useTask$') ||
106
- funcStr.includes('useVisibleTask$') ||
107
- funcStr.includes('_qrl') ||
108
- funcStr.includes('qwik')
109
- ) {
110
- return true;
111
- }
112
- } catch {
113
- // Ignore errors from toString()
114
- }
115
- }
116
-
117
- // Check if it's a Qwik component object (wrapped or exported)
118
- if (typeof component === 'object' && component !== null) {
119
- const obj = component as Record<string, unknown>;
120
-
121
- // Check for default export pattern
122
- if (obj.default && typeof obj.default === 'function') {
123
- return this.canHandle(obj.default);
124
- }
125
-
126
- // Check for Qwik QRL markers
127
- if (obj.__qrl || obj.__brand === 'QwikComponent') {
128
- return true;
129
- }
130
- }
131
-
132
- return false;
133
- }
134
-
135
- /**
136
- * Preserve Qwik component state before HMR update
137
- *
138
- * Qwik serializes state into the DOM via q:container attributes and
139
- * inline <script type="qwik/json"> blocks. We capture this serialized
140
- * state so it can be used during re-rendering.
141
- */
142
- override preserveState(island: HTMLElement): QwikStateSnapshot | null {
143
- try {
144
- const baseSnapshot = super.preserveState(island);
145
- if (!baseSnapshot) return null;
146
-
147
- // Get props from the island
148
- const propsAttr = island.getAttribute('data-props');
149
- const capturedProps = propsAttr ? JSON.parse(propsAttr) : {};
150
-
151
- const src = island.getAttribute('data-src') || '';
152
- const componentName = this.extractComponentName(src);
153
-
154
- // Capture Qwik container state — serialized in the DOM
155
- const containerEl = island.closest('[q\\:container]') || island;
156
- const containerState = containerEl.querySelector('script[type="qwik/json"]')?.textContent || null;
157
-
158
- // Capture q: attributes that hold container metadata
159
- const qContainerAttrs: Record<string, string> = {};
160
- const attrs = containerEl.attributes;
161
- if (attrs) {
162
- for (let i = 0; i < attrs.length; i++) {
163
- const attr = attrs[i];
164
- if (attr.name.startsWith('q:')) {
165
- qContainerAttrs[attr.name] = attr.value;
166
- }
167
- }
168
- }
169
-
170
- return {
171
- ...baseSnapshot,
172
- framework: 'qwik',
173
- data: {
174
- componentName,
175
- capturedProps,
176
- containerState,
177
- qContainerAttrs,
178
- },
179
- };
180
- } catch (error) {
181
- console.warn('Failed to preserve Qwik state:', error);
182
- return null;
183
- }
184
- }
185
-
186
- /**
187
- * Update Qwik component with HMR
188
- *
189
- * Qwik's resumability model means we don't hydrate in the traditional sense.
190
- * Instead, during HMR:
191
- * 1. The Qwik optimizer (vite-plugin-qwik) invalidates the affected QRLs
192
- * 2. We re-render the component into the island container
193
- * 3. Qwikloader picks up the new event bindings automatically
194
- * 4. Serialized state is restored from the container
195
- */
196
- async update(
197
- island: HTMLElement,
198
- newComponent: unknown,
199
- props: Record<string, unknown>
200
- ): Promise<void> {
201
- if (!this.canHandle(newComponent)) {
202
- throw new Error('Component is not a valid Qwik component');
203
- }
204
-
205
- // Extract the actual component function
206
- let Component: QwikComponent;
207
- if (typeof newComponent === 'object' && newComponent !== null) {
208
- const obj = newComponent as Record<string, unknown>;
209
- if (obj.default && typeof obj.default === 'function') {
210
- Component = obj.default as QwikComponent;
211
- } else {
212
- throw new Error('Qwik component object must have a default export');
213
- }
214
- } else if (typeof newComponent === 'function') {
215
- Component = newComponent as QwikComponent;
216
- } else {
217
- throw new Error('Invalid Qwik component type');
218
- }
219
-
220
- try {
221
- // Clean up existing container tracking
222
- const existing = this.containers.get(island);
223
- if (existing?.cleanup) {
224
- try {
225
- existing.cleanup();
226
- } catch (error) {
227
- console.warn('Failed to clean up existing Qwik container:', error);
228
- }
229
- }
230
-
231
- // Dynamically import Qwik's client render API
232
- // In development, vite-plugin-qwik makes this available
233
- // Use a variable to prevent Vite's static import analysis from resolving this at build time
234
- const qwikId = '@builder.io/qwik';
235
- const qwikModule = await import(/* @vite-ignore */ qwikId) as QwikModule & Record<string, unknown>;
236
-
237
- if (qwikModule.render) {
238
- // Use Qwik's client-side render to mount the component
239
- // Qwik's render handles setting up the container and resumability
240
- const jsxNode = typeof qwikModule.jsx === 'function'
241
- ? qwikModule.jsx(Component, props)
242
- : Component(props);
243
-
244
- await qwikModule.render(island, jsxNode);
245
- } else {
246
- // Fallback: replace innerHTML and let Qwikloader resume
247
- // This works because Qwik's event listeners are declarative in the DOM
248
- console.warn('Qwik render API not available, using innerHTML fallback');
249
- const result = Component(props);
250
- if (typeof result === 'string') {
251
- island.innerHTML = result;
252
- }
253
- }
254
-
255
- // Track the container for future cleanup
256
- this.containers.set(island, {});
257
-
258
- // Mark as hydrated (resumed, in Qwik terms)
259
- island.setAttribute('data-hydrated', 'true');
260
- island.setAttribute('data-hydration-status', 'success');
261
-
262
- } catch (error) {
263
- console.error('Qwik HMR update failed:', error);
264
- island.setAttribute('data-hydration-status', 'error');
265
- throw error;
266
- }
267
- }
268
-
269
- /**
270
- * Restore Qwik component state after HMR update
271
- *
272
- * Qwik's serialized state lives in the DOM, so restoring the container
273
- * state script block and q: attributes is sufficient for the framework
274
- * to resume correctly.
275
- */
276
- override restoreState(island: HTMLElement, state: StateSnapshot): void {
277
- try {
278
- // Restore DOM state (scroll, focus, form values)
279
- super.restoreState(island, state);
280
-
281
- // Qwik's resumability handles reactive state automatically
282
- // through its serialized container state in the DOM
283
-
284
- } catch (error) {
285
- console.warn('Failed to restore Qwik state:', error);
286
- }
287
- }
288
-
289
- /**
290
- * Handle errors during Qwik HMR update
291
- */
292
- override handleError(island: HTMLElement, error: Error): void {
293
- console.error('Qwik HMR error:', error);
294
-
295
- super.handleError(island, error);
296
-
297
- const errorIndicator = island.querySelector('.hmr-error-indicator');
298
- if (errorIndicator) {
299
- const errorMessage = error.message;
300
-
301
- let hint = '';
302
- if (errorMessage.includes('component$') || errorMessage.includes('component\\$')) {
303
- hint = ' (Hint: Ensure component is wrapped with component$())';
304
- } else if (errorMessage.includes('useSignal') || errorMessage.includes('useStore')) {
305
- hint = ' (Hint: Qwik hooks must be called inside component$() body)';
306
- } else if (errorMessage.includes('QRL') || errorMessage.includes('qrl')) {
307
- hint = ' (Hint: Check that lazy-loaded boundaries use $ correctly)';
308
- } else if (errorMessage.includes('serialize') || errorMessage.includes('container')) {
309
- hint = ' (Hint: Ensure all state is serializable — Qwik serializes state to the DOM)';
310
- } else if (errorMessage.includes('resumable') || errorMessage.includes('resume')) {
311
- hint = ' (Hint: Server and client container state must match for resumability)';
312
- }
313
-
314
- errorIndicator.textContent = `Qwik HMR Error: ${errorMessage}${hint}`;
315
- }
316
- }
317
-
318
- /**
319
- * Extract component name from source path
320
- */
321
- private extractComponentName(src: string): string {
322
- const parts = src.split('/');
323
- const filename = parts[parts.length - 1];
324
- return filename.replace(/\.qwik\.(tsx?|jsx?)$/, '').replace(/\.(tsx?|jsx?)$/, '');
325
- }
326
-
327
- /**
328
- * Clean up Qwik component when island is removed
329
- */
330
- unmount(island: HTMLElement): void {
331
- const container = this.containers.get(island);
332
- if (container) {
333
- try {
334
- if (container.cleanup) {
335
- container.cleanup();
336
- }
337
- this.containers.delete(island);
338
- } catch (error) {
339
- console.warn('Failed to unmount Qwik component:', error);
340
- }
341
- }
342
- }
343
- }
344
-
345
- export const qwikAdapter = new QwikHMRAdapter();