@useavalon/avalon 0.1.11 → 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 (141) hide show
  1. package/README.md +54 -54
  2. package/mod.ts +302 -302
  3. package/package.json +49 -26
  4. package/src/build/integration-bundler-plugin.ts +116 -116
  5. package/src/build/integration-config.ts +168 -168
  6. package/src/build/integration-detection-plugin.ts +117 -117
  7. package/src/build/integration-resolver-plugin.ts +90 -90
  8. package/src/build/island-manifest.ts +269 -269
  9. package/src/build/island-types-generator.ts +476 -476
  10. package/src/build/mdx-island-transform.ts +464 -464
  11. package/src/build/mdx-plugin.ts +98 -98
  12. package/src/build/page-island-transform.ts +598 -598
  13. package/src/build/prop-extractors/index.ts +21 -21
  14. package/src/build/prop-extractors/lit.ts +140 -140
  15. package/src/build/prop-extractors/qwik.ts +16 -16
  16. package/src/build/prop-extractors/solid.ts +125 -125
  17. package/src/build/prop-extractors/svelte.ts +194 -194
  18. package/src/build/prop-extractors/vue.ts +111 -111
  19. package/src/build/sidecar-file-manager.ts +104 -104
  20. package/src/build/sidecar-renderer.ts +30 -30
  21. package/src/client/adapters/index.ts +21 -13
  22. package/src/client/components.ts +35 -35
  23. package/src/client/css-hmr-handler.ts +344 -344
  24. package/src/client/framework-adapter.ts +462 -462
  25. package/src/client/hmr-coordinator.ts +396 -396
  26. package/src/client/hmr-error-overlay.js +533 -533
  27. package/src/client/main.js +824 -816
  28. package/src/client/types/framework-runtime.d.ts +68 -68
  29. package/src/client/types/vite-hmr.d.ts +46 -46
  30. package/src/client/types/vite-virtual-modules.d.ts +70 -60
  31. package/src/components/Image.tsx +123 -123
  32. package/src/components/IslandErrorBoundary.tsx +145 -145
  33. package/src/components/LayoutDataErrorBoundary.tsx +141 -141
  34. package/src/components/LayoutErrorBoundary.tsx +127 -127
  35. package/src/components/PersistentIsland.tsx +52 -52
  36. package/src/components/StreamingErrorBoundary.tsx +233 -233
  37. package/src/components/StreamingLayout.tsx +538 -538
  38. package/src/core/components/component-analyzer.ts +192 -192
  39. package/src/core/components/component-detection.ts +508 -508
  40. package/src/core/components/enhanced-framework-detector.ts +500 -500
  41. package/src/core/components/framework-registry.ts +563 -563
  42. package/src/core/content/mdx-processor.ts +46 -46
  43. package/src/core/integrations/index.ts +19 -19
  44. package/src/core/integrations/loader.ts +125 -125
  45. package/src/core/integrations/registry.ts +175 -175
  46. package/src/core/islands/island-persistence.ts +325 -325
  47. package/src/core/islands/island-state-serializer.ts +258 -258
  48. package/src/core/islands/persistent-island-context.tsx +80 -80
  49. package/src/core/islands/use-persistent-state.ts +68 -68
  50. package/src/core/layout/enhanced-layout-resolver.ts +322 -322
  51. package/src/core/layout/layout-cache-manager.ts +485 -485
  52. package/src/core/layout/layout-composer.ts +357 -357
  53. package/src/core/layout/layout-data-loader.ts +516 -516
  54. package/src/core/layout/layout-discovery.ts +243 -243
  55. package/src/core/layout/layout-matcher.ts +299 -299
  56. package/src/core/layout/layout-types.ts +110 -110
  57. package/src/core/modules/framework-module-resolver.ts +273 -273
  58. package/src/islands/component-analysis.ts +213 -213
  59. package/src/islands/css-utils.ts +565 -565
  60. package/src/islands/discovery/index.ts +80 -80
  61. package/src/islands/discovery/registry.ts +340 -340
  62. package/src/islands/discovery/resolver.ts +477 -477
  63. package/src/islands/discovery/scanner.ts +386 -386
  64. package/src/islands/discovery/types.ts +117 -117
  65. package/src/islands/discovery/validator.ts +544 -544
  66. package/src/islands/discovery/watcher.ts +368 -368
  67. package/src/islands/framework-detection.ts +428 -428
  68. package/src/islands/integration-loader.ts +490 -490
  69. package/src/islands/island.tsx +565 -565
  70. package/src/islands/render-cache.ts +550 -550
  71. package/src/islands/types.ts +80 -80
  72. package/src/islands/universal-css-collector.ts +157 -157
  73. package/src/islands/universal-head-collector.ts +137 -137
  74. package/src/layout-system.d.ts +592 -592
  75. package/src/layout-system.ts +218 -218
  76. package/src/middleware/discovery.ts +268 -268
  77. package/src/middleware/executor.ts +315 -315
  78. package/src/middleware/index.ts +76 -76
  79. package/src/middleware/types.ts +99 -99
  80. package/src/nitro/build-config.ts +575 -575
  81. package/src/nitro/config.ts +483 -483
  82. package/src/nitro/error-handler.ts +636 -636
  83. package/src/nitro/index.ts +173 -173
  84. package/src/nitro/island-manifest.ts +584 -584
  85. package/src/nitro/middleware-adapter.ts +260 -260
  86. package/src/nitro/renderer.ts +1471 -1471
  87. package/src/nitro/route-discovery.ts +439 -439
  88. package/src/nitro/types.ts +321 -321
  89. package/src/render/collect-css.ts +198 -198
  90. package/src/render/error-pages.ts +79 -79
  91. package/src/render/isolated-ssr-renderer.ts +654 -654
  92. package/src/render/ssr.ts +1030 -1030
  93. package/src/schemas/api.ts +30 -30
  94. package/src/schemas/core.ts +64 -64
  95. package/src/schemas/index.ts +212 -212
  96. package/src/schemas/layout.ts +279 -279
  97. package/src/schemas/routing/index.ts +38 -38
  98. package/src/schemas/routing.ts +376 -376
  99. package/src/types/as-island.ts +20 -20
  100. package/src/types/image.d.ts +106 -106
  101. package/src/types/index.d.ts +22 -22
  102. package/src/types/island-jsx.d.ts +33 -33
  103. package/src/types/island-prop.d.ts +20 -20
  104. package/src/types/layout.ts +285 -285
  105. package/src/types/mdx.d.ts +6 -6
  106. package/src/types/routing.ts +555 -555
  107. package/src/types/types.ts +5 -5
  108. package/src/types/urlpattern.d.ts +49 -49
  109. package/src/types/vite-env.d.ts +11 -11
  110. package/src/utils/dev-logger.ts +299 -299
  111. package/src/utils/fs.ts +151 -151
  112. package/src/vite-plugin/auto-discover.ts +551 -551
  113. package/src/vite-plugin/config.ts +266 -266
  114. package/src/vite-plugin/errors.ts +127 -127
  115. package/src/vite-plugin/image-optimization.ts +156 -156
  116. package/src/vite-plugin/integration-activator.ts +126 -126
  117. package/src/vite-plugin/island-sidecar-plugin.ts +176 -176
  118. package/src/vite-plugin/module-discovery.ts +189 -189
  119. package/src/vite-plugin/nitro-integration.ts +1354 -1354
  120. package/src/vite-plugin/plugin.ts +403 -409
  121. package/src/vite-plugin/types.ts +327 -327
  122. package/src/vite-plugin/validation.ts +228 -228
  123. package/src/client/adapters/index.js +0 -12
  124. package/src/client/adapters/lit-adapter.js +0 -467
  125. package/src/client/adapters/lit-adapter.ts +0 -654
  126. package/src/client/adapters/preact-adapter.js +0 -223
  127. package/src/client/adapters/preact-adapter.ts +0 -331
  128. package/src/client/adapters/qwik-adapter.js +0 -259
  129. package/src/client/adapters/qwik-adapter.ts +0 -345
  130. package/src/client/adapters/react-adapter.js +0 -220
  131. package/src/client/adapters/react-adapter.ts +0 -353
  132. package/src/client/adapters/solid-adapter.js +0 -295
  133. package/src/client/adapters/solid-adapter.ts +0 -451
  134. package/src/client/adapters/svelte-adapter.js +0 -368
  135. package/src/client/adapters/svelte-adapter.ts +0 -524
  136. package/src/client/adapters/vue-adapter.js +0 -278
  137. package/src/client/adapters/vue-adapter.ts +0 -467
  138. package/src/client/components.js +0 -23
  139. package/src/client/css-hmr-handler.js +0 -263
  140. package/src/client/framework-adapter.js +0 -283
  141. package/src/client/hmr-coordinator.js +0 -274
