@useavalon/avalon 0.1.13 → 0.1.15

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/dist/mod.js +1 -0
  2. package/dist/src/build/integration-bundler-plugin.js +1 -0
  3. package/dist/src/build/integration-config.js +1 -0
  4. package/dist/src/build/integration-detection-plugin.js +1 -0
  5. package/dist/src/build/integration-resolver-plugin.js +1 -0
  6. package/dist/src/build/island-manifest.js +1 -0
  7. package/dist/src/build/island-types-generator.js +5 -0
  8. package/dist/src/build/mdx-island-transform.js +2 -0
  9. package/dist/src/build/mdx-plugin.js +1 -0
  10. package/dist/src/build/page-island-transform.js +3 -0
  11. package/dist/src/build/prop-extractors/index.js +1 -0
  12. package/dist/src/build/prop-extractors/lit.js +1 -0
  13. package/dist/src/build/prop-extractors/qwik.js +1 -0
  14. package/dist/src/build/prop-extractors/solid.js +1 -0
  15. package/dist/src/build/prop-extractors/svelte.js +1 -0
  16. package/dist/src/build/prop-extractors/vue.js +1 -0
  17. package/dist/src/build/sidecar-file-manager.js +1 -0
  18. package/dist/src/build/sidecar-renderer.js +6 -0
  19. package/dist/src/client/adapters/index.js +1 -0
  20. package/dist/src/client/components.js +1 -0
  21. package/dist/src/client/css-hmr-handler.js +1 -0
  22. package/dist/src/client/framework-adapter.js +13 -0
  23. package/dist/src/client/hmr-coordinator.js +1 -0
  24. package/dist/src/client/hmr-error-overlay.js +214 -0
  25. package/dist/src/client/main.js +39 -0
  26. package/dist/src/components/Image.js +1 -0
  27. package/dist/src/components/IslandErrorBoundary.js +1 -0
  28. package/dist/src/components/LayoutDataErrorBoundary.js +1 -0
  29. package/dist/src/components/LayoutErrorBoundary.js +1 -0
  30. package/dist/src/components/PersistentIsland.js +1 -0
  31. package/dist/src/components/StreamingErrorBoundary.js +1 -0
  32. package/dist/src/components/StreamingLayout.js +29 -0
  33. package/dist/src/core/components/component-analyzer.js +1 -0
  34. package/dist/src/core/components/component-detection.js +5 -0
  35. package/dist/src/core/components/enhanced-framework-detector.js +1 -0
  36. package/dist/src/core/components/framework-registry.js +1 -0
  37. package/dist/src/core/content/mdx-processor.js +1 -0
  38. package/dist/src/core/integrations/index.js +1 -0
  39. package/dist/src/core/integrations/loader.js +1 -0
  40. package/dist/src/core/integrations/registry.js +1 -0
  41. package/dist/src/core/islands/island-persistence.js +1 -0
  42. package/dist/src/core/islands/island-state-serializer.js +1 -0
  43. package/dist/src/core/islands/persistent-island-context.js +1 -0
  44. package/dist/src/core/islands/use-persistent-state.js +1 -0
  45. package/dist/src/core/layout/enhanced-layout-resolver.js +1 -0
  46. package/dist/src/core/layout/layout-cache-manager.js +1 -0
  47. package/dist/src/core/layout/layout-composer.js +1 -0
  48. package/dist/src/core/layout/layout-data-loader.js +1 -0
  49. package/dist/src/core/layout/layout-discovery.js +1 -0
  50. package/dist/src/core/layout/layout-matcher.js +1 -0
  51. package/dist/src/core/layout/layout-types.js +1 -0
  52. package/dist/src/core/modules/framework-module-resolver.js +1 -0
  53. package/dist/src/islands/component-analysis.js +1 -0
  54. package/dist/src/islands/css-utils.js +17 -0
  55. package/dist/src/islands/discovery/index.js +1 -0
  56. package/dist/src/islands/discovery/registry.js +1 -0
  57. package/dist/src/islands/discovery/resolver.js +2 -0
  58. package/dist/src/islands/discovery/scanner.js +1 -0
  59. package/dist/src/islands/discovery/types.js +1 -0
  60. package/dist/src/islands/discovery/validator.js +18 -0
  61. package/dist/src/islands/discovery/watcher.js +1 -0
  62. package/dist/src/islands/framework-detection.js +1 -0
  63. package/dist/src/islands/integration-loader.js +1 -0
  64. package/dist/src/islands/island.js +1 -0
  65. package/dist/src/islands/render-cache.js +1 -0
  66. package/dist/src/islands/types.js +1 -0
  67. package/dist/src/islands/universal-css-collector.js +5 -0
  68. package/dist/src/islands/universal-head-collector.js +2 -0
  69. package/dist/src/layout-system.js +1 -0
  70. package/dist/src/middleware/discovery.js +1 -0
  71. package/dist/src/middleware/executor.js +1 -0
  72. package/dist/src/middleware/index.js +1 -0
  73. package/dist/src/middleware/types.js +1 -0
  74. package/dist/src/nitro/build-config.js +1 -0
  75. package/dist/src/nitro/config.js +1 -0
  76. package/dist/src/nitro/error-handler.js +198 -0
  77. package/dist/src/nitro/index.js +1 -0
  78. package/dist/src/nitro/island-manifest.js +2 -0
  79. package/dist/src/nitro/middleware-adapter.js +1 -0
  80. package/dist/src/nitro/renderer.js +183 -0
  81. package/dist/src/nitro/route-discovery.js +1 -0
  82. package/dist/src/nitro/types.js +1 -0
  83. package/dist/src/render/collect-css.js +3 -0
  84. package/dist/src/render/error-pages.js +48 -0
  85. package/dist/src/render/isolated-ssr-renderer.js +1 -0
  86. package/dist/src/render/ssr.js +90 -0
  87. package/dist/src/schemas/api.js +1 -0
  88. package/dist/src/schemas/core.js +1 -0
  89. package/dist/src/schemas/index.js +1 -0
  90. package/dist/src/schemas/layout.js +1 -0
  91. package/dist/src/schemas/routing/index.js +1 -0
  92. package/dist/src/schemas/routing.js +1 -0
  93. package/dist/src/types/as-island.js +1 -0
  94. package/dist/src/types/layout.js +1 -0
  95. package/dist/src/types/routing.js +1 -0
  96. package/dist/src/types/types.js +1 -0
  97. package/dist/src/utils/dev-logger.js +12 -0
  98. package/dist/src/utils/fs.js +1 -0
  99. package/dist/src/vite-plugin/auto-discover.js +1 -0
  100. package/dist/src/vite-plugin/config.js +1 -0
  101. package/dist/src/vite-plugin/errors.js +1 -0
  102. package/dist/src/vite-plugin/image-optimization.js +45 -0
  103. package/dist/src/vite-plugin/integration-activator.js +1 -0
  104. package/dist/src/vite-plugin/island-sidecar-plugin.js +1 -0
  105. package/dist/src/vite-plugin/module-discovery.js +1 -0
  106. package/dist/src/vite-plugin/nitro-integration.js +42 -0
  107. package/dist/src/vite-plugin/plugin.js +1 -0
  108. package/dist/src/vite-plugin/types.js +1 -0
  109. package/dist/src/vite-plugin/validation.js +2 -0
  110. package/package.json +14 -20
  111. package/mod.ts +0 -302
  112. package/src/build/integration-bundler-plugin.ts +0 -116
  113. package/src/build/integration-config.ts +0 -168
  114. package/src/build/integration-detection-plugin.ts +0 -117
  115. package/src/build/integration-resolver-plugin.ts +0 -90
  116. package/src/build/island-manifest.ts +0 -269
  117. package/src/build/island-types-generator.ts +0 -476
  118. package/src/build/mdx-island-transform.ts +0 -464
  119. package/src/build/mdx-plugin.ts +0 -98
  120. package/src/build/page-island-transform.ts +0 -598
  121. package/src/build/prop-extractors/index.ts +0 -21
  122. package/src/build/prop-extractors/lit.ts +0 -140
  123. package/src/build/prop-extractors/qwik.ts +0 -16
  124. package/src/build/prop-extractors/solid.ts +0 -125
  125. package/src/build/prop-extractors/svelte.ts +0 -194
  126. package/src/build/prop-extractors/vue.ts +0 -111
  127. package/src/build/sidecar-file-manager.ts +0 -104
  128. package/src/build/sidecar-renderer.ts +0 -30
  129. package/src/client/adapters/index.ts +0 -21
  130. package/src/client/components.ts +0 -35
  131. package/src/client/css-hmr-handler.ts +0 -344
  132. package/src/client/framework-adapter.ts +0 -462
  133. package/src/client/hmr-coordinator.ts +0 -396
  134. package/src/client/hmr-error-overlay.js +0 -533
  135. package/src/client/main.js +0 -824
  136. package/src/components/Image.tsx +0 -123
  137. package/src/components/IslandErrorBoundary.tsx +0 -145
  138. package/src/components/LayoutDataErrorBoundary.tsx +0 -141
  139. package/src/components/LayoutErrorBoundary.tsx +0 -127
  140. package/src/components/PersistentIsland.tsx +0 -52
  141. package/src/components/StreamingErrorBoundary.tsx +0 -233
  142. package/src/components/StreamingLayout.tsx +0 -538
  143. package/src/core/components/component-analyzer.ts +0 -192
  144. package/src/core/components/component-detection.ts +0 -508
  145. package/src/core/components/enhanced-framework-detector.ts +0 -500
  146. package/src/core/components/framework-registry.ts +0 -563
  147. package/src/core/content/mdx-processor.ts +0 -46
  148. package/src/core/integrations/index.ts +0 -19
  149. package/src/core/integrations/loader.ts +0 -125
  150. package/src/core/integrations/registry.ts +0 -175
  151. package/src/core/islands/island-persistence.ts +0 -325
  152. package/src/core/islands/island-state-serializer.ts +0 -258
  153. package/src/core/islands/persistent-island-context.tsx +0 -80
  154. package/src/core/islands/use-persistent-state.ts +0 -68
  155. package/src/core/layout/enhanced-layout-resolver.ts +0 -322
  156. package/src/core/layout/layout-cache-manager.ts +0 -485
  157. package/src/core/layout/layout-composer.ts +0 -357
  158. package/src/core/layout/layout-data-loader.ts +0 -516
  159. package/src/core/layout/layout-discovery.ts +0 -243
  160. package/src/core/layout/layout-matcher.ts +0 -299
  161. package/src/core/layout/layout-types.ts +0 -110
  162. package/src/core/modules/framework-module-resolver.ts +0 -273
  163. package/src/islands/component-analysis.ts +0 -213
  164. package/src/islands/css-utils.ts +0 -565
  165. package/src/islands/discovery/index.ts +0 -80
  166. package/src/islands/discovery/registry.ts +0 -340
  167. package/src/islands/discovery/resolver.ts +0 -477
  168. package/src/islands/discovery/scanner.ts +0 -386
  169. package/src/islands/discovery/types.ts +0 -117
  170. package/src/islands/discovery/validator.ts +0 -544
  171. package/src/islands/discovery/watcher.ts +0 -368
  172. package/src/islands/framework-detection.ts +0 -428
  173. package/src/islands/integration-loader.ts +0 -490
  174. package/src/islands/island.tsx +0 -565
  175. package/src/islands/render-cache.ts +0 -550
  176. package/src/islands/types.ts +0 -80
  177. package/src/islands/universal-css-collector.ts +0 -157
  178. package/src/islands/universal-head-collector.ts +0 -137
  179. package/src/layout-system.ts +0 -218
  180. package/src/middleware/discovery.ts +0 -268
  181. package/src/middleware/executor.ts +0 -315
  182. package/src/middleware/index.ts +0 -76
  183. package/src/middleware/types.ts +0 -99
  184. package/src/nitro/build-config.ts +0 -576
  185. package/src/nitro/config.ts +0 -483
  186. package/src/nitro/error-handler.ts +0 -636
  187. package/src/nitro/index.ts +0 -173
  188. package/src/nitro/island-manifest.ts +0 -584
  189. package/src/nitro/middleware-adapter.ts +0 -260
  190. package/src/nitro/renderer.ts +0 -1471
  191. package/src/nitro/route-discovery.ts +0 -439
  192. package/src/nitro/types.ts +0 -321
  193. package/src/render/collect-css.ts +0 -198
  194. package/src/render/error-pages.ts +0 -79
  195. package/src/render/isolated-ssr-renderer.ts +0 -654
  196. package/src/render/ssr.ts +0 -1030
  197. package/src/schemas/api.ts +0 -30
  198. package/src/schemas/core.ts +0 -64
  199. package/src/schemas/index.ts +0 -212
  200. package/src/schemas/layout.ts +0 -279
  201. package/src/schemas/routing/index.ts +0 -38
  202. package/src/schemas/routing.ts +0 -376
  203. package/src/types/as-island.ts +0 -20
  204. package/src/types/layout.ts +0 -285
  205. package/src/types/routing.ts +0 -555
  206. package/src/types/types.ts +0 -5
  207. package/src/utils/dev-logger.ts +0 -299
  208. package/src/utils/fs.ts +0 -151
  209. package/src/vite-plugin/auto-discover.ts +0 -551
  210. package/src/vite-plugin/config.ts +0 -266
  211. package/src/vite-plugin/errors.ts +0 -127
  212. package/src/vite-plugin/image-optimization.ts +0 -156
  213. package/src/vite-plugin/integration-activator.ts +0 -126
  214. package/src/vite-plugin/island-sidecar-plugin.ts +0 -176
  215. package/src/vite-plugin/module-discovery.ts +0 -189
  216. package/src/vite-plugin/nitro-integration.ts +0 -1354
  217. package/src/vite-plugin/plugin.ts +0 -403
  218. package/src/vite-plugin/types.ts +0 -327
  219. package/src/vite-plugin/validation.ts +0 -228
  220. /package/{src → dist/src}/client/types/framework-runtime.d.ts +0 -0
  221. /package/{src → dist/src}/client/types/vite-hmr.d.ts +0 -0
  222. /package/{src → dist/src}/client/types/vite-virtual-modules.d.ts +0 -0
  223. /package/{src → dist/src}/layout-system.d.ts +0 -0
  224. /package/{src → dist/src}/types/image.d.ts +0 -0
  225. /package/{src → dist/src}/types/index.d.ts +0 -0
  226. /package/{src → dist/src}/types/island-jsx.d.ts +0 -0
  227. /package/{src → dist/src}/types/island-prop.d.ts +0 -0
  228. /package/{src → dist/src}/types/mdx.d.ts +0 -0
  229. /package/{src → dist/src}/types/urlpattern.d.ts +0 -0
  230. /package/{src → dist/src}/types/vite-env.d.ts +0 -0
@@ -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
- }