@useavalon/avalon 0.1.49 → 0.1.51
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.
- package/dist/src/build/page-island-transform.d.ts +1 -1
- package/dist/src/build/page-island-transform.js +2 -3
- package/dist/src/nitro/config.d.ts +13 -0
- package/dist/src/nitro/renderer.d.ts +15 -5
- package/dist/src/nitro/renderer.js +20 -20
- package/dist/src/post-build/index.d.ts +57 -0
- package/dist/src/post-build/index.js +31 -0
- package/dist/src/types/index.d.ts +8 -5
- package/dist/src/types/virtual-modules.d.ts +56 -0
- package/dist/src/vite-plugin/nitro-integration.d.ts +8 -0
- package/dist/src/vite-plugin/nitro-integration.js +15 -10
- package/package.json +6 -2
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*
|
|
24
24
|
* Only applies to files inside the configured pages or layouts directories.
|
|
25
25
|
*/
|
|
26
|
-
import type { Plugin } from
|
|
26
|
+
import type { Plugin } from "vite";
|
|
27
27
|
export interface PageIslandTransformOptions {
|
|
28
28
|
/** Directory containing page files (default: src/pages/) */
|
|
29
29
|
pagesDir?: string;
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import{dirname as e}from"node:path";function t(e){let t=[],n=/^[ \t]*import\s+([A-Z]\w*)\s+from\s+(['"][^'"]+['"])/gm,r;for(
|
|
2
|
-
`,s),n=t===-1?e.length:t+1;o+=e.slice(s,n),s=n;continue}if(e[s]===`/`&&e[s+1]===`*`){let t=e.indexOf(`*/`,s+2),n=t===-1?e.length:t+2;o+=e.slice(s,n),s=n;continue}if(!x(e,s,a)){o+=e[s],s++;continue}let c=y(e,s,t);if(!c||!c.islandProp&&!i){let t=c?c.endIdx:s+1;o+=e.slice(s,t),s=t;continue}o+=b(c,n,r,i&&!c.islandProp,t),s=c.endIdx}return o}export function pageIslandTransform(e={}){let{pagesDir:n=`src/pages`,layoutsDir:r=`src/layouts`,modules:o=null}=e;return{name:`avalon:page-island-transform`,enforce:`pre`,transform(e,s){let d=a(s,r,o);if(!i(s,n,o)&&!d)return null;let f=t(e);if(f.length===0||!c(e,f.map(e=>e.localName))&&!l(e,f))return null;let p=u(e,f,s);if(p.size===0)return null;let m=`import { renderIsland as __pageRenderIsland } from '@useavalon/avalon';
|
|
3
|
-
`+e;for(let[e,t]of p)m=S(m,e,t.srcPath,t.framework,t.autoIsland);return{code:m,map:null}}}}
|
|
1
|
+
import{dirname as e}from"node:path";function t(e){let t=[],n=/^[ \t]*import\s+([A-Z]\w*)\s+from\s+(['"][^'"]+['"])/gm,r=null;for(r=n.exec(e);r!==null;r=n.exec(e))t.push({localName:r[1],importPath:r[2].slice(1,-1),fullMatch:r[0].trimStart()});return t}function n(t,n){if(t.startsWith(`/src/`)||t.startsWith(`/app/`)||t.startsWith(`/`))return t;if(t.startsWith(`@/`))return`/app/${t.slice(2)}`;if(t.startsWith(`@shared/`))return`/app/shared/${t.slice(8)}`;if(t.startsWith(`@modules/`))return`/app/modules/${t.slice(9)}`;if(t.startsWith(`$components/`))return`/src/components/${t.slice(12)}`;if(t.startsWith(`$islands/`))return`/src/islands/${t.slice(9)}`;if(t.startsWith(`~/`))return`/src/${t.slice(2)}`;if(t.startsWith(`.`)){let r=n.replaceAll(`\\`,`/`),i=r.indexOf(`/app/`);if(i===-1&&(i=r.indexOf(`/src/`)),i!==-1){let n=e(r.slice(i)).split(`/`),a=t.split(`/`);for(let e of a)e===`..`?n.pop():e!==`.`&&n.push(e);return n.join(`/`)}}return`/src/${t.split(`/`).pop()}`}function r(e){if(e.endsWith(`.vue`))return`vue`;if(e.endsWith(`.svelte`))return`svelte`;if(e.includes(`.solid.`))return`solid`;if(e.includes(`.lit.`))return`lit`;if(e.includes(`.qwik.`))return`qwik`;if(e.includes(`.react.`))return`react`;if(e.endsWith(`.tsx`)||e.endsWith(`.jsx`))return`preact`}function i(e,t,n){let r=e.replaceAll(`\\`,`/`),i=t.replace(/^\//,``);if(r.includes(`/${i}/`)&&/\.(tsx|jsx)$/.test(r))return!0;if(n){let e=n.dir.replace(/^\//,``);if(RegExp(`/${e}/[^/]+/${n.pagesDirName}/`).test(r)&&/\.(tsx|jsx)$/.test(r))return!0}return!1}function a(e,t,n){let r=e.replaceAll(`\\`,`/`),i=t.replace(/^\//,``);if(r.includes(`/${i}/`)&&/\.(tsx|jsx)$/.test(r))return!0;if(n){let e=n.dir.replace(/^\//,``);if(RegExp(`/${e}/[^/]+/${n.layoutsDirName}/`).test(r)&&/\.(tsx|jsx)$/.test(r))return!0}return!1}const o=new Set([`qwik`]);function s(e){let t=r(e);return t!==void 0&&o.has(t)}function c(e,t){return t.some(t=>RegExp(`<${t}${String.raw`[\s][^>]*island[\s]*[={]`}`).test(e))}function l(e,t){return t.some(t=>s(t.importPath)?RegExp(`<${t.localName}${String.raw`[\s/>]`}`).test(e):!1)}function u(e,t,i){let a=new Map;for(let s of t){let t=n(s.importPath,i),c=r(t);if(RegExp(`<${s.localName}${String.raw`[\s][^>]*island[\s]*[={]`}`).test(e)){a.set(s.localName,{srcPath:t,framework:c,importPath:s.importPath,autoIsland:!1});continue}c&&o.has(c)&&RegExp(`<${s.localName}${String.raw`[\s/>]`}`).test(e)&&a.set(s.localName,{srcPath:t,framework:c,importPath:s.importPath,autoIsland:!0})}return a}function d(e,t){for(;t<e.length&&/\s/.test(e[t]);)t++;return t}function f(e,t){let n=e[t];for(t++;t<e.length&&e[t]!==n;)e[t]===`\\`&&t++,t++;return t<e.length?t+1:t}function p(e,t){for(t++;t<e.length&&e[t]!=="`";){if(e[t]===`\\`){t+=2;continue}if(e[t]===`$`&&e[t+1]===`{`){t=m(t+1,e);continue}t++}return t<e.length?t+1:t}function m(e,t){let n=e+1,r=1;for(;n<t.length&&r>0;){let e=t[n];e===`{`?(r++,n++):e===`}`?(r--,r>0&&n++):e===`'`||e===`"`||e==="`"?n=f(t,n):n++}return n<t.length?n+1:n}function h(e,t){let n=t+1,r=m(t,e);return{value:e.slice(n,r-1),endIdx:r}}function g(e,t){let n=e[t],r=t+1;for(;r<e.length&&e[r]!==n;)e[r]===`\\`&&r++,r++;return{value:`"${e.slice(t+1,r)}"`,endIdx:r+1}}function _(e,t){let n=t,r=t;for(;r<e.length&&/[a-zA-Z0-9_$]/.test(e[r]);)r++;let i=e.slice(n,r);if(!i)return null;if(r=d(e,r),e[r]!==`=`)return{name:i,value:null,endIdx:r};if(r=d(e,r+1),e[r]===`{`){let t=h(e,r);return{name:i,value:t.value,endIdx:t.endIdx}}if(e[r]===`"`||e[r]===`'`){let t=g(e,r);return{name:i,value:t.value,endIdx:t.endIdx}}return null}function v(e,t,n){if(e[t]===`/`&&e[t+1]===`>`)return{endIdx:t+2,selfClosing:!0};if(e[t]===`>`){let r=`</${n}>`,i=e.indexOf(r,t+1);return i===-1?null:{endIdx:i+r.length,selfClosing:!1}}return null}function y(e,t,n){let r=d(e,t+1+n.length),i=null,a=[];for(;r<e.length;){r=d(e,r);let t=v(e,r,n);if(t)return{endIdx:t.endIdx,islandProp:i,otherProps:a};let o=_(e,r);if(!o)return null;if(r=o.endIdx,o.name===`island`)i=o.value??`{}`;else{let e=o.value===null?`${o.name}: true`:`${o.name}: ${o.value}`;a.push(e)}}return null}function b(e,t,n,r,i){let a=n?`, framework: "${n}"`:``,o=e.otherProps.length>0?`, props: { ${e.otherProps.join(`, `)} }`:``,s=`, component: ${i}`;if(r)return`{await __pageRenderIsland({ src: "`+t+`"`+a+s+o+`, ssr: true, ssrOnly: true })}`;let c=e.islandProp??``;return`{await __pageRenderIsland({ src: "`+t+`"`+a+s+`, ...(`+c+`)`+o+`, ssr: (`+c+`).ssr !== undefined ? (`+c+`).ssr : true })}`}function x(e,t,n){if(!e.startsWith(n,t))return!1;let r=t+n.length;return r>=e.length||!/[a-zA-Z0-9_$]/.test(e[r])}function S(e,t,n,r,i){let a=`<${t}`,o=``,s=0;for(;s<e.length;){if(e[s]==="`"){let t=s;s=p(e,s),o+=e.slice(t,s);continue}if(e[s]===`{`&&e[s+1]===`/`&&e[s+2]===`*`){let t=e.indexOf(`*/`,s+3);if(t!==-1){let n=t+2;for(;n<e.length&&/\s/.test(e[n]);)n++;if(n<e.length&&e[n]===`}`){o+=e.slice(s,n+1),s=n+1;continue}}}if(e[s]===`/`&&e[s+1]===`/`){let t=e.indexOf(`
|
|
2
|
+
`,s),n=t===-1?e.length:t+1;o+=e.slice(s,n),s=n;continue}if(e[s]===`/`&&e[s+1]===`*`){let t=e.indexOf(`*/`,s+2),n=t===-1?e.length:t+2;o+=e.slice(s,n),s=n;continue}if(!x(e,s,a)){o+=e[s],s++;continue}let c=y(e,s,t);if(!c||!c.islandProp&&!i){let t=c?c.endIdx:s+1;o+=e.slice(s,t),s=t;continue}o+=b(c,n,r,i&&!c.islandProp,t),s=c.endIdx}return o}export function pageIslandTransform(e={}){let{pagesDir:n=`src/pages`,layoutsDir:r=`src/layouts`,modules:o=null}=e;return{name:`avalon:page-island-transform`,enforce:`pre`,transform(e,s){let d=a(s,r,o);if(!i(s,n,o)&&!d)return null;let f=t(e);if(f.length===0||!c(e,f.map(e=>e.localName))&&!l(e,f))return null;let p=u(e,f,s);if(p.size===0)return null;let m=`import { renderIsland as __pageRenderIsland } from '@useavalon/avalon';\n${e}`;for(let[e,t]of p)m=S(m,e,t.srcPath,t.framework,t.autoIsland);return{code:m,map:null}}}}
|
|
@@ -177,6 +177,19 @@ export interface AvalonNitroConfig {
|
|
|
177
177
|
/** Write `/about` as `/about/index.html` @default true */
|
|
178
178
|
autoSubfolderIndex?: boolean;
|
|
179
179
|
};
|
|
180
|
+
/**
|
|
181
|
+
* Path to the client entry file, relative to the project root.
|
|
182
|
+
* Used by the auto-generated renderer to import client assets.
|
|
183
|
+
* @default "app/entry-client" (or "src/entry-client")
|
|
184
|
+
*/
|
|
185
|
+
clientEntry?: string;
|
|
186
|
+
/**
|
|
187
|
+
* Global CSS files to include in the client entry.
|
|
188
|
+
* Paths are relative to the project root.
|
|
189
|
+
* Layout CSS is auto-discovered — this is for additional global stylesheets.
|
|
190
|
+
* @example ["app/shared/styles/main.css"]
|
|
191
|
+
*/
|
|
192
|
+
globalCSS?: string[];
|
|
180
193
|
}
|
|
181
194
|
/**
|
|
182
195
|
* Nitro configuration output structure
|
|
@@ -43,9 +43,9 @@ export interface ResolvedPageRoute {
|
|
|
43
43
|
/**
|
|
44
44
|
* Render handler options
|
|
45
45
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
46
|
+
* Route resolution is handled by Nitro's file-system routing.
|
|
47
|
+
* Custom resolvers are optional and primarily used for
|
|
48
|
+
* development/testing scenarios.
|
|
49
49
|
*/
|
|
50
50
|
export interface RenderHandlerOptions {
|
|
51
51
|
/** Avalon runtime configuration */
|
|
@@ -94,6 +94,12 @@ export interface RenderHandlerOptions {
|
|
|
94
94
|
* @returns NitroRenderContext for use in rendering
|
|
95
95
|
*/
|
|
96
96
|
export declare function createRenderContext(event: H3Event, params?: Record<string, string>): NitroRenderContext;
|
|
97
|
+
/**
|
|
98
|
+
* Creates a render context directly from a web Request.
|
|
99
|
+
* Used by the `.fetch()` wrapper to avoid h3 event conversion issues
|
|
100
|
+
* when Nitro's SSR dispatcher passes a plain Request.
|
|
101
|
+
*/
|
|
102
|
+
export declare function createRenderContextFromRequest(request: Request, params?: Record<string, string>): NitroRenderContext;
|
|
97
103
|
/**
|
|
98
104
|
* Gets the request URL from an H3 event
|
|
99
105
|
*/
|
|
@@ -274,7 +280,9 @@ export declare function createStreamingResponse(stream: ReadableStream<Uint8Arra
|
|
|
274
280
|
* @param options - Render handler options
|
|
275
281
|
* @returns Handler function for Nitro
|
|
276
282
|
*/
|
|
277
|
-
export declare function createNitroRenderer(options: RenderHandlerOptions): (event: H3Event) => Promise<Response
|
|
283
|
+
export declare function createNitroRenderer(options: RenderHandlerOptions): ((event: H3Event) => Promise<Response>) & {
|
|
284
|
+
fetch(request: Request): Promise<Response>;
|
|
285
|
+
};
|
|
278
286
|
/**
|
|
279
287
|
* Options for the Nitro catch-all renderer
|
|
280
288
|
*/
|
|
@@ -337,7 +345,9 @@ export interface NitroCatchAllOptions {
|
|
|
337
345
|
* @param options - Catch-all renderer options
|
|
338
346
|
* @returns Nitro event handler function
|
|
339
347
|
*/
|
|
340
|
-
export declare function createNitroCatchAllRenderer(options: NitroCatchAllOptions): (event: H3Event) => Promise<Response
|
|
348
|
+
export declare function createNitroCatchAllRenderer(options: NitroCatchAllOptions): ((event: H3Event) => Promise<Response>) & {
|
|
349
|
+
fetch(request: Request): Promise<Response>;
|
|
350
|
+
};
|
|
341
351
|
/**
|
|
342
352
|
* Re-export middleware cache clearing for hot reload support
|
|
343
353
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{getRequestURL as e}from"h3";import{h as t}from"preact";import n from"preact-render-to-string";import{discoverScopedMiddleware as r,executeScopedMiddleware as i}from"../middleware/index.js";import{discoverErrorPages as a,handleRenderError as o}from"./error-handler.js";import{createNotFoundError as s,isHttpError as c}from"./types.js";export function createRenderContext(e,t={}){let n=getRequestURL(e);return{url:n,params:t,query:Object.fromEntries(n.searchParams),request:toRequest(e),event:e}}export function getRequestURL(t){return new URL(e(t).pathname,`http://localhost`)}export function toRequest(e){let t=getRequestURL(e);return new Request(t,{method:e.req.method,headers:getRequestHeaders(e)})}export function getRequestHeaders(e){return new Headers}export function setResponseHeader(e,t,n){e.context.responseHeaders||(e.context.responseHeaders={}),e.context.responseHeaders[t]=n}export function createErrorResponse(e,t){let n=c(e)?e.statusCode:500;if(t){let t=
|
|
1
|
+
import{getRequestURL as e}from"h3";import{h as t}from"preact";import n from"preact-render-to-string";import{discoverScopedMiddleware as r,executeScopedMiddleware as i}from"../middleware/index.js";import{discoverErrorPages as a,handleRenderError as o}from"./error-handler.js";import{createNotFoundError as s,isHttpError as c}from"./types.js";export function createRenderContext(e,t={}){let n=getRequestURL(e);return{url:n,params:t,query:Object.fromEntries(n.searchParams),request:toRequest(e),event:e}}export function createRenderContextFromRequest(e,t={}){let n=new URL(e.url,`http://localhost`),r={method:e.method,path:n.pathname+n.search,context:{params:t}};return{url:n,params:t,query:Object.fromEntries(n.searchParams),request:e,event:r}}export function getRequestURL(t){return new URL(e(t).pathname,`http://localhost`)}export function toRequest(e){let t=getRequestURL(e);return new Request(t,{method:e.req.method,headers:getRequestHeaders(e)})}export function getRequestHeaders(e){return new Headers}export function setResponseHeader(e,t,n){e.context.responseHeaders||(e.context.responseHeaders={}),e.context.responseHeaders[t]=n}export function createErrorResponse(e,t){let n=c(e)?e.statusCode:500;if(t){let t=g(e,n);return new Response(t,{status:n,headers:{"Content-Type":`text/html; charset=utf-8`}})}let r=_(n);return new Response(r,{status:n,headers:{"Content-Type":`text/html; charset=utf-8`}})}function g(e,t){return`<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
@@ -56,15 +56,15 @@ import{getRequestURL as e}from"h3";import{h as t}from"preact";import n from"prea
|
|
|
56
56
|
<body>
|
|
57
57
|
<div class="error-container">
|
|
58
58
|
<div class="status-code">${t}</div>
|
|
59
|
-
<h1>${
|
|
60
|
-
<p class="message">${
|
|
59
|
+
<h1>${v(t)}</h1>
|
|
60
|
+
<p class="message">${y(e.message)}</p>
|
|
61
61
|
${e.stack?`
|
|
62
62
|
<div class="stack-title">Stack Trace</div>
|
|
63
|
-
<pre>${
|
|
63
|
+
<pre>${y(e.stack)}</pre>
|
|
64
64
|
`:``}
|
|
65
65
|
</div>
|
|
66
66
|
</body>
|
|
67
|
-
</html>`}function
|
|
67
|
+
</html>`}function _(e){return`<!DOCTYPE html>
|
|
68
68
|
<html lang="en">
|
|
69
69
|
<head>
|
|
70
70
|
<meta charset="utf-8">
|
|
@@ -113,47 +113,47 @@ import{getRequestURL as e}from"h3";import{h as t}from"preact";import n from"prea
|
|
|
113
113
|
<body>
|
|
114
114
|
<div class="error-container">
|
|
115
115
|
<div class="status-code">${e}</div>
|
|
116
|
-
<h1>${
|
|
116
|
+
<h1>${v(e)}</h1>
|
|
117
117
|
<p><a href="/">Return to home</a></p>
|
|
118
118
|
</div>
|
|
119
119
|
</body>
|
|
120
|
-
</html>`}function
|
|
121
|
-
`);return e.includes(`</body>`)?e.replace(`</body>`,`${a}\n</body>`):e+a}export function validateHydrationMarkers(e){let t=extractIslandMarkers(e),n=e.match(/data-framework="[^"]+"/g)||[],r=e.match(/data-src="[^"]+"/g)||[],i=e.match(/data-props="[^"]*"/g)||[],a=e.includes(`/src/client/main.js`)||e.includes(`/dist/client.js`)||e.includes(`client/main.js`),o=t.every(e=>e.framework&&e.src),s=t.length===0||o&&a;return{hasFrameworkAttr:n.length>0,hasSrcAttr:r.length>0,hasPropsAttr:i.length>0,islandCount:n.length,islands:t,hasClientScript:a,isValid:s}}export function processHydrationRequirements(e,t){let n=injectHydrationScript(e,t);return{html:n,validation:validateHydrationMarkers(n)}}export async function renderPage(e,t,n={},r){try{let i={};return e.getServerSideProps&&(i=await e.getServerSideProps(t)),{html:await
|
|
120
|
+
</html>`}function v(e){return{400:`Bad Request`,401:`Unauthorized`,403:`Forbidden`,404:`Page Not Found`,405:`Method Not Allowed`,500:`Internal Server Error`,502:`Bad Gateway`,503:`Service Unavailable`}[e]||`Error`}function y(e){return e.replaceAll(`&`,`&`).replaceAll(`<`,`<`).replaceAll(`>`,`>`).replaceAll(`"`,`"`).replaceAll(`'`,`'`)}export function extractIslandMarkers(e){let t=[],n=/<[^>]*data-framework="([^"]+)"[^>]*>/g,r=n.exec(e);for(;r!==null;){let i=r[0],a=r[1],o=/data-src="([^"]+)"/.exec(i),s=o?o[1]:``,c=/data-props="([^"]*)"/.exec(i),l=c?c[1]:void 0,u=/data-hydrate="([^"]+)"/.exec(i),d=u?u[1]:void 0;t.push({framework:a,src:s,props:l,hydrate:d}),r=n.exec(e)}return t}export function ensureHydrationMarkers(e,t){let n=e;return t.framework&&!n.includes(`data-framework=`)&&(n=n.replace(/>/,` data-framework="${t.framework}">`)),t.src&&!n.includes(`data-src=`)&&(n=n.replace(/>/,` data-src="${t.src}">`)),t.props!==void 0&&!n.includes(`data-props=`)&&(n=n.replace(/>/,` data-props="${t.props}">`)),n}export function injectHydrationScript(e,t,n={}){if(!(e.includes(`data-framework=`)||e.includes(`data-src=`))&&!n.forceInject||[`/src/client/main.js`,`/dist/client.js`,`client/main.js`,`entry-client`].some(t=>e.includes(t)))return e;let r=n.scriptPath||(t?`/src/client/main.js`:`/dist/client.js`),i=[];i.push(`<script type="module" src="${r}"><\/script>`),n.additionalScripts&&i.push(...n.additionalScripts);let a=i.join(`
|
|
121
|
+
`);return e.includes(`</body>`)?e.replace(`</body>`,`${a}\n</body>`):e+a}export function validateHydrationMarkers(e){let t=extractIslandMarkers(e),n=e.match(/data-framework="[^"]+"/g)||[],r=e.match(/data-src="[^"]+"/g)||[],i=e.match(/data-props="[^"]*"/g)||[],a=e.includes(`/src/client/main.js`)||e.includes(`/dist/client.js`)||e.includes(`client/main.js`),o=t.every(e=>e.framework&&e.src),s=t.length===0||o&&a;return{hasFrameworkAttr:n.length>0,hasSrcAttr:r.length>0,hasPropsAttr:i.length>0,islandCount:n.length,islands:t,hasClientScript:a,isValid:s}}export function processHydrationRequirements(e,t){let n=injectHydrationScript(e,t);return{html:n,validation:validateHydrationMarkers(n)}}export async function renderPage(e,t,n={},r){try{let i={};return e.getServerSideProps&&(i=await e.getServerSideProps(t)),{html:await b(e,i,t,n,r),statusCode:200,headers:{"Content-Type":`text/html; charset=utf-8`}}}catch(e){throw console.error(`[SSR Error]`,e),n.onError&&e instanceof Error&&n.onError(e),e}}async function b(e,r,i,a,o){let s=e.default,c=e.metadata||{},l=process.env.NODE_ENV!==`production`,u;try{let e=s(r);u=e instanceof Promise?await e:e}catch(e){console.error(`[renderer] Error calling page component:`,e),u=t(`div`,null,`Error rendering page`)}let d;try{d=n(u)}catch(e){console.error(`[renderer] Error in preactRenderToString:`,e),d=`<div>Error rendering page</div>`}if(o)return await o(d,e,i);let f=l?`
|
|
122
122
|
<script type="module" src="/src/client/main.js"><\/script>`:``;return`<!DOCTYPE html>
|
|
123
123
|
<html lang="en">
|
|
124
124
|
<head>
|
|
125
125
|
<meta charset="utf-8">
|
|
126
126
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
127
|
-
<title>${
|
|
128
|
-
${c.description?`<meta name="description" content="${
|
|
127
|
+
<title>${y(String(c.title||`Avalon App`))}</title>
|
|
128
|
+
${c.description?`<meta name="description" content="${y(String(c.description))}">`:``}
|
|
129
129
|
</head>
|
|
130
130
|
<body>
|
|
131
131
|
<div id="app">
|
|
132
132
|
${d}
|
|
133
133
|
</div>${f}
|
|
134
134
|
</body>
|
|
135
|
-
</html>`}export async function renderPageStream(e,t,r={}){let i=new TextEncoder,a=null,o={shellSent:!1,contentSent:!1,closed:!1,error:null},s=r.shellReadyTimeout,c=r.allReadyTimeout,l=null,u=null,d=()=>{l&&=(clearTimeout(l),null),u&&=(clearTimeout(u),null)};function f(e,t,n,r,i,a,o){if(n.error=e,a(),console.error(`[Streaming Error]`,{message:e.message,stack:e.stack,shellSent:n.shellSent,isShellError:t,timestamp:new Date().toISOString()}),t&&o.onShellError&&o.onShellError(e),o.onError&&o.onError(e),!n.closed&&r){if(n.shellSent){let t=
|
|
135
|
+
</html>`}export async function renderPageStream(e,t,r={}){let i=new TextEncoder,a=null,o={shellSent:!1,contentSent:!1,closed:!1,error:null},s=r.shellReadyTimeout,c=r.allReadyTimeout,l=null,u=null,d=()=>{l&&=(clearTimeout(l),null),u&&=(clearTimeout(u),null)};function f(e,t,n,r,i,a,o){if(n.error=e,a(),console.error(`[Streaming Error]`,{message:e.message,stack:e.stack,shellSent:n.shellSent,isShellError:t,timestamp:new Date().toISOString()}),t&&o.onShellError&&o.onShellError(e),o.onError&&o.onError(e),!n.closed&&r){if(n.shellSent){let t=w(e);r.enqueue(i.encode(t));let n=C();r.enqueue(i.encode(n))}else{let t=g(e,500);r.enqueue(i.encode(t))}n.closed=!0,r.close()}}async function p(s){a=s;let p={};e.getServerSideProps&&(p=await e.getServerSideProps(t));let m=x(e.metadata||{},t);if(o.closed||(s.enqueue(i.encode(m)),o.shellSent=!0,l&&=(clearTimeout(l),null),r.onShellReady&&r.onShellReady()),c&&c>0&&(u=setTimeout(()=>{!o.contentSent&&!o.closed&&f(Error(`All ready timeout after ${c}ms`),!1,o,a,i,d,r)},c)),!o.closed){let t=e.default;if(t&&typeof t==`function`){let r=t(p);if(r&&typeof r.then==`function`)try{let e=n(await r);s.enqueue(i.encode(` <div id="app">${e}</div>\n`))}catch(t){console.error(`[streaming] Async component error:`,t);let n=S(e,p);s.enqueue(i.encode(n))}else{let t=S(e,p);s.enqueue(i.encode(t))}}else{let t=S(e,p);s.enqueue(i.encode(t))}o.contentSent=!0}if(!o.closed){let e=C();s.enqueue(i.encode(e))}d(),r.onAllReady&&!o.closed&&r.onAllReady(),o.closed||(o.closed=!0,s.close())}return new ReadableStream({async start(e){a=e,s&&s>0&&(l=setTimeout(()=>{!o.shellSent&&!o.closed&&f(Error(`Shell ready timeout after ${s}ms`),!0,o,a,i,d,r)},s));try{await p(e)}catch(e){f(e instanceof Error?e:Error(String(e)),!o.shellSent,o,a,i,d,r)}},cancel(){if(d(),!o.closed&&a){o.closed=!0;try{a.close()}catch{}}}})}function x(e,t){return`<!DOCTYPE html>
|
|
136
136
|
<html lang="en">
|
|
137
137
|
<head>
|
|
138
138
|
<meta charset="utf-8">
|
|
139
139
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
140
|
-
<title>${
|
|
141
|
-
${e.description?`<meta name="description" content="${
|
|
140
|
+
<title>${y(String(e.title||`Avalon App`))}</title>
|
|
141
|
+
${e.description?`<meta name="description" content="${y(String(e.description))}">`:``}
|
|
142
142
|
</head>
|
|
143
143
|
<body>
|
|
144
|
-
`}function
|
|
144
|
+
`}function S(e,t){let r=e.default;if(r&&typeof r==`function`)try{let e=r(t);if(e&&typeof e.then==`function`){let e=r.name||`Page`;return` <div id="app" data-page="${y(String(e))}" data-props='${y(JSON.stringify(t))}'>
|
|
145
145
|
<!-- Async component — awaiting hydration -->
|
|
146
|
-
</div>\n`}return` <div id="app">${n(e)}</div>\n`}catch(e){console.error(`[streaming] Error rendering page component:`,e)}let i=e.default?.name||`Page`;return` <div id="app" data-page="${
|
|
146
|
+
</div>\n`}return` <div id="app">${n(e)}</div>\n`}catch(e){console.error(`[streaming] Error rendering page component:`,e)}let i=e.default?.name||`Page`;return` <div id="app" data-page="${y(String(i))}" data-props='${y(JSON.stringify(t))}'>
|
|
147
147
|
<!-- Component render fallback -->
|
|
148
|
-
</div>\n`}function
|
|
149
|
-
</html>`}function
|
|
148
|
+
</div>\n`}function C(){return` </body>
|
|
149
|
+
</html>`}function w(e){let t=process.env.NODE_ENV!==`production`,n=e.stack?`<pre style="
|
|
150
150
|
background: #f5f5f5;
|
|
151
151
|
padding: 10px;
|
|
152
152
|
border-radius: 4px;
|
|
153
153
|
overflow-x: auto;
|
|
154
154
|
font-size: 12px;
|
|
155
155
|
margin-top: 10px;
|
|
156
|
-
">${
|
|
156
|
+
">${y(e.stack)}</pre>`:``;return`
|
|
157
157
|
<div class="streaming-error-boundary" data-error-boundary="true" style="
|
|
158
158
|
background: #fff3cd;
|
|
159
159
|
border: 2px solid #ffc107;
|
|
@@ -175,10 +175,10 @@ import{getRequestURL as e}from"h3";import{h as t}from"preact";import n from"prea
|
|
|
175
175
|
Error Details (Development Mode)
|
|
176
176
|
</summary>
|
|
177
177
|
<div style="margin-top: 10px;">
|
|
178
|
-
<p><strong>Error:</strong> ${
|
|
178
|
+
<p><strong>Error:</strong> ${y(e.message)}</p>
|
|
179
179
|
${n}
|
|
180
180
|
</div>
|
|
181
181
|
</details>
|
|
182
182
|
`:``}
|
|
183
183
|
</div>
|
|
184
|
-
`}export function createStreamingResponse(e,t={}){let n=new Headers({"Content-Type":`text/html; charset=utf-8`,"Transfer-Encoding":`chunked`,...t.headers});return new Response(e,{status:t.status||200,headers:n})}function
|
|
184
|
+
`}export function createStreamingResponse(e,t={}){let n=new Headers({"Content-Type":`text/html; charset=utf-8`,"Transfer-Encoding":`chunked`,...t.headers});return new Response(e,{status:t.status||200,headers:n})}function T(e,t,n){return async()=>(e.value??=await r({baseDir:t,devMode:n}),e.value)}function E(e,t,n){return async(r,i)=>e?o(r,i,t):createErrorResponse(r,n)}export function createNitroRenderer(e){let{avalonConfig:t,isDev:n=!1,enableCustomErrorPages:r=!0}=e,o={isDev:n,avalonConfig:t,loadPageModule:e.loadPageModule,pagesDir:t.pagesDir};r&&a(o).catch(e=>{console.warn(`[renderer] Failed to discover error pages:`,e)});let c=T({value:null},t.srcDir||`src`,n),f=E(r,o,n);async function p(r){let a=getRequestURL(r).pathname;try{let o=await i(r,await c(),{devMode:n});if(o)return n&&console.log(`[renderer] Middleware terminated request for ${a}`),o;let u=r.context.route,d=null;if(d=u||(e.resolvePageRoute?await e.resolvePageRoute(a,t.pagesDir):await D(a,t.pagesDir)),!d)return f(s(`Page not found: ${a}`),r);let p=e.loadPageModule?await e.loadPageModule(d.filePath):await O(d.filePath),h=createRenderContext(r,r.context.params||d.params);if(e.resolveLayouts&&(h.layoutContext={layouts:await e.resolveLayouts(a,t)}),t.streaming&&!e.wrapWithLayouts){let e=await renderPageStream(p,h,{onShellReady:()=>{setResponseHeader(r,`Content-Type`,`text/html; charset=utf-8`)}});return new Response(e,{headers:{"Content-Type":`text/html; charset=utf-8`}})}else{let t=await renderPage(p,h,{},e.wrapWithLayouts),r=injectHydrationScript(t.html,n);return new Response(r,{status:t.statusCode,headers:t.headers})}}catch(e){return console.error(`[Nitro Renderer Error]`,e),f(e instanceof Error?e:Error(String(e)),r)}}return Object.assign(p,{async fetch(r){let i=new URL(r.url,`http://localhost`).pathname;try{let a=null;if(a=e.resolvePageRoute?await e.resolvePageRoute(i,t.pagesDir):await D(i,t.pagesDir),!a)return createErrorResponse(s(`Page not found: ${i}`),n);let o=await renderPage(e.loadPageModule?await e.loadPageModule(a.filePath):await O(a.filePath),createRenderContextFromRequest(r,a.params),{},e.wrapWithLayouts),c=injectHydrationScript(o.html,n);return new Response(c,{status:o.statusCode,headers:o.headers})}catch(e){return console.error(`[Nitro Renderer .fetch() Error]`,e),createErrorResponse(e instanceof Error?e:Error(String(e)),n)}}})}async function D(e,t){return e===`/`||e===``?{filePath:`src/pages/index.tsx`,pattern:`/`,params:{}}:{filePath:`src/pages/${e.replace(/^\//,``).replace(/\/$/,``)}.tsx`,pattern:e,params:{}}}async function O(e){return{default:()=>null,metadata:{title:`Avalon Page`}}}export function createNitroCatchAllRenderer(e){let{avalonConfig:t,isDev:n=!1,loadPageModule:r,resolveLayouts:o,enableCustomErrorPages:c=!0}=e,f={isDev:n,avalonConfig:t,loadPageModule:r,pagesDir:t.pagesDir};c&&a(f).catch(e=>{console.warn(`[renderer] Failed to discover error pages:`,e)});let p=T({value:null},t.srcDir||`src`,n),g=E(c,f,n);async function _(a){let c=getRequestURL(a).pathname;try{let u=await i(a,await p(),{devMode:n});if(u)return n&&console.log(`[renderer] Middleware terminated request for ${c}`),u;let d=a.context.params||{},f=d.slug||c.replace(/^\//,``)||`index`,h=`${t.pagesDir}/${f}.tsx`,_;try{_=await r(h)}catch(e){try{_=await r(`${t.pagesDir}/${f}/index.tsx`)}catch(r){return n&&(console.debug(`[renderer] Page not found: ${h}`,e),console.debug(`[renderer] Index fallback not found: ${t.pagesDir}/${f}/index.tsx`,r)),g(s(`Page not found: ${c}`),a)}}let v=createRenderContext(a,d);if(o&&(v.layoutContext={layouts:await o(c,t)}),t.streaming&&!e.wrapWithLayouts){let e=await renderPageStream(_,v,{onShellReady:()=>{setResponseHeader(a,`Content-Type`,`text/html; charset=utf-8`)}});return new Response(e,{headers:{"Content-Type":`text/html; charset=utf-8`}})}else{let t=await renderPage(_,v,{},e.wrapWithLayouts),r=injectHydrationScript(t.html,n);return new Response(r,{status:t.statusCode,headers:t.headers})}}catch(e){return console.error(`[Nitro Catch-All Renderer Error]`,e),g(e instanceof Error?e:Error(String(e)),a)}}return Object.assign(_,{async fetch(i){let a=new URL(i.url,`http://localhost`).pathname;try{let o=a.replace(/^\//,``)||`index`,c=`${t.pagesDir}/${o}.tsx`,l;try{l=await r(c)}catch{try{l=await r(`${t.pagesDir}/${o}/index.tsx`)}catch{return createErrorResponse(s(`Page not found: ${a}`),n)}}let d=createRenderContextFromRequest(i),f=await renderPage(l,d,{},e.wrapWithLayouts),p=injectHydrationScript(f.html,n);return new Response(p,{status:f.statusCode,headers:f.headers})}catch(e){return console.error(`[Nitro CatchAll .fetch() Error]`,e),createErrorResponse(e instanceof Error?e:Error(String(e)),n)}}})}export{clearMiddlewareCache as clearRendererMiddlewareCache}from"../middleware/index.js";
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Avalon Post-Build Script
|
|
3
|
+
*
|
|
4
|
+
* Handles all post-build tasks for production deployments:
|
|
5
|
+
*
|
|
6
|
+
* 1. Cleanup: Removes stale Vite template index.html files
|
|
7
|
+
* 2. CSS Patching: Copies SSR CSS to client assets, patches SSR bundle
|
|
8
|
+
* 3. Island Redirects: Generates _redirects for clean island paths (Netlify)
|
|
9
|
+
* 4. Adapter Copying: Copies framework adapters to dist/
|
|
10
|
+
* 5. Netlify Function Copying: Copies server function to all Netlify paths
|
|
11
|
+
* 6. Prerendering: Boots built server, fetches routes, writes static HTML
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* import { runPostBuild } from '@useavalon/avalon/post-build';
|
|
15
|
+
* await runPostBuild();
|
|
16
|
+
*
|
|
17
|
+
* With custom prerender config:
|
|
18
|
+
* await runPostBuild({
|
|
19
|
+
* prerender: { routes: ['/'], crawlLinks: true, ignore: ['/admin'] },
|
|
20
|
+
* });
|
|
21
|
+
*/
|
|
22
|
+
export interface PrerenderConfig {
|
|
23
|
+
/** Routes to prerender (default: ['/']) */
|
|
24
|
+
routes?: string[];
|
|
25
|
+
/** Crawl links found in prerendered pages (default: true) */
|
|
26
|
+
crawlLinks?: boolean;
|
|
27
|
+
/** Routes to ignore (default: []) */
|
|
28
|
+
ignore?: string[];
|
|
29
|
+
/** Fail build on prerender errors (default: false) */
|
|
30
|
+
failOnError?: boolean;
|
|
31
|
+
/** Max concurrent fetches (default: 4) */
|
|
32
|
+
concurrency?: number;
|
|
33
|
+
/** Create index.html in subdirectories (default: true) */
|
|
34
|
+
autoSubfolderIndex?: boolean;
|
|
35
|
+
/** Number of retry attempts (default: 3) */
|
|
36
|
+
retry?: number;
|
|
37
|
+
/** Delay between retries in ms (default: 500) */
|
|
38
|
+
retryDelay?: number;
|
|
39
|
+
}
|
|
40
|
+
export interface PostBuildOptions {
|
|
41
|
+
/** Project root directory (default: process.cwd()) */
|
|
42
|
+
cwd?: string;
|
|
43
|
+
/** Prerender configuration (default: { routes: ['/'], crawlLinks: true }) */
|
|
44
|
+
prerender?: PrerenderConfig | false;
|
|
45
|
+
/** Port for prerender server (default: 13172) */
|
|
46
|
+
prerenderPort?: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Run all post-build tasks.
|
|
50
|
+
*
|
|
51
|
+
* Usage in consumer's post-build.mjs:
|
|
52
|
+
* ```js
|
|
53
|
+
* import { runPostBuild } from '@useavalon/avalon/post-build';
|
|
54
|
+
* await runPostBuild();
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function runPostBuild(options?: PostBuildOptions): Promise<void>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import{copyFileSync as e,cpSync as t,existsSync as n,mkdirSync as r,readdirSync as i,readFileSync as a,unlinkSync as o,writeFileSync as s}from"node:fs";import{dirname as c,join as l,relative as u}from"node:path";function d(e,t,r=[]){if(!n(e))return r;for(let n of i(e,{withFileTypes:!0})){let i=l(e,n.name);n.isDirectory()?d(i,t,r):t(n.name)&&r.push(i)}return r}function f(e){if(!n(e))return!1;let t=a(e,`utf-8`);return t.length<500&&!t.includes(`data-framework`)&&!t.includes(`avalon`)}function p(e){for(let t of[`dist/index.html`,`.netlify/functions-internal/server/public/index.html`]){let r=l(e,t);f(r)?(o(r),console.log(`[cleanup] Removed stale Vite template ${t}`)):n(r)&&console.log(`[cleanup] Preserved prerendered ${t}`)}}function m(e,t,r){if(!n(e))return;let i=[l(t,`assets`),l(r,`.netlify`,`functions-internal`,`server`,`public`,`assets`),l(r,`.output`,`public`,`assets`)].find(e=>n(e));if(!i)return;let o=d(i,e=>e.endsWith(`.css`)).map(e=>`/assets${e.substring(i.length).replaceAll(`\\`,`/`)}`);console.log(`[patch] Found ${o.length} CSS files in ${i}`);let c=a(e,`utf-8`);for(let{re:t,hrefRe:n,q:r}of[{re:/css:\[(\{href:`[^`]+`\}(?:,\{href:`[^`]+`\})*)\]/,hrefRe:/href:`([^`]+)`/g,q:"`"},{re:/css:\[(\{href:"[^"]+"\}(?:,\{href:"[^"]+"\})*)\]/,hrefRe:/href:"([^"]+)"/g,q:`"`}]){let i=t.exec(c);if(!i)continue;let a=new Set([...i[1].matchAll(n)].map(e=>e[1])),l=o.filter(e=>!a.has(e));if(l.length===0){console.log(`[patch] All CSS already included`);return}let u=l.map(e=>`{href:${r}${e}${r}}`).join(`,`);c=c.replace(i[0],`css:[${i[1]},${u}]`),s(e,c),console.log(`[patch] ✅ Added ${l.length} CSS files to SSR bundle`);return}console.warn(`[patch] Could not find CSS array in SSR bundle`)}function h(t,o){let c=[l(t,`node_modules`,`.nitro`,`vite`,`services`,`ssr`,`assets`)];for(let u of c){if(!n(u))continue;let c=i(u).filter(e=>e.endsWith(`.css`));if(c.length===0)continue;let d=[l(o,`assets`),l(t,`.output`,`public`,`assets`),l(t,`.netlify`,`functions-internal`,`server`,`public`,`assets`)];for(let t of d){r(t,{recursive:!0});for(let n of c)e(l(u,n),l(t,`ssr-${n}`))}let f=c[0],p=a(l(u,f)).length,m=`ssr-${f}`;console.log(`[ssr-css] Copied SSR CSS → /assets/${m} (${p} bytes)`);let h=[l(t,`.output`,`server`,`index.mjs`),l(t,`.netlify`,`functions-internal`,`server`,`main.mjs`)];for(let e of h){if(!n(e))continue;let t=a(e,`utf-8`),r=`/assets/${m}`;if(t.includes(r))continue;let i=/"\/assets\/[^"]+\.css":\{type:`text\/css[^}]+\}/.exec(t);if(i){let n=new Date().toISOString(),a=`,"${r}":{type:\`text/css; charset=utf-8\`,etag:\`${`"${p.toString(16)}-ssr"`}\`,mtime:\`${n}\`,size:${p},path:\`../public/assets/${m}\`}`;t=t.replace(i[0],i[0]+a),s(e,t),console.log(`[ssr-css] ✅ Patched asset manifest in ${e}`)}}return}console.log(`[ssr-css] No SSR CSS files found`)}function g(t){let i=[l(t,`assets`,`islands`),l(t,`islands`)].find(e=>n(e));if(!i)return;let o=d(i,e=>e.endsWith(`.js`)&&!e.endsWith(`.js.map`));if(o.length===0)return;let f=o.filter(e=>/-[A-Za-z0-9_-]{6,12}\.js$/.test(e));if(f.length===0){console.log(`[redirects] ${o.length} island(s) found with clean paths — no redirects needed`);return}let p=[];for(let n of f){let i=`/${u(t,n).replaceAll(`\\`,`/`)}`,a=i.replace(`/assets/`,`/`).replace(/-[A-Za-z0-9_-]{6,12}\.js$/,`.js`);p.push(`${a} ${i} 200`);let o=l(t,a.slice(1));r(c(o),{recursive:!0}),e(n,o)}let m=l(t,`_redirects`),h=n(m)?a(m,`utf-8`):``;h=h.replaceAll(/# Island JS path rewrites[^\n]*\n(?:\/islands\/[^\n]*\n)*/g,``).trim();let g=`# Island JS path rewrites (generated by Avalon post-build)
|
|
2
|
+
`;s(m,h?`${h}\n\n${g}${p.join(`
|
|
3
|
+
`)}\n`:`${g+p.join(`
|
|
4
|
+
`)}\n`),console.log(`[redirects] ✅ Wrote ${p.length} island redirects + local copies`)}function _(t,a){let o=[l(t,`.output`,`public`,`_adapters`),l(a,`_adapters`)];for(let t of o){if(!n(t))continue;let o=i(t).filter(e=>e.endsWith(`.js`));if(o.length===0)continue;let s=l(a,`_adapters`);r(s,{recursive:!0});for(let n of o){let r=l(t,n),i=l(s,n);r!==i&&e(r,i)}console.log(`[adapters] ✅ Copied ${o.length} framework adapters`);return}console.log(`[adapters] No _adapters/ directory found`)}function v(e){let r=l(e,`.netlify`,`functions-internal`,`server`);if(!n(r))return;let i=[l(e,`.netlify`,`v1`,`functions`,`server`)];for(let n of i){t(r,n,{recursive:!0,force:!0});let i=n.substring(e.length).replaceAll(`\\`,`/`);console.log(`[netlify-fn] ✅ Copied server function to ${i}/`)}}function y(e){let t=a(e,`utf-8`);return t.includes(`path: "/*"`)||t.includes("path:`/*`")}function b(e,t,r){let i=l(c(e),`_prerender-server.mjs`),a=``,o=[l(r,`node_modules`,`urlpattern-polyfill`,`index.js`),l(r,`node_modules`,`urlpattern-polyfill`,`dist`,`urlpattern.js`)].find(e=>n(e));return o&&(a=`import '${o.replaceAll(`\\`,`/`)}';`),s(i,`
|
|
5
|
+
${a}
|
|
6
|
+
import { createServer } from 'node:http';
|
|
7
|
+
import handler from './main.mjs';
|
|
8
|
+
const PORT = ${t};
|
|
9
|
+
const server = createServer(async (req, res) => {
|
|
10
|
+
try {
|
|
11
|
+
const url = new URL(req.url, 'http://localhost:' + PORT);
|
|
12
|
+
const headers = new Headers();
|
|
13
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
14
|
+
if (value) headers.set(key, Array.isArray(value) ? value.join(', ') : value);
|
|
15
|
+
}
|
|
16
|
+
const request = new Request(url.toString(), { method: req.method, headers });
|
|
17
|
+
const response = await handler(request);
|
|
18
|
+
res.writeHead(response.status, Object.fromEntries(response.headers.entries()));
|
|
19
|
+
const body = await response.text();
|
|
20
|
+
res.end(body);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
console.error('[prerender-wrapper] Error:', err);
|
|
23
|
+
res.writeHead(500);
|
|
24
|
+
res.end('Internal Server Error');
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
server.listen(PORT, '127.0.0.1', () => {
|
|
28
|
+
console.log('[prerender-wrapper] Listening on http://127.0.0.1:' + PORT);
|
|
29
|
+
});
|
|
30
|
+
`),i}function x(e){let t=[],n=/<a\s[^>]*href=["']([^"'#?]+)/gi,r=null;for(r=n.exec(e);r!==null;r=n.exec(e)){let e=r[1];e.startsWith(`/`)&&!e.startsWith(`//`)&&!e.startsWith(`/assets/`)&&!e.startsWith(`/islands/`)&&!e.startsWith(`/chunks/`)&&!e.startsWith(`/_`)&&!e.match(/\.\w{2,5}$/)&&t.push(e)}return[...new Set(t)]}async function S(t,i,d,f){let p=[l(t,`.netlify`,`functions-internal`,`server`,`server.mjs`),l(t,`.netlify`,`v1`,`functions`,`server`,`server.mjs`),l(t,`.output`,`server`,`index.mjs`)].find(e=>n(e));if(!p){console.log(`[prerender] No server entry found, skipping prerender`);return}let m=[l(t,`.output`,`public`),i].find(e=>n(e));if(!m){console.log(`[prerender] No output directory found, skipping prerender`);return}let h={routes:d.routes??[`/`],crawlLinks:d.crawlLinks??!0,ignore:d.ignore??[],failOnError:d.failOnError??!1,concurrency:d.concurrency??4,autoSubfolderIndex:d.autoSubfolderIndex??!0,retry:d.retry??3,retryDelay:d.retryDelay??500},g=`http://localhost:${f}`,_=y(p),v=p;if(_){let e=l(c(p),`main.mjs`);if(!n(e)){console.error(`[prerender] Netlify handler detected but main.mjs not found`);return}v=b(e,f,t),console.log(`[prerender] Netlify handler detected — using wrapper`)}{let e=[p,l(c(p),`main.mjs`)].filter(e=>n(e));for(let n of e){let e=a(n,`utf-8`),r=/"\/[^"]*\.html":\{[^}]+\},?/g,i=e.length;e=e.replaceAll(r,``),i!==e.length&&(s(n,e),console.log(`[prerender] Patched ${u(t,n)}: removed HTML asset entries`))}}console.log(`[prerender] Starting prerender with server: ${u(t,v)}`);let{spawn:S}=await import(`node:child_process`),C;try{C=S(`node`,[v],{env:{...process.env,PORT:String(f),NITRO_PORT:String(f),HOST:`127.0.0.1`,NITRO_HOST:`127.0.0.1`,NODE_ENV:`production`},stdio:[`ignore`,`pipe`,`pipe`]}),C.stdout?.on(`data`,e=>{let t=e.toString().trim();t&&console.log(`[prerender:server] ${t}`)}),C.stderr?.on(`data`,e=>{let t=e.toString().trim();t&&console.error(`[prerender:server:err] ${t}`)});let e=Date.now(),t=!1;for(;Date.now()-e<15e3;){try{let e=await fetch(`${g}/`);if(e.ok||e.status<500){t=!0;break}}catch{}await new Promise(e=>setTimeout(e,200))}if(!t)throw Error(`Server did not become ready within 15s`);console.log(`[prerender] Server ready at ${g}`)}catch(e){C?.kill(`SIGKILL`),console.error(`[prerender] Failed to start server:`,e.message);return}let w=new Set,T=[...h.routes],E=[],D=[];function O(e){for(let t of h.ignore)if(typeof t==`string`&&e===t||typeof t==`string`&&t.endsWith(`/**`)&&e.startsWith(t.slice(0,-2)))return!0;return!1}try{for(;T.length>0;){let e=T.splice(0,h.concurrency);await Promise.all(e.map(async e=>{let t=e.endsWith(`/`)&&e!==`/`?e.slice(0,-1):e;if(w.has(t)||(w.add(t),O(t)))return;let n=null;for(let e=1;e<=h.retry;e++)try{let e=await fetch(`${g}${t}`);n={html:await e.text(),status:e.status};break}catch{e<h.retry&&await new Promise(e=>setTimeout(e,h.retryDelay))}if(!n){D.push({route:t,error:`Failed after ${h.retry} attempts`});return}if(n.status>=400){D.push({route:t,error:`Returned ${n.status}`});return}let i=h.autoSubfolderIndex?l(t,`index.html`):`${t}.html`,a=l(m,i);if(r(c(a),{recursive:!0}),s(a,n.html.replace(`<!DOCTYPE html>`,`<!DOCTYPE html>
|
|
31
|
+
<!-- SSG: prerendered at build time -->`)),E.push(t),console.log(`[prerender] ✅ ${t} → ${i}`),h.crawlLinks)for(let e of x(n.html)){let t=e.endsWith(`/`)&&e!==`/`?e.slice(0,-1):e;!w.has(t)&&!O(t)&&T.push(t)}}))}}finally{console.log(`[prerender] Shutting down server...`),C?.kill(`SIGKILL`)}if(console.log(`[prerender] Done: ${E.length} page(s) prerendered`+(D.length>0?`, ${D.length} error(s)`:``)),_){let e=l(c(p),`_prerender-server.mjs`);n(e)&&(o(e),console.log(`[prerender] Cleaned up wrapper script`))}if(E.length>0){let a=[i,l(t,`.netlify`,`functions-internal`,`server`,`public`),l(t,`.netlify`,`v1`,`functions`,`server`,`public`)].filter(e=>e!==m&&n(c(e)));for(let t of a)for(let i of E){let a=l(i,`index.html`),o=l(m,a),s=l(t,a);n(o)&&(r(c(s),{recursive:!0}),e(o,s))}a.length>0&&console.log(`[prerender] Copied prerendered HTML to ${a.length} additional output dir(s)`)}}export async function runPostBuild(e={}){let t=e.cwd??process.cwd(),r=l(t,`dist`),i=e.prerenderPort??13172;p(t),h(t,r);for(let e of[l(t,`.netlify`,`functions-internal`,`server`,`_ssr`,`ssr.mjs`),l(t,`.netlify`,`v1`,`functions`,`server`,`_ssr`,`ssr.mjs`),l(t,`.output`,`server`,`_ssr`,`ssr.mjs`)])n(e)&&(console.log(`[patch] Patching ${e}`),m(e,r,t));g(r),_(t,r),v(t),e.prerender!==!1&&await S(t,r,e.prerender??{},i),console.log(`[post-build] ✅ Complete`)}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Avalon type definitions.
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Include this in your tsconfig.json `types` array to get island prop support:
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* ```json
|
|
7
7
|
* {
|
|
8
8
|
* "compilerOptions": {
|
|
@@ -13,10 +13,13 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
// Re-export island prop types
|
|
16
|
-
export * from
|
|
16
|
+
export * from "./island-prop.d.ts";
|
|
17
17
|
|
|
18
18
|
// Import JSX augmentations (side-effect import for type augmentation)
|
|
19
|
-
import
|
|
19
|
+
import "./island-jsx.d.ts";
|
|
20
20
|
|
|
21
21
|
// Import image type declarations
|
|
22
|
-
import
|
|
22
|
+
import "./image.d.ts";
|
|
23
|
+
|
|
24
|
+
// Import virtual module declarations (virtual:avalon/*)
|
|
25
|
+
import "./virtual-modules.d.ts";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type declarations for Avalon virtual modules.
|
|
3
|
+
*
|
|
4
|
+
* These modules are resolved at build time by Avalon's Vite plugin.
|
|
5
|
+
* Including `@useavalon/avalon/types` in your tsconfig `types` array
|
|
6
|
+
* makes these declarations available project-wide.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
declare module "virtual:avalon/page-loader" {
|
|
10
|
+
export function loadPage(
|
|
11
|
+
pathname: string,
|
|
12
|
+
): { default: unknown; metadata?: Record<string, unknown> } | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare module "virtual:avalon/config" {
|
|
16
|
+
const config: {
|
|
17
|
+
streaming: boolean;
|
|
18
|
+
pagesDir: string;
|
|
19
|
+
layoutsDir: string;
|
|
20
|
+
isDev: boolean;
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
};
|
|
23
|
+
export default config;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
declare module "virtual:avalon/layouts" {
|
|
27
|
+
import type { PageModule, NitroRenderContext } from "@useavalon/avalon/nitro/types";
|
|
28
|
+
export function wrapWithLayouts(
|
|
29
|
+
pageHtml: string,
|
|
30
|
+
pageModule: PageModule,
|
|
31
|
+
context: NitroRenderContext,
|
|
32
|
+
injectAssets?: (html: string) => string,
|
|
33
|
+
): Promise<string>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare module "virtual:avalon/assets" {
|
|
37
|
+
export function injectAssets(html: string): string;
|
|
38
|
+
export const clientAssets: {
|
|
39
|
+
css: Array<{ href: string; [key: string]: string }>;
|
|
40
|
+
js: Array<{ href: string; [key: string]: string }>;
|
|
41
|
+
entry: string;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
declare module "virtual:avalon/renderer" {
|
|
46
|
+
const handler: {
|
|
47
|
+
(event: unknown): Promise<Response>;
|
|
48
|
+
fetch(request: Request): Promise<Response>;
|
|
49
|
+
};
|
|
50
|
+
export default handler;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
declare module "virtual:avalon/client-entry" {
|
|
54
|
+
// Side-effect-only module: imports hydration runtime, global CSS, and layout CSS.
|
|
55
|
+
// No exports — just import it as your client entry point.
|
|
56
|
+
}
|
|
@@ -15,6 +15,10 @@ export declare const VIRTUAL_MODULE_IDS: {
|
|
|
15
15
|
readonly ISLAND_MANIFEST: "virtual:avalon/island-manifest";
|
|
16
16
|
readonly RUNTIME_CONFIG: "virtual:avalon/runtime-config";
|
|
17
17
|
readonly CONFIG: "virtual:avalon/config";
|
|
18
|
+
readonly LAYOUTS: "virtual:avalon/layouts";
|
|
19
|
+
readonly ASSETS: "virtual:avalon/assets";
|
|
20
|
+
readonly RENDERER: "virtual:avalon/renderer";
|
|
21
|
+
readonly CLIENT_ENTRY: "virtual:avalon/client-entry";
|
|
18
22
|
};
|
|
19
23
|
export declare const RESOLVED_VIRTUAL_IDS: {
|
|
20
24
|
readonly PAGE_ROUTES: string;
|
|
@@ -22,6 +26,10 @@ export declare const RESOLVED_VIRTUAL_IDS: {
|
|
|
22
26
|
readonly ISLAND_MANIFEST: string;
|
|
23
27
|
readonly RUNTIME_CONFIG: string;
|
|
24
28
|
readonly CONFIG: string;
|
|
29
|
+
readonly LAYOUTS: string;
|
|
30
|
+
readonly ASSETS: string;
|
|
31
|
+
readonly RENDERER: string;
|
|
32
|
+
readonly CLIENT_ENTRY: string;
|
|
25
33
|
};
|
|
26
34
|
export interface NitroIntegrationResult {
|
|
27
35
|
nitroOptions: NitroConfigOutput;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{existsSync as e}from"node:fs";import{stat as t}from"node:fs/promises";import{createRequire as n}from"node:module";import{dirname as r,join as i}from"node:path";import{nitro as a}from"nitro/vite";import{isRunnableDevEnvironment as o}from"vite";import{getUniversalCSSForHead as s}from"../islands/universal-css-collector.js";import{getUniversalHeadForInjection as c}from"../islands/universal-head-collector.js";import{clearMiddlewareCache as l,discoverScopedMiddleware as u,executeScopedMiddleware as d}from"../middleware/index.js";import{createNitroConfig as f}from"../nitro/config.js";import{createIslandManifestPlugin as p,createNitroBuildPlugin as m,createSourceMapConfig as h,createSourceMapPlugin as g}from"../nitro/index.js";import{collectCssFromModuleGraph as _,injectSsrCss as v}from"../render/collect-css.js";import{generateErrorPage as y,generateFallback404 as b}from"../render/error-pages.js";function x(t){let a=i(r(n(import.meta.url).resolve(`@useavalon/avalon`)),t);if(t.endsWith(`.ts`)&&!e(a)){let t=a.replace(/\.ts$/,`.js`);if(e(t))return t}return a}function S(t,a){let o=i(r(n(i(process.cwd(),`package.json`)).resolve(`@useavalon/${t}`)),a);if(a.endsWith(`.ts`)&&!e(o)){let t=o.replace(/\.ts$/,`.js`);if(e(t))return t}return o}export const VIRTUAL_MODULE_IDS={PAGE_ROUTES:`virtual:avalon/page-routes`,PAGE_LOADER:`virtual:avalon/page-loader`,ISLAND_MANIFEST:`virtual:avalon/island-manifest`,RUNTIME_CONFIG:`virtual:avalon/runtime-config`,CONFIG:`virtual:avalon/config`};export const RESOLVED_VIRTUAL_IDS={PAGE_ROUTES:`\0`+VIRTUAL_MODULE_IDS.PAGE_ROUTES,PAGE_LOADER:`\0`+VIRTUAL_MODULE_IDS.PAGE_LOADER,ISLAND_MANIFEST:`\0`+VIRTUAL_MODULE_IDS.ISLAND_MANIFEST,RUNTIME_CONFIG:`\0`+VIRTUAL_MODULE_IDS.RUNTIME_CONFIG,CONFIG:`\0`+VIRTUAL_MODULE_IDS.CONFIG};export function createNitroIntegration(e,t={}){let n=f(t,e),r={preset:n.preset,serverDir:t.serverDir??n.serverDir??`./server`,routeRules:n.routeRules,runtimeConfig:n.runtimeConfig,compatibilityDate:n.compatibilityDate,scanDirs:[`.`],noExternals:[`estree-walker`,/^@useavalon\//,/^estree-util/]};t.renderer===!1?r.renderer=!1:n.renderer&&(r.renderer=n.renderer),n.publicRuntimeConfig&&(r.publicRuntimeConfig=n.publicRuntimeConfig),n.publicAssets&&(r.publicAssets=n.publicAssets),n.compressPublicAssets&&(r.compressPublicAssets=n.compressPublicAssets),n.serverEntry&&(r.serverEntry=n.serverEntry);let i=n.traceDeps??[];r.traceDeps=[...new Set([`undici`,...i])],n.prerender&&(r.prerender={routes:[],crawlLinks:!1});let o=a(r),s=createNitroCoordinationPlugin({avalonConfig:e,nitroConfig:t,verbose:e.verbose}),c=createVirtualModulesPlugin({avalonConfig:e,nitroConfig:t,verbose:e.verbose}),l=m(e,t),u=p(e,{verbose:e.verbose,generatePreloadHints:!0}),d=g(h(t.preset??`node_server`,e.isDev));return{nitroOptions:n,plugins:[...Array.isArray(o)?o:[o],s,c,l,u,d]}}export function createNitroCoordinationPlugin(e){let{avalonConfig:t,verbose:n}=e;return{name:`avalon:nitro-coordination`,enforce:`pre`,configResolved(e){globalThis.__avalonConfig=t},configureServer(e){globalThis.__viteDevServer=e;let r=null;async function i(){return r||=await u({baseDir:`${e.config.root||process.cwd()}/src`,devMode:!1}),r}function a(){r=null}j(e,t,n,a),i().catch(e=>{console.warn(`[middleware] Failed to discover middleware:`,e)});let s=e.environments?.ssr,c=!!s&&o(s);c&&k(e,t.integrations,n).catch(e=>{console.error(`[prewarm] Core modules pre-warm failed:`,e)}),e.middlewares.use(async(r,a,o)=>{if(!c)return o();let s=r.url||`/`;if(s.endsWith(`.html`)&&(s=s.slice(0,-5)||`/`),s===`/index`&&(s=`/`),s.startsWith(`/@`)||s.startsWith(`/__`)||s.startsWith(`/node_modules/`)||s.startsWith(`/src/client/`)||s.startsWith(`/packages/`)||s.includes(`.`)&&!s.endsWith(`/`)||s.startsWith(`/api/`))return o();try{if(await D(e,s,r,a,i,n)||await
|
|
1
|
+
import{existsSync as e}from"node:fs";import{stat as t}from"node:fs/promises";import{createRequire as n}from"node:module";import{dirname as r,join as i}from"node:path";import{nitro as a}from"nitro/vite";import{isRunnableDevEnvironment as o}from"vite";import{getUniversalCSSForHead as s}from"../islands/universal-css-collector.js";import{getUniversalHeadForInjection as c}from"../islands/universal-head-collector.js";import{clearMiddlewareCache as l,discoverScopedMiddleware as u,executeScopedMiddleware as d}from"../middleware/index.js";import{createNitroConfig as f}from"../nitro/config.js";import{createIslandManifestPlugin as p,createNitroBuildPlugin as m,createSourceMapConfig as h,createSourceMapPlugin as g}from"../nitro/index.js";import{collectCssFromModuleGraph as _,injectSsrCss as v}from"../render/collect-css.js";import{generateErrorPage as y,generateFallback404 as b}from"../render/error-pages.js";function x(t){let a=i(r(n(import.meta.url).resolve(`@useavalon/avalon`)),t);if(t.endsWith(`.ts`)&&!e(a)){let t=a.replace(/\.ts$/,`.js`);if(e(t))return t}return a}function S(t,a){let o=i(r(n(i(process.cwd(),`package.json`)).resolve(`@useavalon/${t}`)),a);if(a.endsWith(`.ts`)&&!e(o)){let t=o.replace(/\.ts$/,`.js`);if(e(t))return t}return o}export const VIRTUAL_MODULE_IDS={PAGE_ROUTES:`virtual:avalon/page-routes`,PAGE_LOADER:`virtual:avalon/page-loader`,ISLAND_MANIFEST:`virtual:avalon/island-manifest`,RUNTIME_CONFIG:`virtual:avalon/runtime-config`,CONFIG:`virtual:avalon/config`,LAYOUTS:`virtual:avalon/layouts`,ASSETS:`virtual:avalon/assets`,RENDERER:`virtual:avalon/renderer`,CLIENT_ENTRY:`virtual:avalon/client-entry`};export const RESOLVED_VIRTUAL_IDS={PAGE_ROUTES:`\0`+VIRTUAL_MODULE_IDS.PAGE_ROUTES,PAGE_LOADER:`\0`+VIRTUAL_MODULE_IDS.PAGE_LOADER,ISLAND_MANIFEST:`\0`+VIRTUAL_MODULE_IDS.ISLAND_MANIFEST,RUNTIME_CONFIG:`\0`+VIRTUAL_MODULE_IDS.RUNTIME_CONFIG,CONFIG:`\0`+VIRTUAL_MODULE_IDS.CONFIG,LAYOUTS:`\0`+VIRTUAL_MODULE_IDS.LAYOUTS,ASSETS:`\0`+VIRTUAL_MODULE_IDS.ASSETS,RENDERER:`\0`+VIRTUAL_MODULE_IDS.RENDERER,CLIENT_ENTRY:`\0`+VIRTUAL_MODULE_IDS.CLIENT_ENTRY};export function createNitroIntegration(e,t={}){let n=f(t,e),r={preset:n.preset,serverDir:t.serverDir??n.serverDir??`./server`,routeRules:n.routeRules,runtimeConfig:n.runtimeConfig,compatibilityDate:n.compatibilityDate,scanDirs:[`.`],noExternals:[`estree-walker`,/^@useavalon\//,/^estree-util/]};t.renderer===!1?r.renderer=!1:n.renderer&&(r.renderer=n.renderer),n.publicRuntimeConfig&&(r.publicRuntimeConfig=n.publicRuntimeConfig),n.publicAssets&&(r.publicAssets=n.publicAssets),n.compressPublicAssets&&(r.compressPublicAssets=n.compressPublicAssets),n.serverEntry&&(r.serverEntry=n.serverEntry);let i=n.traceDeps??[];r.traceDeps=[...new Set([`undici`,...i])],n.prerender&&(r.prerender={routes:[],crawlLinks:!1});let o=a(r),s=createNitroCoordinationPlugin({avalonConfig:e,nitroConfig:t,verbose:e.verbose}),c=createVirtualModulesPlugin({avalonConfig:e,nitroConfig:t,verbose:e.verbose}),l=m(e,t),u=p(e,{verbose:e.verbose,generatePreloadHints:!0}),d=g(h(t.preset??`node_server`,e.isDev));return{nitroOptions:n,plugins:[...Array.isArray(o)?o:[o],s,c,l,u,d]}}export function createNitroCoordinationPlugin(e){let{avalonConfig:t,verbose:n}=e;return{name:`avalon:nitro-coordination`,enforce:`pre`,configResolved(e){globalThis.__avalonConfig=t},configureServer(e){globalThis.__viteDevServer=e;let r=null;async function i(){return r||=await u({baseDir:`${e.config.root||process.cwd()}/src`,devMode:!1}),r}function a(){r=null}j(e,t,n,a),i().catch(e=>{console.warn(`[middleware] Failed to discover middleware:`,e)});let s=e.environments?.ssr,c=!!s&&o(s);c&&k(e,t.integrations,n).catch(e=>{console.error(`[prewarm] Core modules pre-warm failed:`,e)}),e.middlewares.use(async(r,a,o)=>{if(!c)return o();let s=r.url||`/`;if(s.endsWith(`.html`)&&(s=s.slice(0,-5)||`/`),s===`/index`&&(s=`/`),s.startsWith(`/@`)||s.startsWith(`/__`)||s.startsWith(`/node_modules/`)||s.startsWith(`/src/client/`)||s.startsWith(`/packages/`)||s.includes(`.`)&&!s.endsWith(`/`)||s.startsWith(`/api/`))return o();try{if(await D(e,s,r,a,i,n)||await U(e,s,t,a))return;let o=await W(e,s,t);if(o){a.statusCode=200,a.setHeader(`Content-Type`,`text/html`),a.end(o);return}await O(e,s,a,t)}catch(e){console.error(`[SSR Error]`,e),a.statusCode=500,a.setHeader(`Content-Type`,`text/html`),a.end(y(e))}})},buildStart(){}}}async function D(e,t,n,r,i,a){let o=performance.now(),s=await i();if(s.length===0)return!1;let c={};for(let[e,t]of Object.entries(n.headers))typeof t==`string`?c[e]=t:Array.isArray(t)&&(c[e]=t.join(`, `));let l=`http://${n.headers.host||`localhost`}${t}`,u=await d({url:l,method:n.method||`GET`,path:t,node:{req:n,res:r},req:new Request(l,{method:n.method||`GET`,headers:c}),context:{}},s,{devMode:!1}),f=performance.now()-o;return f>100&&console.warn(`⚠️ Slow middleware: ${f.toFixed(0)}ms for ${t}`),u?(r.statusCode=u.status,u.headers.forEach((e,t)=>{r.setHeader(t,e)}),r.end(await u.text()),!0):!1}async function O(e,t,n,r){try{let{discoverErrorPages:i,getErrorPageModule:a,generateDefaultErrorPage:o}=await import(`../nitro/error-handler.js`),s=a(404,await i({isDev:r.isDev,pagesDir:r.pagesDir,loadPageModule:async t=>await e.ssrLoadModule(t)}));if(s?.default&&typeof s.default==`function`){let{renderToHtml:e}=await import(`../render/ssr.js`),r=s.default,i=await e({component:()=>r({statusCode:404,message:`Page not found: ${t}`,url:t})},{});n.statusCode=404,n.setHeader(`Content-Type`,`text/html`),n.end(i);return}let c=o(404,`Page not found: ${t}`,r.isDev);n.statusCode=404,n.setHeader(`Content-Type`,`text/html`),n.end(c)}catch{n.statusCode=404,n.setHeader(`Content-Type`,`text/html`),n.end(b(t))}}async function k(e,t,n){let r=performance.now(),i=[{path:x(`src/render/ssr.ts`),assignTo:`ssr`},{path:x(`src/core/layout/enhanced-layout-resolver.ts`),assignTo:`layout`},{path:x(`src/middleware/index.ts`),assignTo:null},...t.map(e=>({path:S(e,`server/renderer.ts`),assignTo:null}))],a=(await Promise.allSettled(i.map(async({path:t,assignTo:n})=>{let r=await e.ssrLoadModule(t);n===`ssr`&&(V=r),n===`layout`&&(H=r)}))).filter(e=>e.status===`fulfilled`).length,o=performance.now()-r;n&&a>0&&console.log(`🔥 SSR ready in ${o.toFixed(0)}ms (${a}/${i.length} core modules)`)}export function createVirtualModulesPlugin(e){let{avalonConfig:t,nitroConfig:n,verbose:r}=e;return{name:`avalon:nitro-virtual-modules`,enforce:`pre`,resolveId(e){return e===VIRTUAL_MODULE_IDS.PAGE_ROUTES?RESOLVED_VIRTUAL_IDS.PAGE_ROUTES:e===VIRTUAL_MODULE_IDS.PAGE_LOADER?RESOLVED_VIRTUAL_IDS.PAGE_LOADER:e===VIRTUAL_MODULE_IDS.ISLAND_MANIFEST?RESOLVED_VIRTUAL_IDS.ISLAND_MANIFEST:e===VIRTUAL_MODULE_IDS.RUNTIME_CONFIG?RESOLVED_VIRTUAL_IDS.RUNTIME_CONFIG:e===VIRTUAL_MODULE_IDS.CONFIG?RESOLVED_VIRTUAL_IDS.CONFIG:e===VIRTUAL_MODULE_IDS.LAYOUTS?RESOLVED_VIRTUAL_IDS.LAYOUTS:e===VIRTUAL_MODULE_IDS.ASSETS?RESOLVED_VIRTUAL_IDS.ASSETS:e===VIRTUAL_MODULE_IDS.RENDERER?RESOLVED_VIRTUAL_IDS.RENDERER:e===VIRTUAL_MODULE_IDS.CLIENT_ENTRY?RESOLVED_VIRTUAL_IDS.CLIENT_ENTRY:null},async load(e){return e===RESOLVED_VIRTUAL_IDS.PAGE_ROUTES?await M(t,r):e===RESOLVED_VIRTUAL_IDS.PAGE_LOADER?await N(t,r):e===RESOLVED_VIRTUAL_IDS.ISLAND_MANIFEST?P():e===RESOLVED_VIRTUAL_IDS.RUNTIME_CONFIG?F(t,n):e===RESOLVED_VIRTUAL_IDS.CONFIG?generateConfigModule(t,n):e===RESOLVED_VIRTUAL_IDS.LAYOUTS?await I(t):e===RESOLVED_VIRTUAL_IDS.ASSETS?L(n):e===RESOLVED_VIRTUAL_IDS.RENDERER?R(t):e===RESOLVED_VIRTUAL_IDS.CLIENT_ENTRY?await z(t,n):null},handleHotUpdate({file:e,server:n}){if(e.includes(t.pagesDir)){let e=n.moduleGraph.getModuleById(RESOLVED_VIRTUAL_IDS.PAGE_ROUTES);e&&n.moduleGraph.invalidateModule(e)}if(e.includes(`/layouts/`)||e.includes(`_layout`)){let e=n.moduleGraph.getModuleById(RESOLVED_VIRTUAL_IDS.LAYOUTS);e&&n.moduleGraph.invalidateModule(e);let t=n.moduleGraph.getModuleById(RESOLVED_VIRTUAL_IDS.CLIENT_ENTRY);t&&n.moduleGraph.invalidateModule(t)}if(e.includes(`vite.config`)||e.includes(`avalon.config`)||e.includes(`nitro.config`)){let e=n.moduleGraph.getModuleById(RESOLVED_VIRTUAL_IDS.CONFIG);e&&n.moduleGraph.invalidateModule(e)}}}}function j(e,t,n,r){e.watcher.on(`change`,e=>{e.includes(`_middleware`)&&(l(),r?.()),(e.includes(`/render/`)||e.includes(`/layout/`)||e.includes(`/islands/`))&&(V=null,H=null),(e.includes(`/layouts/`)||e.includes(`_layout`))&&globalThis.__avalonLayoutResolver?.clearCache?.()}),e.watcher.on(`add`,e=>{e.includes(`_middleware`)&&(l(),r?.())}),e.watcher.on(`unlink`,e=>{e.includes(`_middleware`)&&(l(),r?.())})}async function M(e,t){try{let{getAllPageDirs:t}=await import(`./module-discovery.js`),{discoverPageRoutesFromMultipleDirs:n}=await import(`../nitro/route-discovery.js`),r=await n(await t(e.pagesDir,e.modules,process.cwd()),{developmentMode:e.isDev});return`export const pageRoutes = ${JSON.stringify(r,null,2)};\nexport default pageRoutes;\n`}catch{return`export const pageRoutes = [];
|
|
2
2
|
export default pageRoutes;
|
|
3
3
|
`}}async function N(e,t){try{let{getAllPageDirs:t}=await import(`./module-discovery.js`),{discoverPageRoutesFromMultipleDirs:n}=await import(`../nitro/route-discovery.js`),{relative:r}=await import(`node:path`),i=process.cwd(),a=await n(await t(e.pagesDir,e.modules,i),{developmentMode:e.isDev}),o=[],s=[];for(let e=0;e<a.length;e++){let t=a[e],n=`page_${e}`,c=r(i,t.filePath).replaceAll(`\\`,`/`),l=c.startsWith(`/`)?c:`/`+c;o.push(`import * as ${n} from '${l}';`),s.push(` { pattern: ${JSON.stringify(t.pattern)}, params: ${JSON.stringify(t.params)}, module: ${n} }`)}return[...o,``,`const routes = [`,s.join(`,
|
|
4
4
|
`),`];`,``,`/**`,` * Match a pathname against discovered routes and return the page module.`,` * Uses the same pattern matching as Avalon's route discovery.`,` */`,`export function loadPage(pathname) {`,` const cleanPath = pathname.split('?')[0];`,` for (const route of routes) {`,` if (matchRoute(cleanPath, route.pattern, route.params)) {`,` return route.module;`,` }`,` }`,` return null;`,`}`,``,`function matchRoute(pathname, pattern, paramNames) {`,` // Exact match`,` if (pattern === pathname) return true;`,` // Normalize trailing slashes`,` const normPath = pathname === '/' ? '/' : pathname.replace(/\\/$/, '');`,` const normPattern = pattern === '/' ? '/' : pattern.replace(/\\/$/, '');`,` if (normPath === normPattern) return true;`,` // Dynamic segments: /users/:id matches /users/123`,` if (paramNames.length > 0) {`,` const patternParts = normPattern.split('/');`,` const pathParts = normPath.split('/');`,` if (patternParts.length !== pathParts.length) return false;`,` return patternParts.every((part, i) => part.startsWith(':') || part === pathParts[i]);`,` }`,` return false;`,`}`,``,`export default { loadPage, routes };`,``].join(`
|
|
@@ -6,41 +6,46 @@ export default pageRoutes;
|
|
|
6
6
|
export default { loadPage, routes: [] };
|
|
7
7
|
`}}function P(){return`export const islandManifest = { islands: {}, clientEntry: "", css: [] };
|
|
8
8
|
export default islandManifest;
|
|
9
|
-
`}function F(e,t){let n={avalon:{streaming:t.streaming??!0,pagesDir:e.pagesDir,layoutsDir:e.layoutsDir,isDev:e.isDev},...t.runtimeConfig};return`export const runtimeConfig = ${JSON.stringify(n,null,2)};\nexport function useRuntimeConfig() { return runtimeConfig; }\nexport default runtimeConfig;\n`}export function generateConfigModule(e,t){let n={streaming:t.streaming??!0,pagesDir:e.pagesDir,layoutsDir:e.layoutsDir,isDev:e.isDev,...t.runtimeConfig};return`const config = ${JSON.stringify(n,null,2)};\nexport function useAvalonConfig() { return config; }\nexport default config;\n`}
|
|
9
|
+
`}function F(e,t){let n={avalon:{streaming:t.streaming??!0,pagesDir:e.pagesDir,layoutsDir:e.layoutsDir,isDev:e.isDev},...t.runtimeConfig};return`export const runtimeConfig = ${JSON.stringify(n,null,2)};\nexport function useRuntimeConfig() { return runtimeConfig; }\nexport default runtimeConfig;\n`}export function generateConfigModule(e,t){let n={streaming:t.streaming??!0,pagesDir:e.pagesDir,layoutsDir:e.layoutsDir,isDev:e.isDev,...t.runtimeConfig};return`const config = ${JSON.stringify(n,null,2)};\nexport function useAvalonConfig() { return config; }\nexport default config;\n`}async function I(e){let{getAllLayoutDirs:t}=await import(`./module-discovery.js`),{relative:n,resolve:r}=await import(`node:path`),{stat:a}=await import(`node:fs/promises`),o=process.cwd(),s=await t(e.layoutsDir,e.modules,o),c=[],l=0,u=r(o,e.layoutsDir);for(let{dir:t,prefix:r}of s){let s=i(t,`_layout.tsx`);try{if(!(await a(s)).isFile())continue}catch{continue}let d=n(o,s).replaceAll(`\\`,`/`),f=d.startsWith(`/`)?d:`/`+d,p=t.startsWith(u),m=p&&!e.modules?`RootLayout`:`Layout_${l}`;c.push({prefix:r,importPath:f,varName:m,isShared:p}),l++}let d=c.filter(e=>e.isShared),f=c.filter(e=>!e.isShared),p=c.map(e=>`import ${e.varName} from '${e.importPath}';`),m=f.sort((e,t)=>t.prefix.length-e.prefix.length).map(e=>{let t=e.prefix===`/`;return` { prefix: ${JSON.stringify(e.prefix)}, Layout: ${e.varName}, skipRoot: ${t} }`}),h=d.length>0?d[0].varName:`null`;return[`// Auto-generated by Avalon — do not edit`,`import { h } from 'preact';`,`import preactRenderToString from 'preact-render-to-string';`,`import { getUniversalCSSForHead } from '@useavalon/avalon/islands/universal-css-collector';`,`import { getUniversalHeadForInjection } from '@useavalon/avalon/islands/universal-head-collector';`,...p,``,`const RootLayoutComponent = ${h};`,``,`const moduleLayouts = [`,m.join(`,
|
|
10
|
+
`),`];`,``,`function getLayoutsForPath(pathname) {`,` for (const entry of moduleLayouts) {`,` if (entry.prefix === '/' ? pathname === '/' : pathname.startsWith(entry.prefix)) {`,` return entry;`,` }`,` }`,` return null;`,`}`,``,`function injectUniversalAssets(html) {`,` const universalCSS = getUniversalCSSForHead(true);`,` if (universalCSS && html.includes('</head>')) {`,` html = html.replace('</head>', universalCSS + '\\n</head>');`,` }`,` const universalHead = getUniversalHeadForInjection(true);`,` if (universalHead && html.includes('</head>')) {`,` html = html.replace('</head>', universalHead + '\\n</head>');`,` }`,` return html;`,`}`,``,`export async function wrapWithLayouts(pageHtml, pageModule, context, injectAssets) {`,` const pathname = context.url.pathname;`,` const frontmatter = {`,` ...(pageModule.frontmatter || {}),`,` ...(pageModule.metadata || {}),`,` currentPath: pathname,`,` };`,` const pageLayoutConfig = pageModule.layoutConfig;`,` const skipAll = pageLayoutConfig?.skipLayouts?.includes('_layout');`,``,` const layoutEntry = getLayoutsForPath(pathname);`,` const routeInfo = { path: pathname, params: context.params, query: context.url.searchParams };`,` let html;`,``,` if (!layoutEntry || skipAll) {`,` const title = String(frontmatter.title || 'Avalon');`,` html = [`,` '<!DOCTYPE html>',`,` '<html lang="en">',`,` '<head>',`,` '<meta charset="utf-8">',`,` '<meta name="viewport" content="width=device-width, initial-scale=1">',`,` '<title>' + title + '</title>',`,` '</head>',`,` '<body>',`,` '<div id="app">' + pageHtml + '</div>',`,` '</body>',`,` '</html>',`,` ].join('\\n');`,` } else {`,` const layoutProps = {`,` children: h('div', { dangerouslySetInnerHTML: { __html: pageHtml } }),`,` frontmatter,`,` data: {},`,` route: routeInfo,`,` };`,` const layoutResult = layoutEntry.Layout(layoutProps);`,` const resolvedLayout = layoutResult instanceof Promise ? await layoutResult : layoutResult;`,` let wrappedHtml = preactRenderToString(resolvedLayout);`,``,` if (!layoutEntry.skipRoot && RootLayoutComponent) {`,` const rootProps = {`,` children: h('div', { dangerouslySetInnerHTML: { __html: wrappedHtml } }),`,` frontmatter,`,` data: {},`,` route: routeInfo,`,` };`,` const rootResult = RootLayoutComponent(rootProps);`,` const resolvedRoot = rootResult instanceof Promise ? await rootResult : rootResult;`,` wrappedHtml = preactRenderToString(resolvedRoot);`,` }`,``,` html = '<!DOCTYPE html>\\n' + wrappedHtml;`,` }`,``,` if (injectAssets) {`,` html = injectAssets(html);`,` }`,` return injectUniversalAssets(html);`,`}`,``,`export default { wrapWithLayouts };`,``].join(`
|
|
11
|
+
`)}function L(e){let t=e.clientEntry??`app/entry-client`;return[`// Auto-generated by Avalon — do not edit`,`// @ts-ignore — virtual import resolved by Nitro's Vite assets plugin at build time`,`import clientAssets from '${t.startsWith(`/`)?t:`/`+t}?assets=client';`,``,`function buildAssetTags() {`,` const cssLinks = (clientAssets?.css ?? [])`,` .map(attr => '<link rel="stylesheet" href="' + attr.href + '">')`,` .join('\\n');`,` const jsPreloads = (clientAssets?.js ?? [])`,` .map(attr => '<link rel="modulepreload" href="' + attr.href + '">')`,` .join('\\n');`,` const entryScript = clientAssets?.entry`,` ? '<script type="module" src="' + clientAssets.entry + '"><\/script>'`,` : '';`,` return { cssLinks, jsPreloads, entryScript };`,`}`,``,`export function injectAssets(html) {`,` const { cssLinks, jsPreloads, entryScript } = buildAssetTags();`,` if (html.includes('</head>')) {`,` html = html.replace('</head>', cssLinks + '\\n' + jsPreloads + '\\n</head>');`,` }`,` if (html.includes('</body>')) {`,` html = html.replace('</body>', entryScript + '\\n</body>');`,` }`,` return html;`,`}`,``,`export { clientAssets };`,`export default { injectAssets, clientAssets };`,``].join(`
|
|
12
|
+
`)}function R(e){let t=Array.isArray(e.integrations)?e.integrations:[],n=[],r=[];for(let e of t){let t=`${e}Integration`;n.push(`import { ${t} } from '@useavalon/${e}';`),r.push(`registry.register(${t});`)}return[`// Auto-generated by Avalon — do not edit`,`import { createNitroRenderer } from '@useavalon/avalon/nitro/renderer';`,`import { registerBuiltinDirectives } from '@useavalon/avalon';`,`import { registry } from '@useavalon/avalon/islands/integration-registry';`,`import avalonConfig from 'virtual:avalon/config';`,`import { loadPage } from 'virtual:avalon/page-loader';`,`import { wrapWithLayouts } from 'virtual:avalon/layouts';`,`import { injectAssets } from 'virtual:avalon/assets';`,...n,``,`// Pre-register framework integrations for SSR`,...r,``,`// Register built-in custom hydration directives (on:delay, on:scroll, etc.)`,`registerBuiltinDirectives();`,``,`export default createNitroRenderer({`,` avalonConfig,`,` isDev: avalonConfig.isDev,`,` resolvePageRoute: async (pathname) => {`,` const mod = loadPage(pathname);`,` if (!mod || !('default' in mod)) return null;`,` return { filePath: '[virtual:' + pathname + ']', pattern: pathname, params: {} };`,` },`,` loadPageModule: async (filePath) => {`,` const match = filePath.match(/^\\[virtual:(.+)\\]$/);`,` const pathname = match ? match[1] : filePath;`,` const mod = loadPage(pathname);`,` if (mod) return mod;`,` return { default: () => null, metadata: { title: 'Avalon' } };`,` },`,` wrapWithLayouts: (pageHtml, pageModule, context) =>`,` wrapWithLayouts(pageHtml, pageModule, context, injectAssets),`,`});`,``].join(`
|
|
13
|
+
`)}async function z(e,t){let{getAllLayoutDirs:n}=await import(`./module-discovery.js`),{readdir:r}=await import(`node:fs/promises`),{relative:i,join:a}=await import(`node:path`),o=process.cwd(),s=await n(e.layoutsDir,e.modules,o),c=[];for(let{dir:e}of s)try{let t=await r(e,{withFileTypes:!0});for(let n of t){if(!n.isFile()||!n.name.endsWith(`.css`))continue;let t=i(o,a(e,n.name)).replaceAll(`\\`,`/`),r=t.startsWith(`/`)?t:`/`+t;c.push(r)}}catch{}let l=[`// Auto-generated by Avalon — do not edit`,`// Island hydration runtime`,`import '@useavalon/avalon/client/main';`,``],u=t.globalCSS??[];for(let e of u){let t=e.startsWith(`/`)?e:`/`+e;l.push(`// Global CSS`),l.push(`import '${t}';`)}if(u.length>0&&l.push(``),c.length>0){l.push(`// Layout CSS (auto-discovered)`);for(let e of c)l.push(`import '${e}';`)}return l.push(``),l.join(`
|
|
14
|
+
`)}export function getViteDevServer(){return globalThis.__viteDevServer}export function getAvalonConfig(){return globalThis.__avalonConfig}export function isDevelopmentMode(){return globalThis.__avalonConfig?.isDev??!0}const B=`<!--AVALON_STREAM_BOUNDARY-->`;let V=null,H=null;async function U(e,t,n,r){if(!n.modules)return!1;let i=t.split(`?`)[0],a=await J(i,n,e);if(!a)return!1;try{let t=await e.ssrLoadModule(a),n=t.default;if(!n)return!1;let o=t.layoutConfig,l=await _(e,a),u=await q(i,e),d=[];for(let t of u){let n=await e.ssrLoadModule(t);d.push({file:t,module:n})}for(let t of u){let n=await _(e,t);l.push(...n)}if(d.length===0)return!1;let{render:f}=await e.ssrLoadModule(`preact-render-to-string`),{h:p}=await e.ssrLoadModule(`preact`),m=o?.skipLayouts||[],h=d.filter(({file:e})=>{let t=e.split(`/`).pop()?.replace(/\.[^.]+$/,``)||``;return!m.includes(t)}),g=t.frontmatter,v=t.metadata,y={children:null,frontmatter:{...g,...v,currentPath:i},params:{},url:i},b=[],x=[];for(let e of h){let t=e.module.default;if(!(!t||typeof t!=`function`))try{let n=t({...y,children:p(`div`,null,`test`)}),r=f(n instanceof Promise?await n:n);r.trim().startsWith(`<html`)||r.includes(`<!DOCTYPE`)?b.push(e):x.push(e)}catch{x.push(e)}}if(b.length===0)return!1;let{module:S}=b[b.length-1],C=S.default;if(!C||typeof C!=`function`)return!1;let w;try{let e=C({...y,children:p(`div`,{dangerouslySetInnerHTML:{__html:B}})});w=f(e instanceof Promise?await e:e)}catch{return!1}let T=w.indexOf(B);if(T===-1)return!1;let E=w.slice(0,T),D=w.slice(T+29),O=E;if(l.length>0){let e=`<style data-avalon-ssr-css>${l.join(`
|
|
10
15
|
`)}</style>`;O=E.includes(`</head>`)?E.replace(`</head>`,`${e}\n</head>`):E+e}O.trim().toLowerCase().startsWith(`<!doctype`)||(O=`<!DOCTYPE html>
|
|
11
16
|
`+O);let k=s(!0);k&&O.includes(`</head>`)&&(O=O.replace(`</head>`,`${k}\n</head>`));let A=c(!0);A&&O.includes(`</head>`)&&(O=O.replace(`</head>`,`${A}\n</head>`)),r.statusCode=200,r.setHeader(`Content-Type`,`text/html; charset=utf-8`),r.setHeader(`Transfer-Encoding`,`chunked`),r.setHeader(`X-Avalon-Streaming`,`1`),r.flushHeaders(),r.write(O);let j;try{let e=typeof n==`function`?n():n;j=f(e instanceof Promise?await e:e)}catch(e){console.error(`[SSR Streaming] Error rendering page component:`,e),j=`<div>Error rendering page</div>`}if(j.trim().startsWith(`<!DOCTYPE html>`)||j.trim().startsWith(`<html`))return r.end(j),!0;let M=j;for(let{module:e}of x){let t=e.default;if(!(!t||typeof t!=`function`))try{let e=t({...y,children:p(`div`,{dangerouslySetInnerHTML:{__html:M}})});M=f(e instanceof Promise?await e:e)}catch(e){console.error(`[SSR Streaming] Error rendering wrapper layout:`,e)}}let N=M+D;if(!N.includes(`/src/client/main.js`)&&!N.includes(`/@vite/client`)){let e=N.lastIndexOf(`</body>`);e!==-1&&(N=N.slice(0,e)+`
|
|
12
17
|
<script type="module" src="/@vite/client"><\/script>
|
|
13
18
|
<script type="module" src="/src/client/main.js"><\/script>
|
|
14
|
-
`+N.slice(e))}return r.end(N),!0}catch(e){return r.headersSent?(r.end(`<div>Streaming SSR error: ${e.message}</div></body></html>`),!0):!1}}async function
|
|
19
|
+
`+N.slice(e))}return r.end(N),!0}catch(e){return r.headersSent?(r.end(`<div>Streaming SSR error: ${e.message}</div></body></html>`),!0):!1}}async function W(e,t,n){let r=t.split(`?`)[0],i=await J(r,n,e);if(!i)return null;try{let t=await e.ssrLoadModule(i),a=t.default;if(!a)return console.warn(`[SSR] Page ${i} has no default export`),null;let o=await _(e,i),s=await q(r,e),c=[];for(let t of s){let n=await e.ssrLoadModule(t);c.push({file:t,module:n})}for(let t of s){let n=await _(e,t);o.push(...n)}let l;return l=n.modules&&c.length>0?await G(a,t,c,r,n,e):await Y(a,t,r,n,e),o.length>0&&(l=v(l,o)),l}catch(e){throw console.error(`[SSR] Error rendering ${i}:`,e),e}}async function G(e,t,n,r,i,a){let{render:o}=await a.ssrLoadModule(`preact-render-to-string`),{h:s}=await a.ssrLoadModule(`preact`),c=t.layoutConfig?.skipLayouts||[],l=n.filter(({file:e})=>{let t=e.split(`/`).pop()?.replace(/\.[^.]+$/,``)||``;return!c.includes(t)}),u;try{let t=typeof e==`function`?e():e;u=o(t instanceof Promise?await t:t)}catch(e){console.error(`[SSR] Error rendering page component:`,e),u=`<div>Error rendering page</div>`}if(u.trim().startsWith(`<!DOCTYPE html>`)||u.trim().startsWith(`<html`))return K(u);let d=t.frontmatter,f=t.metadata,p={children:null,frontmatter:{...d,...f,currentPath:r},params:{},url:r},m=[],h=[];for(let e of l){let t=e.module.default;if(!(!t||typeof t!=`function`))try{let n=t({...p,children:s(`div`,null,`test`)}),r=o(n instanceof Promise?await n:n);r.trim().startsWith(`<html`)||r.includes(`<!DOCTYPE`)?m.push(e):h.push(e)}catch{h.push(e)}}let g=u;for(let{module:e}of h){let t=e.default;if(!(!t||typeof t!=`function`))try{let e=t({...p,children:s(`div`,{dangerouslySetInnerHTML:{__html:g}})});g=o(e instanceof Promise?await e:e)}catch(e){console.error(`[SSR] Error rendering wrapper layout:`,e)}}if(m.length>0){let{module:e}=m[m.length-1],t=e.default;if(t&&typeof t==`function`)try{let e=t({...p,children:s(`div`,{dangerouslySetInnerHTML:{__html:g}})});g=o(e instanceof Promise?await e:e)}catch(e){console.error(`[SSR] Error rendering shell layout:`,e)}}if(g.trim().startsWith(`<!DOCTYPE html>`)||g.trim().startsWith(`<html`))return K(g);let _=t.metadata||{},v=_.title||`Avalon App`,y=_.description||``;return`<!DOCTYPE html>
|
|
15
20
|
<html lang="en">
|
|
16
21
|
<head>
|
|
17
22
|
<meta charset="utf-8">
|
|
18
23
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
19
|
-
<title>${
|
|
20
|
-
${y?`<meta name="description" content="${
|
|
24
|
+
<title>${X(v)}</title>
|
|
25
|
+
${y?`<meta name="description" content="${X(y)}">`:``}
|
|
21
26
|
<script type="module" src="/@vite/client"><\/script>
|
|
22
27
|
</head>
|
|
23
28
|
<body>
|
|
24
29
|
${g}
|
|
25
30
|
<script type="module" src="/src/client/main.js"><\/script>
|
|
26
31
|
</body>
|
|
27
|
-
</html>`}function
|
|
32
|
+
</html>`}function K(e){let t=e;if(t.trim().toLowerCase().startsWith(`<!doctype`)||(t=`<!DOCTYPE html>
|
|
28
33
|
`+t),!t.includes(`data-universal-ssr="true"`)){let e=s(!0);e&&t.includes(`</head>`)&&(t=t.replace(`</head>`,`${e}\n</head>`))}let n=c(!0);if(n&&t.includes(`</head>`)&&(t=t.replace(`</head>`,`${n}\n</head>`)),t.includes(`/src/client/main.js`)||t.includes(`/@vite/client`))return t;let r=t.lastIndexOf(`</body>`);return r===-1?t+`
|
|
29
34
|
<script type="module" src="/@vite/client"><\/script>
|
|
30
35
|
<script type="module" src="/src/client/main.js"><\/script>`:t.slice(0,r)+`
|
|
31
36
|
<script type="module" src="/@vite/client"><\/script>
|
|
32
37
|
<script type="module" src="/src/client/main.js"><\/script>
|
|
33
|
-
`+t.slice(r)}async function
|
|
38
|
+
`+t.slice(r)}async function q(e,n){let r=n.config.root||process.cwd(),i=globalThis.__avalonConfig,a=`_layout.tsx`,o=[],s=e.split(`/`).filter(Boolean),c=[``];for(let e=0;e<s.length;e++)c.push(`/`+s.slice(0,e+1).join(`/`));async function l(e){try{if((await t(e)).isFile()){let t=e.slice(r.length);o.includes(t)||o.push(t)}}catch{}}if(i?.layoutsDir&&await l(`${`${r}/${i.layoutsDir}`}/${a}`),i?.modules){let e=`${r}/${i.modules.dir}`,t=i.modules.layoutsDirName,n=s[0]||``,o=[`home`,`root`,`main`,`index`];if(!n||o.includes(n.toLowerCase()))for(let n of o)await l(`${e}/${n}/${t}/${a}`);else await l(`${e}/${n}/${t}/${a}`)}let u=`${r}/src/layouts`;for(let e of c)await l(e===``?`${u}/${a}`:`${u}${e}/${a}`);return o}async function J(e,n,r){let i=e;i.endsWith(`/`)&&i!==`/`&&(i=i.slice(0,-1)),i===`/`&&(i=`/index`);let a=[`.tsx`,`.ts`,`.jsx`,`.js`,`.mdx`,`.md`],o=r.config.root||process.cwd();async function s(e){try{if((await t(`${o}/${e}`)).isFile())return`/${e}`}catch{}return null}if(n.modules){let t=n.modules.dir,r=n.modules.pagesDirName,o=e.split(`/`).filter(Boolean),c=o[0]||``,l=[`home`,`root`,`main`,`index`],u,d;if(!c||l.includes(c.toLowerCase()))u=`home`,d=i;else{u=c;let e=o.slice(1);d=e.length>0?`/`+e.join(`/`):`/index`}for(let e of a){let n=await s(`${t}/${u}/${r}${d}${e}`);if(n)return n}if(!d.endsWith(`/index`))for(let e of a){let n=await s(`${t}/${u}/${r}${d}/index${e}`);if(n)return n}}let c=n.pagesDir;for(let e of a){let t=await s(`${c}${i}${e}`);if(t)return t}if(!i.endsWith(`/index`))for(let e of a){let t=await s(`${c}${i}/index${e}`);if(t)return t}return null}async function Y(e,t,n,r,i){let a=t.metadata||{};try{V||=await i.ssrLoadModule(x(`src/render/ssr.ts`));let o=V;H||=await i.ssrLoadModule(x(`src/core/layout/enhanced-layout-resolver.ts`));let s=H,c={component:()=>typeof e==`function`?e():e,options:{title:a.title||`Avalon App`},frontmatter:t.frontmatter};if(o.renderToHtmlWithLayouts&&s.EnhancedLayoutResolver&&s.EnhancedLayoutResolverUtils)try{let e=i.config.root||process.cwd();if(!globalThis.__avalonLayoutResolver){let t=s.EnhancedLayoutResolver,n=r.layoutsDir||`src/layouts`;globalThis.__avalonLayoutResolver=new t({baseDirectory:`${e}/${n}`,filePattern:`_layout.tsx`,excludeDirectories:[`node_modules`,`.git`,`dist`,`build`],enableWatching:!0,developmentMode:!1,enableCaching:!0,cacheTTL:60*1e3,maxCacheSize:100,enableStreaming:!0,enableErrorBoundaries:!0,enableMetrics:!1,enableDebugInfo:!1,modulesDir:r.modules?`${e}/${r.modules.dir}`:void 0,modulesLayoutsDirName:r.modules?.layoutsDirName})}let t=`http://localhost${n}`,l={params:{},query:{},url:t,request:{method:`GET`,url:t,headers:new Headers}};return await o.renderToHtmlWithLayouts(c,globalThis.__avalonLayoutResolver,l,n,{title:a.title||`Avalon App`},void 0,{suppressWarnings:!0})}catch{}if(o.renderToHtml)return await o.renderToHtml(c,{title:a.title||`Avalon App`},void 0,{suppressWarnings:!0})}catch{}let o=a.title||`Avalon App`,s=a.description||``,c=``;try{let t=await i.ssrLoadModule(`preact-render-to-string`);t.render&&typeof e==`function`&&(c=t.render(e()))}catch{c=`<p>Loading page: ${X(n)}</p>`}return`<!DOCTYPE html>
|
|
34
39
|
<html lang="en">
|
|
35
40
|
<head>
|
|
36
41
|
<meta charset="utf-8">
|
|
37
42
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
38
|
-
<title>${
|
|
39
|
-
${s?`<meta name="description" content="${
|
|
43
|
+
<title>${X(o)}</title>
|
|
44
|
+
${s?`<meta name="description" content="${X(s)}">`:``}
|
|
40
45
|
<script type="module" src="/@vite/client"><\/script>
|
|
41
46
|
</head>
|
|
42
47
|
<body>
|
|
43
48
|
<div id="app">${c}</div>
|
|
44
49
|
<script type="module" src="/src/client/main.js"><\/script>
|
|
45
50
|
</body>
|
|
46
|
-
</html>`}function
|
|
51
|
+
</html>`}function X(e){return e.replaceAll(`&`,`&`).replaceAll(`<`,`<`).replaceAll(`>`,`>`).replaceAll(`"`,`"`).replaceAll(`'`,`'`)}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@useavalon/avalon",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.51",
|
|
4
4
|
"description": "Multi-framework islands architecture for the modern web",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -89,6 +89,10 @@
|
|
|
89
89
|
"./prerender": {
|
|
90
90
|
"types": "./dist/src/prerender/index.d.ts",
|
|
91
91
|
"default": "./dist/src/prerender/index.js"
|
|
92
|
+
},
|
|
93
|
+
"./post-build": {
|
|
94
|
+
"types": "./dist/src/post-build/index.d.ts",
|
|
95
|
+
"default": "./dist/src/post-build/index.js"
|
|
92
96
|
}
|
|
93
97
|
},
|
|
94
98
|
"typesVersions": {
|
|
@@ -128,7 +132,7 @@
|
|
|
128
132
|
}
|
|
129
133
|
},
|
|
130
134
|
"dependencies": {
|
|
131
|
-
"@useavalon/core": "
|
|
135
|
+
"@useavalon/core": "workspace:^",
|
|
132
136
|
"@mdx-js/rollup": "^3.0.0",
|
|
133
137
|
"h3": "^2.0.1-rc.16",
|
|
134
138
|
"marked": "^17.0.4",
|