@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,198 +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
- }
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
+ }
@@ -1,79 +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
- }
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
+ }