@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,654 +0,0 @@
1
- /**
2
- * Lit HMR Adapter
3
- *
4
- * Provides Hot Module Replacement support for Lit components.
5
- * Handles custom element re-registration and updates all element instances.
6
- * Preserves element properties across updates.
7
- *
8
- * Requirements: 2.6
9
- */
10
-
11
- /// <reference lib="dom" />
12
-
13
- import { BaseFrameworkAdapter, type StateSnapshot } from '../framework-adapter.ts';
14
-
15
- /**
16
- * Lit element type
17
- * Lit components are classes that extend LitElement
18
- */
19
- interface LitElementConstructor {
20
- new (): LitElementInstance;
21
- elementName?: string;
22
- }
23
-
24
- /**
25
- * Lit element instance interface
26
- */
27
- interface LitElementInstance extends HTMLElement {
28
- /**
29
- * Lit-specific properties
30
- */
31
- _$litElement$?: unknown;
32
-
33
- /**
34
- * Request update method
35
- */
36
- requestUpdate?(): void;
37
-
38
- /**
39
- * Update complete promise
40
- */
41
- updateComplete?: Promise<boolean>;
42
- }
43
-
44
- /**
45
- * Lit-specific state snapshot
46
- * Extends base snapshot with Lit-specific state like element properties
47
- */
48
- interface LitStateSnapshot extends StateSnapshot {
49
- framework: 'lit';
50
- data: {
51
- /**
52
- * Element properties captured from the Lit element
53
- */
54
- elementProperties?: Record<string, unknown>;
55
-
56
- /**
57
- * Element attributes
58
- */
59
- elementAttributes?: Record<string, string>;
60
-
61
- /**
62
- * Component display name for debugging
63
- */
64
- componentName?: string;
65
-
66
- /**
67
- * Tag name of the custom element
68
- */
69
- tagName?: string;
70
-
71
- /**
72
- * Props at time of state capture
73
- */
74
- capturedProps?: Record<string, unknown>;
75
- };
76
- }
77
-
78
- /**
79
- * Lit HMR Adapter
80
- *
81
- * Handles HMR for Lit components by:
82
- * 1. Re-registering the custom element with a new definition
83
- * 2. Updating all existing instances of the element
84
- * 3. Preserving element properties and attributes
85
- *
86
- * Lit components are Web Components (custom elements), so HMR requires:
87
- * - Undefining the old custom element (if possible)
88
- * - Defining the new custom element
89
- * - Updating all instances in the DOM
90
- * - Preserving reactive properties
91
- */
92
- export class LitHMRAdapter extends BaseFrameworkAdapter {
93
- readonly name = 'lit';
94
-
95
- /**
96
- * Store element constructors for each island
97
- */
98
- private elementConstructors: WeakMap<HTMLElement, LitElementConstructor> = new WeakMap();
99
-
100
- /**
101
- * Store tag names for each island
102
- */
103
- private tagNames: WeakMap<HTMLElement, string> = new WeakMap();
104
-
105
- /**
106
- * Check if a component is a Lit component
107
- *
108
- * Lit components are:
109
- * - Classes that extend LitElement
110
- * - Typically decorated with @customElement
111
- * - Have .lit.ts or .lit.js extension (but not always)
112
- */
113
- canHandle(component: unknown): boolean {
114
- if (!component) return false;
115
-
116
- // Check if it's a class (Lit components are classes)
117
- if (typeof component === 'function') {
118
- const comp = component as unknown as Record<string, unknown>;
119
-
120
- // Check for Lit-specific markers
121
- // Lit elements may have __litElement marker
122
- if (comp.__litElement) {
123
- return true;
124
- }
125
-
126
- // Check if it extends LitElement by looking at the prototype chain
127
- try {
128
- const proto = (component as { prototype?: Record<string, unknown> }).prototype;
129
- if (proto) {
130
- // Check for Lit-specific methods on prototype
131
- if (
132
- 'render' in proto &&
133
- 'requestUpdate' in proto &&
134
- 'updateComplete' in proto
135
- ) {
136
- return true;
137
- }
138
-
139
- // Check for LitElement in prototype chain
140
- let currentProto = proto;
141
- while (currentProto && currentProto !== Object.prototype) {
142
- const constructor = currentProto.constructor as { name?: string };
143
- if (constructor && constructor.name === 'LitElement') {
144
- return true;
145
- }
146
- currentProto = Object.getPrototypeOf(currentProto);
147
- }
148
- }
149
- } catch {
150
- // Ignore errors from prototype inspection
151
- }
152
-
153
- // Check for @customElement decorator metadata
154
- // The decorator adds metadata to the class
155
- if (comp.elementName || comp.tagName) {
156
- return true;
157
- }
158
-
159
- // Check function signature - Lit components typically have specific patterns
160
- try {
161
- const funcStr = component.toString();
162
-
163
- // Look for Lit-specific patterns
164
- if (
165
- funcStr.includes('LitElement') ||
166
- funcStr.includes('customElement') ||
167
- funcStr.includes('html`') ||
168
- funcStr.includes('css`') ||
169
- funcStr.includes('render()') ||
170
- funcStr.includes('requestUpdate')
171
- ) {
172
- return true;
173
- }
174
- } catch {
175
- // Ignore errors from toString()
176
- }
177
- }
178
-
179
- // Check if it's a Lit component object (wrapped or exported)
180
- if (typeof component !== 'object') {
181
- return false;
182
- }
183
-
184
- const obj = component as Record<string, unknown>;
185
-
186
- // Check for default export pattern
187
- if (obj.default && typeof obj.default === 'function') {
188
- return this.canHandle(obj.default);
189
- }
190
-
191
- // Check for Lit component markers
192
- if (obj.__litElement) {
193
- return true;
194
- }
195
-
196
- return false;
197
- }
198
-
199
- /**
200
- * Preserve Lit element state before HMR update
201
- *
202
- * Captures:
203
- * - Element properties (reactive properties defined with @property)
204
- * - Element attributes
205
- * - DOM state (scroll, focus, form values)
206
- */
207
- override preserveState(island: HTMLElement): LitStateSnapshot | null {
208
- try {
209
- // Get base DOM state
210
- const baseSnapshot = super.preserveState(island);
211
- if (!baseSnapshot) return null;
212
-
213
- // Get Lit-specific data
214
- const propsAttr = island.getAttribute('data-props');
215
- const capturedProps = propsAttr ? JSON.parse(propsAttr) : {};
216
-
217
- // Try to get component name from the island
218
- const src = island.getAttribute('data-src') || '';
219
- const componentName = this.extractComponentName(src);
220
-
221
- // Get tag name
222
- const tagName = island.getAttribute('data-tag-name') || this.tagNames.get(island);
223
-
224
- // Find the Lit element inside the island
225
- const litElement = tagName
226
- ? island.querySelector(tagName) as LitElementInstance
227
- : island.querySelector('[data-lit-element]') as LitElementInstance;
228
-
229
- // Capture element properties and attributes
230
- const elementProperties: Record<string, unknown> = {};
231
- const elementAttributes: Record<string, string> = {};
232
-
233
- if (litElement) {
234
- // Capture all properties
235
- // Lit stores reactive properties on the element instance
236
- for (const key in litElement) {
237
- if (litElement.hasOwnProperty(key) && !key.startsWith('_')) {
238
- try {
239
- const value = litElement[key as keyof LitElementInstance];
240
- // Only capture serializable values
241
- if (
242
- value !== undefined &&
243
- value !== null &&
244
- typeof value !== 'function' &&
245
- typeof value !== 'symbol'
246
- ) {
247
- elementProperties[key] = value;
248
- }
249
- } catch {
250
- // Skip properties that throw on access
251
- }
252
- }
253
- }
254
-
255
- // Capture all attributes
256
- for (let i = 0; i < litElement.attributes.length; i++) {
257
- const attr = litElement.attributes[i];
258
- elementAttributes[attr.name] = attr.value;
259
- }
260
- }
261
-
262
- const litSnapshot: LitStateSnapshot = {
263
- ...baseSnapshot,
264
- framework: 'lit',
265
- data: {
266
- componentName,
267
- tagName: tagName || undefined,
268
- capturedProps,
269
- elementProperties,
270
- elementAttributes,
271
- },
272
- };
273
-
274
- return litSnapshot;
275
- } catch (error) {
276
- console.warn('Failed to preserve Lit state:', error);
277
- return null;
278
- }
279
- }
280
-
281
- /**
282
- * Update Lit component with HMR
283
- *
284
- * This method handles custom element re-registration:
285
- * 1. Extract the tag name from the component or island
286
- * 2. Find all instances of the element in the DOM
287
- * 3. Preserve their properties and attributes
288
- * 4. Undefine the old custom element (if possible)
289
- * 5. Define the new custom element
290
- * 6. Update all instances with the new definition
291
- * 7. Restore properties and attributes
292
- */
293
- async update(
294
- island: HTMLElement,
295
- newComponent: unknown,
296
- props: Record<string, unknown>
297
- ): Promise<void> {
298
- if (!this.canHandle(newComponent)) {
299
- throw new Error('Component is not a valid Lit component');
300
- }
301
-
302
- // Extract the actual component class
303
- let ElementClass: LitElementConstructor;
304
- if (typeof newComponent === 'object' && newComponent !== null) {
305
- const obj = newComponent as Record<string, unknown>;
306
- if (obj.default && typeof obj.default === 'function') {
307
- ElementClass = obj.default as LitElementConstructor;
308
- } else {
309
- throw new Error('Lit component object must have a default export');
310
- }
311
- } else if (typeof newComponent === 'function') {
312
- ElementClass = newComponent as LitElementConstructor;
313
- } else {
314
- throw new Error('Invalid Lit component type');
315
- }
316
-
317
- try {
318
- // Determine the tag name
319
- // Priority: data-tag-name attribute > elementName property > derive from class name
320
- let tagName = island.getAttribute('data-tag-name');
321
-
322
- if (!tagName) {
323
- // Try to get from the class
324
- tagName = (ElementClass as unknown as Record<string, unknown>).elementName as string;
325
- }
326
-
327
- if (!tagName) {
328
- // Derive from class name (convert PascalCase to kebab-case)
329
- tagName = ElementClass.name
330
- .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
331
- .toLowerCase();
332
- }
333
-
334
- if (!tagName || !tagName.includes('-')) {
335
- throw new Error('Invalid custom element tag name: ' + tagName);
336
- }
337
-
338
- // Store tag name for future updates
339
- this.tagNames.set(island, tagName);
340
- island.setAttribute('data-tag-name', tagName);
341
-
342
- // Find all instances of this element in the island
343
- const elements = Array.from(island.querySelectorAll(tagName)) as LitElementInstance[];
344
-
345
- // If no elements exist, create one
346
- if (elements.length === 0) {
347
- // Check if the custom element is already defined
348
- const existingDefinition = customElements.get(tagName);
349
-
350
- if (!existingDefinition) {
351
- // Define the custom element
352
- customElements.define(tagName, ElementClass as CustomElementConstructor);
353
- } else if (existingDefinition !== ElementClass) {
354
- // Element is already defined with a different class
355
- // We need to re-register it
356
- await this.reregisterCustomElement(tagName, ElementClass);
357
- }
358
-
359
- // Create a new element
360
- const newElement = document.createElement(tagName) as LitElementInstance;
361
-
362
- // Set properties from props
363
- Object.entries(props).forEach(([key, value]) => {
364
- try {
365
- (newElement as unknown as Record<string, unknown>)[key] = value;
366
- } catch (error) {
367
- console.warn(`Failed to set property ${key} on Lit element:`, error);
368
- }
369
- });
370
-
371
- // Append to island
372
- island.appendChild(newElement);
373
-
374
- // Store constructor
375
- this.elementConstructors.set(island, ElementClass);
376
-
377
- // Mark as hydrated
378
- island.setAttribute('data-hydrated', 'true');
379
- island.setAttribute('data-hydration-status', 'success');
380
-
381
- return;
382
- }
383
-
384
- // Update existing elements
385
- // First, check if we need to re-register the custom element
386
- const existingDefinition = customElements.get(tagName);
387
-
388
- if (existingDefinition && existingDefinition !== ElementClass) {
389
- // Re-register the custom element with the new definition
390
- await this.reregisterCustomElement(tagName, ElementClass);
391
-
392
- // After re-registration, we need to replace all existing elements
393
- // because they're instances of the old class
394
- for (const oldElement of elements) {
395
- // Preserve state
396
- const properties: Record<string, unknown> = {};
397
- const attributes: Record<string, string> = {};
398
-
399
- // Capture properties
400
- for (const key in oldElement) {
401
- if (oldElement.hasOwnProperty(key) && !key.startsWith('_')) {
402
- try {
403
- const value = oldElement[key as keyof LitElementInstance];
404
- if (
405
- value !== undefined &&
406
- value !== null &&
407
- typeof value !== 'function' &&
408
- typeof value !== 'symbol'
409
- ) {
410
- properties[key] = value;
411
- }
412
- } catch {
413
- // Skip properties that throw on access
414
- }
415
- }
416
- }
417
-
418
- // Capture attributes
419
- for (let i = 0; i < oldElement.attributes.length; i++) {
420
- const attr = oldElement.attributes[i];
421
- attributes[attr.name] = attr.value;
422
- }
423
-
424
- // Create new element
425
- const newElement = document.createElement(tagName) as LitElementInstance;
426
-
427
- // Restore attributes
428
- Object.entries(attributes).forEach(([name, value]) => {
429
- newElement.setAttribute(name, value);
430
- });
431
-
432
- // Restore properties
433
- Object.entries(properties).forEach(([key, value]) => {
434
- try {
435
- (newElement as unknown as Record<string, unknown>)[key] = value;
436
- } catch (error) {
437
- console.warn(`Failed to restore property ${key}:`, error);
438
- }
439
- });
440
-
441
- // Replace old element with new element
442
- oldElement.parentNode?.replaceChild(newElement, oldElement);
443
- }
444
- } else if (!existingDefinition) {
445
- // Define the custom element for the first time
446
- customElements.define(tagName, ElementClass as CustomElementConstructor);
447
-
448
- // Trigger update on all elements
449
- for (const element of elements) {
450
- if (element.requestUpdate) {
451
- element.requestUpdate();
452
- }
453
- }
454
- } else {
455
- // Same class, just trigger updates
456
- for (const element of elements) {
457
- // Update properties from props
458
- Object.entries(props).forEach(([key, value]) => {
459
- try {
460
- (element as unknown as Record<string, unknown>)[key] = value;
461
- } catch (error) {
462
- console.warn(`Failed to update property ${key}:`, error);
463
- }
464
- });
465
-
466
- // Request update
467
- if (element.requestUpdate) {
468
- element.requestUpdate();
469
- }
470
- }
471
- }
472
-
473
- // Store constructor
474
- this.elementConstructors.set(island, ElementClass);
475
-
476
- // Mark as hydrated
477
- island.setAttribute('data-hydrated', 'true');
478
- island.setAttribute('data-hydration-status', 'success');
479
-
480
- } catch (error) {
481
- console.error('Lit HMR update failed:', error);
482
- island.setAttribute('data-hydration-status', 'error');
483
- throw error;
484
- }
485
- }
486
-
487
- /**
488
- * Re-register a custom element with a new definition
489
- *
490
- * Custom elements cannot be undefined once defined, so we need to:
491
- * 1. Create a new tag name (with a version suffix)
492
- * 2. Define the new element with the new tag name
493
- * 3. Update all references to use the new tag name
494
- *
495
- * Alternative approach (used here):
496
- * 1. Use a wrapper element that delegates to the actual element
497
- * 2. Update the wrapper to use the new element class
498
- */
499
- private async reregisterCustomElement(
500
- tagName: string,
501
- ElementClass: LitElementConstructor
502
- ): Promise<void> {
503
- // Unfortunately, custom elements cannot be truly undefined once defined
504
- // The best we can do is define a new version with a suffix
505
-
506
- // However, for HMR purposes, we can use a different approach:
507
- // We'll just replace all instances of the old element with new instances
508
- // This is handled in the update() method above
509
-
510
- // For now, we'll just log a warning
511
- console.warn(
512
- `Custom element ${tagName} is already defined. ` +
513
- `Replacing all instances with new definition.`
514
- );
515
-
516
- // Note: In a production HMR system, you might want to:
517
- // 1. Use a versioned tag name (e.g., my-element-v2)
518
- // 2. Use a proxy element that delegates to the actual element
519
- // 3. Use a custom element registry that supports re-registration
520
-
521
- // For this implementation, we rely on replacing element instances
522
- // which is handled in the update() method
523
- }
524
-
525
- /**
526
- * Restore Lit element state after HMR update
527
- *
528
- * Restores:
529
- * - Element properties
530
- * - Element attributes
531
- * - DOM state (scroll, focus, form values)
532
- */
533
- override restoreState(island: HTMLElement, state: StateSnapshot): void {
534
- try {
535
- // Restore DOM state (scroll, focus, form values)
536
- super.restoreState(island, state);
537
-
538
- // Restore Lit-specific state
539
- const litState = state as LitStateSnapshot;
540
- const tagName = litState.data.tagName;
541
-
542
- if (tagName) {
543
- const litElement = island.querySelector(tagName) as LitElementInstance;
544
-
545
- if (litElement) {
546
- // Restore element properties
547
- if (litState.data.elementProperties) {
548
- Object.entries(litState.data.elementProperties).forEach(([key, value]) => {
549
- try {
550
- (litElement as unknown as Record<string, unknown>)[key] = value;
551
- } catch (error) {
552
- console.warn(`Failed to restore property ${key}:`, error);
553
- }
554
- });
555
- }
556
-
557
- // Restore element attributes
558
- if (litState.data.elementAttributes) {
559
- Object.entries(litState.data.elementAttributes).forEach(([name, value]) => {
560
- try {
561
- litElement.setAttribute(name, value);
562
- } catch (error) {
563
- console.warn(`Failed to restore attribute ${name}:`, error);
564
- }
565
- });
566
- }
567
-
568
- // Request update to apply changes
569
- if (litElement.requestUpdate) {
570
- litElement.requestUpdate();
571
- }
572
- }
573
- }
574
-
575
- } catch (error) {
576
- console.warn('Failed to restore Lit state:', error);
577
- }
578
- }
579
-
580
- /**
581
- * Handle errors during Lit HMR update
582
- *
583
- * Provides Lit-specific error handling with helpful messages
584
- */
585
- override handleError(island: HTMLElement, error: Error): void {
586
- console.error('Lit HMR error:', error);
587
-
588
- // Use base error handling
589
- super.handleError(island, error);
590
-
591
- // Add Lit-specific error information
592
- const errorIndicator = island.querySelector('.hmr-error-indicator');
593
- if (errorIndicator) {
594
- const errorMessage = error.message;
595
-
596
- // Provide helpful hints for common Lit errors
597
- let hint = '';
598
- if (errorMessage.includes('custom element') || errorMessage.includes('define')) {
599
- hint = ' (Hint: Check that your element has a valid tag name with a hyphen)';
600
- } else if (errorMessage.includes('tag name')) {
601
- hint = ' (Hint: Custom element tag names must contain a hyphen)';
602
- } else if (errorMessage.includes('property') || errorMessage.includes('attribute')) {
603
- hint = ' (Hint: Check @property decorators and attribute names)';
604
- } else if (errorMessage.includes('render')) {
605
- hint = ' (Hint: Check the render() method for errors)';
606
- } else if (errorMessage.includes('shadow')) {
607
- hint = ' (Hint: Check Shadow DOM usage and styles)';
608
- }
609
-
610
- errorIndicator.textContent = `Lit HMR Error: ${errorMessage}${hint}`;
611
- }
612
- }
613
-
614
- /**
615
- * Extract component name from source path
616
- * Used for debugging and error messages
617
- */
618
- private extractComponentName(src: string): string {
619
- const parts = src.split('/');
620
- const filename = parts[parts.length - 1];
621
- return filename.replace(/\.lit\.(ts|js)$/, '').replace(/\.(ts|js)$/, '');
622
- }
623
-
624
- /**
625
- * Clean up Lit element when island is removed
626
- * This should be called when an island is unmounted
627
- */
628
- unmount(island: HTMLElement): void {
629
- try {
630
- const tagName = this.tagNames.get(island);
631
-
632
- if (tagName) {
633
- // Find all elements and disconnect them
634
- const elements = island.querySelectorAll(tagName) as NodeListOf<LitElementInstance>;
635
- elements.forEach(element => {
636
- // Lit elements clean up automatically when disconnected
637
- // but we can help by removing them from the DOM
638
- element.remove();
639
- });
640
-
641
- this.tagNames.delete(island);
642
- }
643
-
644
- this.elementConstructors.delete(island);
645
- } catch (error) {
646
- console.warn('Failed to unmount Lit element:', error);
647
- }
648
- }
649
- }
650
-
651
- /**
652
- * Create and export a singleton instance of the Lit HMR adapter
653
- */
654
- export const litAdapter = new LitHMRAdapter();