@useavalon/avalon 0.1.12 → 0.1.13

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 (230) hide show
  1. package/mod.ts +302 -0
  2. package/package.json +9 -17
  3. package/src/build/integration-bundler-plugin.ts +116 -0
  4. package/src/build/integration-config.ts +168 -0
  5. package/src/build/integration-detection-plugin.ts +117 -0
  6. package/src/build/integration-resolver-plugin.ts +90 -0
  7. package/src/build/island-manifest.ts +269 -0
  8. package/src/build/island-types-generator.ts +476 -0
  9. package/src/build/mdx-island-transform.ts +464 -0
  10. package/src/build/mdx-plugin.ts +98 -0
  11. package/src/build/page-island-transform.ts +598 -0
  12. package/src/build/prop-extractors/index.ts +21 -0
  13. package/src/build/prop-extractors/lit.ts +140 -0
  14. package/src/build/prop-extractors/qwik.ts +16 -0
  15. package/src/build/prop-extractors/solid.ts +125 -0
  16. package/src/build/prop-extractors/svelte.ts +194 -0
  17. package/src/build/prop-extractors/vue.ts +111 -0
  18. package/src/build/sidecar-file-manager.ts +104 -0
  19. package/src/build/sidecar-renderer.ts +30 -0
  20. package/src/client/adapters/index.ts +21 -0
  21. package/src/client/components.ts +35 -0
  22. package/src/client/css-hmr-handler.ts +344 -0
  23. package/src/client/framework-adapter.ts +462 -0
  24. package/src/client/hmr-coordinator.ts +396 -0
  25. package/src/client/hmr-error-overlay.js +533 -0
  26. package/src/client/main.js +824 -0
  27. package/src/components/Image.tsx +123 -0
  28. package/src/components/IslandErrorBoundary.tsx +145 -0
  29. package/src/components/LayoutDataErrorBoundary.tsx +141 -0
  30. package/src/components/LayoutErrorBoundary.tsx +127 -0
  31. package/src/components/PersistentIsland.tsx +52 -0
  32. package/src/components/StreamingErrorBoundary.tsx +233 -0
  33. package/src/components/StreamingLayout.tsx +538 -0
  34. package/src/core/components/component-analyzer.ts +192 -0
  35. package/src/core/components/component-detection.ts +508 -0
  36. package/src/core/components/enhanced-framework-detector.ts +500 -0
  37. package/src/core/components/framework-registry.ts +563 -0
  38. package/src/core/content/mdx-processor.ts +46 -0
  39. package/src/core/integrations/index.ts +19 -0
  40. package/src/core/integrations/loader.ts +125 -0
  41. package/src/core/integrations/registry.ts +175 -0
  42. package/src/core/islands/island-persistence.ts +325 -0
  43. package/src/core/islands/island-state-serializer.ts +258 -0
  44. package/src/core/islands/persistent-island-context.tsx +80 -0
  45. package/src/core/islands/use-persistent-state.ts +68 -0
  46. package/src/core/layout/enhanced-layout-resolver.ts +322 -0
  47. package/src/core/layout/layout-cache-manager.ts +485 -0
  48. package/src/core/layout/layout-composer.ts +357 -0
  49. package/src/core/layout/layout-data-loader.ts +516 -0
  50. package/src/core/layout/layout-discovery.ts +243 -0
  51. package/src/core/layout/layout-matcher.ts +299 -0
  52. package/src/core/layout/layout-types.ts +110 -0
  53. package/src/core/modules/framework-module-resolver.ts +273 -0
  54. package/src/islands/component-analysis.ts +213 -0
  55. package/src/islands/css-utils.ts +565 -0
  56. package/src/islands/discovery/index.ts +80 -0
  57. package/src/islands/discovery/registry.ts +340 -0
  58. package/src/islands/discovery/resolver.ts +477 -0
  59. package/src/islands/discovery/scanner.ts +386 -0
  60. package/src/islands/discovery/types.ts +117 -0
  61. package/src/islands/discovery/validator.ts +544 -0
  62. package/src/islands/discovery/watcher.ts +368 -0
  63. package/src/islands/framework-detection.ts +428 -0
  64. package/src/islands/integration-loader.ts +490 -0
  65. package/src/islands/island.tsx +565 -0
  66. package/src/islands/render-cache.ts +550 -0
  67. package/src/islands/types.ts +80 -0
  68. package/src/islands/universal-css-collector.ts +157 -0
  69. package/src/islands/universal-head-collector.ts +137 -0
  70. package/src/layout-system.ts +218 -0
  71. package/src/middleware/discovery.ts +268 -0
  72. package/src/middleware/executor.ts +315 -0
  73. package/src/middleware/index.ts +76 -0
  74. package/src/middleware/types.ts +99 -0
  75. package/src/nitro/build-config.ts +576 -0
  76. package/src/nitro/config.ts +483 -0
  77. package/src/nitro/error-handler.ts +636 -0
  78. package/src/nitro/index.ts +173 -0
  79. package/src/nitro/island-manifest.ts +584 -0
  80. package/src/nitro/middleware-adapter.ts +260 -0
  81. package/src/nitro/renderer.ts +1471 -0
  82. package/src/nitro/route-discovery.ts +439 -0
  83. package/src/nitro/types.ts +321 -0
  84. package/src/render/collect-css.ts +198 -0
  85. package/src/render/error-pages.ts +79 -0
  86. package/src/render/isolated-ssr-renderer.ts +654 -0
  87. package/src/render/ssr.ts +1030 -0
  88. package/src/schemas/api.ts +30 -0
  89. package/src/schemas/core.ts +64 -0
  90. package/src/schemas/index.ts +212 -0
  91. package/src/schemas/layout.ts +279 -0
  92. package/src/schemas/routing/index.ts +38 -0
  93. package/src/schemas/routing.ts +376 -0
  94. package/src/types/as-island.ts +20 -0
  95. package/src/types/layout.ts +285 -0
  96. package/src/types/routing.ts +555 -0
  97. package/src/types/types.ts +5 -0
  98. package/src/utils/dev-logger.ts +299 -0
  99. package/src/utils/fs.ts +151 -0
  100. package/src/vite-plugin/auto-discover.ts +551 -0
  101. package/src/vite-plugin/config.ts +266 -0
  102. package/src/vite-plugin/errors.ts +127 -0
  103. package/src/vite-plugin/image-optimization.ts +156 -0
  104. package/src/vite-plugin/integration-activator.ts +126 -0
  105. package/src/vite-plugin/island-sidecar-plugin.ts +176 -0
  106. package/src/vite-plugin/module-discovery.ts +189 -0
  107. package/src/vite-plugin/nitro-integration.ts +1354 -0
  108. package/src/vite-plugin/plugin.ts +403 -0
  109. package/src/vite-plugin/types.ts +327 -0
  110. package/src/vite-plugin/validation.ts +228 -0
  111. package/dist/mod.js +0 -1
  112. package/dist/src/build/integration-bundler-plugin.js +0 -1
  113. package/dist/src/build/integration-config.js +0 -1
  114. package/dist/src/build/integration-detection-plugin.js +0 -1
  115. package/dist/src/build/integration-resolver-plugin.js +0 -1
  116. package/dist/src/build/island-manifest.js +0 -1
  117. package/dist/src/build/island-types-generator.js +0 -5
  118. package/dist/src/build/mdx-island-transform.js +0 -2
  119. package/dist/src/build/mdx-plugin.js +0 -1
  120. package/dist/src/build/page-island-transform.js +0 -3
  121. package/dist/src/build/prop-extractors/index.js +0 -1
  122. package/dist/src/build/prop-extractors/lit.js +0 -1
  123. package/dist/src/build/prop-extractors/qwik.js +0 -1
  124. package/dist/src/build/prop-extractors/solid.js +0 -1
  125. package/dist/src/build/prop-extractors/svelte.js +0 -1
  126. package/dist/src/build/prop-extractors/vue.js +0 -1
  127. package/dist/src/build/sidecar-file-manager.js +0 -1
  128. package/dist/src/build/sidecar-renderer.js +0 -6
  129. package/dist/src/client/adapters/index.js +0 -1
  130. package/dist/src/client/components.js +0 -1
  131. package/dist/src/client/css-hmr-handler.js +0 -1
  132. package/dist/src/client/framework-adapter.js +0 -13
  133. package/dist/src/client/hmr-coordinator.js +0 -1
  134. package/dist/src/client/hmr-error-overlay.js +0 -214
  135. package/dist/src/client/main.js +0 -39
  136. package/dist/src/components/Image.js +0 -1
  137. package/dist/src/components/IslandErrorBoundary.js +0 -1
  138. package/dist/src/components/LayoutDataErrorBoundary.js +0 -1
  139. package/dist/src/components/LayoutErrorBoundary.js +0 -1
  140. package/dist/src/components/PersistentIsland.js +0 -1
  141. package/dist/src/components/StreamingErrorBoundary.js +0 -1
  142. package/dist/src/components/StreamingLayout.js +0 -29
  143. package/dist/src/core/components/component-analyzer.js +0 -1
  144. package/dist/src/core/components/component-detection.js +0 -5
  145. package/dist/src/core/components/enhanced-framework-detector.js +0 -1
  146. package/dist/src/core/components/framework-registry.js +0 -1
  147. package/dist/src/core/content/mdx-processor.js +0 -1
  148. package/dist/src/core/integrations/index.js +0 -1
  149. package/dist/src/core/integrations/loader.js +0 -1
  150. package/dist/src/core/integrations/registry.js +0 -1
  151. package/dist/src/core/islands/island-persistence.js +0 -1
  152. package/dist/src/core/islands/island-state-serializer.js +0 -1
  153. package/dist/src/core/islands/persistent-island-context.js +0 -1
  154. package/dist/src/core/islands/use-persistent-state.js +0 -1
  155. package/dist/src/core/layout/enhanced-layout-resolver.js +0 -1
  156. package/dist/src/core/layout/layout-cache-manager.js +0 -1
  157. package/dist/src/core/layout/layout-composer.js +0 -1
  158. package/dist/src/core/layout/layout-data-loader.js +0 -1
  159. package/dist/src/core/layout/layout-discovery.js +0 -1
  160. package/dist/src/core/layout/layout-matcher.js +0 -1
  161. package/dist/src/core/layout/layout-types.js +0 -1
  162. package/dist/src/core/modules/framework-module-resolver.js +0 -1
  163. package/dist/src/islands/component-analysis.js +0 -1
  164. package/dist/src/islands/css-utils.js +0 -17
  165. package/dist/src/islands/discovery/index.js +0 -1
  166. package/dist/src/islands/discovery/registry.js +0 -1
  167. package/dist/src/islands/discovery/resolver.js +0 -2
  168. package/dist/src/islands/discovery/scanner.js +0 -1
  169. package/dist/src/islands/discovery/types.js +0 -1
  170. package/dist/src/islands/discovery/validator.js +0 -18
  171. package/dist/src/islands/discovery/watcher.js +0 -1
  172. package/dist/src/islands/framework-detection.js +0 -1
  173. package/dist/src/islands/integration-loader.js +0 -1
  174. package/dist/src/islands/island.js +0 -1
  175. package/dist/src/islands/render-cache.js +0 -1
  176. package/dist/src/islands/types.js +0 -1
  177. package/dist/src/islands/universal-css-collector.js +0 -5
  178. package/dist/src/islands/universal-head-collector.js +0 -2
  179. package/dist/src/layout-system.js +0 -1
  180. package/dist/src/middleware/discovery.js +0 -1
  181. package/dist/src/middleware/executor.js +0 -1
  182. package/dist/src/middleware/index.js +0 -1
  183. package/dist/src/middleware/types.js +0 -1
  184. package/dist/src/nitro/build-config.js +0 -1
  185. package/dist/src/nitro/config.js +0 -1
  186. package/dist/src/nitro/error-handler.js +0 -198
  187. package/dist/src/nitro/index.js +0 -1
  188. package/dist/src/nitro/island-manifest.js +0 -2
  189. package/dist/src/nitro/middleware-adapter.js +0 -1
  190. package/dist/src/nitro/renderer.js +0 -183
  191. package/dist/src/nitro/route-discovery.js +0 -1
  192. package/dist/src/nitro/types.js +0 -1
  193. package/dist/src/render/collect-css.js +0 -3
  194. package/dist/src/render/error-pages.js +0 -48
  195. package/dist/src/render/isolated-ssr-renderer.js +0 -1
  196. package/dist/src/render/ssr.js +0 -90
  197. package/dist/src/schemas/api.js +0 -1
  198. package/dist/src/schemas/core.js +0 -1
  199. package/dist/src/schemas/index.js +0 -1
  200. package/dist/src/schemas/layout.js +0 -1
  201. package/dist/src/schemas/routing/index.js +0 -1
  202. package/dist/src/schemas/routing.js +0 -1
  203. package/dist/src/types/as-island.js +0 -1
  204. package/dist/src/types/layout.js +0 -1
  205. package/dist/src/types/routing.js +0 -1
  206. package/dist/src/types/types.js +0 -1
  207. package/dist/src/utils/dev-logger.js +0 -12
  208. package/dist/src/utils/fs.js +0 -1
  209. package/dist/src/vite-plugin/auto-discover.js +0 -1
  210. package/dist/src/vite-plugin/config.js +0 -1
  211. package/dist/src/vite-plugin/errors.js +0 -1
  212. package/dist/src/vite-plugin/image-optimization.js +0 -45
  213. package/dist/src/vite-plugin/integration-activator.js +0 -1
  214. package/dist/src/vite-plugin/island-sidecar-plugin.js +0 -1
  215. package/dist/src/vite-plugin/module-discovery.js +0 -1
  216. package/dist/src/vite-plugin/nitro-integration.js +0 -42
  217. package/dist/src/vite-plugin/plugin.js +0 -1
  218. package/dist/src/vite-plugin/types.js +0 -1
  219. package/dist/src/vite-plugin/validation.js +0 -2
  220. /package/{dist/src → src}/client/types/framework-runtime.d.ts +0 -0
  221. /package/{dist/src → src}/client/types/vite-hmr.d.ts +0 -0
  222. /package/{dist/src → src}/client/types/vite-virtual-modules.d.ts +0 -0
  223. /package/{dist/src → src}/layout-system.d.ts +0 -0
  224. /package/{dist/src → src}/types/image.d.ts +0 -0
  225. /package/{dist/src → src}/types/index.d.ts +0 -0
  226. /package/{dist/src → src}/types/island-jsx.d.ts +0 -0
  227. /package/{dist/src → src}/types/island-prop.d.ts +0 -0
  228. /package/{dist/src → src}/types/mdx.d.ts +0 -0
  229. /package/{dist/src → src}/types/urlpattern.d.ts +0 -0
  230. /package/{dist/src → src}/types/vite-env.d.ts +0 -0
