@useavalon/avalon 0.1.12 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/mod.ts +302 -0
  2. package/package.json +9 -17
  3. package/src/build/integration-bundler-plugin.ts +116 -0
  4. package/src/build/integration-config.ts +168 -0
  5. package/src/build/integration-detection-plugin.ts +117 -0
  6. package/src/build/integration-resolver-plugin.ts +90 -0
  7. package/src/build/island-manifest.ts +269 -0
  8. package/src/build/island-types-generator.ts +476 -0
  9. package/src/build/mdx-island-transform.ts +464 -0
  10. package/src/build/mdx-plugin.ts +98 -0
  11. package/src/build/page-island-transform.ts +598 -0
  12. package/src/build/prop-extractors/index.ts +21 -0
  13. package/src/build/prop-extractors/lit.ts +140 -0
  14. package/src/build/prop-extractors/qwik.ts +16 -0
  15. package/src/build/prop-extractors/solid.ts +125 -0
  16. package/src/build/prop-extractors/svelte.ts +194 -0
  17. package/src/build/prop-extractors/vue.ts +111 -0
  18. package/src/build/sidecar-file-manager.ts +104 -0
  19. package/src/build/sidecar-renderer.ts +30 -0
  20. package/src/client/adapters/index.ts +21 -0
  21. package/src/client/components.ts +35 -0
  22. package/src/client/css-hmr-handler.ts +344 -0
  23. package/src/client/framework-adapter.ts +462 -0
  24. package/src/client/hmr-coordinator.ts +396 -0
  25. package/src/client/hmr-error-overlay.js +533 -0
  26. package/src/client/main.js +824 -0
  27. package/src/components/Image.tsx +123 -0
  28. package/src/components/IslandErrorBoundary.tsx +145 -0
  29. package/src/components/LayoutDataErrorBoundary.tsx +141 -0
  30. package/src/components/LayoutErrorBoundary.tsx +127 -0
  31. package/src/components/PersistentIsland.tsx +52 -0
  32. package/src/components/StreamingErrorBoundary.tsx +233 -0
  33. package/src/components/StreamingLayout.tsx +538 -0
  34. package/src/core/components/component-analyzer.ts +192 -0
  35. package/src/core/components/component-detection.ts +508 -0
  36. package/src/core/components/enhanced-framework-detector.ts +500 -0
  37. package/src/core/components/framework-registry.ts +563 -0
  38. package/src/core/content/mdx-processor.ts +46 -0
  39. package/src/core/integrations/index.ts +19 -0
  40. package/src/core/integrations/loader.ts +125 -0
  41. package/src/core/integrations/registry.ts +175 -0
  42. package/src/core/islands/island-persistence.ts +325 -0
  43. package/src/core/islands/island-state-serializer.ts +258 -0
  44. package/src/core/islands/persistent-island-context.tsx +80 -0
  45. package/src/core/islands/use-persistent-state.ts +68 -0
  46. package/src/core/layout/enhanced-layout-resolver.ts +322 -0
  47. package/src/core/layout/layout-cache-manager.ts +485 -0
  48. package/src/core/layout/layout-composer.ts +357 -0
  49. package/src/core/layout/layout-data-loader.ts +516 -0
  50. package/src/core/layout/layout-discovery.ts +243 -0
  51. package/src/core/layout/layout-matcher.ts +299 -0
  52. package/src/core/layout/layout-types.ts +110 -0
  53. package/src/core/modules/framework-module-resolver.ts +273 -0
  54. package/src/islands/component-analysis.ts +213 -0
  55. package/src/islands/css-utils.ts +565 -0
  56. package/src/islands/discovery/index.ts +80 -0
  57. package/src/islands/discovery/registry.ts +340 -0
  58. package/src/islands/discovery/resolver.ts +477 -0
  59. package/src/islands/discovery/scanner.ts +386 -0
  60. package/src/islands/discovery/types.ts +117 -0
  61. package/src/islands/discovery/validator.ts +544 -0
  62. package/src/islands/discovery/watcher.ts +368 -0
  63. package/src/islands/framework-detection.ts +428 -0
  64. package/src/islands/integration-loader.ts +490 -0
  65. package/src/islands/island.tsx +565 -0
  66. package/src/islands/render-cache.ts +550 -0
  67. package/src/islands/types.ts +80 -0
  68. package/src/islands/universal-css-collector.ts +157 -0
  69. package/src/islands/universal-head-collector.ts +137 -0
  70. package/src/layout-system.ts +218 -0
  71. package/src/middleware/discovery.ts +268 -0
  72. package/src/middleware/executor.ts +315 -0
  73. package/src/middleware/index.ts +76 -0
  74. package/src/middleware/types.ts +99 -0
  75. package/src/nitro/build-config.ts +576 -0
  76. package/src/nitro/config.ts +483 -0
  77. package/src/nitro/error-handler.ts +636 -0
  78. package/src/nitro/index.ts +173 -0
  79. package/src/nitro/island-manifest.ts +584 -0
  80. package/src/nitro/middleware-adapter.ts +260 -0
  81. package/src/nitro/renderer.ts +1471 -0
  82. package/src/nitro/route-discovery.ts +439 -0
  83. package/src/nitro/types.ts +321 -0
  84. package/src/render/collect-css.ts +198 -0
  85. package/src/render/error-pages.ts +79 -0
  86. package/src/render/isolated-ssr-renderer.ts +654 -0
  87. package/src/render/ssr.ts +1030 -0
  88. package/src/schemas/api.ts +30 -0
  89. package/src/schemas/core.ts +64 -0
  90. package/src/schemas/index.ts +212 -0
  91. package/src/schemas/layout.ts +279 -0
  92. package/src/schemas/routing/index.ts +38 -0
  93. package/src/schemas/routing.ts +376 -0
  94. package/src/types/as-island.ts +20 -0
  95. package/src/types/layout.ts +285 -0
  96. package/src/types/routing.ts +555 -0
  97. package/src/types/types.ts +5 -0
  98. package/src/utils/dev-logger.ts +299 -0
  99. package/src/utils/fs.ts +151 -0
  100. package/src/vite-plugin/auto-discover.ts +551 -0
  101. package/src/vite-plugin/config.ts +266 -0
  102. package/src/vite-plugin/errors.ts +127 -0
  103. package/src/vite-plugin/image-optimization.ts +156 -0
  104. package/src/vite-plugin/integration-activator.ts +126 -0
  105. package/src/vite-plugin/island-sidecar-plugin.ts +176 -0
  106. package/src/vite-plugin/module-discovery.ts +189 -0
  107. package/src/vite-plugin/nitro-integration.ts +1354 -0
  108. package/src/vite-plugin/plugin.ts +403 -0
  109. package/src/vite-plugin/types.ts +327 -0
  110. package/src/vite-plugin/validation.ts +228 -0
  111. package/dist/mod.js +0 -1
  112. package/dist/src/build/integration-bundler-plugin.js +0 -1
  113. package/dist/src/build/integration-config.js +0 -1
  114. package/dist/src/build/integration-detection-plugin.js +0 -1
  115. package/dist/src/build/integration-resolver-plugin.js +0 -1
  116. package/dist/src/build/island-manifest.js +0 -1
  117. package/dist/src/build/island-types-generator.js +0 -5
  118. package/dist/src/build/mdx-island-transform.js +0 -2
  119. package/dist/src/build/mdx-plugin.js +0 -1
  120. package/dist/src/build/page-island-transform.js +0 -3
  121. package/dist/src/build/prop-extractors/index.js +0 -1
  122. package/dist/src/build/prop-extractors/lit.js +0 -1
  123. package/dist/src/build/prop-extractors/qwik.js +0 -1
  124. package/dist/src/build/prop-extractors/solid.js +0 -1
  125. package/dist/src/build/prop-extractors/svelte.js +0 -1
  126. package/dist/src/build/prop-extractors/vue.js +0 -1
  127. package/dist/src/build/sidecar-file-manager.js +0 -1
  128. package/dist/src/build/sidecar-renderer.js +0 -6
  129. package/dist/src/client/adapters/index.js +0 -1
  130. package/dist/src/client/components.js +0 -1
  131. package/dist/src/client/css-hmr-handler.js +0 -1
  132. package/dist/src/client/framework-adapter.js +0 -13
  133. package/dist/src/client/hmr-coordinator.js +0 -1
  134. package/dist/src/client/hmr-error-overlay.js +0 -214
  135. package/dist/src/client/main.js +0 -39
  136. package/dist/src/components/Image.js +0 -1
  137. package/dist/src/components/IslandErrorBoundary.js +0 -1
  138. package/dist/src/components/LayoutDataErrorBoundary.js +0 -1
  139. package/dist/src/components/LayoutErrorBoundary.js +0 -1
  140. package/dist/src/components/PersistentIsland.js +0 -1
  141. package/dist/src/components/StreamingErrorBoundary.js +0 -1
  142. package/dist/src/components/StreamingLayout.js +0 -29
  143. package/dist/src/core/components/component-analyzer.js +0 -1
  144. package/dist/src/core/components/component-detection.js +0 -5
  145. package/dist/src/core/components/enhanced-framework-detector.js +0 -1
  146. package/dist/src/core/components/framework-registry.js +0 -1
  147. package/dist/src/core/content/mdx-processor.js +0 -1
  148. package/dist/src/core/integrations/index.js +0 -1
  149. package/dist/src/core/integrations/loader.js +0 -1
  150. package/dist/src/core/integrations/registry.js +0 -1
  151. package/dist/src/core/islands/island-persistence.js +0 -1
  152. package/dist/src/core/islands/island-state-serializer.js +0 -1
  153. package/dist/src/core/islands/persistent-island-context.js +0 -1
  154. package/dist/src/core/islands/use-persistent-state.js +0 -1
  155. package/dist/src/core/layout/enhanced-layout-resolver.js +0 -1
  156. package/dist/src/core/layout/layout-cache-manager.js +0 -1
  157. package/dist/src/core/layout/layout-composer.js +0 -1
  158. package/dist/src/core/layout/layout-data-loader.js +0 -1
  159. package/dist/src/core/layout/layout-discovery.js +0 -1
  160. package/dist/src/core/layout/layout-matcher.js +0 -1
  161. package/dist/src/core/layout/layout-types.js +0 -1
  162. package/dist/src/core/modules/framework-module-resolver.js +0 -1
  163. package/dist/src/islands/component-analysis.js +0 -1
  164. package/dist/src/islands/css-utils.js +0 -17
  165. package/dist/src/islands/discovery/index.js +0 -1
  166. package/dist/src/islands/discovery/registry.js +0 -1
  167. package/dist/src/islands/discovery/resolver.js +0 -2
  168. package/dist/src/islands/discovery/scanner.js +0 -1
  169. package/dist/src/islands/discovery/types.js +0 -1
  170. package/dist/src/islands/discovery/validator.js +0 -18
  171. package/dist/src/islands/discovery/watcher.js +0 -1
  172. package/dist/src/islands/framework-detection.js +0 -1
  173. package/dist/src/islands/integration-loader.js +0 -1
  174. package/dist/src/islands/island.js +0 -1
  175. package/dist/src/islands/render-cache.js +0 -1
  176. package/dist/src/islands/types.js +0 -1
  177. package/dist/src/islands/universal-css-collector.js +0 -5
  178. package/dist/src/islands/universal-head-collector.js +0 -2
  179. package/dist/src/layout-system.js +0 -1
  180. package/dist/src/middleware/discovery.js +0 -1
  181. package/dist/src/middleware/executor.js +0 -1
  182. package/dist/src/middleware/index.js +0 -1
  183. package/dist/src/middleware/types.js +0 -1
  184. package/dist/src/nitro/build-config.js +0 -1
  185. package/dist/src/nitro/config.js +0 -1
  186. package/dist/src/nitro/error-handler.js +0 -198
  187. package/dist/src/nitro/index.js +0 -1
  188. package/dist/src/nitro/island-manifest.js +0 -2
  189. package/dist/src/nitro/middleware-adapter.js +0 -1
  190. package/dist/src/nitro/renderer.js +0 -183
  191. package/dist/src/nitro/route-discovery.js +0 -1
  192. package/dist/src/nitro/types.js +0 -1
  193. package/dist/src/render/collect-css.js +0 -3
  194. package/dist/src/render/error-pages.js +0 -48
  195. package/dist/src/render/isolated-ssr-renderer.js +0 -1
  196. package/dist/src/render/ssr.js +0 -90
  197. package/dist/src/schemas/api.js +0 -1
  198. package/dist/src/schemas/core.js +0 -1
  199. package/dist/src/schemas/index.js +0 -1
  200. package/dist/src/schemas/layout.js +0 -1
  201. package/dist/src/schemas/routing/index.js +0 -1
  202. package/dist/src/schemas/routing.js +0 -1
  203. package/dist/src/types/as-island.js +0 -1
  204. package/dist/src/types/layout.js +0 -1
  205. package/dist/src/types/routing.js +0 -1
  206. package/dist/src/types/types.js +0 -1
  207. package/dist/src/utils/dev-logger.js +0 -12
  208. package/dist/src/utils/fs.js +0 -1
  209. package/dist/src/vite-plugin/auto-discover.js +0 -1
  210. package/dist/src/vite-plugin/config.js +0 -1
  211. package/dist/src/vite-plugin/errors.js +0 -1
  212. package/dist/src/vite-plugin/image-optimization.js +0 -45
  213. package/dist/src/vite-plugin/integration-activator.js +0 -1
  214. package/dist/src/vite-plugin/island-sidecar-plugin.js +0 -1
  215. package/dist/src/vite-plugin/module-discovery.js +0 -1
  216. package/dist/src/vite-plugin/nitro-integration.js +0 -42
  217. package/dist/src/vite-plugin/plugin.js +0 -1
  218. package/dist/src/vite-plugin/types.js +0 -1
  219. package/dist/src/vite-plugin/validation.js +0 -2
  220. /package/{dist/src → src}/client/types/framework-runtime.d.ts +0 -0
  221. /package/{dist/src → src}/client/types/vite-hmr.d.ts +0 -0
  222. /package/{dist/src → src}/client/types/vite-virtual-modules.d.ts +0 -0
  223. /package/{dist/src → src}/layout-system.d.ts +0 -0
  224. /package/{dist/src → src}/types/image.d.ts +0 -0
  225. /package/{dist/src → src}/types/index.d.ts +0 -0
  226. /package/{dist/src → src}/types/island-jsx.d.ts +0 -0
  227. /package/{dist/src → src}/types/island-prop.d.ts +0 -0
  228. /package/{dist/src → src}/types/mdx.d.ts +0 -0
  229. /package/{dist/src → src}/types/urlpattern.d.ts +0 -0
  230. /package/{dist/src → src}/types/vite-env.d.ts +0 -0