@@ -1,654 +1,654 @@
1
- /**
2
- * Isolated SSR Renderer
3
- *
4
- * This module provides framework-specific SSR contexts to prevent cross-contamination
5
- * between different frameworks during server-side rendering. Each framework gets its
6
- * own isolated import context and rendering pipeline.
7
- */
8
-
9
- import type { JSX } from 'preact';
10
- import { render as preactRenderToString } from 'preact-render-to-string';
11
- import { readFile } from 'node:fs/promises';
12
- import { EnhancedFrameworkDetector } from '../core/components/enhanced-framework-detector.ts';
13
- import { toImportSpecifier } from '../middleware/executor.ts';
14
-
15
- export interface FrameworkSSRContext {
16
- framework: string;
17
- imports: Map<string, unknown>;
18
- renderFunction: (component: unknown, props: unknown) => Promise<string>;
19
- cleanup: () => void;
20
- isActive: boolean;
21
- }
22
-
23
- export interface SSRIsolationConfig {
24
- enableStrictIsolation: boolean;
25
- allowedCrossFrameworkImports: string[];
26
- errorHandling: 'strict' | 'fallback' | 'ignore';
27
- debugLogging: boolean;
28
- }
29
-
30
- export interface IsolatedRenderRequest {
31
- componentPath: string;
32
- component: () => JSX.Element | Promise<JSX.Element>;
33
- framework?: string;
34
- props?: Record<string, unknown>;
35
- }
36
-
37
- export interface IsolatedRenderResult {
38
- html: string;
39
- framework: string;
40
- success: boolean;
41
- errors: string[];
42
- warnings: string[];
43
- }
44
-
45
- /**
46
- * Isolated SSR Renderer with framework-specific contexts
47
- */
48
- export class IsolatedSSRRenderer {
49
- private readonly contexts: Map<string, FrameworkSSRContext>;
50
- private readonly detector: EnhancedFrameworkDetector;
51
- private config: SSRIsolationConfig;
52
- private activeContext: string | null = null;
53
-
54
- constructor(config: Partial<SSRIsolationConfig> = {}) {
55
- this.contexts = new Map();
56
- this.detector = new EnhancedFrameworkDetector();
57
- this.config = {
58
- enableStrictIsolation: true,
59
- allowedCrossFrameworkImports: ['preact', 'preact-render-to-string'],
60
- errorHandling: 'fallback',
61
- debugLogging: false,
62
- ...config,
63
- };
64
-
65
- this.initializeFrameworkContexts();
66
- }
67
-
68
- /**
69
- * Renders a component with framework isolation
70
- */
71
- async renderWithIsolation(request: IsolatedRenderRequest): Promise<IsolatedRenderResult> {
72
- const errors: string[] = [];
73
- const warnings: string[] = [];
74
-
75
- try {
76
- const framework = await this.resolveFramework(request, warnings);
77
- return await this.renderInContext(request, framework, errors, warnings);
78
- } catch (error) {
79
- errors.push(`SSR rendering failed: ${error instanceof Error ? error.message : String(error)}`);
80
- return await this.tryFallbackRender(request, errors, warnings);
81
- }
82
- }
83
-
84
- private async resolveFramework(request: IsolatedRenderRequest, warnings: string[]): Promise<string> {
85
- if (request.framework) return request.framework;
86
-
87
- try {
88
- const content = await this.getComponentContent(request.componentPath);
89
- const detection = this.detector.detectFramework(request.componentPath, content);
90
-
91
- if (detection.confidence === 'low') {
92
- warnings.push(`Low confidence framework detection for ${request.componentPath}: ${detection.framework}`);
93
- }
94
- if (this.config.debugLogging) {
95
- console.log(`[SSR Isolation] Detected framework: ${detection.framework} for ${request.componentPath}`);
96
- console.log(`[SSR Isolation] Evidence: ${detection.evidence.join(', ')}`);
97
- }
98
- return detection.framework;
99
- } catch {
100
- // Component path is a virtual/route path — can't read file, default to preact
101
- warnings.push(
102
- `Could not read component file for framework detection: ${request.componentPath}, defaulting to preact`,
103
- );
104
- return 'preact';
105
- }
106
- }
107
-
108
- private async renderInContext(
109
- request: IsolatedRenderRequest,
110
- framework: string,
111
- errors: string[],
112
- warnings: string[],
113
- ): Promise<IsolatedRenderResult> {
114
- const context = this.getFrameworkContext(framework);
115
- if (!context) throw new Error(`No SSR context available for framework: ${framework}`);
116
-
117
- await this.switchToContext(framework);
118
- try {
119
- const componentResult = request.component();
120
- const resolvedComponent = componentResult instanceof Promise ? await componentResult : componentResult;
121
- const html = await context.renderFunction(resolvedComponent, request.props || {});
122
- return { html, framework, success: true, errors, warnings };
123
- } finally {
124
- this.cleanupContext(framework);
125
- }
126
- }
127
-
128
- private async tryFallbackRender(
129
- request: IsolatedRenderRequest,
130
- errors: string[],
131
- warnings: string[],
132
- ): Promise<IsolatedRenderResult> {
133
- if (this.config.errorHandling !== 'fallback') {
134
- return { html: '', framework: request.framework || 'unknown', success: false, errors, warnings };
135
- }
136
- try {
137
- const resolvedComponent = await request.component();
138
- const html = preactRenderToString(resolvedComponent);
139
- warnings.push('Fell back to Preact rendering due to framework-specific error');
140
- return { html, framework: 'preact', success: true, errors, warnings };
141
- } catch (fallbackError) {
142
- errors.push(
143
- `Fallback rendering also failed: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`,
144
- );
145
- return { html: '', framework: request.framework || 'unknown', success: false, errors, warnings };
146
- }
147
- }
148
-
149
- /**
150
- * Initializes framework-specific SSR contexts
151
- */
152
- private initializeFrameworkContexts(): void {
153
- // Preact context
154
- this.contexts.set('preact', {
155
- framework: 'preact',
156
- imports: new Map(),
157
- renderFunction: (component: unknown) => {
158
- return Promise.resolve(preactRenderToString(component as JSX.Element));
159
- },
160
- cleanup: () => {
161
- this.clearFrameworkGlobals('preact');
162
- },
163
- isActive: false,
164
- });
165
-
166
- this.contexts.set('solid', {
167
- framework: 'solid',
168
- imports: new Map(),
169
- renderFunction: async (component: unknown) => {
170
- try {
171
- // Import Solid SSR modules in isolation
172
- const solidWeb = (await this.importFrameworkModule('solid-js/web', 'solid')) as Record<string, unknown>;
173
- if (solidWeb && typeof solidWeb.renderToString === 'function') {
174
- return (solidWeb.renderToString as (fn: () => unknown) => string)(() => component);
175
- }
176
- throw new Error('Solid renderToString not available');
177
- } catch (error) {
178
- throw new Error(`Solid SSR failed: ${error instanceof Error ? error.message : String(error)}`);
179
- }
180
- },
181
- cleanup: () => {
182
- this.clearFrameworkGlobals('solid');
183
- },
184
- isActive: false,
185
- });
186
-
187
- this.contexts.set('vue', {
188
- framework: 'vue',
189
- imports: new Map(),
190
- renderFunction: async (component: unknown) => {
191
- try {
192
- // Import Vue SSR modules in isolation
193
- const vueServerRenderer = (await this.importFrameworkModule('vue/server-renderer', 'vue')) as Record<
194
- string,
195
- unknown
196
- >;
197
- if (vueServerRenderer && typeof vueServerRenderer.renderToString === 'function') {
198
- return await (vueServerRenderer.renderToString as (component: unknown) => Promise<string>)(component);
199
- }
200
- throw new Error('Vue renderToString not available');
201
- } catch (error) {
202
- throw new Error(`Vue SSR failed: ${error instanceof Error ? error.message : String(error)}`);
203
- }
204
- },
205
- cleanup: () => {
206
- this.clearFrameworkGlobals('vue');
207
- },
208
- isActive: false,
209
- });
210
-
211
- this.contexts.set('svelte', {
212
- framework: 'svelte',
213
- imports: new Map(),
214
- renderFunction: (component: unknown) => {
215
- try {
216
- // Svelte components have a render method
217
- if (component && typeof component === 'object' && 'render' in component) {
218
- const renderResult = (component as { render: () => { html?: string } }).render();
219
- return Promise.resolve(renderResult.html || '');
220
- }
221
- throw new Error('Svelte component does not have render method');
222
- } catch (error) {
223
- throw new Error(`Svelte SSR failed: ${error instanceof Error ? error.message : String(error)}`);
224
- }
225
- },
226
- cleanup: () => {
227
- this.clearFrameworkGlobals('svelte');
228
- },
229
- isActive: false,
230
- });
231
-
232
- this.contexts.set('unknown', {
233
- framework: 'unknown',
234
- imports: new Map(),
235
- renderFunction: (component: unknown) => {
236
- // Fallback to Preact rendering
237
- return Promise.resolve(preactRenderToString(component as JSX.Element));
238
- },
239
- cleanup: () => {
240
- // No specific cleanup needed
241
- },
242
- isActive: false,
243
- });
244
- }
245
-
246
- /**
247
- * Gets or creates a framework context
248
- */
249
- private getFrameworkContext(framework: string): FrameworkSSRContext | null {
250
- const context = this.contexts.get(framework);
251
- if (context) {
252
- return context;
253
- }
254
-
255
- // If framework not found, use unknown/fallback context
256
- return this.contexts.get('unknown') || null;
257
- }
258
-
259
- /**
260
- * Switches to a specific framework context with isolation
261
- */
262
- private async switchToContext(framework: string): Promise<void> {
263
- // Cleanup previous context if active
264
- if (this.activeContext && this.activeContext !== framework) {
265
- this.cleanupContext(this.activeContext);
266
- }
267
-
268
- const context = this.contexts.get(framework);
269
- if (!context) {
270
- throw new Error(`Framework context not found: ${framework}`);
271
- }
272
-
273
- // Activate the context
274
- context.isActive = true;
275
- this.activeContext = framework;
276
-
277
- // Set up framework-specific environment
278
- await this.setupFrameworkEnvironment(framework);
279
- }
280
-
281
- /**
282
- * Cleans up a framework context
283
- */
284
- private cleanupContext(framework: string): void {
285
- const context = this.contexts.get(framework);
286
- if (!context) {
287
- return;
288
- }
289
-
290
- // Run framework-specific cleanup
291
- context.cleanup();
292
- context.isActive = false;
293
-
294
- // Clear framework-specific imports
295
- context.imports.clear();
296
-
297
- if (this.activeContext === framework) {
298
- this.activeContext = null;
299
- }
300
- }
301
-
302
- /**
303
- * Sets up framework-specific environment
304
- */
305
- private async setupFrameworkEnvironment(framework: string): Promise<void> {
306
- // Framework-specific setup logic
307
- switch (framework) {
308
- case 'solid':
309
- // Ensure Solid-specific globals are available
310
- await this.ensureSolidEnvironment();
311
- break;
312
- case 'vue':
313
- // Ensure Vue-specific globals are available
314
- await this.ensureVueEnvironment();
315
- break;
316
- case 'svelte':
317
- // Ensure Svelte-specific globals are available
318
- this.ensureSvelteEnvironment();
319
- break;
320
- case 'preact':
321
- default:
322
- // Preact is the default, no special setup needed
323
- break;
324
- }
325
- }
326
-
327
- /**
328
- * Imports a framework module with isolation
329
- */
330
- private async importFrameworkModule(modulePath: string, framework: string): Promise<unknown> {
331
- const context = this.contexts.get(framework);
332
- if (!context) {
333
- throw new Error(`No context for framework: ${framework}`);
334
- }
335
-
336
- // Check if module is already imported in this context
337
- if (context.imports.has(modulePath)) {
338
- return context.imports.get(modulePath);
339
- }
340
-
341
- // Validate import is allowed for this framework
342
- if (this.config.enableStrictIsolation && !this.isImportAllowed(modulePath, framework)) {
343
- throw new Error(`Import not allowed in ${framework} context: ${modulePath}`);
344
- }
345
-
346
- try {
347
- // Import the module
348
- const module = await import(/* @vite-ignore */ toImportSpecifier(modulePath));
349
-
350
- // Store in context-specific imports
351
- context.imports.set(modulePath, module);
352
-
353
- if (this.config.debugLogging) {
354
- console.log(`[SSR Isolation] Imported ${modulePath} in ${framework} context`);
355
- }
356
-
357
- return module;
358
- } catch (error) {
359
- throw new Error(
360
- `Failed to import ${modulePath} in ${framework} context: ${
361
- error instanceof Error ? error.message : String(error)
362
- }`,
363
- );
364
- }
365
- }
366
-
367
- /**
368
- * Checks if an import is allowed for a specific framework
369
- */
370
- private isImportAllowed(modulePath: string, framework: string): boolean {
371
- // Always allow framework-specific modules
372
- const frameworkConfig = this.detector.getFrameworkConfigs().get(framework);
373
- if (frameworkConfig) {
374
- const allowedModules = [...frameworkConfig.ssrModules, ...frameworkConfig.hydrationModules];
375
- if (allowedModules.some(allowed => modulePath.startsWith(allowed))) {
376
- return true;
377
- }
378
- }
379
-
380
- // Check globally allowed cross-framework imports
381
- return this.config.allowedCrossFrameworkImports.some(allowed => modulePath.startsWith(allowed));
382
- }
383
-
384
- /**
385
- * Clears framework-specific globals
386
- */
387
- private clearFrameworkGlobals(framework: string): void {
388
- // Clear framework-specific globals to prevent contamination
389
- const globals = globalThis as Record<string, unknown>;
390
- switch (framework) {
391
- case 'solid':
392
- // Clear Solid-specific globals if they exist
393
- if (typeof globalThis !== 'undefined') {
394
- delete globals._$HY;
395
- delete globals.Solid;
396
- }
397
- break;
398
- case 'vue':
399
- // Clear Vue-specific globals if they exist
400
- if (typeof globalThis !== 'undefined') {
401
- delete globals.__VUE__;
402
- delete globals.Vue;
403
- }
404
- break;
405
- case 'svelte':
406
- // Clear Svelte-specific globals if they exist
407
- if (typeof globalThis !== 'undefined') {
408
- delete globals.__SVELTE__;
409
- }
410
- break;
411
- }
412
- }
413
-
414
- /**
415
- * Ensures Solid environment is properly set up
416
- */
417
- private async ensureSolidEnvironment(): Promise<void> {
418
- try {
419
- // Import Solid modules needed for SSR
420
- await this.importFrameworkModule('solid-js/web', 'solid');
421
- } catch (error) {
422
- if (this.config.debugLogging) {
423
- console.warn('[SSR Isolation] Failed to set up Solid environment:', error);
424
- }
425
- }
426
- }
427
-
428
- /**
429
- * Ensures Vue environment is properly set up
430
- */
431
- private async ensureVueEnvironment(): Promise<void> {
432
- try {
433
- // Import Vue modules needed for SSR
434
- await this.importFrameworkModule('vue/server-renderer', 'vue');
435
- } catch (error) {
436
- if (this.config.debugLogging) {
437
- console.warn('[SSR Isolation] Failed to set up Vue environment:', error);
438
- }
439
- }
440
- }
441
-
442
- /**
443
- * Ensures Svelte environment is properly set up
444
- */
445
- private ensureSvelteEnvironment(): void {
446
- try {
447
- // Svelte components are typically pre-compiled, no special setup needed
448
- if (this.config.debugLogging) {
449
- console.log('[SSR Isolation] Svelte environment ready');
450
- }
451
- } catch (error) {
452
- if (this.config.debugLogging) {
453
- console.warn('[SSR Isolation] Failed to set up Svelte environment:', error);
454
- }
455
- }
456
- }
457
-
458
- /**
459
- * Gets component content for framework detection
460
- */
461
- private async getComponentContent(componentPath: string): Promise<string> {
462
- try {
463
- // Try to read the component file
464
- let resolvedPath = componentPath;
465
-
466
- // Handle different path formats
467
- if (componentPath.startsWith('/')) {
468
- resolvedPath = componentPath.substring(1);
469
- }
470
-
471
- // Try multiple path variations
472
- const pathVariations = [
473
- resolvedPath,
474
- `src/islands/${resolvedPath.split('/').pop()}`,
475
- `islands/${resolvedPath.split('/').pop()}`,
476
- `examples/${resolvedPath.split('/').pop()}`,
477
- ];
478
-
479
- for (const pathVariation of pathVariations) {
480
- try {
481
- return await readFile(pathVariation, 'utf-8');
482
- } catch {
483
- // Continue to next path variation
484
- continue;
485
- }
486
- }
487
-
488
- throw new Error(`Component file not found: ${componentPath}`);
489
- } catch (error) {
490
- throw new Error(`Failed to read component content: ${error instanceof Error ? error.message : String(error)}`);
491
- }
492
- }
493
-
494
- /**
495
- * Gets current active context
496
- */
497
- getActiveContext(): string | null {
498
- return this.activeContext;
499
- }
500
-
501
- /**
502
- * Gets all framework contexts
503
- */
504
- getContexts(): Map<string, FrameworkSSRContext> {
505
- return new Map(this.contexts);
506
- }
507
-
508
- /**
509
- * Updates configuration
510
- */
511
- updateConfig(config: Partial<SSRIsolationConfig>): void {
512
- this.config = { ...this.config, ...config };
513
- }
514
-
515
- /**
516
- * Resets all contexts
517
- */
518
- resetAllContexts(): void {
519
- for (const [framework] of this.contexts) {
520
- this.cleanupContext(framework);
521
- }
522
- this.activeContext = null;
523
- }
524
-
525
- /**
526
- * Creates fallback rendering when framework modules are unavailable
527
- */
528
- async renderWithFallback(
529
- component: () => JSX.Element | Promise<JSX.Element>,
530
- preferredFramework: string,
531
- ): Promise<IsolatedRenderResult> {
532
- const errors: string[] = [];
533
- const warnings: string[] = [];
534
-
535
- // Try preferred framework first
536
- try {
537
- const request: IsolatedRenderRequest = {
538
- componentPath: 'fallback-component',
539
- component,
540
- framework: preferredFramework,
541
- };
542
-
543
- const result = await this.renderWithIsolation(request);
544
- if (result.success) {
545
- return result;
546
- }
547
-
548
- errors.push(...result.errors);
549
- warnings.push(...result.warnings);
550
- } catch (error) {
551
- errors.push(
552
- `Preferred framework (${preferredFramework}) failed: ${error instanceof Error ? error.message : String(error)}`,
553
- );
554
- }
555
-
556
- // Try fallback frameworks in order of preference
557
- const fallbackOrder = ['preact', 'unknown'];
558
-
559
- for (const fallbackFramework of fallbackOrder) {
560
- if (fallbackFramework === preferredFramework) {
561
- continue; // Already tried
562
- }
563
-
564
- try {
565
- const request: IsolatedRenderRequest = {
566
- componentPath: 'fallback-component',
567
- component,
568
- framework: fallbackFramework,
569
- };
570
-
571
- const result = await this.renderWithIsolation(request);
572
- if (result.success) {
573
- warnings.push(`Fell back to ${fallbackFramework} rendering from ${preferredFramework}`);
574
- return {
575
- ...result,
576
- warnings: [...warnings, ...result.warnings],
577
- };
578
- }
579
-
580
- errors.push(...result.errors);
581
- } catch (error) {
582
- errors.push(
583
- `Fallback framework (${fallbackFramework}) failed: ${error instanceof Error ? error.message : String(error)}`,
584
- );
585
- }
586
- }
587
-
588
- // All frameworks failed, return error result
589
- return {
590
- html: '',
591
- framework: preferredFramework,
592
- success: false,
593
- errors,
594
- warnings,
595
- };
596
- }
597
-
598
- /**
599
- * Validates that a framework context is properly set up
600
- */
601
- async validateFrameworkContext(framework: string): Promise<boolean> {
602
- const context = this.contexts.get(framework);
603
- if (!context) {
604
- return false;
605
- }
606
-
607
- try {
608
- // Try to set up the framework environment
609
- await this.setupFrameworkEnvironment(framework);
610
- return true;
611
- } catch (error) {
612
- if (this.config.debugLogging) {
613
- console.warn(`[SSR Isolation] Framework context validation failed for ${framework}:`, error);
614
- }
615
- return false;
616
- }
617
- }
618
-
619
- /**
620
- * Gets framework-specific error recovery strategies
621
- */
622
- getErrorRecoveryStrategies(framework: string): string[] {
623
- const strategies: Record<string, string[]> = {
624
- solid: [
625
- 'Ensure solid-js and solid-js/web are installed',
626
- 'Check that Solid components use proper JSX import source: /** @jsxImportSource solid-js */',
627
- 'Verify Solid components export default function',
628
- ],
629
- vue: [
630
- 'Ensure vue and vue/server-renderer are installed',
631
- 'Check that Vue components have proper <template>, <script>, and <style> sections',
632
- 'Verify Vue components are properly compiled for SSR',
633
- ],
634
- svelte: [
635
- 'Ensure svelte is installed and components are compiled',
636
- 'Check that Svelte components export default class or function',
637
- 'Verify Svelte components have proper script and style sections',
638
- ],
639
- preact: [
640
- 'Ensure preact and preact-render-to-string are installed',
641
- 'Check that Preact components use proper JSX import source: /** @jsxImportSource preact */',
642
- 'Verify Preact components export default function',
643
- ],
644
- };
645
-
646
- return (
647
- strategies[framework] || [
648
- 'Check that the framework is properly installed',
649
- 'Verify component syntax is correct for the detected framework',
650
- 'Consider adding explicit framework detection hints',
651
- ]
652
- );
653
- }
654
- }
1
+ /**
2
+ * Isolated SSR Renderer
3
+ *
4
+ * This module provides framework-specific SSR contexts to prevent cross-contamination
5
+ * between different frameworks during server-side rendering. Each framework gets its
6
+ * own isolated import context and rendering pipeline.
7
+ */
8
+
9
+ import type { JSX } from 'preact';
10
+ import { render as preactRenderToString } from 'preact-render-to-string';
11
+ import { readFile } from 'node:fs/promises';
12
+ import { EnhancedFrameworkDetector } from '../core/components/enhanced-framework-detector.ts';
13
+ import { toImportSpecifier } from '../middleware/executor.ts';
14
+
15
+ export interface FrameworkSSRContext {
16
+ framework: string;
17
+ imports: Map<string, unknown>;
18
+ renderFunction: (component: unknown, props: unknown) => Promise<string>;
19
+ cleanup: () => void;
20
+ isActive: boolean;
21
+ }
22
+
23
+ export interface SSRIsolationConfig {
24
+ enableStrictIsolation: boolean;
25
+ allowedCrossFrameworkImports: string[];
26
+ errorHandling: 'strict' | 'fallback' | 'ignore';
27
+ debugLogging: boolean;
28
+ }
29
+
30
+ export interface IsolatedRenderRequest {
31
+ componentPath: string;
32
+ component: () => JSX.Element | Promise<JSX.Element>;
33
+ framework?: string;
34
+ props?: Record<string, unknown>;
35
+ }
36
+
37
+ export interface IsolatedRenderResult {
38
+ html: string;
39
+ framework: string;
40
+ success: boolean;
41
+ errors: string[];
42
+ warnings: string[];
43
+ }
44
+
45
+ /**
46
+ * Isolated SSR Renderer with framework-specific contexts
47
+ */
48
+ export class IsolatedSSRRenderer {
49
+ private readonly contexts: Map<string, FrameworkSSRContext>;
50
+ private readonly detector: EnhancedFrameworkDetector;
51
+ private config: SSRIsolationConfig;
52
+ private activeContext: string | null = null;
53
+
54
+ constructor(config: Partial<SSRIsolationConfig> = {}) {
55
+ this.contexts = new Map();
56
+ this.detector = new EnhancedFrameworkDetector();
57
+ this.config = {
58
+ enableStrictIsolation: true,
59
+ allowedCrossFrameworkImports: ['preact', 'preact-render-to-string'],
60
+ errorHandling: 'fallback',
61
+ debugLogging: false,
62
+ ...config,
63
+ };
64
+
65
+ this.initializeFrameworkContexts();
66
+ }
67
+
68
+ /**
69
+ * Renders a component with framework isolation
70
+ */
71
+ async renderWithIsolation(request: IsolatedRenderRequest): Promise<IsolatedRenderResult> {
72
+ const errors: string[] = [];
73
+ const warnings: string[] = [];
74
+
75
+ try {
76
+ const framework = await this.resolveFramework(request, warnings);
77
+ return await this.renderInContext(request, framework, errors, warnings);
78
+ } catch (error) {
79
+ errors.push(`SSR rendering failed: ${error instanceof Error ? error.message : String(error)}`);
80
+ return await this.tryFallbackRender(request, errors, warnings);
81
+ }
82
+ }
83
+
84
+ private async resolveFramework(request: IsolatedRenderRequest, warnings: string[]): Promise<string> {
85
+ if (request.framework) return request.framework;
86
+
87
+ try {
88
+ const content = await this.getComponentContent(request.componentPath);
89
+ const detection = this.detector.detectFramework(request.componentPath, content);
90
+
91
+ if (detection.confidence === 'low') {
92
+ warnings.push(`Low confidence framework detection for ${request.componentPath}: ${detection.framework}`);
93
+ }
94
+ if (this.config.debugLogging) {
95
+ console.log(`[SSR Isolation] Detected framework: ${detection.framework} for ${request.componentPath}`);
96
+ console.log(`[SSR Isolation] Evidence: ${detection.evidence.join(', ')}`);
97
+ }
98
+ return detection.framework;
99
+ } catch {
100
+ // Component path is a virtual/route path — can't read file, default to preact
101
+ warnings.push(
102
+ `Could not read component file for framework detection: ${request.componentPath}, defaulting to preact`,
103
+ );
104
+ return 'preact';
105
+ }
106
+ }
107
+
108
+ private async renderInContext(
109
+ request: IsolatedRenderRequest,
110
+ framework: string,
111
+ errors: string[],
112
+ warnings: string[],
113
+ ): Promise<IsolatedRenderResult> {
114
+ const context = this.getFrameworkContext(framework);
115
+ if (!context) throw new Error(`No SSR context available for framework: ${framework}`);
116
+
117
+ await this.switchToContext(framework);
118
+ try {
119
+ const componentResult = request.component();
120
+ const resolvedComponent = componentResult instanceof Promise ? await componentResult : componentResult;
121
+ const html = await context.renderFunction(resolvedComponent, request.props || {});
122
+ return { html, framework, success: true, errors, warnings };
123
+ } finally {
124
+ this.cleanupContext(framework);
125
+ }
126
+ }
127
+
128
+ private async tryFallbackRender(
129
+ request: IsolatedRenderRequest,
130
+ errors: string[],
131
+ warnings: string[],
132
+ ): Promise<IsolatedRenderResult> {
133
+ if (this.config.errorHandling !== 'fallback') {
134
+ return { html: '', framework: request.framework || 'unknown', success: false, errors, warnings };
135
+ }
136
+ try {
137
+ const resolvedComponent = await request.component();
138
+ const html = preactRenderToString(resolvedComponent);
139
+ warnings.push('Fell back to Preact rendering due to framework-specific error');
140
+ return { html, framework: 'preact', success: true, errors, warnings };
141
+ } catch (fallbackError) {
142
+ errors.push(
143
+ `Fallback rendering also failed: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`,
144
+ );
145
+ return { html: '', framework: request.framework || 'unknown', success: false, errors, warnings };
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Initializes framework-specific SSR contexts
151
+ */
152
+ private initializeFrameworkContexts(): void {
153
+ // Preact context
154
+ this.contexts.set('preact', {
155
+ framework: 'preact',
156
+ imports: new Map(),
157
+ renderFunction: (component: unknown) => {
158
+ return Promise.resolve(preactRenderToString(component as JSX.Element));
159
+ },
160
+ cleanup: () => {
161
+ this.clearFrameworkGlobals('preact');
162
+ },
163
+ isActive: false,
164
+ });
165
+
166
+ this.contexts.set('solid', {
167
+ framework: 'solid',
168
+ imports: new Map(),
169
+ renderFunction: async (component: unknown) => {
170
+ try {
171
+ // Import Solid SSR modules in isolation
172
+ const solidWeb = (await this.importFrameworkModule('solid-js/web', 'solid')) as Record<string, unknown>;
173
+ if (solidWeb && typeof solidWeb.renderToString === 'function') {
174
+ return (solidWeb.renderToString as (fn: () => unknown) => string)(() => component);
175
+ }
176
+ throw new Error('Solid renderToString not available');
177
+ } catch (error) {
178
+ throw new Error(`Solid SSR failed: ${error instanceof Error ? error.message : String(error)}`);
179
+ }
180
+ },
181
+ cleanup: () => {
182
+ this.clearFrameworkGlobals('solid');
183
+ },
184
+ isActive: false,
185
+ });
186
+
187
+ this.contexts.set('vue', {
188
+ framework: 'vue',
189
+ imports: new Map(),
190
+ renderFunction: async (component: unknown) => {
191
+ try {
192
+ // Import Vue SSR modules in isolation
193
+ const vueServerRenderer = (await this.importFrameworkModule('vue/server-renderer', 'vue')) as Record<
194
+ string,
195
+ unknown
196
+ >;
197
+ if (vueServerRenderer && typeof vueServerRenderer.renderToString === 'function') {
198
+ return await (vueServerRenderer.renderToString as (component: unknown) => Promise<string>)(component);
199
+ }
200
+ throw new Error('Vue renderToString not available');
201
+ } catch (error) {
202
+ throw new Error(`Vue SSR failed: ${error instanceof Error ? error.message : String(error)}`);
203
+ }
204
+ },
205
+ cleanup: () => {
206
+ this.clearFrameworkGlobals('vue');
207
+ },
208
+ isActive: false,
209
+ });
210
+
211
+ this.contexts.set('svelte', {
212
+ framework: 'svelte',
213
+ imports: new Map(),
214
+ renderFunction: (component: unknown) => {
215
+ try {
216
+ // Svelte components have a render method
217
+ if (component && typeof component === 'object' && 'render' in component) {
218
+ const renderResult = (component as { render: () => { html?: string } }).render();
219
+ return Promise.resolve(renderResult.html || '');
220
+ }
221
+ throw new Error('Svelte component does not have render method');
222
+ } catch (error) {
223
+ throw new Error(`Svelte SSR failed: ${error instanceof Error ? error.message : String(error)}`);
224
+ }
225
+ },
226
+ cleanup: () => {
227
+ this.clearFrameworkGlobals('svelte');
228
+ },
229
+ isActive: false,
230
+ });
231
+
232
+ this.contexts.set('unknown', {
233
+ framework: 'unknown',
234
+ imports: new Map(),
235
+ renderFunction: (component: unknown) => {
236
+ // Fallback to Preact rendering
237
+ return Promise.resolve(preactRenderToString(component as JSX.Element));
238
+ },
239
+ cleanup: () => {
240
+ // No specific cleanup needed
241
+ },
242
+ isActive: false,
243
+ });
244
+ }
245
+
246
+ /**
247
+ * Gets or creates a framework context
248
+ */
249
+ private getFrameworkContext(framework: string): FrameworkSSRContext | null {
250
+ const context = this.contexts.get(framework);
251
+ if (context) {
252
+ return context;
253
+ }
254
+
255
+ // If framework not found, use unknown/fallback context
256
+ return this.contexts.get('unknown') || null;
257
+ }
258
+
259
+ /**
260
+ * Switches to a specific framework context with isolation
261
+ */
262
+ private async switchToContext(framework: string): Promise<void> {
263
+ // Cleanup previous context if active
264
+ if (this.activeContext && this.activeContext !== framework) {
265
+ this.cleanupContext(this.activeContext);
266
+ }
267
+
268
+ const context = this.contexts.get(framework);
269
+ if (!context) {
270
+ throw new Error(`Framework context not found: ${framework}`);
271
+ }
272
+
273
+ // Activate the context
274
+ context.isActive = true;
275
+ this.activeContext = framework;
276
+
277
+ // Set up framework-specific environment
278
+ await this.setupFrameworkEnvironment(framework);
279
+ }
280
+
281
+ /**
282
+ * Cleans up a framework context
283
+ */
284
+ private cleanupContext(framework: string): void {
285
+ const context = this.contexts.get(framework);
286
+ if (!context) {
287
+ return;
288
+ }
289
+
290
+ // Run framework-specific cleanup
291
+ context.cleanup();
292
+ context.isActive = false;
293
+
294
+ // Clear framework-specific imports
295
+ context.imports.clear();
296
+
297
+ if (this.activeContext === framework) {
298
+ this.activeContext = null;
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Sets up framework-specific environment
304
+ */
305
+ private async setupFrameworkEnvironment(framework: string): Promise<void> {
306
+ // Framework-specific setup logic
307
+ switch (framework) {
308
+ case 'solid':
309
+ // Ensure Solid-specific globals are available
310
+ await this.ensureSolidEnvironment();
311
+ break;
312
+ case 'vue':
313
+ // Ensure Vue-specific globals are available
314
+ await this.ensureVueEnvironment();
315
+ break;
316
+ case 'svelte':
317
+ // Ensure Svelte-specific globals are available
318
+ this.ensureSvelteEnvironment();
319
+ break;
320
+ case 'preact':
321
+ default:
322
+ // Preact is the default, no special setup needed
323
+ break;
324
+ }
325
+ }
326
+
327
+ /**
328
+ * Imports a framework module with isolation
329
+ */
330
+ private async importFrameworkModule(modulePath: string, framework: string): Promise<unknown> {
331
+ const context = this.contexts.get(framework);
332
+ if (!context) {
333
+ throw new Error(`No context for framework: ${framework}`);
334
+ }
335
+
336
+ // Check if module is already imported in this context
337
+ if (context.imports.has(modulePath)) {
338
+ return context.imports.get(modulePath);
339
+ }
340
+
341
+ // Validate import is allowed for this framework
342
+ if (this.config.enableStrictIsolation && !this.isImportAllowed(modulePath, framework)) {
343
+ throw new Error(`Import not allowed in ${framework} context: ${modulePath}`);
344
+ }
345
+
346
+ try {
347
+ // Import the module
348
+ const module = await import(/* @vite-ignore */ toImportSpecifier(modulePath));
349
+
350
+ // Store in context-specific imports
351
+ context.imports.set(modulePath, module);
352
+
353
+ if (this.config.debugLogging) {
354
+ console.log(`[SSR Isolation] Imported ${modulePath} in ${framework} context`);
355
+ }
356
+
357
+ return module;
358
+ } catch (error) {
359
+ throw new Error(
360
+ `Failed to import ${modulePath} in ${framework} context: ${
361
+ error instanceof Error ? error.message : String(error)
362
+ }`,
363
+ );
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Checks if an import is allowed for a specific framework
369
+ */
370
+ private isImportAllowed(modulePath: string, framework: string): boolean {
371
+ // Always allow framework-specific modules
372
+ const frameworkConfig = this.detector.getFrameworkConfigs().get(framework);
373
+ if (frameworkConfig) {
374
+ const allowedModules = [...frameworkConfig.ssrModules, ...frameworkConfig.hydrationModules];
375
+ if (allowedModules.some(allowed => modulePath.startsWith(allowed))) {
376
+ return true;
377
+ }
378
+ }
379
+
380
+ // Check globally allowed cross-framework imports
381
+ return this.config.allowedCrossFrameworkImports.some(allowed => modulePath.startsWith(allowed));
382
+ }
383
+
384
+ /**
385
+ * Clears framework-specific globals
386
+ */
387
+ private clearFrameworkGlobals(framework: string): void {
388
+ // Clear framework-specific globals to prevent contamination
389
+ const globals = globalThis as Record<string, unknown>;
390
+ switch (framework) {
391
+ case 'solid':
392
+ // Clear Solid-specific globals if they exist
393
+ if (typeof globalThis !== 'undefined') {
394
+ delete globals._$HY;
395
+ delete globals.Solid;
396
+ }
397
+ break;
398
+ case 'vue':
399
+ // Clear Vue-specific globals if they exist
400
+ if (typeof globalThis !== 'undefined') {
401
+ delete globals.__VUE__;
402
+ delete globals.Vue;
403
+ }
404
+ break;
405
+ case 'svelte':
406
+ // Clear Svelte-specific globals if they exist
407
+ if (typeof globalThis !== 'undefined') {
408
+ delete globals.__SVELTE__;
409
+ }
410
+ break;
411
+ }
412
+ }
413
+
414
+ /**
415
+ * Ensures Solid environment is properly set up
416
+ */
417
+ private async ensureSolidEnvironment(): Promise<void> {
418
+ try {
419
+ // Import Solid modules needed for SSR
420
+ await this.importFrameworkModule('solid-js/web', 'solid');
421
+ } catch (error) {
422
+ if (this.config.debugLogging) {
423
+ console.warn('[SSR Isolation] Failed to set up Solid environment:', error);
424
+ }
425
+ }
426
+ }
427
+
428
+ /**
429
+ * Ensures Vue environment is properly set up
430
+ */
431
+ private async ensureVueEnvironment(): Promise<void> {
432
+ try {
433
+ // Import Vue modules needed for SSR
434
+ await this.importFrameworkModule('vue/server-renderer', 'vue');
435
+ } catch (error) {
436
+ if (this.config.debugLogging) {
437
+ console.warn('[SSR Isolation] Failed to set up Vue environment:', error);
438
+ }
439
+ }
440
+ }
441
+
442
+ /**
443
+ * Ensures Svelte environment is properly set up
444
+ */
445
+ private ensureSvelteEnvironment(): void {
446
+ try {
447
+ // Svelte components are typically pre-compiled, no special setup needed
448
+ if (this.config.debugLogging) {
449
+ console.log('[SSR Isolation] Svelte environment ready');
450
+ }
451
+ } catch (error) {
452
+ if (this.config.debugLogging) {
453
+ console.warn('[SSR Isolation] Failed to set up Svelte environment:', error);
454
+ }
455
+ }
456
+ }
457
+
458
+ /**
459
+ * Gets component content for framework detection
460
+ */
461
+ private async getComponentContent(componentPath: string): Promise<string> {
462
+ try {
463
+ // Try to read the component file
464
+ let resolvedPath = componentPath;
465
+
466
+ // Handle different path formats
467
+ if (componentPath.startsWith('/')) {
468
+ resolvedPath = componentPath.substring(1);
469
+ }
470
+
471
+ // Try multiple path variations
472
+ const pathVariations = [
473
+ resolvedPath,
474
+ `src/islands/${resolvedPath.split('/').pop()}`,
475
+ `islands/${resolvedPath.split('/').pop()}`,
476
+ `examples/${resolvedPath.split('/').pop()}`,
477
+ ];
478
+
479
+ for (const pathVariation of pathVariations) {
480
+ try {
481
+ return await readFile(pathVariation, 'utf-8');
482
+ } catch {
483
+ // Continue to next path variation
484
+ continue;
485
+ }
486
+ }
487
+
488
+ throw new Error(`Component file not found: ${componentPath}`);
489
+ } catch (error) {
490
+ throw new Error(`Failed to read component content: ${error instanceof Error ? error.message : String(error)}`);
491
+ }
492
+ }
493
+
494
+ /**
495
+ * Gets current active context
496
+ */
497
+ getActiveContext(): string | null {
498
+ return this.activeContext;
499
+ }
500
+
501
+ /**
502
+ * Gets all framework contexts
503
+ */
504
+ getContexts(): Map<string, FrameworkSSRContext> {
505
+ return new Map(this.contexts);
506
+ }
507
+
508
+ /**
509
+ * Updates configuration
510
+ */
511
+ updateConfig(config: Partial<SSRIsolationConfig>): void {
512
+ this.config = { ...this.config, ...config };
513
+ }
514
+
515
+ /**
516
+ * Resets all contexts
517
+ */
518
+ resetAllContexts(): void {
519
+ for (const [framework] of this.contexts) {
520
+ this.cleanupContext(framework);
521
+ }
522
+ this.activeContext = null;
523
+ }
524
+
525
+ /**
526
+ * Creates fallback rendering when framework modules are unavailable
527
+ */
528
+ async renderWithFallback(
529
+ component: () => JSX.Element | Promise<JSX.Element>,
530
+ preferredFramework: string,
531
+ ): Promise<IsolatedRenderResult> {
532
+ const errors: string[] = [];
533
+ const warnings: string[] = [];
534
+
535
+ // Try preferred framework first
536
+ try {
537
+ const request: IsolatedRenderRequest = {
538
+ componentPath: 'fallback-component',
539
+ component,
540
+ framework: preferredFramework,
541
+ };
542
+
543
+ const result = await this.renderWithIsolation(request);
544
+ if (result.success) {
545
+ return result;
546
+ }
547
+
548
+ errors.push(...result.errors);
549
+ warnings.push(...result.warnings);
550
+ } catch (error) {
551
+ errors.push(
552
+ `Preferred framework (${preferredFramework}) failed: ${error instanceof Error ? error.message : String(error)}`,
553
+ );
554
+ }
555
+
556
+ // Try fallback frameworks in order of preference
557
+ const fallbackOrder = ['preact', 'unknown'];
558
+
559
+ for (const fallbackFramework of fallbackOrder) {
560
+ if (fallbackFramework === preferredFramework) {
561
+ continue; // Already tried
562
+ }
563
+
564
+ try {
565
+ const request: IsolatedRenderRequest = {
566
+ componentPath: 'fallback-component',
567
+ component,
568
+ framework: fallbackFramework,
569
+ };
570
+
571
+ const result = await this.renderWithIsolation(request);
572
+ if (result.success) {
573
+ warnings.push(`Fell back to ${fallbackFramework} rendering from ${preferredFramework}`);
574
+ return {
575
+ ...result,
576
+ warnings: [...warnings, ...result.warnings],
577
+ };
578
+ }
579
+
580
+ errors.push(...result.errors);
581
+ } catch (error) {
582
+ errors.push(
583
+ `Fallback framework (${fallbackFramework}) failed: ${error instanceof Error ? error.message : String(error)}`,
584
+ );
585
+ }
586
+ }
587
+
588
+ // All frameworks failed, return error result
589
+ return {
590
+ html: '',
591
+ framework: preferredFramework,
592
+ success: false,
593
+ errors,
594
+ warnings,
595
+ };
596
+ }
597
+
598
+ /**
599
+ * Validates that a framework context is properly set up
600
+ */
601
+ async validateFrameworkContext(framework: string): Promise<boolean> {
602
+ const context = this.contexts.get(framework);
603
+ if (!context) {
604
+ return false;
605
+ }
606
+
607
+ try {
608
+ // Try to set up the framework environment
609
+ await this.setupFrameworkEnvironment(framework);
610
+ return true;
611
+ } catch (error) {
612
+ if (this.config.debugLogging) {
613
+ console.warn(`[SSR Isolation] Framework context validation failed for ${framework}:`, error);
614
+ }
615
+ return false;
616
+ }
617
+ }
618
+
619
+ /**
620
+ * Gets framework-specific error recovery strategies
621
+ */
622
+ getErrorRecoveryStrategies(framework: string): string[] {
623
+ const strategies: Record<string, string[]> = {
624
+ solid: [
625
+ 'Ensure solid-js and solid-js/web are installed',
626
+ 'Check that Solid components use proper JSX import source: /** @jsxImportSource solid-js */',
627
+ 'Verify Solid components export default function',
628
+ ],
629
+ vue: [
630
+ 'Ensure vue and vue/server-renderer are installed',
631
+ 'Check that Vue components have proper <template>, <script>, and <style> sections',
632
+ 'Verify Vue components are properly compiled for SSR',
633
+ ],
634
+ svelte: [
635
+ 'Ensure svelte is installed and components are compiled',
636
+ 'Check that Svelte components export default class or function',
637
+ 'Verify Svelte components have proper script and style sections',
638
+ ],
639
+ preact: [
640
+ 'Ensure preact and preact-render-to-string are installed',
641
+ 'Check that Preact components use proper JSX import source: /** @jsxImportSource preact */',
642
+ 'Verify Preact components export default function',
643
+ ],
644
+ };
645
+
646
+ return (
647
+ strategies[framework] || [
648
+ 'Check that the framework is properly installed',
649
+ 'Verify component syntax is correct for the detected framework',
650
+ 'Consider adding explicit framework detection hints',
651
+ ]
652
+ );
653
+ }
654
+ }