@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,30 +0,0 @@
1
- /** Header comment placed at the top of every generated sidecar file. */
2
- export const SIDECAR_HEADER = "// Auto-generated by avalon — do not edit";
3
-
4
- /**
5
- * Render the full content of a sidecar declaration file.
6
- *
7
- * Pure function: takes a props type string (e.g. `"{ count: number }"` or
8
- * `"Record<string, unknown>"`) and returns the complete file content.
9
- *
10
- * The `island` prop is always included so the component can be used with
11
- * `<Component island={{ condition: 'on:interaction' }} />` in page files.
12
- * The Vite page-island-transform strips it at build time.
13
- *
14
- * The output follows this exact template:
15
- * ```
16
- * // Auto-generated by avalon — do not edit
17
- * import type { ComponentType } from 'preact';
18
- * import type { IslandDirective } from '@useavalon/avalon';
19
- * declare const component: ComponentType<PROPS & { island?: IslandDirective }>;
20
- * export default component;
21
- * ```
22
- */
23
- export function renderSidecarContent(propsType: string): string {
24
- return `${SIDECAR_HEADER}
25
- import type { ComponentType } from 'preact';
26
- import type { IslandDirective } from '@useavalon/avalon';
27
- declare const component: ComponentType<${propsType} & { island?: IslandDirective }>;
28
- export default component;
29
- `;
30
- }
@@ -1,12 +0,0 @@
1
- /**
2
- * Framework HMR Adapters
3
- *
4
- * Exports all framework-specific HMR adapters for easy registration
5
- */
6
- export { ReactHMRAdapter, reactAdapter } from "./react-adapter.js";
7
- export { PreactHMRAdapter, preactAdapter } from "./preact-adapter.js";
8
- export { VueHMRAdapter, vueAdapter } from "./vue-adapter.js";
9
- export { SvelteHMRAdapter, svelteAdapter } from "./svelte-adapter.js";
10
- export { SolidHMRAdapter, solidAdapter } from "./solid-adapter.js";
11
- export { LitHMRAdapter, litAdapter } from "./lit-adapter.js";
12
- export { QwikHMRAdapter, qwikAdapter } from "./qwik-adapter.js";
@@ -1,13 +0,0 @@
1
- /**
2
- * Framework HMR Adapters
3
- *
4
- * Exports all framework-specific HMR adapters for easy registration
5
- */
6
-
7
- export { ReactHMRAdapter, reactAdapter } from './react-adapter.ts';
8
- export { PreactHMRAdapter, preactAdapter } from './preact-adapter.ts';
9
- export { VueHMRAdapter, vueAdapter } from './vue-adapter.ts';
10
- export { SvelteHMRAdapter, svelteAdapter } from './svelte-adapter.ts';
11
- export { SolidHMRAdapter, solidAdapter } from './solid-adapter.ts';
12
- export { LitHMRAdapter, litAdapter } from './lit-adapter.ts';
13
- export { QwikHMRAdapter, qwikAdapter } from './qwik-adapter.ts';
@@ -1,467 +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
- /// <reference lib="dom" />
11
- import { BaseFrameworkAdapter } from "../framework-adapter.js";
12
- /**
13
- * Lit HMR Adapter
14
- *
15
- * Handles HMR for Lit components by:
16
- * 1. Re-registering the custom element with a new definition
17
- * 2. Updating all existing instances of the element
18
- * 3. Preserving element properties and attributes
19
- *
20
- * Lit components are Web Components (custom elements), so HMR requires:
21
- * - Undefining the old custom element (if possible)
22
- * - Defining the new custom element
23
- * - Updating all instances in the DOM
24
- * - Preserving reactive properties
25
- */
26
- export class LitHMRAdapter extends BaseFrameworkAdapter {
27
- name = "lit";
28
- /**
29
- * Store element constructors for each island
30
- */
31
- elementConstructors = new WeakMap();
32
- /**
33
- * Store tag names for each island
34
- */
35
- tagNames = new WeakMap();
36
- /**
37
- * Check if a component is a Lit component
38
- *
39
- * Lit components are:
40
- * - Classes that extend LitElement
41
- * - Typically decorated with @customElement
42
- * - Have .lit.ts or .lit.js extension (but not always)
43
- */
44
- canHandle(component) {
45
- if (!component) return false;
46
- // Check if it's a class (Lit components are classes)
47
- if (typeof component === "function") {
48
- const comp = component;
49
- // Check for Lit-specific markers
50
- // Lit elements may have __litElement marker
51
- if (comp.__litElement) {
52
- return true;
53
- }
54
- // Check if it extends LitElement by looking at the prototype chain
55
- try {
56
- const proto = component.prototype;
57
- if (proto) {
58
- // Check for Lit-specific methods on prototype
59
- if ("render" in proto && "requestUpdate" in proto && "updateComplete" in proto) {
60
- return true;
61
- }
62
- // Check for LitElement in prototype chain
63
- let currentProto = proto;
64
- while (currentProto && currentProto !== Object.prototype) {
65
- const constructor = currentProto.constructor;
66
- if (constructor && constructor.name === "LitElement") {
67
- return true;
68
- }
69
- currentProto = Object.getPrototypeOf(currentProto);
70
- }
71
- }
72
- } catch {}
73
- // Check for @customElement decorator metadata
74
- // The decorator adds metadata to the class
75
- if (comp.elementName || comp.tagName) {
76
- return true;
77
- }
78
- // Check function signature - Lit components typically have specific patterns
79
- try {
80
- const funcStr = component.toString();
81
- // Look for Lit-specific patterns
82
- if (funcStr.includes("LitElement") || funcStr.includes("customElement") || funcStr.includes("html`") || funcStr.includes("css`") || funcStr.includes("render()") || funcStr.includes("requestUpdate")) {
83
- return true;
84
- }
85
- } catch {}
86
- }
87
- // Check if it's a Lit component object (wrapped or exported)
88
- if (typeof component !== "object") {
89
- return false;
90
- }
91
- const obj = component;
92
- // Check for default export pattern
93
- if (obj.default && typeof obj.default === "function") {
94
- return this.canHandle(obj.default);
95
- }
96
- // Check for Lit component markers
97
- if (obj.__litElement) {
98
- return true;
99
- }
100
- return false;
101
- }
102
- /**
103
- * Preserve Lit element state before HMR update
104
- *
105
- * Captures:
106
- * - Element properties (reactive properties defined with @property)
107
- * - Element attributes
108
- * - DOM state (scroll, focus, form values)
109
- */
110
- preserveState(island) {
111
- try {
112
- // Get base DOM state
113
- const baseSnapshot = super.preserveState(island);
114
- if (!baseSnapshot) return null;
115
- // Get Lit-specific data
116
- const propsAttr = island.getAttribute("data-props");
117
- const capturedProps = propsAttr ? JSON.parse(propsAttr) : {};
118
- // Try to get component name from the island
119
- const src = island.getAttribute("data-src") || "";
120
- const componentName = this.extractComponentName(src);
121
- // Get tag name
122
- const tagName = island.getAttribute("data-tag-name") || this.tagNames.get(island);
123
- // Find the Lit element inside the island
124
- const litElement = tagName ? island.querySelector(tagName) : island.querySelector("[data-lit-element]");
125
- // Capture element properties and attributes
126
- const elementProperties = {};
127
- const elementAttributes = {};
128
- if (litElement) {
129
- // Capture all properties
130
- // Lit stores reactive properties on the element instance
131
- for (const key in litElement) {
132
- if (litElement.hasOwnProperty(key) && !key.startsWith("_")) {
133
- try {
134
- const value = litElement[key];
135
- // Only capture serializable values
136
- if (value !== undefined && value !== null && typeof value !== "function" && typeof value !== "symbol") {
137
- elementProperties[key] = value;
138
- }
139
- } catch {}
140
- }
141
- }
142
- // Capture all attributes
143
- for (let i = 0; i < litElement.attributes.length; i++) {
144
- const attr = litElement.attributes[i];
145
- elementAttributes[attr.name] = attr.value;
146
- }
147
- }
148
- const litSnapshot = {
149
- ...baseSnapshot,
150
- framework: "lit",
151
- data: {
152
- componentName,
153
- tagName: tagName || undefined,
154
- capturedProps,
155
- elementProperties,
156
- elementAttributes
157
- }
158
- };
159
- return litSnapshot;
160
- } catch (error) {
161
- console.warn("Failed to preserve Lit state:", error);
162
- return null;
163
- }
164
- }
165
- /**
166
- * Update Lit component with HMR
167
- *
168
- * This method handles custom element re-registration:
169
- * 1. Extract the tag name from the component or island
170
- * 2. Find all instances of the element in the DOM
171
- * 3. Preserve their properties and attributes
172
- * 4. Undefine the old custom element (if possible)
173
- * 5. Define the new custom element
174
- * 6. Update all instances with the new definition
175
- * 7. Restore properties and attributes
176
- */
177
- async update(island, newComponent, props) {
178
- if (!this.canHandle(newComponent)) {
179
- throw new Error("Component is not a valid Lit component");
180
- }
181
- // Extract the actual component class
182
- let ElementClass;
183
- if (typeof newComponent === "object" && newComponent !== null) {
184
- const obj = newComponent;
185
- if (obj.default && typeof obj.default === "function") {
186
- ElementClass = obj.default;
187
- } else {
188
- throw new Error("Lit component object must have a default export");
189
- }
190
- } else if (typeof newComponent === "function") {
191
- ElementClass = newComponent;
192
- } else {
193
- throw new Error("Invalid Lit component type");
194
- }
195
- try {
196
- // Determine the tag name
197
- // Priority: data-tag-name attribute > elementName property > derive from class name
198
- let tagName = island.getAttribute("data-tag-name");
199
- if (!tagName) {
200
- // Try to get from the class
201
- tagName = ElementClass.elementName;
202
- }
203
- if (!tagName) {
204
- // Derive from class name (convert PascalCase to kebab-case)
205
- tagName = ElementClass.name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
206
- }
207
- if (!tagName || !tagName.includes("-")) {
208
- throw new Error("Invalid custom element tag name: " + tagName);
209
- }
210
- // Store tag name for future updates
211
- this.tagNames.set(island, tagName);
212
- island.setAttribute("data-tag-name", tagName);
213
- // Find all instances of this element in the island
214
- const elements = Array.from(island.querySelectorAll(tagName));
215
- // If no elements exist, create one
216
- if (elements.length === 0) {
217
- // Check if the custom element is already defined
218
- const existingDefinition = customElements.get(tagName);
219
- if (!existingDefinition) {
220
- // Define the custom element
221
- customElements.define(tagName, ElementClass);
222
- } else if (existingDefinition !== ElementClass) {
223
- // Element is already defined with a different class
224
- // We need to re-register it
225
- await this.reregisterCustomElement(tagName, ElementClass);
226
- }
227
- // Create a new element
228
- const newElement = document.createElement(tagName);
229
- // Set properties from props
230
- Object.entries(props).forEach(([key, value]) => {
231
- try {
232
- newElement[key] = value;
233
- } catch (error) {
234
- console.warn(`Failed to set property ${key} on Lit element:`, error);
235
- }
236
- });
237
- // Append to island
238
- island.appendChild(newElement);
239
- // Store constructor
240
- this.elementConstructors.set(island, ElementClass);
241
- // Mark as hydrated
242
- island.setAttribute("data-hydrated", "true");
243
- island.setAttribute("data-hydration-status", "success");
244
- return;
245
- }
246
- // Update existing elements
247
- // First, check if we need to re-register the custom element
248
- const existingDefinition = customElements.get(tagName);
249
- if (existingDefinition && existingDefinition !== ElementClass) {
250
- // Re-register the custom element with the new definition
251
- await this.reregisterCustomElement(tagName, ElementClass);
252
- // After re-registration, we need to replace all existing elements
253
- // because they're instances of the old class
254
- for (const oldElement of elements) {
255
- // Preserve state
256
- const properties = {};
257
- const attributes = {};
258
- // Capture properties
259
- for (const key in oldElement) {
260
- if (oldElement.hasOwnProperty(key) && !key.startsWith("_")) {
261
- try {
262
- const value = oldElement[key];
263
- if (value !== undefined && value !== null && typeof value !== "function" && typeof value !== "symbol") {
264
- properties[key] = value;
265
- }
266
- } catch {}
267
- }
268
- }
269
- // Capture attributes
270
- for (let i = 0; i < oldElement.attributes.length; i++) {
271
- const attr = oldElement.attributes[i];
272
- attributes[attr.name] = attr.value;
273
- }
274
- // Create new element
275
- const newElement = document.createElement(tagName);
276
- // Restore attributes
277
- Object.entries(attributes).forEach(([name, value]) => {
278
- newElement.setAttribute(name, value);
279
- });
280
- // Restore properties
281
- Object.entries(properties).forEach(([key, value]) => {
282
- try {
283
- newElement[key] = value;
284
- } catch (error) {
285
- console.warn(`Failed to restore property ${key}:`, error);
286
- }
287
- });
288
- // Replace old element with new element
289
- oldElement.parentNode?.replaceChild(newElement, oldElement);
290
- }
291
- } else if (!existingDefinition) {
292
- // Define the custom element for the first time
293
- customElements.define(tagName, ElementClass);
294
- // Trigger update on all elements
295
- for (const element of elements) {
296
- if (element.requestUpdate) {
297
- element.requestUpdate();
298
- }
299
- }
300
- } else {
301
- // Same class, just trigger updates
302
- for (const element of elements) {
303
- // Update properties from props
304
- Object.entries(props).forEach(([key, value]) => {
305
- try {
306
- element[key] = value;
307
- } catch (error) {
308
- console.warn(`Failed to update property ${key}:`, error);
309
- }
310
- });
311
- // Request update
312
- if (element.requestUpdate) {
313
- element.requestUpdate();
314
- }
315
- }
316
- }
317
- // Store constructor
318
- this.elementConstructors.set(island, ElementClass);
319
- // Mark as hydrated
320
- island.setAttribute("data-hydrated", "true");
321
- island.setAttribute("data-hydration-status", "success");
322
- } catch (error) {
323
- console.error("Lit HMR update failed:", error);
324
- island.setAttribute("data-hydration-status", "error");
325
- throw error;
326
- }
327
- }
328
- /**
329
- * Re-register a custom element with a new definition
330
- *
331
- * Custom elements cannot be undefined once defined, so we need to:
332
- * 1. Create a new tag name (with a version suffix)
333
- * 2. Define the new element with the new tag name
334
- * 3. Update all references to use the new tag name
335
- *
336
- * Alternative approach (used here):
337
- * 1. Use a wrapper element that delegates to the actual element
338
- * 2. Update the wrapper to use the new element class
339
- */
340
- async reregisterCustomElement(tagName, ElementClass) {
341
- // Unfortunately, custom elements cannot be truly undefined once defined
342
- // The best we can do is define a new version with a suffix
343
- // However, for HMR purposes, we can use a different approach:
344
- // We'll just replace all instances of the old element with new instances
345
- // This is handled in the update() method above
346
- // For now, we'll just log a warning
347
- console.warn(`Custom element ${tagName} is already defined. ` + `Replacing all instances with new definition.`);
348
- // Note: In a production HMR system, you might want to:
349
- // 1. Use a versioned tag name (e.g., my-element-v2)
350
- // 2. Use a proxy element that delegates to the actual element
351
- // 3. Use a custom element registry that supports re-registration
352
- // For this implementation, we rely on replacing element instances
353
- // which is handled in the update() method
354
- }
355
- /**
356
- * Restore Lit element state after HMR update
357
- *
358
- * Restores:
359
- * - Element properties
360
- * - Element attributes
361
- * - DOM state (scroll, focus, form values)
362
- */
363
- restoreState(island, state) {
364
- try {
365
- // Restore DOM state (scroll, focus, form values)
366
- super.restoreState(island, state);
367
- // Restore Lit-specific state
368
- const litState = state;
369
- const tagName = litState.data.tagName;
370
- if (tagName) {
371
- const litElement = island.querySelector(tagName);
372
- if (litElement) {
373
- // Restore element properties
374
- if (litState.data.elementProperties) {
375
- Object.entries(litState.data.elementProperties).forEach(([key, value]) => {
376
- try {
377
- litElement[key] = value;
378
- } catch (error) {
379
- console.warn(`Failed to restore property ${key}:`, error);
380
- }
381
- });
382
- }
383
- // Restore element attributes
384
- if (litState.data.elementAttributes) {
385
- Object.entries(litState.data.elementAttributes).forEach(([name, value]) => {
386
- try {
387
- litElement.setAttribute(name, value);
388
- } catch (error) {
389
- console.warn(`Failed to restore attribute ${name}:`, error);
390
- }
391
- });
392
- }
393
- // Request update to apply changes
394
- if (litElement.requestUpdate) {
395
- litElement.requestUpdate();
396
- }
397
- }
398
- }
399
- } catch (error) {
400
- console.warn("Failed to restore Lit state:", error);
401
- }
402
- }
403
- /**
404
- * Handle errors during Lit HMR update
405
- *
406
- * Provides Lit-specific error handling with helpful messages
407
- */
408
- handleError(island, error) {
409
- console.error("Lit HMR error:", error);
410
- // Use base error handling
411
- super.handleError(island, error);
412
- // Add Lit-specific error information
413
- const errorIndicator = island.querySelector(".hmr-error-indicator");
414
- if (errorIndicator) {
415
- const errorMessage = error.message;
416
- // Provide helpful hints for common Lit errors
417
- let hint = "";
418
- if (errorMessage.includes("custom element") || errorMessage.includes("define")) {
419
- hint = " (Hint: Check that your element has a valid tag name with a hyphen)";
420
- } else if (errorMessage.includes("tag name")) {
421
- hint = " (Hint: Custom element tag names must contain a hyphen)";
422
- } else if (errorMessage.includes("property") || errorMessage.includes("attribute")) {
423
- hint = " (Hint: Check @property decorators and attribute names)";
424
- } else if (errorMessage.includes("render")) {
425
- hint = " (Hint: Check the render() method for errors)";
426
- } else if (errorMessage.includes("shadow")) {
427
- hint = " (Hint: Check Shadow DOM usage and styles)";
428
- }
429
- errorIndicator.textContent = `Lit HMR Error: ${errorMessage}${hint}`;
430
- }
431
- }
432
- /**
433
- * Extract component name from source path
434
- * Used for debugging and error messages
435
- */
436
- extractComponentName(src) {
437
- const parts = src.split("/");
438
- const filename = parts[parts.length - 1];
439
- return filename.replace(/\.lit\.(ts|js)$/, "").replace(/\.(ts|js)$/, "");
440
- }
441
- /**
442
- * Clean up Lit element when island is removed
443
- * This should be called when an island is unmounted
444
- */
445
- unmount(island) {
446
- try {
447
- const tagName = this.tagNames.get(island);
448
- if (tagName) {
449
- // Find all elements and disconnect them
450
- const elements = island.querySelectorAll(tagName);
451
- elements.forEach((element) => {
452
- // Lit elements clean up automatically when disconnected
453
- // but we can help by removing them from the DOM
454
- element.remove();
455
- });
456
- this.tagNames.delete(island);
457
- }
458
- this.elementConstructors.delete(island);
459
- } catch (error) {
460
- console.warn("Failed to unmount Lit element:", error);
461
- }
462
- }
463
- }
464
- /**
465
- * Create and export a singleton instance of the Lit HMR adapter
466
- */
467
- export const litAdapter = new LitHMRAdapter();