@@ -0,0 +1,321 @@
1
+ /**
2
+ * Nitro Types and Interfaces for Avalon
3
+ *
4
+ * This module defines the core types used throughout the Nitro integration,
5
+ * including render context, runtime configuration, and route definitions.
6
+ *
7
+ * In Nitro v3, canonical types should be imported from `nitro/types`.
8
+ * Avalon-specific types that extend or complement Nitro's types are defined here.
9
+ */
10
+
11
+ import type { MiddlewareContext } from "../nitro/middleware-adapter.ts";
12
+ import type { ApiMethod } from "../schemas/api.ts";
13
+
14
+ /**
15
+ * ServerRequest type reference from Nitro v3 (`nitro/types`).
16
+ * Re-exported here for convenience within Avalon's Nitro integration.
17
+ *
18
+ * Represents the server-side request object in Nitro v3's type system.
19
+ * When the full `nitro/types` package is available, prefer importing directly.
20
+ */
21
+ export interface ServerRequest {
22
+ /** HTTP method */
23
+ method: string;
24
+ /** Request URL */
25
+ url: string;
26
+ /** Request headers */
27
+ headers: Record<string, string | string[] | undefined>;
28
+ /** Request body (if applicable) */
29
+ body?: unknown;
30
+ }
31
+
32
+ /**
33
+ * H3 Event type placeholder
34
+ * This represents the H3 event object from Nitro's h3 library
35
+ */
36
+ export interface H3Event {
37
+ /** HTTP method */
38
+ method: string;
39
+ /** Node.js request object */
40
+ node?: {
41
+ req: unknown;
42
+ res?: unknown;
43
+ };
44
+ /** Event context for storing data */
45
+ context: Record<string, unknown>;
46
+ /** Request path */
47
+ path: string;
48
+ }
49
+
50
+ /**
51
+ * Avalon-specific runtime configuration stored in Nitro's runtimeConfig
52
+ * This is accessed via useRuntimeConfig().avalon in handlers
53
+ */
54
+ export interface AvalonRuntimeConfig {
55
+ /** Enable streaming SSR responses */
56
+ streaming: boolean;
57
+ /** Pages directory path relative to project root */
58
+ pagesDir: string;
59
+ /** Layouts directory path relative to project root */
60
+ layoutsDir: string;
61
+ }
62
+
63
+ /**
64
+ * Render context provided to the SSR renderer
65
+ * Contains all information needed to render a page
66
+ */
67
+ export interface NitroRenderContext {
68
+ /** Request URL */
69
+ url: URL;
70
+ /** Route parameters extracted from dynamic segments */
71
+ params: Record<string, string>;
72
+ /** Query parameters from URL search string */
73
+ query: Record<string, string | string[]>;
74
+ /** Original HTTP request */
75
+ request: Request;
76
+ /** H3 event for advanced use cases */
77
+ event: H3Event;
78
+ /** Middleware context if middleware was executed */
79
+ middlewareContext?: MiddlewareContext;
80
+ /** Layout context for rendering */
81
+ layoutContext?: LayoutContext;
82
+ }
83
+
84
+ /**
85
+ * Layout context for page rendering
86
+ */
87
+ export interface LayoutContext {
88
+ /** Layout component paths in nesting order (outermost first) */
89
+ layouts: string[];
90
+ /** Layout data loaded from layout modules */
91
+ layoutData?: Record<string, unknown>;
92
+ }
93
+
94
+ /**
95
+ * Discovered route information
96
+ */
97
+ export interface DiscoveredRoute {
98
+ /** Route type: page or API */
99
+ type: "page" | "api";
100
+ /** Absolute file path to the route handler */
101
+ filePath: string;
102
+ /** Route pattern for matching (e.g., /users/:id) */
103
+ pattern: string;
104
+ /** Extracted parameter names from dynamic segments */
105
+ params: string[];
106
+ /** HTTP method for API routes with method suffix */
107
+ method?: ApiMethod;
108
+ }
109
+
110
+ /**
111
+ * Nitro route configuration
112
+ */
113
+ export interface NitroRouteConfig {
114
+ /** Route pattern (e.g., /users/:id) */
115
+ pattern: string;
116
+ /** Route handler type */
117
+ type: "page" | "api" | "middleware";
118
+ /** File path to the handler */
119
+ filePath: string;
120
+ /** Extracted parameter names */
121
+ params: string[];
122
+ /** HTTP method (for API routes) */
123
+ method?: ApiMethod;
124
+ /** Layout chain for pages */
125
+ layouts?: string[];
126
+ }
127
+
128
+ /**
129
+ * SSR render options
130
+ */
131
+ export interface SSRRenderOptions {
132
+ /** Enable streaming SSR */
133
+ streaming?: boolean;
134
+ /** Callback when shell is ready (for streaming) */
135
+ onShellReady?: () => void;
136
+ /** Callback when all content is ready */
137
+ onAllReady?: () => void;
138
+ /** Callback on render error */
139
+ onError?: (error: Error) => void;
140
+ }
141
+
142
+ /**
143
+ * SSR render result
144
+ */
145
+ export interface SSRRenderResult {
146
+ /** Rendered HTML string or stream */
147
+ html: string | ReadableStream<Uint8Array>;
148
+ /** HTTP status code */
149
+ statusCode: number;
150
+ /** Response headers */
151
+ headers: Record<string, string>;
152
+ }
153
+
154
+ /**
155
+ * Island manifest entry for a single island
156
+ */
157
+ export interface IslandEntry {
158
+ /** Compiled JavaScript path */
159
+ src: string;
160
+ /** Framework identifier (react, vue, svelte, etc.) */
161
+ framework: string;
162
+ /** CSS dependencies */
163
+ css?: string[];
164
+ /** Preload dependencies */
165
+ preload?: string[];
166
+ }
167
+
168
+ /**
169
+ * Island manifest for production builds
170
+ */
171
+ export interface IslandManifest {
172
+ /** Map of island ID to compiled asset information */
173
+ islands: Record<string, IslandEntry>;
174
+ /** Client entry script path */
175
+ clientEntry: string;
176
+ /** CSS assets to inject */
177
+ css: string[];
178
+ }
179
+
180
+ /**
181
+ * Page module export interface
182
+ */
183
+ export interface PageModule {
184
+ /** Default export - the page component */
185
+ default: unknown;
186
+ /** Optional page metadata */
187
+ metadata?: PageMetadata;
188
+ /** Optional getStaticProps for static generation */
189
+ getStaticProps?: () => Promise<Record<string, unknown>>;
190
+ /** Optional getServerSideProps for SSR */
191
+ getServerSideProps?: (
192
+ context: NitroRenderContext
193
+ ) => Promise<Record<string, unknown>>;
194
+ }
195
+
196
+ /**
197
+ * Page metadata for SEO and configuration
198
+ */
199
+ export interface PageMetadata {
200
+ /** Page title */
201
+ title?: string;
202
+ /** Page description */
203
+ description?: string;
204
+ /** Open Graph metadata */
205
+ openGraph?: {
206
+ title?: string;
207
+ description?: string;
208
+ image?: string;
209
+ };
210
+ /** Additional head elements */
211
+ head?: Array<{
212
+ tag: string;
213
+ attrs?: Record<string, string>;
214
+ content?: string;
215
+ }>;
216
+ }
217
+
218
+ /**
219
+ * Error response structure
220
+ */
221
+ export interface ErrorResponse {
222
+ /** HTTP status code */
223
+ statusCode: number;
224
+ /** Error message */
225
+ message: string;
226
+ /** Stack trace (development only) */
227
+ stack?: string;
228
+ /** Additional error data */
229
+ data?: Record<string, unknown>;
230
+ }
231
+
232
+ /**
233
+ * HTTP error class for typed error handling
234
+ */
235
+ export class HttpError extends Error {
236
+ /** HTTP status code */
237
+ statusCode: number;
238
+ /** Additional error data */
239
+ data?: Record<string, unknown>;
240
+
241
+ constructor(
242
+ statusCode: number,
243
+ message: string,
244
+ data?: Record<string, unknown>
245
+ ) {
246
+ super(message);
247
+ this.name = "HttpError";
248
+ this.statusCode = statusCode;
249
+ this.data = data;
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Create a 404 Not Found error
255
+ */
256
+ export function createNotFoundError(message = "Not Found"): HttpError {
257
+ return new HttpError(404, message);
258
+ }
259
+
260
+ /**
261
+ * Create a 405 Method Not Allowed error
262
+ */
263
+ export function createMethodNotAllowedError(
264
+ allowedMethods: string[]
265
+ ): HttpError {
266
+ return new HttpError(405, "Method Not Allowed", { allowed: allowedMethods });
267
+ }
268
+
269
+ /**
270
+ * Create a 500 Internal Server Error
271
+ */
272
+ export function createInternalError(message = "Internal Server Error"): HttpError {
273
+ return new HttpError(500, message);
274
+ }
275
+
276
+ /**
277
+ * Type guard to check if an error is an HttpError
278
+ */
279
+ export function isHttpError(error: unknown): error is HttpError {
280
+ return error instanceof HttpError;
281
+ }
282
+
283
+ /**
284
+ * Avalon context stored in H3 event.context
285
+ */
286
+ export interface AvalonEventContext {
287
+ /** Middleware context after middleware execution */
288
+ middlewareContext?: MiddlewareContext;
289
+ /** Resolved route information */
290
+ route?: NitroRouteConfig;
291
+ /** Page props from getServerSideProps */
292
+ pageProps?: Record<string, unknown>;
293
+ }
294
+
295
+ /**
296
+ * Development server options
297
+ */
298
+ export interface DevServerOptions {
299
+ /** Port to listen on */
300
+ port?: number;
301
+ /** Host to bind to */
302
+ host?: string;
303
+ /** Enable HTTPS */
304
+ https?: boolean;
305
+ /** Open browser on start */
306
+ open?: boolean;
307
+ }
308
+
309
+ /**
310
+ * Build options for production
311
+ */
312
+ export interface BuildOptions {
313
+ /** Output directory */
314
+ outDir?: string;
315
+ /** Enable source maps */
316
+ sourcemap?: boolean;
317
+ /** Minify output */
318
+ minify?: boolean;
319
+ /** Target preset */
320
+ preset?: string;
321
+ }
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Collect CSS from Vite's module graph for SSR.
3
+ *
4
+ * After ssrLoadModule loads a page, Vite's module graph knows about all
5
+ * imported CSS files (including CSS modules). This utility walks the graph
6
+ * starting from the page module and collects all CSS content so it can be
7
+ * injected into the SSR HTML as <style> tags — preventing FOUC.
8
+ */
9
+
10
+ import type { ViteDevServer, ModuleNode } from "vite";
11
+
12
+ /**
13
+ * Collect all CSS imported (directly or transitively) by a given module URL.
14
+ *
15
+ * Walks Vite's module graph breadth-first, collecting the transformed CSS
16
+ * source for every `.css` dependency (including `.module.css`).
17
+ *
18
+ * @param server - The Vite dev server instance
19
+ * @param moduleUrl - The module URL to start from (e.g. "/src/pages/frameworks.tsx")
20
+ * @returns Array of CSS strings ready to be injected as <style> tags
21
+ */
22
+ export async function collectCssFromModuleGraph(
23
+ server: ViteDevServer,
24
+ moduleUrl: string,
25
+ ): Promise<string[]> {
26
+ const cssContents: string[] = [];
27
+ const visited = new Set<string>();
28
+
29
+ const entryModule = await server.moduleGraph.getModuleByUrl(moduleUrl);
30
+ if (!entryModule) return cssContents;
31
+
32
+ const queue: ModuleNode[] = [entryModule];
33
+
34
+ while (queue.length > 0) {
35
+ const mod = queue.shift()!;
36
+ const id = mod.id ?? mod.url;
37
+
38
+ if (visited.has(id)) continue;
39
+ visited.add(id);
40
+
41
+ // If this module is a CSS file, collect its transformed content
42
+ if (isCssModule(id)) {
43
+ const css = await getCssContent(server, mod);
44
+ if (css) {
45
+ cssContents.push(css);
46
+ }
47
+ }
48
+
49
+ // Walk imported modules
50
+ for (const imported of mod.importedModules) {
51
+ const importedId = imported.id ?? imported.url;
52
+ if (!visited.has(importedId)) {
53
+ queue.push(imported);
54
+ }
55
+ }
56
+ }
57
+
58
+ return cssContents;
59
+ }
60
+
61
+ /**
62
+ * Check if a module ID represents a CSS file.
63
+ */
64
+ function isCssModule(id: string): boolean {
65
+ // Strip query params for extension check
66
+ const cleanId = id.split("?")[0];
67
+ return (
68
+ cleanId.endsWith(".css") ||
69
+ cleanId.endsWith(".scss") ||
70
+ cleanId.endsWith(".sass") ||
71
+ cleanId.endsWith(".less") ||
72
+ cleanId.endsWith(".styl") ||
73
+ cleanId.endsWith(".stylus")
74
+ );
75
+ }
76
+
77
+ /**
78
+ * Get the transformed CSS content for a module.
79
+ *
80
+ * Uses Vite's transformRequest to get the processed CSS (with module
81
+ * class name hashing, PostCSS transforms, etc. already applied).
82
+ */
83
+ async function getCssContent(
84
+ server: ViteDevServer,
85
+ mod: ModuleNode,
86
+ ): Promise<string | null> {
87
+ try {
88
+ // Use the module's URL for transform (includes query params Vite needs)
89
+ const url = mod.url;
90
+ const result = await server.transformRequest(url + "?direct");
91
+
92
+ if (result && typeof result.code === "string") {
93
+ // Vite wraps CSS in a JS module for HMR. For SSR injection we need
94
+ // the raw CSS. The transformed code contains the CSS as a string
95
+ // in an `__vite__css` or similar export. Try to extract it.
96
+ const rawCss = extractCssFromTransformedModule(result.code);
97
+ return rawCss;
98
+ }
99
+
100
+ return null;
101
+ } catch {
102
+ return null;
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Extract raw CSS from Vite's transformed CSS module JS wrapper.
108
+ *
109
+ * Vite transforms CSS files into JS modules that look like:
110
+ * const __vite__css = "...actual css..."
111
+ * __vite__updateStyle(...)
112
+ *
113
+ * We extract the CSS string from this wrapper.
114
+ */
115
+ function extractCssFromTransformedModule(code: string): string | null {
116
+ // Pattern 1: __vite__css = "..."
117
+ const viteVarMatch = new RegExp(/const\s+__vite__css\s*=\s*"((?:[^"\\]|\\.)*)"/).exec(code);
118
+ if (viteVarMatch) {
119
+ return unescapeJsString(viteVarMatch[1]);
120
+ }
121
+
122
+ // Pattern 2: __vite_ssr_exports__.default = "..."
123
+ const ssrExportMatch = new RegExp(/__vite_ssr_exports__\.default\s*=\s*"((?:[^"\\]|\\.)*)"/).exec(code);
124
+ if (ssrExportMatch) {
125
+ return unescapeJsString(ssrExportMatch[1]);
126
+ }
127
+
128
+ // Pattern 3: export default "..."
129
+ const exportDefaultMatch = new RegExp(/export\s+default\s+"((?:[^"\\]|\\.)*)"/).exec(code);
130
+ if (exportDefaultMatch) {
131
+ return unescapeJsString(exportDefaultMatch[1]);
132
+ }
133
+
134
+ // Pattern 4: the code itself might be raw CSS (no JS wrapper)
135
+ if (!code.includes("export ") && !code.includes("__vite")) {
136
+ return code;
137
+ }
138
+
139
+ return null;
140
+ }
141
+
142
+ /**
143
+ * Unescape a JS string literal
144
+ */
145
+ function unescapeJsString(str: string): string {
146
+ return str
147
+ .replaceAll(String.raw`\n`, "\n")
148
+ .replaceAll(String.raw`\t`, "\t")
149
+ .replaceAll(String.raw`\r`, "\r")
150
+ .replaceAll(String.raw`\"`, '"')
151
+ .replaceAll('\\\\', "\\");
152
+ }
153
+
154
+ /**
155
+ * Inject collected CSS into an HTML string before </head>.
156
+ *
157
+ * Each CSS string is wrapped in a <style data-avalon-ssr-css> tag.
158
+ * CSS content is sanitized to prevent style tag breakout (XSS via </style> injection).
159
+ * If no </head> is found, styles are prepended to the HTML.
160
+ */
161
+ export function injectSsrCss(html: string, cssContents: string[]): string {
162
+ if (cssContents.length === 0) return html;
163
+
164
+ const styleTags = cssContents
165
+ .map((css) => `<style data-avalon-ssr-css>${sanitizeCssForStyleTag(css)}</style>`)
166
+ .join("\n");
167
+
168
+ if (html.includes("</head>")) {
169
+ return html.replace("</head>", `${styleTags}\n</head>`);
170
+ }
171
+
172
+ // Fallback: inject after <head> or at the start
173
+ if (html.includes("<head>")) {
174
+ return html.replace("<head>", `<head>\n${styleTags}`);
175
+ }
176
+
177
+ return styleTags + html;
178
+ }
179
+
180
+ /**
181
+ * Sanitize CSS content for safe injection inside a <style> tag.
182
+ *
183
+ * The only way to break out of a <style> tag is with a closing </style> tag
184
+ * (case-insensitive, with optional whitespace). We neutralize this by escaping
185
+ * any occurrence of `</style` in the CSS content.
186
+ *
187
+ * This prevents XSS via:
188
+ * .foo {} </style><script>alert('xss')</script><style>
189
+ *
190
+ * The CSS source comes from Vite's module graph (local project files and
191
+ * npm dependencies), so this is defense-in-depth against compromised deps.
192
+ */
193
+ function sanitizeCssForStyleTag(css: string): string {
194
+ // Replace </style (case-insensitive) with an escaped version that won't
195
+ // close the tag. Using a backslash escape: <\/style
196
+ // Browsers ignore the backslash in CSS context, so styles still work.
197
+ return css.replaceAll(/<\/style/gi, String.raw`<\/style`);
198
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Shared error page HTML templates for development mode.
3
+ * Extracted from nitro-integration.ts to keep that file focused on coordination.
4
+ *
5
+ * @module render/error-pages
6
+ */
7
+
8
+ /**
9
+ * Escapes HTML special characters
10
+ */
11
+ function escapeHtml(str: string): string {
12
+ return str
13
+ .replaceAll('&', "&amp;")
14
+ .replaceAll('<', "&lt;")
15
+ .replaceAll('>', "&gt;")
16
+ .replaceAll('"', "&quot;")
17
+ .replaceAll('\'', "&#039;");
18
+ }
19
+
20
+ /**
21
+ * Generates a styled 500 error page for development with stack trace.
22
+ */
23
+ export function generateErrorPage(error: Error): string {
24
+ return `<!DOCTYPE html>
25
+ <html lang="en">
26
+ <head>
27
+ <meta charset="utf-8">
28
+ <meta name="viewport" content="width=device-width, initial-scale=1">
29
+ <title>SSR Error</title>
30
+ <style>
31
+ body {
32
+ font-family: system-ui, -apple-system, sans-serif;
33
+ margin: 0;
34
+ padding: 40px;
35
+ background: #1a1a1a;
36
+ color: #fff;
37
+ }
38
+ .error-container {
39
+ max-width: 800px;
40
+ margin: 0 auto;
41
+ background: #2d2d2d;
42
+ padding: 40px;
43
+ border-radius: 8px;
44
+ border-left: 4px solid #ff6b6b;
45
+ }
46
+ h1 { color: #ff6b6b; margin-top: 0; font-size: 24px; }
47
+ .message { font-size: 18px; color: #ccc; margin-bottom: 20px; }
48
+ pre {
49
+ background: #1a1a1a;
50
+ padding: 20px;
51
+ border-radius: 4px;
52
+ overflow-x: auto;
53
+ font-size: 14px;
54
+ line-height: 1.5;
55
+ color: #e0e0e0;
56
+ }
57
+ .stack-title { color: #888; font-size: 12px; text-transform: uppercase; margin-bottom: 10px; }
58
+ </style>
59
+ </head>
60
+ <body>
61
+ <div class="error-container">
62
+ <h1>SSR Error</h1>
63
+ <p class="message">${escapeHtml(error.message)}</p>
64
+ ${error.stack ? `
65
+ <div class="stack-title">Stack Trace</div>
66
+ <pre>${escapeHtml(error.stack)}</pre>
67
+ ` : ""}
68
+ </div>
69
+ <script type="module" src="/@vite/client"></script>
70
+ </body>
71
+ </html>`;
72
+ }
73
+
74
+ /**
75
+ * Generates a minimal fallback 404 page when the error handler itself fails.
76
+ */
77
+ export function generateFallback404(url: string): string {
78
+ return `<!DOCTYPE html><html><head><title>404 Not Found</title></head><body><h1>404 - Page Not Found</h1><p>The page ${escapeHtml(url)} was not found.</p></body></html>`;
79
+ }