@@ -0,0 +1,30 @@
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
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Framework HMR Adapters
3
+ *
4
+ * HMR adapters have been moved to their respective integration packages:
5
+ * - @useavalon/react/client/hmr
6
+ * - @useavalon/preact/client/hmr
7
+ * - @useavalon/vue/client/hmr
8
+ * - @useavalon/svelte/client/hmr
9
+ * - @useavalon/solid/client/hmr
10
+ * - @useavalon/lit/client/hmr
11
+ * - @useavalon/qwik/client/hmr
12
+ *
13
+ * This barrel re-exports from the base framework adapter for backward compatibility.
14
+ */
15
+
16
+ export {
17
+ BaseFrameworkAdapter,
18
+ AdapterRegistry,
19
+ type FrameworkHMRAdapter,
20
+ type StateSnapshot,
21
+ } from '../framework-adapter.ts';
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Client-safe component exports for use in island components.
3
+ *
4
+ * Import from '@useavalon/avalon/client' instead of '@useavalon/avalon' when
5
+ * you need framework components inside islands. The main entry point
6
+ * re-exports server-only code (nitro, h3, vite plugins) that can't
7
+ * be bundled for the browser.
8
+ */
9
+
10
+ // Persistent islands
11
+ export { PersistentIsland } from '../components/PersistentIsland.tsx';
12
+ export {
13
+ usePersistentIslandContext,
14
+ PersistentIslandProvider,
15
+ createPersistentIslandContext,
16
+ } from '../core/islands/persistent-island-context.tsx';
17
+ export { usePersistentState } from '../core/islands/use-persistent-state.ts';
18
+ export { IslandPersistence, defaultIslandPersistence } from '../core/islands/island-persistence.ts';
19
+ export { IslandStateSerializer } from '../core/islands/island-state-serializer.ts';
20
+
21
+ // Error boundaries
22
+ export { IslandErrorBoundary, withIslandErrorBoundary } from '../components/IslandErrorBoundary.tsx';
23
+ export { LayoutErrorBoundary } from '../components/LayoutErrorBoundary.tsx';
24
+ export { LayoutDataErrorBoundary } from '../components/LayoutDataErrorBoundary.tsx';
25
+ export { StreamingErrorBoundary, withStreamingErrorBoundary } from '../components/StreamingErrorBoundary.tsx';
26
+
27
+ // Streaming
28
+ export { StreamingLayout, StreamingSuspense, withStreaming, useStreamingState } from '../components/StreamingLayout.tsx';
29
+
30
+ // Image optimization
31
+ export { Image } from '../components/Image.tsx';
32
+ export type { ImageProps } from '../components/Image.tsx';
33
+
34
+ // Types
35
+ export type { IslandState } from '../schemas/layout.ts';
@@ -0,0 +1,344 @@
1
+ /**
2
+ * CSS HMR Handler
3
+ *
4
+ * Handles Hot Module Replacement for CSS files including:
5
+ * - Global CSS files
6
+ * - CSS modules
7
+ * - Component-scoped styles (Svelte, Vue)
8
+ */
9
+
10
+ /// <reference lib="dom" />
11
+ /// <reference lib="dom.iterable" />
12
+
13
+ import type { Update } from './hmr-coordinator.ts';
14
+
15
+ /**
16
+ * CSS update types
17
+ */
18
+ export type CSSUpdateType = 'global' | 'module' | 'scoped';
19
+
20
+ /**
21
+ * CSS update information
22
+ */
23
+ export interface CSSUpdateInfo {
24
+ type: CSSUpdateType;
25
+ path: string;
26
+ timestamp: number;
27
+ affectedComponents?: string[];
28
+ }
29
+
30
+ /**
31
+ * CSS HMR Handler class
32
+ * Manages CSS hot updates without page reload
33
+ */
34
+ export class CSSHMRHandler {
35
+ private cssModuleMap: Map<string, Set<HTMLElement>> = new Map();
36
+ private styleElementMap: Map<string, HTMLStyleElement> = new Map();
37
+
38
+ /**
39
+ * Handle CSS update from Vite HMR
40
+ */
41
+ handleCSSUpdate(update: Update): void {
42
+ const updateInfo = this.classifyCSSUpdate(update);
43
+
44
+ switch (updateInfo.type) {
45
+ case 'global':
46
+ this.handleGlobalCSSUpdate(updateInfo);
47
+ break;
48
+ case 'module':
49
+ this.handleCSSModuleUpdate(updateInfo);
50
+ break;
51
+ case 'scoped':
52
+ this.handleScopedCSSUpdate(updateInfo);
53
+ break;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Classify the type of CSS update
59
+ */
60
+ private classifyCSSUpdate(update: Update): CSSUpdateInfo {
61
+ const path = update.path || update.acceptedPath;
62
+
63
+ // Check if it's a CSS module
64
+ if (path.includes('.module.css') || path.includes('.module.scss')) {
65
+ return {
66
+ type: 'module',
67
+ path,
68
+ timestamp: update.timestamp,
69
+ };
70
+ }
71
+
72
+ // Check if it's a scoped style (Svelte or Vue)
73
+ if (path.includes('.svelte') || path.includes('.vue')) {
74
+ return {
75
+ type: 'scoped',
76
+ path,
77
+ timestamp: update.timestamp,
78
+ };
79
+ }
80
+
81
+ // Default to global CSS
82
+ return {
83
+ type: 'global',
84
+ path,
85
+ timestamp: update.timestamp,
86
+ };
87
+ }
88
+
89
+ /**
90
+ * Handle global CSS file update
91
+ * Injects updated styles without page reload
92
+ */
93
+ private handleGlobalCSSUpdate(info: CSSUpdateInfo): void {
94
+ try {
95
+ // Find existing style element for this CSS file
96
+ const existingStyle = this.styleElementMap.get(info.path);
97
+
98
+ if (existingStyle) {
99
+ // Remove old style element
100
+ existingStyle.remove();
101
+ this.styleElementMap.delete(info.path);
102
+ }
103
+
104
+ // Vite automatically injects the updated CSS via its HMR mechanism
105
+ // We just need to ensure old styles are removed
106
+ // The new styles will be injected by Vite's CSS handling
107
+
108
+ // Find the newly injected style element
109
+ // Vite adds a data-vite-dev-id attribute to style elements
110
+ const newStyle = document.querySelector<HTMLStyleElement>(
111
+ `style[data-vite-dev-id*="${this.getFileId(info.path)}"]`,
112
+ );
113
+
114
+ if (newStyle) {
115
+ this.styleElementMap.set(info.path, newStyle);
116
+ }
117
+
118
+ // Dispatch event for feedback
119
+ this.dispatchCSSUpdateEvent(info, true);
120
+ } catch (error) {
121
+ console.error(`Failed to update global CSS: ${info.path}`, error);
122
+ this.dispatchCSSUpdateEvent(info, false, error as Error);
123
+ throw error;
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Handle CSS module update
129
+ * Re-renders components using the updated CSS module
130
+ */
131
+ private handleCSSModuleUpdate(info: CSSUpdateInfo): void {
132
+ try {
133
+ const affectedIslands = this.findIslandsUsingCSSModule(info.path);
134
+
135
+ if (affectedIslands.length === 0) {
136
+ return;
137
+ }
138
+
139
+ for (const island of affectedIslands) {
140
+ this.triggerIslandRerender(island, info);
141
+ }
142
+
143
+ this.dispatchCSSUpdateEvent(info, true);
144
+ } catch (error) {
145
+ console.error(`[HMR] Failed to update CSS module: ${info.path}`, error);
146
+ this.dispatchCSSUpdateEvent(info, false, error as Error);
147
+ throw error;
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Handle scoped CSS update (Svelte/Vue)
153
+ * Updates only the affected component's styles
154
+ */
155
+ private handleScopedCSSUpdate(info: CSSUpdateInfo): void {
156
+ try {
157
+ const affectedIslands = this.findIslandsUsingComponent(info.path);
158
+
159
+ if (affectedIslands.length === 0) {
160
+ return;
161
+ }
162
+
163
+ this.dispatchCSSUpdateEvent(info, true);
164
+ } catch (error) {
165
+ console.error(`[HMR] Failed to update scoped CSS: ${info.path}`, error);
166
+ this.dispatchCSSUpdateEvent(info, false, error as Error);
167
+ throw error;
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Find islands that use a specific CSS module
173
+ */
174
+ private findIslandsUsingCSSModule(cssPath: string): HTMLElement[] {
175
+ const islands: HTMLElement[] = [];
176
+ const normalizedPath = this.normalizePath(cssPath);
177
+
178
+ // Check cached mapping first
179
+ const cached = this.cssModuleMap.get(normalizedPath);
180
+ if (cached) {
181
+ return Array.from(cached);
182
+ }
183
+
184
+ // Find all islands
185
+ const allIslands = document.querySelectorAll<HTMLElement>('[data-src]');
186
+
187
+ for (const island of allIslands) {
188
+ const src = island.getAttribute('data-src');
189
+ if (!src) continue;
190
+
191
+ // Check if the island's component likely imports this CSS module
192
+ // This is a heuristic - we look for islands in the same directory
193
+ const componentDir = this.getDirectory(src);
194
+ const cssDir = this.getDirectory(cssPath);
195
+
196
+ if (componentDir === cssDir) {
197
+ islands.push(island);
198
+ }
199
+ }
200
+
201
+ // Cache the mapping
202
+ if (islands.length > 0) {
203
+ this.cssModuleMap.set(normalizedPath, new Set(islands));
204
+ }
205
+
206
+ return islands;
207
+ }
208
+
209
+ /**
210
+ * Find islands using a specific component file
211
+ */
212
+ private findIslandsUsingComponent(componentPath: string): HTMLElement[] {
213
+ const islands: HTMLElement[] = [];
214
+ const normalizedPath = this.normalizePath(componentPath);
215
+
216
+ const allIslands = document.querySelectorAll<HTMLElement>('[data-src]');
217
+
218
+ for (const island of allIslands) {
219
+ const src = island.getAttribute('data-src');
220
+ if (!src) continue;
221
+
222
+ const normalizedSrc = this.normalizePath(src);
223
+
224
+ if (normalizedSrc === normalizedPath || normalizedSrc.includes(normalizedPath)) {
225
+ islands.push(island);
226
+ }
227
+ }
228
+
229
+ return islands;
230
+ }
231
+
232
+ /**
233
+ * Trigger re-render of an island to apply new CSS module classes
234
+ */
235
+ private triggerIslandRerender(island: HTMLElement, info: CSSUpdateInfo): void {
236
+ // Dispatch event to trigger island re-render
237
+ // The HMR coordinator will handle the actual re-render
238
+ island.dispatchEvent(
239
+ new CustomEvent('css-module-update', {
240
+ detail: {
241
+ cssPath: info.path,
242
+ timestamp: info.timestamp,
243
+ },
244
+ bubbles: true,
245
+ }),
246
+ );
247
+
248
+ // Also trigger a standard HMR update event
249
+ // This will be picked up by the HMR coordinator
250
+ const src = island.getAttribute('data-src');
251
+ if (src) {
252
+ island.dispatchEvent(
253
+ new CustomEvent('hmr-update-required', {
254
+ detail: {
255
+ src,
256
+ reason: 'css-module-update',
257
+ cssPath: info.path,
258
+ },
259
+ bubbles: true,
260
+ }),
261
+ );
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Dispatch CSS update event for feedback
267
+ */
268
+ private dispatchCSSUpdateEvent(info: CSSUpdateInfo, success: boolean, error?: Error): void {
269
+ const event = new CustomEvent('css-hmr-update', {
270
+ detail: {
271
+ type: info.type,
272
+ path: info.path,
273
+ timestamp: info.timestamp,
274
+ success,
275
+ error: error?.message,
276
+ },
277
+ bubbles: true,
278
+ });
279
+
280
+ document.dispatchEvent(event);
281
+ }
282
+
283
+ /**
284
+ * Normalize a file path for comparison
285
+ */
286
+ private normalizePath(path: string): string {
287
+ return path.replace(/\\/g, '/').replace(/^\//, '').replace(/\?.*$/, '').replace(/#.*$/, '');
288
+ }
289
+
290
+ /**
291
+ * Get directory from a file path
292
+ */
293
+ private getDirectory(path: string): string {
294
+ const normalized = this.normalizePath(path);
295
+ const lastSlash = normalized.lastIndexOf('/');
296
+ return lastSlash >= 0 ? normalized.substring(0, lastSlash) : '';
297
+ }
298
+
299
+ /**
300
+ * Get file ID from path (for Vite's data-vite-dev-id)
301
+ */
302
+ private getFileId(path: string): string {
303
+ const normalized = this.normalizePath(path);
304
+ // Vite uses the file path as the ID
305
+ return normalized;
306
+ }
307
+
308
+ /**
309
+ * Clear cached mappings
310
+ */
311
+ clearCache(): void {
312
+ this.cssModuleMap.clear();
313
+ this.styleElementMap.clear();
314
+ }
315
+
316
+ /**
317
+ * Register an island as using a CSS module
318
+ * Useful for explicit tracking
319
+ */
320
+ registerCSSModuleUsage(cssPath: string, island: HTMLElement): void {
321
+ const normalized = this.normalizePath(cssPath);
322
+
323
+ if (!this.cssModuleMap.has(normalized)) {
324
+ this.cssModuleMap.set(normalized, new Set());
325
+ }
326
+
327
+ this.cssModuleMap.get(normalized)!.add(island);
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Global CSS HMR handler instance
333
+ */
334
+ let cssHandlerInstance: CSSHMRHandler | null = null;
335
+
336
+ /**
337
+ * Get or create the global CSS HMR handler instance
338
+ */
339
+ export function getCSSHMRHandler(): CSSHMRHandler {
340
+ if (!cssHandlerInstance) {
341
+ cssHandlerInstance = new CSSHMRHandler();
342
+ }
343
+ return cssHandlerInstance;
344
+ }