@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,490 @@
1
+ import { registry } from "../core/integrations/registry.ts";
2
+ import type { Integration } from "@useavalon/core";
3
+ import { devWarn } from "../utils/dev-logger.ts";
4
+
5
+ /**
6
+ * Cache for loaded integrations to avoid repeated lookups
7
+ */
8
+ const frameworkCache = new Map<string, Integration>();
9
+
10
+ // Pattern to match nested island paths like /modules/*/islands/ or /src/*/islands/
11
+ const NESTED_ISLANDS_PATTERN = /\/(?:src\/)?(?:modules\/)?([^/]+\/)*islands\//;
12
+
13
+ /**
14
+ * Load an integration by framework name
15
+ * Uses cache to avoid repeated dynamic imports
16
+ *
17
+ * This function supports on-demand loading: if an integration hasn't been
18
+ * preloaded, it will be loaded and cached on first use. This enables
19
+ * lazy loading at server startup while ensuring fast subsequent renders.
20
+ */
21
+ export async function loadIntegration(framework: string) {
22
+ // Check local cache first (fastest path)
23
+ if (frameworkCache.has(framework)) {
24
+ return frameworkCache.get(framework)!;
25
+ }
26
+
27
+ // Check if already loaded in registry (e.g., by native preloader)
28
+ if (registry.has(framework)) {
29
+ const integration = registry.get(framework)!;
30
+ frameworkCache.set(framework, integration);
31
+ return integration;
32
+ }
33
+
34
+ // On-demand loading: load the integration now and cache it
35
+ try {
36
+ const integration = await registry.load(framework);
37
+ frameworkCache.set(framework, integration);
38
+ return integration;
39
+ } catch (error) {
40
+ throw new Error(
41
+ `Integration '${framework}' could not be loaded. Make sure @useavalon/${framework} is installed.`,
42
+ { cause: error }
43
+ );
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Detect framework from file path and load the appropriate integration
49
+ */
50
+ export async function detectAndLoadIntegration(src: string) {
51
+ const framework = detectFrameworkFromPath(src);
52
+ return await loadIntegration(framework);
53
+ }
54
+
55
+ /**
56
+ * Detect framework from file path based on extension and naming conventions.
57
+ *
58
+ * Updated to support nested island paths like:
59
+ * - /src/islands/Counter.tsx
60
+ * - /src/modules/auth/islands/Counter.tsx
61
+ * - /modules/dashboard/islands/Chart.vue
62
+ *
63
+ * @param src - The source path to detect framework from
64
+ * @returns The detected framework name
65
+ */
66
+ export function detectFrameworkFromPath(src: string) {
67
+ // Normalize path separators
68
+ const normalizedSrc = src.replaceAll('\\', "/");
69
+
70
+ // Vue files (.vue)
71
+ if (normalizedSrc.endsWith(".vue")) {
72
+ return "vue";
73
+ }
74
+
75
+ // Svelte files (.svelte)
76
+ if (normalizedSrc.endsWith(".svelte")) {
77
+ return "svelte";
78
+ }
79
+
80
+ // Solid files (convention: .solid.tsx or .solid.jsx)
81
+ if (normalizedSrc.includes(".solid.")) {
82
+ return "solid";
83
+ }
84
+
85
+ // Qwik files (convention: .qwik.tsx or .qwik.jsx)
86
+ if (normalizedSrc.includes(".qwik.")) {
87
+ return "qwik";
88
+ }
89
+
90
+ // React files (convention: .react.tsx or .react.jsx)
91
+ if (normalizedSrc.includes(".react.")) {
92
+ return "react";
93
+ }
94
+
95
+ // Lit files (convention: .lit.ts or .lit.js, or files starting with "Lit")
96
+ if (normalizedSrc.includes(".lit.")) {
97
+ return "lit";
98
+ }
99
+
100
+ // Lit files by naming convention (LitComponent.ts)
101
+ const fileName = normalizedSrc.split("/").pop() || "";
102
+ if (fileName.startsWith("Lit") && (normalizedSrc.endsWith(".ts") || normalizedSrc.endsWith(".js"))) {
103
+ return "lit";
104
+ }
105
+
106
+ // Check if path is in any islands directory (including nested)
107
+ // Plain .ts/.js files in islands are likely Lit components (Lit doesn't use JSX)
108
+ if (isInIslandsDirectory(normalizedSrc) && (normalizedSrc.endsWith(".ts") || normalizedSrc.endsWith(".js"))) {
109
+ return "lit";
110
+ }
111
+
112
+ // Default to Preact for .tsx and .jsx files
113
+ if (normalizedSrc.endsWith(".tsx") || normalizedSrc.endsWith(".jsx")) {
114
+ return "preact";
115
+ }
116
+
117
+ // Fallback to Preact
118
+ return "preact";
119
+ }
120
+
121
+ /**
122
+ * Check if a path is within any islands directory (including nested).
123
+ *
124
+ * Matches patterns like:
125
+ * - /islands/
126
+ * - /src/islands/
127
+ * - /src/modules/auth/islands/
128
+ * - /modules/dashboard/islands/
129
+ * - /src/features/user/islands/
130
+ *
131
+ * @param path - The path to check
132
+ * @returns True if the path is in an islands directory
133
+ */
134
+ export function isInIslandsDirectory(path: string): boolean {
135
+ const normalized = path.replaceAll('\\', "/");
136
+
137
+ // Check for /islands/ anywhere in the path
138
+ return normalized.includes("/islands/");
139
+ }
140
+
141
+ /**
142
+ * Check if a path is a nested island path (not in default /src/islands/).
143
+ *
144
+ * @param path - The path to check
145
+ * @returns True if the path is a nested island path
146
+ */
147
+ export function isNestedIslandPath(path: string): boolean {
148
+ const normalized = path.replaceAll('\\', "/");
149
+
150
+ // Check if it contains /islands/ but not at the root level
151
+ if (!normalized.includes("/islands/")) {
152
+ return false;
153
+ }
154
+
155
+ // Default path patterns
156
+ const defaultPatterns = [
157
+ /^\/islands\//,
158
+ /^\/src\/islands\//,
159
+ /^src\/islands\//,
160
+ /^islands\//,
161
+ ];
162
+
163
+ for (const pattern of defaultPatterns) {
164
+ if (pattern.test(normalized)) {
165
+ return false;
166
+ }
167
+ }
168
+
169
+ // If it contains /islands/ but doesn't match default patterns, it's nested
170
+ return true;
171
+ }
172
+
173
+ /**
174
+ * Extract the namespace from a nested island path.
175
+ *
176
+ * Examples:
177
+ * - /src/modules/auth/islands/Counter.tsx -> "modules/auth"
178
+ * - /src/features/user/islands/Profile.tsx -> "features/user"
179
+ * - /src/islands/Button.tsx -> ""
180
+ *
181
+ * @param path - The path to extract namespace from
182
+ * @returns The namespace or empty string for default islands
183
+ */
184
+ export function extractNamespaceFromPath(path: string): string {
185
+ const normalized = path.replaceAll('\\', "/");
186
+
187
+ // Match patterns like /src/modules/auth/islands/ or /modules/auth/islands/
188
+ const match = new RegExp(/(?:\/src)?\/(.+?)\/islands\//).exec(normalized);
189
+ if (match) {
190
+ return match[1];
191
+ }
192
+
193
+ return "";
194
+ }
195
+
196
+ /**
197
+ * Detect framework from file content by analyzing imports and patterns.
198
+ *
199
+ * Updated to support nested island paths.
200
+ *
201
+ * @param src - The source path
202
+ * @param content - The file content to analyze
203
+ * @returns The detected framework name
204
+ */
205
+ export function detectFrameworkFromContent(
206
+ src: string,
207
+ content: string
208
+ ) {
209
+ // First try path-based detection
210
+ const pathFramework = detectFrameworkFromPath(src);
211
+
212
+ // If we have a definitive answer from path (not default), use it
213
+ if (pathFramework === "vue" || pathFramework === "svelte" || pathFramework === "react" || pathFramework === "lit") {
214
+ return pathFramework;
215
+ }
216
+
217
+ // For .tsx/.jsx files, analyze content to distinguish between frameworks
218
+
219
+ // Check for React imports (must check before Preact since they share hooks)
220
+ if (
221
+ content.includes("from 'react'") ||
222
+ content.includes('from "react"') ||
223
+ content.includes("from 'react-dom'") ||
224
+ content.includes('from "react-dom"') ||
225
+ content.includes('"use client"') ||
226
+ content.includes("'use client'") ||
227
+ content.includes('"use server"') ||
228
+ content.includes("'use server'")
229
+ ) {
230
+ return "react";
231
+ }
232
+
233
+ // Check for Lit imports
234
+ if (
235
+ content.includes("from 'lit'") ||
236
+ content.includes('from "lit"') ||
237
+ content.includes("@lit-labs/ssr") ||
238
+ content.includes("LitElement") ||
239
+ content.includes("@customElement")
240
+ ) {
241
+ return "lit";
242
+ }
243
+
244
+ // Check for Solid imports
245
+ if (
246
+ content.includes("solid-js") ||
247
+ content.includes("from 'solid-js'") ||
248
+ content.includes('from "solid-js"')
249
+ ) {
250
+ return "solid";
251
+ }
252
+
253
+ // Check for Preact imports
254
+ if (
255
+ content.includes("from 'preact'") ||
256
+ content.includes('from "preact"') ||
257
+ content.includes("preact/hooks")
258
+ ) {
259
+ return "preact";
260
+ }
261
+
262
+ // Check for Lit-specific patterns
263
+ if (
264
+ content.includes("extends LitElement") ||
265
+ content.includes("@property") ||
266
+ content.includes("@state") ||
267
+ content.includes("html`") ||
268
+ content.includes("css`")
269
+ ) {
270
+ return "lit";
271
+ }
272
+
273
+ // Check for Solid-specific patterns
274
+ if (
275
+ content.includes("createSignal") ||
276
+ content.includes("createEffect") ||
277
+ content.includes("createMemo")
278
+ ) {
279
+ return "solid";
280
+ }
281
+
282
+ // Check for React/Preact-specific patterns (hooks)
283
+ // Note: React and Preact share the same hooks API, so we default to Preact
284
+ // unless React imports are explicitly detected above
285
+ if (
286
+ content.includes("useState") ||
287
+ content.includes("useEffect") ||
288
+ content.includes("useRef")
289
+ ) {
290
+ return "preact";
291
+ }
292
+
293
+ // Default to path-based detection
294
+ return pathFramework;
295
+ }
296
+
297
+ /**
298
+ * Get integration for a specific framework, with error handling
299
+ */
300
+ export async function getIntegration(framework: string) {
301
+ try {
302
+ return await loadIntegration(framework);
303
+ } catch (error) {
304
+ console.error(`Failed to load integration for ${framework}:`, error);
305
+ return null;
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Check if an integration is available for a framework
311
+ */
312
+ export async function hasIntegration(framework: string) {
313
+ try {
314
+ await loadIntegration(framework);
315
+ return true;
316
+ } catch {
317
+ return false;
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Get all loaded integrations from cache
323
+ */
324
+ export function getLoadedIntegrations() {
325
+ return Array.from(frameworkCache.values());
326
+ }
327
+
328
+ /**
329
+ * Get all loaded framework names from cache
330
+ */
331
+ export function getLoadedFrameworks() {
332
+ return Array.from(frameworkCache.keys());
333
+ }
334
+
335
+ /**
336
+ * Clear the integration cache
337
+ * Useful for testing or hot module replacement
338
+ */
339
+ export function clearIntegrationCache() {
340
+ frameworkCache.clear();
341
+ }
342
+
343
+ /**
344
+ * Check if an integration is loaded in cache
345
+ */
346
+ export function isIntegrationLoaded(framework: string) {
347
+ return frameworkCache.has(framework);
348
+ }
349
+
350
+ /**
351
+ * Default frameworks to preload at server startup
352
+ * These are the most commonly used frameworks in island architecture
353
+ */
354
+ export const DEFAULT_PRELOAD_FRAMEWORKS = ['preact', 'react', 'vue', 'svelte', 'solid', 'lit'] as const;
355
+
356
+ /**
357
+ * Options for preloading integrations
358
+ */
359
+ export interface PreloadIntegrationsOptions {
360
+ /**
361
+ * When true, only preload integrations that are actually used on the page.
362
+ * This is determined by analyzing page components for framework usage.
363
+ * When false (default), preload all specified frameworks.
364
+ */
365
+ lazy?: boolean;
366
+
367
+ /**
368
+ * Array of framework names to preload.
369
+ * Defaults to DEFAULT_PRELOAD_FRAMEWORKS.
370
+ */
371
+ frameworks?: readonly string[];
372
+
373
+ /**
374
+ * Array of detected frameworks from page analysis.
375
+ * Only used when lazy=true to filter which frameworks to preload.
376
+ */
377
+ detectedFrameworks?: string[];
378
+ }
379
+
380
+ /**
381
+ * Preload integrations for multiple frameworks
382
+ * Useful for warming up the cache during build or startup
383
+ *
384
+ * Uses Promise.allSettled to load all integrations concurrently,
385
+ * ensuring that one failed integration doesn't block others.
386
+ *
387
+ * @param options - Preload options
388
+ * @returns Promise that resolves when all preloading attempts complete
389
+ */
390
+ export async function preloadIntegrations(
391
+ options?: PreloadIntegrationsOptions
392
+ ): Promise<void> {
393
+ let frameworks: readonly string[];
394
+ let lazy = false;
395
+ let detectedFrameworks: string[] | undefined;
396
+
397
+ if (options) {
398
+ frameworks = options.frameworks ?? DEFAULT_PRELOAD_FRAMEWORKS;
399
+ lazy = options.lazy ?? false;
400
+ detectedFrameworks = options.detectedFrameworks;
401
+ } else {
402
+ frameworks = DEFAULT_PRELOAD_FRAMEWORKS;
403
+ }
404
+
405
+ // When lazy mode is enabled, only preload detected frameworks
406
+ if (lazy && detectedFrameworks && detectedFrameworks.length > 0) {
407
+ // Filter to only frameworks that are both in the default list and detected
408
+ const frameworksToLoad = frameworks.filter(fw =>
409
+ detectedFrameworks.includes(fw)
410
+ );
411
+
412
+ if (frameworksToLoad.length === 0) {
413
+ return;
414
+ }
415
+
416
+ frameworks = frameworksToLoad;
417
+ } else if (lazy && (!detectedFrameworks || detectedFrameworks.length === 0)) {
418
+ // Lazy mode but no detected frameworks - skip preloading entirely
419
+ return;
420
+ }
421
+
422
+ const results = await Promise.allSettled(
423
+ frameworks.map(framework => loadIntegration(framework))
424
+ );
425
+
426
+ // Track success/failure counts for logging
427
+ let successCount = 0;
428
+ let failureCount = 0;
429
+
430
+ // Log any failures (dev mode only)
431
+ results.forEach((result, index) => {
432
+ if (result.status === "rejected") {
433
+ failureCount++;
434
+ devWarn(
435
+ `⚠️ Failed to preload integration '${frameworks[index]}':`,
436
+ result.reason
437
+ );
438
+ } else {
439
+ successCount++;
440
+ }
441
+ });
442
+ }
443
+
444
+ /**
445
+ * Detect frameworks used in a page by analyzing component imports.
446
+ * This is used for lazy integration loading to only preload what's needed.
447
+ *
448
+ * @param pageContent - The content of the page file to analyze
449
+ * @returns Array of detected framework names
450
+ */
451
+ export function detectFrameworksFromPageContent(pageContent: string): string[] {
452
+ const detectedFrameworks: Set<string> = new Set();
453
+
454
+ // Check for island imports and their framework hints
455
+ // Look for patterns like: <Island src="/src/islands/Counter.tsx" framework="preact" />
456
+ const frameworkPropMatches = pageContent.matchAll(/framework\s*=\s*["'](\w+)["']/g);
457
+ for (const match of frameworkPropMatches) {
458
+ detectedFrameworks.add(match[1]);
459
+ }
460
+
461
+ // Check for island source paths to detect framework from file extension
462
+ const srcMatches = pageContent.matchAll(/src\s*=\s*["']([^"']+)["']/g);
463
+ for (const match of srcMatches) {
464
+ const src = match[1];
465
+ const framework = detectFrameworkFromPath(src);
466
+ detectedFrameworks.add(framework);
467
+ }
468
+
469
+ // Check for direct framework imports
470
+ if (pageContent.includes("from 'react'") || pageContent.includes('from "react"')) {
471
+ detectedFrameworks.add('react');
472
+ }
473
+ if (pageContent.includes("from 'preact'") || pageContent.includes('from "preact"')) {
474
+ detectedFrameworks.add('preact');
475
+ }
476
+ if (pageContent.includes("from 'vue'") || pageContent.includes('from "vue"')) {
477
+ detectedFrameworks.add('vue');
478
+ }
479
+ if (pageContent.includes("from 'svelte'") || pageContent.includes('from "svelte"')) {
480
+ detectedFrameworks.add('svelte');
481
+ }
482
+ if (pageContent.includes("from 'solid-js'") || pageContent.includes('from "solid-js"')) {
483
+ detectedFrameworks.add('solid');
484
+ }
485
+ if (pageContent.includes("from 'lit'") || pageContent.includes('from "lit"')) {
486
+ detectedFrameworks.add('lit');
487
+ }
488
+
489
+ return Array.from(detectedFrameworks);
490
+ }