@useavalon/avalon 0.4.1-canary.18 → 0.5.0
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/core/layout/layout-discovery.d.ts +1 -0
- package/dist/src/core/layout/layout-discovery.js +1 -1
- package/dist/src/middleware/discovery.d.ts +1 -15
- package/dist/src/middleware/discovery.js +1 -1
- package/dist/src/nitro/renderer.d.ts +1 -0
- package/dist/src/nitro/renderer.js +1 -1
- package/dist/src/polyfills/urlpattern.d.ts +1 -0
- package/dist/src/polyfills/urlpattern.js +1 -0
- package/dist/src/vite-plugin/nitro-integration.js +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
import{statSync as e}from"node:fs";import{join as t,relative as n,resolve as r}from"node:path";function i(e){return/^[A-Za-z]:[\\/]/.test(e)?`file:///${e.replaceAll(`\\`,`/`)}`:e}function a(t){try{return e(t),!0}catch{return!1}}function o(e){let t=e.split(`/`).filter(Boolean),n=[``];for(let e=0;e<t.length;e++)n.push(`/`+t.slice(0,e+1).join(`/`));return n}export class LayoutDiscovery{layoutCache=new Map;routeCache=new Map;baseDirectory;filePattern;developmentMode;constructor(e){this.baseDirectory=r(e.baseDirectory),this.filePattern=e.filePattern||`_layout.tsx`,this.developmentMode=e.developmentMode||!1,this.developmentMode&&console.log(`[LayoutDiscovery] baseDirectory=${this.baseDirectory}, filePattern=${this.filePattern}`)}discoverLayouts(e){let n=`layouts-${e}`;if(this.routeCache.has(n))return Promise.resolve(this.routeCache.get(n));let r=[],i=o(e);for(let e of i){let n=t(e===``?this.baseDirectory:t(this.baseDirectory,e),this.filePattern);if(a(n)){let t=e===``?0:e.split(`/`).filter(Boolean).length;r.push({pattern:new URLPattern({pathname:t===0?`*`:`${e}/*`}),layoutPath:n,priority:t*10,type:t===0?`root`:`nested`,depth:t})}}return r.sort((e,t)=>e.priority-t.priority),this.routeCache.set(n,r),Promise.resolve(r)}async buildLayoutChain(e){let t=await this.discoverLayouts(e.pathname),n=[];for(let e of t)try{let t=await this.loadLayout(e.layoutPath);t&&n.push(t)}catch(t){this.developmentMode&&console.warn(`[Layout] Failed to load ${e.layoutPath}: ${t instanceof Error?t.message:String(t)}`)}return n}async buildLayoutChainWithData(e,t){let n=await this.buildLayoutChain(e),r=[],i=[];for(let e of n)if(e.loader)try{let n=await e.loader(t);r.push(n)}catch(t){this.developmentMode&&console.warn(`[Layout] Data loader error for ${e.path}:`,t),i.push({layoutPath:e.path,errorType:`loader`,timestamp:Date.now()}),r.push({})}else r.push({});return{handlers:n,data:r,errors:i}}async loadLayout(e){if(this.layoutCache.has(e))return this.layoutCache.get(e);try{let t=await import(i(e));if(!t.default||typeof t.default!=`function`)return this.developmentMode&&console.warn(`[Layout] No default export in ${e}`),null;let r=n(this.baseDirectory,e).split(`/`).filter(Boolean),a=Math.max(0,(r.length-1)*10),o={component:t.default,loader:t.layoutLoader,path:e,priority:a};return this.layoutCache.set(e,o),o}catch(t){return this.developmentMode&&console.warn(`[Layout] Failed to load ${e}: ${t instanceof Error?t.message:String(t)}`),null}}clearCache(){this.layoutCache.clear(),this.routeCache.clear()}clearLayoutCache(e){this.layoutCache.delete(e),this.routeCache.clear()}getCacheStats(){return{layoutCount:this.layoutCache.size,routeCacheCount:this.routeCache.size}}getOptions(){return{baseDirectory:this.baseDirectory,filePattern:this.filePattern,developmentMode:this.developmentMode}}}
|
|
1
|
+
import{statSync as e}from"node:fs";import{join as t,relative as n,resolve as r}from"node:path";import"../../polyfills/urlpattern.js";function i(e){return/^[A-Za-z]:[\\/]/.test(e)?`file:///${e.replaceAll(`\\`,`/`)}`:e}function a(t){try{return e(t),!0}catch{return!1}}function o(e){let t=e.split(`/`).filter(Boolean),n=[``];for(let e=0;e<t.length;e++)n.push(`/`+t.slice(0,e+1).join(`/`));return n}export class LayoutDiscovery{layoutCache=new Map;routeCache=new Map;baseDirectory;filePattern;developmentMode;constructor(e){this.baseDirectory=r(e.baseDirectory),this.filePattern=e.filePattern||`_layout.tsx`,this.developmentMode=e.developmentMode||!1,this.developmentMode&&console.log(`[LayoutDiscovery] baseDirectory=${this.baseDirectory}, filePattern=${this.filePattern}`)}discoverLayouts(e){let n=`layouts-${e}`;if(this.routeCache.has(n))return Promise.resolve(this.routeCache.get(n));let r=[],i=o(e);for(let e of i){let n=t(e===``?this.baseDirectory:t(this.baseDirectory,e),this.filePattern);if(a(n)){let t=e===``?0:e.split(`/`).filter(Boolean).length;r.push({pattern:new URLPattern({pathname:t===0?`*`:`${e}/*`}),layoutPath:n,priority:t*10,type:t===0?`root`:`nested`,depth:t})}}return r.sort((e,t)=>e.priority-t.priority),this.routeCache.set(n,r),Promise.resolve(r)}async buildLayoutChain(e){let t=await this.discoverLayouts(e.pathname),n=[];for(let e of t)try{let t=await this.loadLayout(e.layoutPath);t&&n.push(t)}catch(t){this.developmentMode&&console.warn(`[Layout] Failed to load ${e.layoutPath}: ${t instanceof Error?t.message:String(t)}`)}return n}async buildLayoutChainWithData(e,t){let n=await this.buildLayoutChain(e),r=[],i=[];for(let e of n)if(e.loader)try{let n=await e.loader(t);r.push(n)}catch(t){this.developmentMode&&console.warn(`[Layout] Data loader error for ${e.path}:`,t),i.push({layoutPath:e.path,errorType:`loader`,timestamp:Date.now()}),r.push({})}else r.push({});return{handlers:n,data:r,errors:i}}async loadLayout(e){if(this.layoutCache.has(e))return this.layoutCache.get(e);try{let t=await import(i(e));if(!t.default||typeof t.default!=`function`)return this.developmentMode&&console.warn(`[Layout] No default export in ${e}`),null;let r=n(this.baseDirectory,e).split(`/`).filter(Boolean),a=Math.max(0,(r.length-1)*10),o={component:t.default,loader:t.layoutLoader,path:e,priority:a};return this.layoutCache.set(e,o),o}catch(t){return this.developmentMode&&console.warn(`[Layout] Failed to load ${e}: ${t instanceof Error?t.message:String(t)}`),null}}clearCache(){this.layoutCache.clear(),this.routeCache.clear()}clearLayoutCache(e){this.layoutCache.delete(e),this.routeCache.clear()}getCacheStats(){return{layoutCount:this.layoutCache.size,routeCacheCount:this.routeCache.size}}getOptions(){return{baseDirectory:this.baseDirectory,filePattern:this.filePattern,developmentMode:this.developmentMode}}}
|
|
@@ -1,18 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Simplified Middleware Discovery
|
|
3
|
-
*
|
|
4
|
-
* This module provides a streamlined middleware discovery system that scans
|
|
5
|
-
* for _middleware.ts files in src/pages/ directories.
|
|
6
|
-
*
|
|
7
|
-
* Key features:
|
|
8
|
-
* - Scans src/pages/ for page middleware
|
|
9
|
-
* - Calculates priority based on directory depth (parent before child)
|
|
10
|
-
* - Creates URL patterns for route matching
|
|
11
|
-
*
|
|
12
|
-
* Note: API middleware is handled natively by Nitro and is not scanned here.
|
|
13
|
-
*
|
|
14
|
-
* Requirements: 3.1, 3.2, 3.3, 3.4
|
|
15
|
-
*/
|
|
1
|
+
import "../polyfills/urlpattern.ts";
|
|
16
2
|
import type { MiddlewareDiscoveryOptions, MiddlewareRoute } from "./types.ts";
|
|
17
3
|
/**
|
|
18
4
|
* Discovers route-scoped middleware files in the project
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{stat as e,readdir as t}from"node:fs/promises";import{join as n,relative as r,resolve as i}from"node:path";const a={filePattern:`_middleware.ts`,excludeDirs:[`node_modules`,`.git`,`dist`,`.output`,`.vite`],devMode:!1},o={pages:50};export async function discoverScopedMiddleware(e){let{baseDir:t,filePattern:r=a.filePattern,excludeDirs:o=a.excludeDirs,devMode:s=a.devMode}=e,l=i(t),u=[];if(await c(n(l,`pages`),`pages`,r,o,u,s),u.sort((e,t)=>e.priority-t.priority),s&&u.length>0){console.log(`[middleware] Discovered ${u.length} route-scoped middleware:`);for(let e of u)console.log(` - ${e.type}: ${e.filePath} (priority: ${e.priority})`)}return u}async function c(t,n,r,i,a,o){try{if(!(await e(t).catch(()=>null))?.isDirectory())return;await l(t,t,n,r,i,a,o)}catch(e){o&&(!(e instanceof Error)||e.code!==`ENOENT`)&&console.warn(`[middleware] Error scanning ${t}: ${e instanceof Error?e.message:String(e)}`)}}async function l(e,i,a,o,s,c,d){try{let f=await t(i,{withFileTypes:!0});for(let t of f){let f=n(i,t.name);if(!(t.isDirectory()&&s.includes(t.name))){if(t.isDirectory())await l(e,f,a,o,s,c,d);else if(t.name===o){let t=u(f,r(e,i),a);c.push(t)}}}}catch(e){d&&(!(e instanceof Error)||e.code!==`ENOENT`)&&console.warn(`[middleware] Error reading ${i}: ${e instanceof Error?e.message:String(e)}`)}}function u(e,t,n){let r=t?`/${t}{/*}?`:`/*`,i=t?t.split(`/`).filter(Boolean).length:0,a=o[n]+i;return{pattern:new URLPattern({pathname:r}),filePath:e,priority:a,type:n}}export function getMatchingMiddleware(e,t){let n=t.pathname.startsWith(`/api`);return e.filter(e=>!(!e.pattern.test(t)||e.type===`pages`&&n))}export function clearDiscoveryCache(){}
|
|
1
|
+
import"../polyfills/urlpattern.js";import{stat as e,readdir as t}from"node:fs/promises";import{join as n,relative as r,resolve as i}from"node:path";const a={filePattern:`_middleware.ts`,excludeDirs:[`node_modules`,`.git`,`dist`,`.output`,`.vite`],devMode:!1},o={pages:50};export async function discoverScopedMiddleware(e){let{baseDir:t,filePattern:r=a.filePattern,excludeDirs:o=a.excludeDirs,devMode:s=a.devMode}=e,l=i(t),u=[];if(await c(n(l,`pages`),`pages`,r,o,u,s),u.sort((e,t)=>e.priority-t.priority),s&&u.length>0){console.log(`[middleware] Discovered ${u.length} route-scoped middleware:`);for(let e of u)console.log(` - ${e.type}: ${e.filePath} (priority: ${e.priority})`)}return u}async function c(t,n,r,i,a,o){try{if(!(await e(t).catch(()=>null))?.isDirectory())return;await l(t,t,n,r,i,a,o)}catch(e){o&&(!(e instanceof Error)||e.code!==`ENOENT`)&&console.warn(`[middleware] Error scanning ${t}: ${e instanceof Error?e.message:String(e)}`)}}async function l(e,i,a,o,s,c,d){try{let f=await t(i,{withFileTypes:!0});for(let t of f){let f=n(i,t.name);if(!(t.isDirectory()&&s.includes(t.name))){if(t.isDirectory())await l(e,f,a,o,s,c,d);else if(t.name===o){let t=u(f,r(e,i),a);c.push(t)}}}}catch(e){d&&(!(e instanceof Error)||e.code!==`ENOENT`)&&console.warn(`[middleware] Error reading ${i}: ${e instanceof Error?e.message:String(e)}`)}}function u(e,t,n){let r=t?`/${t}{/*}?`:`/*`,i=t?t.split(`/`).filter(Boolean).length:0,a=o[n]+i;return{pattern:new URLPattern({pathname:r}),filePath:e,priority:a,type:n}}export function getMatchingMiddleware(e,t){let n=t.pathname.startsWith(`/api`);return e.filter(e=>!(!e.pattern.test(t)||e.type===`pages`&&n))}export function clearDiscoveryCache(){}
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
*
|
|
26
26
|
* Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 5.1, 5.3, 9.1, 9.2, 9.3, 9.4, 10.5
|
|
27
27
|
*/
|
|
28
|
+
import "../polyfills/urlpattern.ts";
|
|
28
29
|
import type { H3Event } from "h3";
|
|
29
30
|
import type { AvalonRuntimeConfig, HttpError, NitroRenderContext, PageModule, SSRRenderOptions, SSRRenderResult } from "./types.ts";
|
|
30
31
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{getRequestURL as e}from"h3";import{clientNavigationResponseHeaders as t,isClientNavigationDisabled as n,stampClientNavigationOptOut as r}from"../client/router/opt-out.js";import{inlineCriticalCSS as i}from"../islands/critical-css.js";import{injectModulepreloadLinks as a}from"../islands/modulepreload-collector.js";import{discoverScopedMiddleware as o,executeScopedMiddleware as s}from"../middleware/index.js";import{renderShell as c}from"../render/shell-engine.js";import{discoverErrorPages as l,handleRenderError as u}from"./error-handler.js";import{createNotFoundError as d,isHttpError as f}from"./types.js";function p(e,t){return{...e.context.params??{},...t.params}}async function m(e,t,n,r){return e.context.route||(r?r(t,n):L(t,n))}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}function g(e){return{"Content-Type":`text/html; charset=utf-8`,...t(n(e))}}function _(e,t){return a(i(injectHydrationScript(e,t)))}async function v(e,t,n,r){let i=await renderPage(e,t,{},r);return new Response(_(i.html,n),{status:i.statusCode,headers:i.headers})}async function y(e,t,n){let r=await renderPageStream(e,t,{onShellReady:()=>{for(let[t,r]of Object.entries(g(e)))setResponseHeader(n,t,r)}});return new Response(r,{headers:g(e)})}async function b(e,t,n,r,i,a){return i&&a?y(e,t,a):v(e,t,n,r)}async function x(e,t,n,r){try{return await e(t)}catch(i){try{return await e(n)}catch(e){return r&&(console.debug(`[renderer] Page not found: ${t}`,i),console.debug(`[renderer] Index fallback not found: ${n}`,e)),null}}}export function createErrorResponse(e,t){let n=f(e)?e.statusCode:500;if(t){let t=S(e,n);return new Response(t,{status:n,headers:{"Content-Type":`text/html; charset=utf-8`}})}let r=C(n);return new Response(r,{status:n,headers:{"Content-Type":`text/html; charset=utf-8`}})}function S(e,t){return`<!DOCTYPE html>
|
|
1
|
+
import"../polyfills/urlpattern.js";import{getRequestURL as e}from"h3";import{clientNavigationResponseHeaders as t,isClientNavigationDisabled as n,stampClientNavigationOptOut as r}from"../client/router/opt-out.js";import{inlineCriticalCSS as i}from"../islands/critical-css.js";import{injectModulepreloadLinks as a}from"../islands/modulepreload-collector.js";import{discoverScopedMiddleware as o,executeScopedMiddleware as s}from"../middleware/index.js";import{renderShell as c}from"../render/shell-engine.js";import{discoverErrorPages as l,handleRenderError as u}from"./error-handler.js";import{createNotFoundError as d,isHttpError as f}from"./types.js";function p(e,t){return{...e.context.params??{},...t.params}}async function m(e,t,n,r){return e.context.route||(r?r(t,n):L(t,n))}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}function g(e){return{"Content-Type":`text/html; charset=utf-8`,...t(n(e))}}function _(e,t){return a(i(injectHydrationScript(e,t)))}async function v(e,t,n,r){let i=await renderPage(e,t,{},r);return new Response(_(i.html,n),{status:i.statusCode,headers:i.headers})}async function y(e,t,n){let r=await renderPageStream(e,t,{onShellReady:()=>{for(let[t,r]of Object.entries(g(e)))setResponseHeader(n,t,r)}});return new Response(r,{headers:g(e)})}async function b(e,t,n,r,i,a){return i&&a?y(e,t,a):v(e,t,n,r)}async function x(e,t,n,r){try{return await e(t)}catch(i){try{return await e(n)}catch(e){return r&&(console.debug(`[renderer] Page not found: ${t}`,i),console.debug(`[renderer] Index fallback not found: ${n}`,e)),null}}}export function createErrorResponse(e,t){let n=f(e)?e.statusCode:500;if(t){let t=S(e,n);return new Response(t,{status:n,headers:{"Content-Type":`text/html; charset=utf-8`}})}let r=C(n);return new Response(r,{status:n,headers:{"Content-Type":`text/html; charset=utf-8`}})}function S(e,t){return`<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function ensureURLPattern(): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{URLPattern as e}from"urlpattern-polyfill";export function ensureURLPattern(){let t=globalThis;t.URLPattern??=e}ensureURLPattern();
|
|
@@ -18,7 +18,7 @@ export default islandManifest;
|
|
|
18
18
|
`}function Fe(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=globalThis.__avalonConfig?.isDev??e.isDev,r=!!(globalThis.__avalonConfig?.clientRouter??e.clientRouter),i=f(n,r),a={streaming:t.streaming??!0,pagesDir:e.pagesDir,layoutsDir:e.layoutsDir,isDev:n,clientRouter:r,...t.runtimeConfig};return`globalThis.__avalonHydrationMode = "${i}";\nglobalThis.__avalonIsDev = ${n};\nconst config = ${JSON.stringify(a,null,2)};\nexport function useAvalonConfig() { return config; }\nexport default config;\n`}async function Ie(e,t){let{getAllLayoutDirs:n}=await import(`./module-discovery.js`),{relative:r,resolve:i}=await import(`node:path`),{stat:a,readFile:o}=await import(`node:fs/promises`),c=process.cwd(),l=await n(e.layoutsDir,e.modules,c),u=[],d=0,f=i(c,e.layoutsDir);for(let{dir:t,prefix:n}of l){let i=s(t,`_layout.tsx`);try{if(!(await a(i)).isFile())continue}catch{continue}let o=r(c,i).replaceAll(`\\`,`/`),l=o.startsWith(`/`)?o:`/${o}`,p=t.startsWith(f),m=p&&!e.modules?`RootLayout`:`Layout_${d}`;u.push({prefix:n,importPath:l,varName:m,isShared:p,filePath:i}),d++}let p=u.filter(e=>e.isShared),m=u.filter(e=>!e.isShared),h=new Map;for(let e of m)try{let t=(await o(e.filePath,`utf8`)).match(/layoutConfig\s*=\s*{[\s\S]*?skipLayouts\s*:\s*\[([^\]]*)\]/)?.[1]??``,n=/['"`]_layout['"`]/.test(t);h.set(e.importPath,n)}catch{h.set(e.importPath,!1)}let g=u.map(e=>`import ${e.varName} from '${e.importPath}';`),_=m.toSorted((e,t)=>t.prefix.length-e.prefix.length).map(e=>{let t=e.prefix===`/`,n=h.get(e.importPath)??!1,r=t||n;return` { prefix: ${JSON.stringify(e.prefix)}, Layout: ${e.varName}, skipRoot: ${r} }`}),v=p.length>0?p[0].varName:`null`,y=`{"routes":[],"fallbackHrefs":[],"globalHrefs":[]}`;if(e.isDev){let{getAllPageDirs:n}=await import(`./module-discovery.js`),{discoverPageRoutesFromMultipleDirs:r}=await import(`../nitro/route-discovery.js`),i=await r(await n(e.pagesDir,e.modules,c),{developmentMode:e.isDev}),a=ne({cwd:c,globalCSS:t.globalCSS??[],routes:i.map(e=>({pattern:e.pattern,filePath:e.filePath})),layouts:u.map(e=>({prefix:e.prefix,filePath:e.filePath,isRoot:e.isShared,skipRoot:h.get(e.importPath)??!1}))});y=JSON.stringify(a)}return[`// Auto-generated by Avalon — do not edit`,...ve(e.core),`import { getUniversalCSSForHead } from '@useavalon/avalon/islands/universal-css-collector';`,`import { getUniversalHeadForInjection, injectSolidHydrationScriptIfNeeded } from '@useavalon/avalon/islands/universal-head-collector';`,`import { appendDevCssLinks, layoutPrefixMatches, selectDevCssHrefs } from '@useavalon/avalon/render/dev-css-select';`,...g,``,`const RootLayoutComponent = ${v};`,`const _cssTable = ${y};`,``,`const moduleLayouts = [`,_.join(`,
|
|
19
19
|
`),`];`,``,`function getLayoutsForPath(pathname) {`,` return moduleLayouts.find((entry) => layoutPrefixMatches(pathname, entry.prefix)) ?? null;`,`}`,``,`function injectUniversalAssets(html, pathname, skipLayouts) {`,` if (process.env.NODE_ENV !== 'production') {`,` var hrefs = selectDevCssHrefs(_cssTable, pathname, skipLayouts);`,` html = appendDevCssLinks(html, hrefs);`,` }`,` const universalCSS = getUniversalCSSForHead(true);`,` if (universalCSS && html.includes('</head>')) {`,String.raw` html = html.replace('</head>', universalCSS + '\n</head>');`,` }`,` const universalHead = getUniversalHeadForInjection(true);`,` if (universalHead && html.includes('</head>')) {`,String.raw` html = html.replace('</head>', universalHead + '\n</head>');`,` }`,` html = injectSolidHydrationScriptIfNeeded(html);`,` 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) {`,` if (RootLayoutComponent && !skipAll) {`,` const rootProps = {`,` children: h('avalon-page', { id: 'app', dangerouslySetInnerHTML: { __html: pageHtml } }),`,` frontmatter,`,` data: {},`,` route: routeInfo,`,` };`,` const rootResult = RootLayoutComponent(rootProps);`,` const resolvedRoot = rootResult instanceof Promise ? await rootResult : rootResult;`,String.raw` html = '<!DOCTYPE html>\n' + preactRenderToString(resolvedRoot);`,` } else {`,` // skipAll is true — page provides its own HTML shell or needs a minimal one`,` if (pageHtml.trimStart().startsWith('<html')) {`,String.raw` html = '<!DOCTYPE html>\n' + pageHtml;`,` } else {`,` 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>',`,String.raw` ].join('\n');`,` }`,` }`,` } else {`,` const layoutProps = {`,` children: h('avalon-page', { 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 the layout provides its own HTML shell (starts with <html),`,` // skip root wrapping regardless of skipRoot flag — the layout IS the document.`,` const layoutProvidesShell = wrappedHtml.trimStart().startsWith('<html');`,``,` if (!layoutProvidesShell && !layoutEntry.skipRoot && RootLayoutComponent) {`,` const rootProps = {`,` children: h('avalon-page', { dangerouslySetInnerHTML: { __html: wrappedHtml } }),`,` frontmatter,`,` data: {},`,` route: routeInfo,`,` };`,` const rootResult = RootLayoutComponent(rootProps);`,` const resolvedRoot = rootResult instanceof Promise ? await rootResult : rootResult;`,` wrappedHtml = preactRenderToString(resolvedRoot);`,` }`,``,String.raw` html = '<!DOCTYPE html>\n' + wrappedHtml;`,` }`,``,` if (injectAssets) {`,` html = injectAssets(html);`,` }`,` return injectUniversalAssets(html, pathname, skipAll);`,`}`,``,`export default { wrapWithLayouts };`,``].join(`
|
|
20
20
|
`)}function Le(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 ?? [])`,` .filter(attr => {`,` const href = attr.href || '';`,` // The SSR build produces ssr-index-*.css containing all global, reset,`,` // token, and layout CSS module styles. The client build may also emit`,` // entry-client-*.css (a subset) and index-*.css. To avoid duplicates,`,` // prefer ssr-index when it exists; otherwise fall back to the others.`,` var hasSsrIndex = (clientAssets?.css ?? []).some(function(a) { return (a.href || '').includes('ssr-index'); });`,` if (hasSsrIndex) return href.includes('ssr-index');`,` if (href.includes('entry-client') && href.endsWith('.css')) return true;`,String.raw` if (/\/index-[^/]+\.css$/.test(href)) return true;`,` return false;`,` })`,` .map(attr => '<link rel="stylesheet" href="' + attr.href + '">')`,String.raw` .join('\n');`,` const jsPreloads = (clientAssets?.js ?? [])`,` .map(attr => '<link rel="modulepreload" href="' + attr.href + '">')`,String.raw` .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>')) {`,String.raw` html = html.replace('</head>', cssLinks + '\n' + jsPreloads + '\n</head>');`,` }`,` if (html.includes('</body>')) {`,String.raw` html = html.replace('</body>', entryScript + '\n</body>');`,` }`,` return html;`,`}`,``,`export { clientAssets };`,`export default { injectAssets, clientAssets };`,``].join(`
|
|
21
|
-
`)}function Re(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});`)}let i=ye(e.core);return[`// Auto-generated by Avalon — do not edit
|
|
21
|
+
`)}function Re(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});`)}let i=ye(e.core);return[`// Auto-generated by Avalon — do not edit`,`import "urlpattern-polyfill";`,...t.includes(`lit`)?[`import "${O}";`]:[],`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,``,...i,``,`// Pre-register framework integrations for SSR`,...r,``,`// Register built-in custom hydration directives (on:delay, on:scroll, etc.)`,`registerBuiltinDirectives();`,``,`// Set hydration mode flag — intentionally duplicated from virtual:avalon/config`,`// for module-load-order resilience (config import may be tree-shaken or deferred).`,`globalThis.__avalonHydrationMode = (avalonConfig.isDev || avalonConfig.clientRouter) ? "entry-client" : "per-island";`,`globalThis.__avalonIsDev = avalonConfig.isDev;`,``,`export default createNitroRenderer({`,` avalonConfig,`,` isDev: avalonConfig.isDev,`,` resolvePageRoute: async (pathname) => {`,` const match = loadPage(pathname);`,` if (!match || !('default' in match.module)) return null;`,` return { filePath: '[virtual:' + pathname + ']', pattern: pathname, params: match.params };`,` },`,` loadPageModule: async (filePath) => {`,String.raw` const m = filePath.match(/^\[virtual:(.+)\]$/);`,` const pathname = m ? m[1] : filePath;`,` const match = loadPage(pathname);`,` if (match) return match.module;`,` return { default: () => null, metadata: { title: 'Avalon' } };`,` },`,` wrapWithLayouts: (pageHtml, pageModule, context) =>`,` wrapWithLayouts(pageHtml, pageModule, context, injectAssets),`,`});`,``].join(`
|
|
22
22
|
`)}async function ze(e,t){let{readdir:n}=await import(`node:fs/promises`),{relative:r,join:i}=await import(`node:path`),a=[];for(let{dir:o}of e){let e;try{e=await n(o,{withFileTypes:!0})}catch{continue}for(let n of e){if(!n.isFile()||!n.name.endsWith(`.css`))continue;let e=r(t,i(o,n.name)).replaceAll(`\\`,`/`);a.push(e.startsWith(`/`)?e:`/${e}`)}}return a}function Be(e){if(e.length===0)return[];let t=[`// Layout CSS (auto-discovered)`],n=0;for(let r of e)r.includes(`.module.`)?(t.push(`import _lcss${n} from '${r}';`),n++):t.push(`import '${r}';`);return t}async function Ve(e,t){let{getAllLayoutDirs:n}=await import(`./module-discovery.js`),r=process.cwd(),i=await ze(await n(e.layoutsDir,e.modules,r),r),a=globalThis.__avalonConfig?.isDev??e.isDev,o=[`// Auto-generated by Avalon — do not edit`,`// Island hydration runtime`,...d(a,!!(globalThis.__avalonConfig?.clientRouter??e.clientRouter))];a&&o.push(`import '${VIRTUAL_MODULE_IDS.DEV_CSS_HMR}';`),o.push(``);let s=t.globalCSS??[];for(let e of s){let t=e.startsWith(`/`)?e:`/${e}`;o.push(`// Global CSS`,`import '${t}';`)}return s.length>0&&o.push(``),o.push(...Be(i),``),o.join(`
|
|
23
23
|
`)}function He(){return[`// --- Inlined Solid adapter (production) ---`,`// Eliminates a separate chunk + network request for the Solid client adapter.`,`function _ensureHydrationContext() {`,` if (!globalThis._$HY) {`,` globalThis._$HY = { events: [], completed: new WeakSet(), r: {}, fe() {} };`,` }`,`}`,``,`async function _solidHydrate(container, Component, props) {`,` if (!container) throw new Error("Container element is required for hydration");`,` if (!Component || typeof Component !== "function") {`,` throw new Error("Invalid Solid component: expected function, got " + typeof Component);`,` }`,` var el = container;`,` var renderId = el.dataset.solidRenderId || el.dataset.renderId;`,` var { hydrate: solidHydrate, createComponent } = await import("solid-js/web");`,` _ensureHydrationContext();`,` var dispose = solidHydrate(function() { return createComponent(Component, props || {}); }, el, { renderId: renderId || "" });`,` if (typeof dispose === "function") _solidDisposers.set(container, dispose);`,`}`,``,`async function _solidMount(container, Component, props) {`,` if (!container) throw new Error("Container element is required for mount");`,` if (!Component || typeof Component !== "function") {`,` throw new Error("Invalid Solid component: expected function, got " + typeof Component);`,` }`,` var { render: solidRender, createComponent } = await import("solid-js/web");`,` var dispose = solidRender(function() { return createComponent(Component, props || {}); }, container);`,` if (typeof dispose === "function") _solidDisposers.set(container, dispose);`,`}`,``,`function _solidUnmount(container) {`,` var dispose = _solidDisposers.get(container);`,` if (dispose) { dispose(); _solidDisposers.delete(container); }`,`}`,``,`var _solidDisposers = new WeakMap();`,`var _solidModule = { hydrate: _solidHydrate, mount: _solidMount, unmount: _solidUnmount };`,``]}function Ue(e,t,n){let r=[];for(let i of e){let e=t[i];e&&(i===`solid`&&!n?r.push(` case "solid":`,` return _solidModule;`):i===`react`?r.push(` case "react":`):r.push(` case "${i}":`,` return import("${e}");`))}return e.includes(`react`)&&!e.includes(`preact`)&&r.push(` return import("${t.react}");`),r}function We(e,t){let n=[];for(let r of e){let e=t[r];e&&n.push(` case "${r}":`,` return import("${e}").then(m => m.${r}Adapter);`)}return n}export function generateIntegrationLoaderModule(e){let t=(e.integrations??[]).map(e=>typeof e==`string`?e:e.name),n={preact:`@useavalon/preact/client`,react:be(e.core),vue:`@useavalon/vue/client`,svelte:`@useavalon/svelte/client`,solid:`@useavalon/solid/client`,lit:`@useavalon/lit/client`,qwik:`@useavalon/qwik/client`},r={preact:`@useavalon/preact/client/hmr`,react:`@useavalon/react/client/hmr`,vue:`@useavalon/vue/client/hmr`,svelte:`@useavalon/svelte/client/hmr`,solid:`@useavalon/solid/client/hmr`,lit:`@useavalon/lit/client/hmr`,qwik:`@useavalon/qwik/client/hmr`},i=t.includes(`lit`),a=t.includes(`solid`),o=(globalThis.__avalonConfig?.isDev??e.isDev)===!0,s=[`// Auto-generated by Avalon — only includes configured integrations`,``];return a&&!o&&s.push(...He()),s.push(`// --- loadIntegrationModule ---`,`export async function loadIntegrationModule(framework) {`,` switch (framework) {`,...Ue(t,n,o),` default:`," throw new Error(`Unknown or unconfigured framework: ${framework}`);",` }`,`}`,``,`// --- Lit hydration pre-load (only if Lit is configured) ---`),i?s.push(`export async function preLitHydration() {`,` await import("@useavalon/lit/client");`,`}`):s.push(`export async function preLitHydration() {}`),s.push(``,`// --- HMR adapter loader ---`,`export async function loadHMRAdapter(framework) {`,` switch (framework) {`,...We(t,r),` default: return null;`,` }`,`}`),s.join(`
|
|
24
24
|
`)}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){if(t===`react`){let e=await import(`react`),t=await import(`react-dom/server`),n=e.default??e,r=t.default??t;return{h:e.createElement??n.createElement,renderToString:t.renderToString??r.renderToString}}let n=await e.ssrLoadModule(`preact`),r=await e.ssrLoadModule(`preact-render-to-string`);return{h:n.h,renderToString:r.render??r.default}}function W(e){return e.split(`/`).pop()?.replace(/\.[^.]+$/,``)||``}async function G(e,t,n){let r=[];for(let n of t){let t=await e.ssrLoadModule(n);r.push({file:n,module:t})}for(let r of t){let t=await w(e,r);n.push(...t)}return r}async function K(e,t,n,r){let i=[],a=[];for(let o of e){let e=o.module.default;if(!(!e||typeof e!=`function`))try{let s=e({...t,children:n(`div`,null,`test`)}),c=r(s instanceof Promise?await s:s);c.trim().startsWith(`<html`)||c.includes(`<!DOCTYPE`)?i.push(o):a.push(o)}catch{a.push(o)}}return{shellLayouts:i,wrapperLayouts:a}}async function Ge(e,t,n,r){try{let i=e({...t,children:n(`div`,{dangerouslySetInnerHTML:{__html:B}})});return r(i instanceof Promise?await i:i)}catch{return null}}function Ke(e,t){let n=e;if(t.length>0){let e=`<style data-avalon-ssr-css>${t.join(`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@useavalon/avalon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Multi-framework islands architecture for the modern web",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -165,7 +165,7 @@
|
|
|
165
165
|
"dependencies": {
|
|
166
166
|
"@mdx-js/rollup": "^3.0.0",
|
|
167
167
|
"rehype-highlight": "^7.0.0",
|
|
168
|
-
"@useavalon/core": "^0.
|
|
168
|
+
"@useavalon/core": "^0.5.0",
|
|
169
169
|
"h3": "^2.0.1-rc.16",
|
|
170
170
|
"marked": "^17.0.4",
|
|
171
171
|
"oxc-parser": "^0.120.0",
|