@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,565 +0,0 @@
1
- import type { JSX } from 'preact';
2
- import { h } from 'preact';
3
- import type { ViteDevServer } from 'vite';
4
- import type { AnalyzerOptions } from '../core/components/component-analyzer.ts';
5
- import type { Framework } from './types.ts';
6
- import { detectFramework } from './framework-detection.ts';
7
- import { analyzeComponentFile, renderComponentSSROnly } from './component-analysis.ts';
8
- import { loadIntegration, detectFrameworkFromPath } from './integration-loader.ts';
9
- import { addUniversalCSS } from './universal-css-collector.ts';
10
- import { addUniversalHead } from './universal-head-collector.ts';
11
- import { getIslandBundlePath } from '../build/island-manifest.ts';
12
- import type { Integration } from '../../../integrations/core/types.ts';
13
- import { isDev, devLog, devWarn, devError, logRenderTiming } from '../utils/dev-logger.ts';
14
-
15
- // Enhanced global CSS collector for SSR with scoping support
16
- declare global {
17
- var __viteDevServer: ViteDevServer | undefined;
18
- }
19
-
20
- /** Supported hydration conditions for island components */
21
- export type HydrationCondition = 'on:visible' | 'on:interaction' | 'on:idle' | 'on:client' | `media:${string}`;
22
-
23
- /** Supported framework identifiers (without "unknown") */
24
- export type FrameworkId = Exclude<Framework, 'unknown'>;
25
-
26
- export interface IslandProps {
27
- /** Path to the island component (e.g., "/islands/Counter.tsx") */
28
- src: string;
29
- /** Hydration condition */
30
- condition?: HydrationCondition;
31
- /** Props to pass to the island component */
32
- props?: Record<string, unknown>;
33
- /** Children to render inside the island (for SSR) */
34
- children?: import('preact').ComponentChildren;
35
- /** Whether to render server-side (default: true unless condition is 'on:client') */
36
- ssr?: boolean;
37
- /** Framework hint for client hydration */
38
- framework?: FrameworkId;
39
- /** Force SSR-only rendering without hydration */
40
- ssrOnly?: boolean;
41
- /** Component render options for intelligent detection */
42
- renderOptions?: AnalyzerOptions;
43
- /** Hydration data from integration renderer */
44
- hydrationData?: Record<string, unknown>;
45
- }
46
-
47
- // ---------------------------------------------------------------------------
48
- // Shared helpers (extracted to reduce cognitive complexity of Island/renderIsland)
49
- // ---------------------------------------------------------------------------
50
-
51
- /** Generate a deterministic island element ID from the source path */
52
- function toIslandId(src: string): string {
53
- return `island-${src.replaceAll(/[^a-zA-Z0-9]/g, '-')}`;
54
- }
55
-
56
- /** Build the extra hydration data-attributes from integration render output */
57
- function buildHydrationDataAttrs(hydrationData: Record<string, unknown>): Record<string, string> {
58
- const attrs: Record<string, string> = {};
59
- if (hydrationData.renderId) {
60
- attrs['data-solid-render-id'] = hydrationData.renderId as string;
61
- }
62
- const metadata = hydrationData.metadata as Record<string, unknown> | undefined;
63
- if (metadata?.tagName) {
64
- attrs['data-tag-name'] = metadata.tagName as string;
65
- }
66
- return attrs;
67
- }
68
-
69
- /** Build the full set of attributes for an `<avalon-island>` element that will be hydrated */
70
- function buildHydrateAttributes(
71
- src: string,
72
- condition: HydrationCondition,
73
- props: Record<string, unknown>,
74
- hydrationData: Record<string, unknown>,
75
- ): Record<string, string> {
76
- return {
77
- 'data-condition': condition,
78
- 'data-src': getIslandBundlePath(src),
79
- 'data-props': JSON.stringify(props),
80
- 'data-render-strategy': 'hydrate',
81
- ...buildHydrationDataAttrs(hydrationData),
82
- };
83
- }
84
-
85
- /** Detect the head-content type from an HTML string returned by an integration */
86
- function classifyHeadContent(headContent: string): 'script' | 'meta' | 'link' | 'style' | 'other' {
87
- if (headContent.startsWith('<script')) return 'script';
88
- if (headContent.startsWith('<style')) return 'style';
89
- if (headContent.startsWith('<meta')) return 'meta';
90
- if (headContent.startsWith('<link')) return 'link';
91
- if (headContent.includes('window._$HY') || headContent.includes('_$HY=')) return 'script';
92
- return 'other';
93
- }
94
-
95
- /** Extract CSS content from a <style> tag */
96
- function extractCSSFromStyleTag(styleTag: string): string | null {
97
- const match = styleTag.match(/<style[^>]*>([\s\S]*?)<\/style>/i);
98
- return match ? match[1].trim() : null;
99
- }
100
-
101
- /** Collect CSS and head content produced by an integration render */
102
- function collectRenderAssets(
103
- renderResult: { css?: string; head?: string; scopeId?: string },
104
- src: string,
105
- framework: string,
106
- logPrefix: string,
107
- ): void {
108
- if (renderResult.css) {
109
- addUniversalCSS(renderResult.css, src, framework, (renderResult as { scopeId?: string }).scopeId);
110
- }
111
- if (renderResult.head) {
112
- const headContent = renderResult.head.trim();
113
- const contentType = classifyHeadContent(headContent);
114
- if (contentType === 'style') {
115
- // Extract CSS from <style> tag and add to universal CSS collector
116
- const cssContent = extractCSSFromStyleTag(headContent);
117
- if (cssContent) {
118
- devLog(`${logPrefix} Extracting CSS from head <style> tag`);
119
- addUniversalCSS(cssContent, src, framework, (renderResult as { scopeId?: string }).scopeId);
120
- }
121
- return;
122
- }
123
- addUniversalHead(renderResult.head, src, framework, contentType);
124
- }
125
- }
126
-
127
- // ---------------------------------------------------------------------------
128
- // Island – the synchronous component that emits <avalon-island> custom elements
129
- // ---------------------------------------------------------------------------
130
-
131
- /** Render the SSR path: we already have rendered children to embed */
132
- function renderIslandSSR(opts: {
133
- islandId: string;
134
- detectedFramework: string;
135
- shouldSkipHydration: boolean;
136
- src: string;
137
- condition: HydrationCondition;
138
- props: Record<string, unknown>;
139
- hydrationData: Record<string, unknown>;
140
- children: import('preact').ComponentChildren;
141
- }): JSX.Element {
142
- const { islandId, detectedFramework, shouldSkipHydration, src, condition, props, hydrationData, children } = opts;
143
- const baseAttributes: Record<string, string> = {
144
- id: islandId,
145
- 'data-framework': detectedFramework,
146
- };
147
-
148
- const hydrationAttributes = shouldSkipHydration
149
- ? { 'data-render-strategy': 'ssr-only' }
150
- : buildHydrateAttributes(src, condition, props, hydrationData);
151
-
152
- if (detectedFramework === 'lit') {
153
- devLog(`🔍 [Island Component] ${src} - Lit hydration data:`, {
154
- hydrationDataKeys: Object.keys(hydrationData),
155
- metadata: hydrationData.metadata,
156
- });
157
- }
158
-
159
- const allAttributes = { ...baseAttributes, ...hydrationAttributes };
160
-
161
- if (typeof children === 'string') {
162
- return h('avalon-island', { ...allAttributes, dangerouslySetInnerHTML: { __html: children } });
163
- }
164
- return h('avalon-island', allAttributes, children);
165
- }
166
-
167
- /** Render the client-only path: empty shell that will be hydrated on the client */
168
- function renderIslandClientOnly(
169
- islandId: string,
170
- detectedFramework: string,
171
- shouldSkipHydration: boolean,
172
- src: string,
173
- condition: HydrationCondition,
174
- props: Record<string, unknown>,
175
- hydrationData: Record<string, unknown>,
176
- ): JSX.Element {
177
- if (shouldSkipHydration) {
178
- return h('avalon-island', {
179
- id: islandId,
180
- 'data-render-strategy': 'ssr-only',
181
- 'data-framework': detectedFramework,
182
- });
183
- }
184
-
185
- return h('avalon-island', {
186
- id: islandId,
187
- 'data-condition': condition,
188
- 'data-src': getIslandBundlePath(src),
189
- 'data-props': JSON.stringify(props),
190
- 'data-render-strategy': 'hydrate',
191
- 'data-framework': detectedFramework,
192
- ...buildHydrationDataAttrs(hydrationData),
193
- });
194
- }
195
-
196
- /**
197
- * Universal Island component – renders `<avalon-island>` custom elements for better DOM structure.
198
- *
199
- * Uses custom elements instead of div wrappers for cleaner, more semantic markup.
200
- * Supports intelligent rendering strategy detection to skip hydration for SSR-only components.
201
- */
202
- export default function Island({
203
- src,
204
- condition = 'on:client',
205
- props = {},
206
- children,
207
- ssr = condition !== 'on:client',
208
- framework,
209
- ssrOnly = false,
210
- renderOptions = {},
211
- hydrationData = {},
212
- }: IslandProps): JSX.Element {
213
- const islandId = toIslandId(src);
214
- const shouldSkipHydration = ssrOnly || !!renderOptions.forceSSROnly;
215
- const detectedFramework = framework || detectFrameworkFromPath(src);
216
- const hasValidChildren = children !== undefined && children !== null && children !== '';
217
-
218
- devLog(`🔍 [Island Component] ${src}`, { ssr, ssrOnly, hasChildren: hasValidChildren, framework, condition });
219
-
220
- if (ssr && hasValidChildren) {
221
- return renderIslandSSR({
222
- islandId,
223
- detectedFramework,
224
- shouldSkipHydration,
225
- src,
226
- condition,
227
- props,
228
- hydrationData,
229
- children,
230
- });
231
- }
232
-
233
- if (ssr && !hasValidChildren && shouldSkipHydration) {
234
- devWarn(`${src}: SSR-only component has no rendered content. This may indicate a rendering error.`);
235
- }
236
-
237
- return renderIslandClientOnly(islandId, detectedFramework, shouldSkipHydration, src, condition, props, hydrationData);
238
- }
239
-
240
- // ---------------------------------------------------------------------------
241
- // renderErrorPlaceholder
242
- // ---------------------------------------------------------------------------
243
-
244
- /**
245
- * Render an error placeholder when island SSR fails.
246
- * @internal
247
- */
248
- function renderErrorPlaceholder(src: string, error: unknown): JSX.Element {
249
- const errorMessage = error instanceof Error ? error.message : String(error);
250
- devError(`🚨 Island SSR failed for ${src}:`, error);
251
- if (error instanceof Error && error.stack) {
252
- devError(`Stack trace:`, error.stack);
253
- }
254
- return h('avalon-island', {
255
- id: toIslandId(src),
256
- 'data-src': getIslandBundlePath(src),
257
- 'data-ssr-error': errorMessage,
258
- 'data-render-strategy': 'client-only',
259
- });
260
- }
261
-
262
- // ---------------------------------------------------------------------------
263
- // renderWithExplicitFramework (fast path)
264
- // ---------------------------------------------------------------------------
265
-
266
- /**
267
- * Render an island using the fast path when framework is explicitly provided.
268
- * @internal
269
- */
270
- async function renderWithExplicitFramework({
271
- src,
272
- condition,
273
- props,
274
- children,
275
- ssr,
276
- framework,
277
- ssrOnly,
278
- renderOptions,
279
- }: {
280
- src: string;
281
- condition: IslandProps['condition'];
282
- props: Record<string, unknown>;
283
- children?: import('preact').ComponentChildren;
284
- ssr: boolean;
285
- framework: NonNullable<IslandProps['framework']>;
286
- ssrOnly: boolean;
287
- renderOptions: AnalyzerOptions;
288
- }): Promise<JSX.Element> {
289
- const logPrefix = `🏝️ [${src}]`;
290
-
291
- if (!ssr || children) {
292
- return Island({ src, condition, props, children, ssr, framework, ssrOnly, renderOptions });
293
- }
294
-
295
- let integration: Integration;
296
- try {
297
- integration = await loadIntegration(framework);
298
- } catch (error) {
299
- devError(`${logPrefix} Failed to load ${framework} integration:`, error);
300
- return Island({ src, condition, props, ssr: false, framework, ssrOnly, renderOptions });
301
- }
302
-
303
- try {
304
- const renderResult = await integration.render({
305
- component: null,
306
- props,
307
- src,
308
- condition,
309
- ssrOnly,
310
- viteServer: globalThis.__viteDevServer,
311
- isDev: isDev(),
312
- });
313
-
314
- collectRenderAssets(renderResult, src, framework, logPrefix);
315
-
316
- return Island({
317
- src,
318
- condition,
319
- props,
320
- children: renderResult.html,
321
- ssr: true,
322
- framework,
323
- ssrOnly,
324
- renderOptions,
325
- hydrationData: ssrOnly ? undefined : renderResult.hydrationData,
326
- });
327
- } catch (error) {
328
- devError(`${logPrefix} Fast path SSR failed:`, error);
329
- return Island({ src, condition, props, ssr: false, framework, ssrOnly, renderOptions });
330
- }
331
- }
332
-
333
- // ---------------------------------------------------------------------------
334
- // renderIsland slow-path helpers
335
- // ---------------------------------------------------------------------------
336
-
337
- /** Determine whether the component should skip hydration via analysis */
338
- async function analyzeHydrationStrategy(
339
- src: string,
340
- ssrOnly: boolean,
341
- renderOptions: AnalyzerOptions,
342
- logPrefix: string,
343
- ): Promise<boolean> {
344
- if (ssrOnly || renderOptions.detectScripts === false) return ssrOnly;
345
-
346
- try {
347
- const analysisResult = await analyzeComponentFile(src, renderOptions);
348
- if (analysisResult.decision.warnings?.length) {
349
- for (const warning of analysisResult.decision.warnings) {
350
- devWarn(`${logPrefix} Analysis warning: ${warning}`);
351
- }
352
- }
353
- return !analysisResult.decision.shouldHydrate;
354
- } catch (error) {
355
- devWarn(`${logPrefix} Component analysis failed:`, error);
356
- return ssrOnly;
357
- }
358
- }
359
-
360
- /** Auto-detect framework from file extension / content */
361
- async function detectFrameworkForSrc(src: string): Promise<string> {
362
- if (src.endsWith('.vue')) return 'vue';
363
- if (src.endsWith('.svelte')) return 'svelte';
364
- if (src.endsWith('.tsx') || src.endsWith('.jsx') || src.endsWith('.ts') || src.endsWith('.js')) {
365
- return detectFramework(src);
366
- }
367
- return 'unknown';
368
- }
369
-
370
- /** Load an integration and render the component, returning the Island element */
371
- async function renderSlowPathSSR(
372
- src: string,
373
- condition: HydrationCondition,
374
- props: Record<string, unknown>,
375
- ssrOnly: boolean,
376
- renderOptions: AnalyzerOptions,
377
- logPrefix: string,
378
- ): Promise<JSX.Element> {
379
- const detectedFramework = await detectFrameworkForSrc(src);
380
- const frameworkId = detectedFramework as FrameworkId;
381
-
382
- const integration = await loadIntegrationOrThrow(detectedFramework, logPrefix);
383
- const renderResult = await integration.render({
384
- component: null,
385
- props,
386
- src,
387
- condition,
388
- ssrOnly,
389
- viteServer: globalThis.__viteDevServer,
390
- isDev: isDev(),
391
- });
392
-
393
- collectRenderAssets(renderResult, src, detectedFramework, logPrefix);
394
-
395
- const result = Island({
396
- src,
397
- condition,
398
- props,
399
- children: renderResult.html,
400
- ssr: true,
401
- framework: frameworkId,
402
- ssrOnly,
403
- renderOptions,
404
- hydrationData: ssrOnly ? undefined : renderResult.hydrationData,
405
- });
406
-
407
- return result;
408
- }
409
-
410
- /** Load an integration, throwing a descriptive error on failure */
411
- async function loadIntegrationOrThrow(framework: string, logPrefix: string): Promise<Integration> {
412
- try {
413
- devLog(`${logPrefix} Loading integration for framework: ${framework}`);
414
- const integration = await loadIntegration(framework);
415
- devLog(`${logPrefix} ✅ Integration loaded successfully`);
416
- return integration;
417
- } catch (error) {
418
- devError(`${logPrefix} Failed to load ${framework} integration:`, error);
419
- throw new Error(
420
- `Failed to load integration for framework '${framework}'. ` +
421
- `Make sure @useavalon/${framework} is installed.\n` +
422
- `Install it with: deno add @useavalon/${framework}`,
423
- { cause: error },
424
- );
425
- }
426
- }
427
-
428
- // ---------------------------------------------------------------------------
429
- // renderIsland – the main async entry point
430
- // ---------------------------------------------------------------------------
431
-
432
- /**
433
- * Universal renderIsland function – auto-detects framework and handles SSR + hydration.
434
- *
435
- * This is the main function you should use – it automatically:
436
- * - Detects the component framework (Vue, Solid.js, Preact/React)
437
- * - Analyzes component for intelligent rendering strategy detection
438
- * - Handles server-side rendering when possible
439
- * - Falls back to client-only rendering when needed
440
- * - Returns the appropriate Island component
441
- *
442
- * Performance tip: Providing an explicit `framework` prop skips component analysis
443
- * and framework detection, significantly improving render performance.
444
- *
445
- * Error isolation: If SSR fails, returns an error placeholder instead of throwing,
446
- * allowing the page to continue rendering other islands.
447
- */
448
- export async function renderIsland({
449
- src,
450
- condition = 'on:client',
451
- props = {},
452
- children,
453
- ssr = condition !== 'on:client',
454
- framework,
455
- ssrOnly = false,
456
- renderOptions = {},
457
- }: IslandProps): Promise<JSX.Element> {
458
- const startTime = isDev() ? performance.now() : 0;
459
- const logPrefix = `🏝️ [${src}]`;
460
-
461
- try {
462
- // If ssrOnly is true we MUST enable SSR to render the component
463
- if (ssrOnly && !ssr) {
464
- ssr = true;
465
- }
466
-
467
- // Fast path: explicit framework skips all analysis/detection
468
- if (framework) {
469
- return await renderWithExplicitFramework({
470
- src,
471
- condition,
472
- props,
473
- children,
474
- ssr,
475
- framework,
476
- ssrOnly,
477
- renderOptions,
478
- });
479
- }
480
-
481
- // Slow path: detect framework and analyze component
482
- return await renderIslandSlowPath({
483
- src,
484
- condition,
485
- props,
486
- children,
487
- ssr,
488
- ssrOnly,
489
- renderOptions,
490
- logPrefix,
491
- });
492
- } catch (error) {
493
- return renderErrorPlaceholder(src, error);
494
- } finally {
495
- if (isDev()) {
496
- logRenderTiming(src, performance.now() - startTime);
497
- }
498
- }
499
- }
500
-
501
- /** Slow path for renderIsland – framework detection + component analysis */
502
- async function renderIslandSlowPath(opts: {
503
- src: string;
504
- condition: HydrationCondition;
505
- props: Record<string, unknown>;
506
- children: import('preact').ComponentChildren | undefined;
507
- ssr: boolean;
508
- ssrOnly: boolean;
509
- renderOptions: AnalyzerOptions;
510
- logPrefix: string;
511
- }): Promise<JSX.Element> {
512
- const { src, condition, props, children, ssr, ssrOnly, renderOptions, logPrefix } = opts;
513
- devLog(`🔍 [renderIsland] ${src} - Starting render (slow path)`, {
514
- ssr,
515
- ssrOnly,
516
- hasChildren: !!children,
517
- condition,
518
- });
519
-
520
- const shouldSkipHydration = await analyzeHydrationStrategy(src, ssrOnly, renderOptions, logPrefix);
521
-
522
- if (shouldSkipHydration) {
523
- return renderSSROnlyPath(src, condition, props, children, ssr, renderOptions, logPrefix);
524
- }
525
-
526
- // If SSR is disabled or we already have children, use basic Island
527
- if (!ssr || children) {
528
- return Island({ src, condition, props, children, ssr, renderOptions });
529
- }
530
-
531
- // Full SSR rendering with auto-detected framework
532
- try {
533
- return await renderSlowPathSSR(src, condition, props, ssrOnly, renderOptions, logPrefix);
534
- } catch (error) {
535
- const detectedFramework = await detectFrameworkForSrc(src);
536
- devError(`${logPrefix} Framework rendering failed:`, error);
537
- return Island({
538
- src,
539
- condition,
540
- props,
541
- ssr: false,
542
- framework: detectedFramework as FrameworkId,
543
- renderOptions,
544
- });
545
- }
546
- }
547
-
548
- /** Handle the SSR-only path when hydration should be skipped */
549
- function renderSSROnlyPath(
550
- src: string,
551
- condition: HydrationCondition,
552
- props: Record<string, unknown>,
553
- children: import('preact').ComponentChildren | undefined,
554
- ssr: boolean,
555
- renderOptions: AnalyzerOptions,
556
- logPrefix: string,
557
- ): Promise<JSX.Element> | JSX.Element {
558
- if (ssr && !children) {
559
- return renderComponentSSROnly({ src, condition, props, renderOptions }).catch(error => {
560
- devError(`${logPrefix} SSR failed for SSR-only component:`, error);
561
- return Island({ src, condition, props, ssr: false, ssrOnly: true, renderOptions });
562
- });
563
- }
564
- return Island({ src, condition, props, children, ssr, ssrOnly: true, renderOptions });
565
- }