@useavalon/avalon 0.3.2-canary.8 → 0.4.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/mod.d.ts +1 -1
- package/dist/mod.js +1 -1
- package/dist/src/build/integration-config.js +1 -1
- package/dist/src/build/island-client-bundler.js +1 -1
- package/dist/src/build/island-manifest.d.ts +3 -0
- package/dist/src/build/island-manifest.js +1 -1
- package/dist/src/build/island-ssr-flag.d.ts +14 -0
- package/dist/src/build/island-ssr-flag.js +1 -0
- package/dist/src/build/mdx-island-transform.js +2 -2
- package/dist/src/build/mdx-plugin.js +1 -1
- package/dist/src/build/page-island-await.d.ts +18 -0
- package/dist/src/build/page-island-await.js +2 -0
- package/dist/src/build/page-island-transform.d.ts +3 -1
- package/dist/src/build/page-island-transform.js +2 -2
- package/dist/src/client/css-hmr-handler.d.ts +7 -90
- package/dist/src/client/css-hmr-handler.js +1 -1
- package/dist/src/client/custom-directives.js +1 -1
- package/dist/src/client/hmr-coordinator.d.ts +0 -1
- package/dist/src/client/hmr-coordinator.js +1 -1
- package/dist/src/client/hmr-error-overlay.d.ts +18 -0
- package/dist/src/client/hydrate-runtime.d.ts +33 -0
- package/dist/src/client/hydrate-runtime.js +1 -0
- package/dist/src/client/hydration-mode.d.ts +11 -0
- package/dist/src/client/hydration-mode.js +1 -0
- package/dist/src/client/main-slim.js +1 -0
- package/dist/src/client/main.js +2 -15
- package/dist/src/client/router/a11y.d.ts +9 -0
- package/dist/src/client/router/a11y.js +1 -0
- package/dist/src/client/router/eligibility.d.ts +32 -0
- package/dist/src/client/router/eligibility.js +1 -0
- package/dist/src/client/router/events.d.ts +33 -0
- package/dist/src/client/router/events.js +1 -0
- package/dist/src/client/router/forms.d.ts +25 -0
- package/dist/src/client/router/forms.js +1 -0
- package/dist/src/client/router/index.d.ts +20 -0
- package/dist/src/client/router/index.js +1 -0
- package/dist/src/client/router/opt-out.d.ts +30 -0
- package/dist/src/client/router/opt-out.js +1 -0
- package/dist/src/client/router/persist.d.ts +24 -0
- package/dist/src/client/router/persist.js +1 -0
- package/dist/src/client/router/prefetch.d.ts +25 -0
- package/dist/src/client/router/prefetch.js +1 -0
- package/dist/src/client/router/swap.d.ts +31 -0
- package/dist/src/client/router/swap.js +1 -0
- package/dist/src/client/router/transitions.d.ts +22 -0
- package/dist/src/client/router/transitions.js +1 -0
- package/dist/src/client/server-islands-boot.d.ts +12 -0
- package/dist/src/client/server-islands-boot.js +1 -0
- package/dist/src/client/types/vite-virtual-modules.d.ts +7 -0
- package/dist/src/islands/builtin-directives.d.ts +1 -0
- package/dist/src/islands/builtin-directives.js +1 -1
- package/dist/src/islands/critical-css.js +1 -1
- package/dist/src/islands/framework-base-css.d.ts +5 -1
- package/dist/src/islands/framework-base-css.js +1 -1
- package/dist/src/islands/hydration-directives.js +1 -1
- package/dist/src/islands/island.d.ts +24 -3
- package/dist/src/islands/island.js +1 -1
- package/dist/src/islands/per-island-script.js +1 -1
- package/dist/src/islands/types.d.ts +6 -0
- package/dist/src/nitro/renderer.d.ts +2 -1
- package/dist/src/nitro/renderer.js +20 -20
- package/dist/src/nitro/types.d.ts +5 -0
- package/dist/src/post-build/cloudflare-worker-patch.d.ts +32 -0
- package/dist/src/post-build/cloudflare-worker-patch.js +1 -0
- package/dist/src/post-build/fetch-handler-wrapper.d.ts +1 -0
- package/dist/src/post-build/fetch-handler-wrapper.js +56 -0
- package/dist/src/post-build/index.d.ts +16 -0
- package/dist/src/post-build/index.js +6 -51
- package/dist/src/post-build/isolated-island-builder.js +2 -2
- package/dist/src/render/collect-css.d.ts +29 -0
- package/dist/src/render/collect-css.js +3 -3
- package/dist/src/render/dev-css-select.d.ts +40 -0
- package/dist/src/render/dev-css-select.js +5 -0
- package/dist/src/schemas/routing/index.d.ts +2 -0
- package/dist/src/schemas/routing.d.ts +1 -0
- package/dist/src/schemas/routing.js +1 -1
- package/dist/src/server-islands/encryption.js +1 -1
- package/dist/src/server-islands/endpoint.js +3 -3
- package/dist/src/server-islands/renderer.js +6 -5
- package/dist/src/server-islands/route.d.ts +3 -1
- package/dist/src/server-islands/route.js +1 -1
- package/dist/src/types/island-prop.d.ts +28 -9
- package/dist/src/types/virtual-modules.d.ts +26 -6
- package/dist/src/types/vite-env.d.ts +4 -0
- package/dist/src/vite-plugin/config.js +1 -1
- package/dist/src/vite-plugin/dev-css-graph.d.ts +41 -0
- package/dist/src/vite-plugin/dev-css-graph.js +1 -0
- package/dist/src/vite-plugin/dev-css-hmr.d.ts +42 -0
- package/dist/src/vite-plugin/dev-css-hmr.js +2 -0
- package/dist/src/vite-plugin/dev-css-modules.d.ts +8 -0
- package/dist/src/vite-plugin/dev-css-modules.js +1 -0
- package/dist/src/vite-plugin/framework-vite-defaults.d.ts +29 -0
- package/dist/src/vite-plugin/framework-vite-defaults.js +1 -0
- package/dist/src/vite-plugin/nitro-integration.d.ts +4 -0
- package/dist/src/vite-plugin/nitro-integration.js +20 -20
- package/dist/src/vite-plugin/plugin.d.ts +2 -0
- package/dist/src/vite-plugin/plugin.js +1 -1
- package/dist/src/vite-plugin/server-islands-plugin.js +1 -1
- package/dist/src/vite-plugin/ssr-dom-shim-module.d.ts +27 -0
- package/dist/src/vite-plugin/ssr-dom-shim-module.js +121 -0
- package/dist/src/vite-plugin/stub-build-time-packages.d.ts +16 -0
- package/dist/src/vite-plugin/stub-build-time-packages.js +2 -0
- package/dist/src/vite-plugin/types.d.ts +12 -0
- package/package.json +13 -7
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export function stripTrailingSlash(e){return e===`/`?`/`:e.endsWith(`/`)?e.slice(0,-1):e}export function matchPathPattern(t,n){let r=stripTrailingSlash(t),i=stripTrailingSlash(n);if(r===i)return!0;let a=i.split(`/`),o=r.split(`/`);for(let e=0;e<a.length;e++){let t=a[e];if(t===`**`)return!0;if(e>=o.length)return!1;if(t.startsWith(`:`)){if(!o[e])return!1;continue}if(t!==o[e])return!1}return o.length<=a.length}export function layoutPrefixMatches(t,n){if(n===`/`)return t===`/`;let r=stripTrailingSlash(t),i=stripTrailingSlash(n);return r===i||r.startsWith(`${i}/`)}export function selectDevCssHrefs(e,n,r){let i=n.split(`?`)[0]??n,a=e.routes.find(e=>matchPathPattern(i,e.pattern));return a?r?a.pageHrefs:a.hrefs:r?e.globalHrefs:e.fallbackHrefs}function i(e){return e.replaceAll(`&`,`&`).replaceAll(`"`,`"`).replaceAll(`<`,`<`)}export const DEV_CSS_HMR_CLIENT_SRC=`/@id/__x00__virtual:avalon/dev-css-hmr`;export function appendDevCssLinks(e,t){if(!e.includes(`</head>`))return e;let n=t.map(e=>{let t=i(e);return`<link rel="stylesheet" href="${t}?direct" data-avalon-css="${t}">`}).join(`
|
|
2
|
+
`),r=[];n&&r.push(n),e.includes(`/@vite/client`)||r.push(`<script type="module" src="/@vite/client"><\/script>`),e.includes(`virtual:avalon/dev-css-hmr`)||r.push(`<script type="module" src="/@id/__x00__virtual:avalon/dev-css-hmr"><\/script>`);let a=r.join(`
|
|
3
|
+
`);return e.replace(`</head>`,`${a}${a?`
|
|
4
|
+
`:``}</head>`)}export function appendDevStyleTags(e,t){if(t.length===0||!e.includes(`</head>`))return e;let n=t.map(e=>{let t=e.css.replaceAll(/<\/style/gi,String.raw`<\/style`);return`<style type="text/css" data-avalon-css="${i(e.href)}">${t}</style>`}).join(`
|
|
5
|
+
`);return e.replace(`</head>`,`${n}\n</head>`)}
|
|
@@ -15,6 +15,7 @@ export declare const routingValidators: {
|
|
|
15
15
|
generateMetadata?: any;
|
|
16
16
|
loader?: any;
|
|
17
17
|
frontmatter?: Record<string, any>;
|
|
18
|
+
clientNavigation?: boolean;
|
|
18
19
|
};
|
|
19
20
|
readonly routeDiscoveryOptions: (data: unknown) => {
|
|
20
21
|
pagesDirectory: string;
|
|
@@ -105,6 +106,7 @@ export declare const safeRoutingValidators: {
|
|
|
105
106
|
generateMetadata?: any;
|
|
106
107
|
loader?: any;
|
|
107
108
|
frontmatter?: Record<string, any>;
|
|
109
|
+
clientNavigation?: boolean;
|
|
108
110
|
}>;
|
|
109
111
|
readonly routeDiscoveryOptions: (data: unknown) => import("../index.ts").ValidationResult<{
|
|
110
112
|
pagesDirectory: string;
|
|
@@ -37,6 +37,7 @@ export declare const RoutePageModuleSchema: z.ZodObject<{
|
|
|
37
37
|
generateMetadata: z.ZodOptional<z.ZodAny>;
|
|
38
38
|
loader: z.ZodOptional<z.ZodAny>;
|
|
39
39
|
frontmatter: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
40
|
+
clientNavigation: z.ZodOptional<z.ZodBoolean>;
|
|
40
41
|
}, z.core.$strip>;
|
|
41
42
|
/**
|
|
42
43
|
* Route Parameters Schema - Parameters extracted from dynamic routes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{z as e}from"zod";export const RouteTypeSchema=e.enum([`static`,`dynamic`,`catch-all`,`index`,`group`]);export const FileSystemRouteSchema=e.object({pattern:e.any(),filePath:e.string().min(1),routeType:RouteTypeSchema,dynamicSegments:e.array(e.string()),priority:e.number().int().min(0),isPrivate:e.boolean(),routeGroup:e.string().optional()});export const RoutePageModuleSchema=e.object({default:e.any(),layoutConfig:e.any().optional(),generateMetadata:e.any().optional(),loader:e.any().optional(),frontmatter:e.record(e.string(),e.any()).optional()});export const RouteParamsSchema=e.record(e.string(),e.string());export const LoaderContextSchema=e.object({request:e.instanceof(Request),url:e.instanceof(URL),params:RouteParamsSchema,query:e.instanceof(URLSearchParams),state:e.instanceof(Map)});export const PagePropsSchema=e.object({params:RouteParamsSchema,query:e.instanceof(URLSearchParams),data:e.unknown().optional()});export const MetadataSchema=e.object({title:e.string().optional(),description:e.string().optional(),keywords:e.array(e.string()).optional(),openGraph:e.object({title:e.string().optional(),description:e.string().optional(),image:e.url().optional(),url:e.url().optional(),type:e.string().optional(),siteName:e.string().optional()}).optional(),twitter:e.object({card:e.enum([`summary`,`summary_large_image`,`app`,`player`]).optional(),title:e.string().optional(),description:e.string().optional(),image:e.url().optional(),site:e.string().optional()}).optional(),schema:e.array(e.record(e.string(),e.unknown())).optional(),canonical:e.url().optional(),robots:e.string().optional()});export const ResolvedMetadataSchema=MetadataSchema.extend({sources:e.array(e.string()).optional(),resolvedAt:e.number().optional()});export const MetadataChainSchema=e.object({global:MetadataSchema.optional(),sections:e.array(e.object({path:e.string(),metadata:MetadataSchema})),page:MetadataSchema.optional()});export const RouteDiscoveryOptionsSchema=e.object({pagesDirectory:e.string().min(1).default(`src/pages`),apiDirectory:e.string().min(1).default(`src/api`),extensions:e.array(e.string()).default([`.tsx`,`.ts`,`.jsx`,`.js`]),excludeDirectories:e.array(e.string()).default([`node_modules`,`.git`]),enableWatching:e.boolean().default(!1),developmentMode:e.boolean().default(!1),quietMode:e.boolean().default(!1)});export const FileSystemRouterConfigSchema=e.object({discovery:RouteDiscoveryOptionsSchema.optional(),enabled:e.boolean().default(!0),fallbackToManual:e.boolean().default(!0),enableCaching:e.boolean().default(!0),cacheTTL:e.number().positive().default(3e5)});export const PageFileSchema=e.object({filePath:e.string().min(1),relativePath:e.string().min(1),extension:e.string().min(1),isPrivate:e.boolean(),routeGroup:e.string().optional(),mtime:e.number().optional()});export const RouteHandlerSchema=e.object({pattern:e.any(),handler:e.any(),metadata:e.object({filePath:e.string(),routeType:RouteTypeSchema,priority:e.number(),dynamicSegments:e.array(e.string())})});export const RouteCacheEntrySchema=e.object({routes:e.array(FileSystemRouteSchema),timestamp:e.number(),fileMtimes:e.record(e.string(),e.number())});export const FileSystemApiRouteSchema=e.object({pattern:e.any(),filePath:e.string().min(1),methods:e.array(e.enum([`GET`,`POST`,`PUT`,`DELETE`,`PATCH`,`HEAD`,`OPTIONS`])),priority:e.number().int().min(0),dynamicSegments:e.array(e.string())});export const FileSystemApiModuleSchema=e.object({GET:e.any().optional(),POST:e.any().optional(),PUT:e.any().optional(),DELETE:e.any().optional(),PATCH:e.any().optional(),HEAD:e.any().optional(),OPTIONS:e.any().optional()});export function isFileSystemRoute(e){return FileSystemRouteSchema.safeParse(e).success}export function isRoutePageModule(e){return RoutePageModuleSchema.safeParse(e).success}export function isMetadata(e){return MetadataSchema.safeParse(e).success}export function isRouteParams(e){return RouteParamsSchema.safeParse(e).success}export function isFileSystemApiModule(e){return FileSystemApiModuleSchema.safeParse(e).success}export{createTypedApiHandler,createTypedMetadataGenerator,createTypedPageComponent,createTypedPageLoader,isValidPageProps,isValidRouteParams,isValidRoutePattern,validatePageComponent}from"../types/routing.js";
|
|
1
|
+
import{z as e}from"zod";export const RouteTypeSchema=e.enum([`static`,`dynamic`,`catch-all`,`index`,`group`]);export const FileSystemRouteSchema=e.object({pattern:e.any(),filePath:e.string().min(1),routeType:RouteTypeSchema,dynamicSegments:e.array(e.string()),priority:e.number().int().min(0),isPrivate:e.boolean(),routeGroup:e.string().optional()});export const RoutePageModuleSchema=e.object({default:e.any(),layoutConfig:e.any().optional(),generateMetadata:e.any().optional(),loader:e.any().optional(),frontmatter:e.record(e.string(),e.any()).optional(),clientNavigation:e.boolean().optional()});export const RouteParamsSchema=e.record(e.string(),e.string());export const LoaderContextSchema=e.object({request:e.instanceof(Request),url:e.instanceof(URL),params:RouteParamsSchema,query:e.instanceof(URLSearchParams),state:e.instanceof(Map)});export const PagePropsSchema=e.object({params:RouteParamsSchema,query:e.instanceof(URLSearchParams),data:e.unknown().optional()});export const MetadataSchema=e.object({title:e.string().optional(),description:e.string().optional(),keywords:e.array(e.string()).optional(),openGraph:e.object({title:e.string().optional(),description:e.string().optional(),image:e.url().optional(),url:e.url().optional(),type:e.string().optional(),siteName:e.string().optional()}).optional(),twitter:e.object({card:e.enum([`summary`,`summary_large_image`,`app`,`player`]).optional(),title:e.string().optional(),description:e.string().optional(),image:e.url().optional(),site:e.string().optional()}).optional(),schema:e.array(e.record(e.string(),e.unknown())).optional(),canonical:e.url().optional(),robots:e.string().optional()});export const ResolvedMetadataSchema=MetadataSchema.extend({sources:e.array(e.string()).optional(),resolvedAt:e.number().optional()});export const MetadataChainSchema=e.object({global:MetadataSchema.optional(),sections:e.array(e.object({path:e.string(),metadata:MetadataSchema})),page:MetadataSchema.optional()});export const RouteDiscoveryOptionsSchema=e.object({pagesDirectory:e.string().min(1).default(`src/pages`),apiDirectory:e.string().min(1).default(`src/api`),extensions:e.array(e.string()).default([`.tsx`,`.ts`,`.jsx`,`.js`]),excludeDirectories:e.array(e.string()).default([`node_modules`,`.git`]),enableWatching:e.boolean().default(!1),developmentMode:e.boolean().default(!1),quietMode:e.boolean().default(!1)});export const FileSystemRouterConfigSchema=e.object({discovery:RouteDiscoveryOptionsSchema.optional(),enabled:e.boolean().default(!0),fallbackToManual:e.boolean().default(!0),enableCaching:e.boolean().default(!0),cacheTTL:e.number().positive().default(3e5)});export const PageFileSchema=e.object({filePath:e.string().min(1),relativePath:e.string().min(1),extension:e.string().min(1),isPrivate:e.boolean(),routeGroup:e.string().optional(),mtime:e.number().optional()});export const RouteHandlerSchema=e.object({pattern:e.any(),handler:e.any(),metadata:e.object({filePath:e.string(),routeType:RouteTypeSchema,priority:e.number(),dynamicSegments:e.array(e.string())})});export const RouteCacheEntrySchema=e.object({routes:e.array(FileSystemRouteSchema),timestamp:e.number(),fileMtimes:e.record(e.string(),e.number())});export const FileSystemApiRouteSchema=e.object({pattern:e.any(),filePath:e.string().min(1),methods:e.array(e.enum([`GET`,`POST`,`PUT`,`DELETE`,`PATCH`,`HEAD`,`OPTIONS`])),priority:e.number().int().min(0),dynamicSegments:e.array(e.string())});export const FileSystemApiModuleSchema=e.object({GET:e.any().optional(),POST:e.any().optional(),PUT:e.any().optional(),DELETE:e.any().optional(),PATCH:e.any().optional(),HEAD:e.any().optional(),OPTIONS:e.any().optional()});export function isFileSystemRoute(e){return FileSystemRouteSchema.safeParse(e).success}export function isRoutePageModule(e){return RoutePageModuleSchema.safeParse(e).success}export function isMetadata(e){return MetadataSchema.safeParse(e).success}export function isRouteParams(e){return RouteParamsSchema.safeParse(e).success}export function isFileSystemApiModule(e){return FileSystemApiModuleSchema.safeParse(e).success}export{createTypedApiHandler,createTypedMetadataGenerator,createTypedPageComponent,createTypedPageLoader,isValidPageProps,isValidRouteParams,isValidRoutePattern,validatePageComponent}from"../types/routing.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createCipheriv as e,createDecipheriv as t,randomBytes as n}from"node:crypto";let r=``;try{r=(await import(`virtual:server-island-key`)).serverIslandKey??``}catch{}const i=`aes-256-gcm`;let a,o=!1;export function generateKey(){return n(32).toString(`base64`)}
|
|
1
|
+
import{createCipheriv as e,createDecipheriv as t,randomBytes as n}from"node:crypto";let r=``;try{r=(await import(`virtual:server-island-key`)).serverIslandKey??``}catch{}const i=`aes-256-gcm`;let a,o=!1;export function generateKey(){return n(32).toString(`base64`)}function c(){try{let e=globalThis.process?.env;if(e&&typeof e==`object`)return e}catch{}return{}}export function getKey(){let e=c(),t=e.AVALON_KEY;if(t){let e=Buffer.from(t,`base64`);if(e.length!==32)throw Error(`AVALON_KEY must be a base64-encoded 256-bit (32-byte) key. Got ${e.length} bytes.`);return t}if(r&&Buffer.from(r,`base64`).length===32)return r;if(e.NODE_ENV===`production`)throw Error("AVALON_KEY is required in production for server islands. Generate one with `npx avalon key` and set it as an environment variable so all server instances share the same encryption secret.");return o||(o=!0,console.warn(`[avalon] AVALON_KEY not set — using a per-process key for server islands. This only works for single-process development. Set AVALON_KEY for production.`)),a||=generateKey(),a}function l(e){return e.replaceAll(`+`,`-`).replaceAll(`/`,`_`).replace(/=+$/,``)}function u(e){let t=e.replaceAll(`-`,`+`).replaceAll(`_`,`/`),n=t.length%4;return n===2?t+=`==`:n===3&&(t+=`=`),t}export function encrypt(t,r){let a=Buffer.from(r??getKey(),`base64`);if(a.length!==32)throw Error(`Encryption key must be 32 bytes. Got ${a.length} bytes.`);let o=n(12),s=e(i,a,o),c=Buffer.concat([s.update(t,`utf8`),s.final()]),u=s.getAuthTag();return l(Buffer.concat([o,c,u]).toString(`base64`))}export function decrypt(e,n){let r=Buffer.from(n??getKey(),`base64`);if(r.length!==32)throw Error(`Encryption key must be 32 bytes. Got ${r.length} bytes.`);let a=Buffer.from(u(e),`base64`);if(a.length<28)throw Error(`Invalid encrypted data: too short`);let o=a.subarray(0,12),s=a.subarray(a.length-16),c=a.subarray(12,a.length-16),l=t(i,r,o);return l.setAuthTag(s),Buffer.concat([l.update(c),l.final()]).toString(`utf8`)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{serverIslandCSS as
|
|
1
|
+
import{ensureServerIslandIntegrations as e}from"virtual:server-island-integrations";import{serverIslandCSS as t,serverIslandLoaders as n,serverIslandManifest as r}from"virtual:server-island-manifest";import{h as i}from"preact";import a from"preact-render-to-string";import{loadIntegration as o}from"../islands/integration-loader.js";import{escapeJsonForScript as s,generatePerIslandScript as c}from"../islands/per-island-script.js";import{decrypt as l}from"./encryption.js";import{lookupComponent as u}from"./manifest.js";const d=n??{};function f(){return d}function p(e,t){return new Response(e,{status:t,headers:{"Content-Type":`text/plain`}})}export function defineServerIslandHandler(t={}){let{isDev:n=process.env.NODE_ENV!==`production`,defaultCacheControl:i=`private, no-store`}=t;return async t=>{e();let a=x(t);if(!a)return p(`Missing component ID`,400);let o=await h(t);if(o instanceof Response)return o;let s=g(o,n);if(s instanceof Response)return s;let{props:c,islandMeta:l,srcPath:d}=s,f=u(a)??r?.[a]??(n?d:void 0);if(!f)return p(`Component not found`,404);let m=await _(a,f,n,!!l);if(m instanceof Response)return m;let y=await v({componentModule:m,props:c,islandMeta:l,componentId:a,modulePath:f,isDev:n});if(y instanceof Response)return y;let S=y.html;return l&&(S+=b({componentId:a,islandMeta:l,modulePath:f,props:c,ssrFailed:y.ssrFailed,hydrationRenderId:y.hydrationRenderId,isDev:n})),new Response(S,{status:200,headers:{"Content-Type":`text/html`,"Cache-Control":i}})}}async function h(e){let t;try{t=await w(e)}catch{return p(`Failed to read request body`,400)}return t||p(`Missing encrypted props`,400)}function g(e,t){try{let n;n=t&&e.startsWith(`dev.`)?Buffer.from(e.slice(4),`base64url`).toString(`utf8`):l(e);let r=JSON.parse(n),i={props:r};return r.__island&&(i.islandMeta=r.__island,delete r.__island),r.__src&&(i.srcPath=r.__src,delete r.__src),i}catch{return p(`Bad Request: decryption failed`,400)}}async function _(e,t,n,r){try{let r=f()[e],i;if(r?i=await r():(n&&console.log(`[server-islands] Loading component from:`,t),i=await import(t)),!i.default)throw TypeError(`Module "${t}" does not have a default export`);return i}catch(e){return r&&n?(console.warn(`[server-islands] Component import failed (will render client-side):`,t,e instanceof Error?e.message:e),{default:null}):p(n&&e instanceof Error?e.message:`Component import failed`,500)}}async function v(e){let{componentModule:t,props:n,islandMeta:r,componentId:o,modulePath:s,isDev:c}=e;if(!t.default)return{html:``,ssrFailed:!0};let l=r?.framework??`preact`;try{if(l===`preact`||l===`react`){let e=t.default;return{html:a(i(e,n)),ssrFailed:!1}}return await y({framework:l,component:t.default,props:n,islandMeta:r,componentId:o,modulePath:s,isDev:c})}catch(e){if(r&&c)return{html:``,ssrFailed:!0};let t=c&&e instanceof Error?`${e.message}\n${e.stack}`:``;return console.error(`[server-islands] Render error for`,s,e),p(t||`Internal Server Error`,500)}}async function y(e){let{framework:n,component:r,props:i,islandMeta:a,componentId:s,modulePath:c,isDev:l}=e,u=await(await o(n)).render({component:r,props:i,src:c,condition:a?.condition??`on:client`,ssrOnly:!1,viteServer:void 0,isDev:l}),d=u.html;u.css?d=`${d}<style>${u.css}</style>`:t[s]&&(d=`${d}<style>${t[s]}</style>`);let f=u.hydrationData?.renderId;return{html:d,ssrFailed:!1,hydrationRenderId:f}}function b(e){let{componentId:t,islandMeta:n,modulePath:r,props:i,ssrFailed:a,hydrationRenderId:o,isDev:l}=e,u=n.elementId??`si-${t}`,d=n.componentSrc??r,f;try{f=JSON.stringify(i)}catch(e){let n=e instanceof Error?e.message:String(e);throw Error(`Failed to serialize props for server island (componentId=${t}, module=${r}, elementId=${u}): ${n}`)}if(l){let e=a?`on:client`:n.condition??`on:client`,t=new URL(`../client/server-island-hydrate.ts`,import.meta.url).pathname,r=n.framework??`preact`,i=o?JSON.stringify(o):`undefined`;return`<script type="module">
|
|
2
2
|
import{hydrateServerIsland}from"/@fs${t}";
|
|
3
|
-
hydrateServerIsland(${JSON.stringify(u)},${JSON.stringify(d)},${
|
|
4
|
-
<\/script>`}return
|
|
3
|
+
hydrateServerIsland(${JSON.stringify(u)},${JSON.stringify(d)},${s(f)},${JSON.stringify(e)},${JSON.stringify(r)},${i});
|
|
4
|
+
<\/script>`}return c({islandId:u,componentSrc:d,framework:n.framework,condition:n.condition,conditionArg:n.conditionArg,propsJson:f})}function x(e){let t=e.context?.params;if(t?.componentId)return t.componentId;let n=S(e);return/\/_server-islands\/([^/?]+)/.exec(n)?.[1]}function S(e){if(e.url)return e.url.pathname;let t=e.req?.url;return t?new URL(t,`http://localhost`).pathname:`/`}function C(e){return e.req?.method??`GET`}async function w(e){let t=C(e).toUpperCase();if(t===`GET`)return(e.url?e.url.searchParams:new URLSearchParams(``)).get(`p`)||void 0;if(t===`POST`){let t=e.web?.request;if(t)return await t.text();if(e.node?.req)return await T(e.node.req);if(e._body!==void 0)return String(e._body)}}function T(e){return new Promise((t,n)=>{let r=[];e.on(`data`,e=>r.push(e)),e.on(`end`,()=>t(Buffer.concat(r).toString(`utf8`))),e.on(`error`,n)})}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import e from"preact-render-to-string";import{encrypt as t}from"./encryption.js";
|
|
1
|
+
import e from"preact-render-to-string";import{encrypt as t}from"./encryption.js";function n(){try{if(import.meta.env?.DEV===!0)return!0;if(import.meta.env?.PROD===!0)return!1}catch{}return!1}function r(e){return n()?`dev.${Buffer.from(e,`utf8`).toString(`base64url`)}`:t(e)}let i=0;function a(e,t){try{return JSON.stringify(e)}catch(e){let n=e instanceof Error?e.message:String(e);throw Error(`Failed to serialize server island props (elementId=${t.elementId}, componentSrc=${t.componentSrc??`unknown`}, combinedIsland=${t.hasIsland}). Props must be JSON-serializable (no functions, circular refs, or BigInt): ${n}`)}}export function renderServerIsland(t,n,o,l,u,d,f){let p=`si-${t}-${i++}`,m={...n};d&&(m.__src=d),l&&(m.__island={condition:l.condition??`on:client`,conditionArg:l.conditionArg,framework:f??`preact`,componentSrc:u,elementId:p});let h=r(a(m,{componentSrc:u,elementId:p,hasIsland:!!l})),g=o.fallback?e(o.fallback):``,_=o.timeout??1e4,v=`/_server-islands/${t}`,y=s({elementId:p,endpointPath:v,timeout:_,islandProp:l});return`${`<avalon-server-island id="${p}" data-endpoint="${c(v)}" data-timeout="${_}" data-p="${c(h)}">`}${g}</avalon-server-island>\n${y}`}function s(e){let{elementId:t,endpointPath:n,timeout:r,islandProp:i}=e,a=i?`el.querySelectorAll('script[type="module"]').forEach(s=>{const n=document.createElement("script");n.type="module";n.textContent=s.textContent;s.replaceWith(n);});`:``;return`<script type="module">
|
|
2
2
|
(async()=>{
|
|
3
3
|
const el=document.getElementById("${t}");
|
|
4
|
-
if(!el)return;
|
|
4
|
+
if(!el||el.dataset.siStarted)return;
|
|
5
|
+
el.dataset.siStarted="1";
|
|
5
6
|
const p=el.dataset.p;
|
|
6
|
-
const url="${n}?p="+encodeURIComponent(p);
|
|
7
|
+
const url=(el.dataset.endpoint||"${n}")+"?p="+encodeURIComponent(p);
|
|
7
8
|
const ctrl=new AbortController();
|
|
8
|
-
const t=setTimeout(()=>ctrl.abort()
|
|
9
|
+
const t=setTimeout(()=>ctrl.abort(),Number(el.dataset.timeout)||${r});
|
|
9
10
|
try{
|
|
10
|
-
const r=await fetch(...(url.length>2048?["${n}",{method:"POST",body:p,headers:{"content-type":"text/plain"},signal:ctrl.signal}]:[url,{signal:ctrl.signal}]));
|
|
11
|
+
const r=await fetch(...(url.length>2048?[el.dataset.endpoint||"${n}",{method:"POST",body:p,headers:{"content-type":"text/plain"},signal:ctrl.signal}]:[url,{signal:ctrl.signal}]));
|
|
11
12
|
clearTimeout(t);
|
|
12
13
|
if(!r.ok)return;
|
|
13
14
|
const html=await r.text();
|
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
* - GET: encrypted props in the `p` query parameter
|
|
10
10
|
* - POST: encrypted props in the request body
|
|
11
11
|
*
|
|
12
|
+
* Framework SSR integrations are registered via the import in `endpoint.ts`
|
|
13
|
+
* (`virtual:server-island-integrations`).
|
|
14
|
+
*
|
|
12
15
|
* @module server-islands/route
|
|
13
16
|
*/
|
|
14
|
-
import "virtual:server-island-integrations";
|
|
15
17
|
declare const handler: (event: import("h3").H3Event) => Promise<Response>;
|
|
16
18
|
export default handler;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import{defineServerIslandHandler as e}from"./endpoint.js";const t=e();export default t;
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import type { ServerIslandProp } from "../server-islands/types.ts";
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Type augmentation for the `island` prop on island components.
|
|
5
3
|
*
|
|
6
|
-
* When
|
|
7
|
-
*
|
|
8
|
-
* plugin intercepts this at build time and converts it to a renderIsland() call.
|
|
4
|
+
* When using a component in a page with the `island` prop, the Vite transform
|
|
5
|
+
* intercepts it at build time and converts it to a renderIsland() call.
|
|
9
6
|
*
|
|
10
7
|
* Usage:
|
|
11
|
-
* import Counter from '../
|
|
8
|
+
* import Counter from '../components/Counter.tsx';
|
|
12
9
|
* <Counter island={{ condition: 'on:interaction' }} someProp={42} />
|
|
13
10
|
*
|
|
14
11
|
* Custom directives:
|
|
@@ -45,12 +42,21 @@ export type IslandDirective = {
|
|
|
45
42
|
| `on:${string}`;
|
|
46
43
|
|
|
47
44
|
/**
|
|
48
|
-
* Stable
|
|
49
|
-
*
|
|
50
|
-
* If omitted, Avalon generates
|
|
45
|
+
* Stable HTML `id` for this island instance.
|
|
46
|
+
* Required when two copies of the same component appear on one page.
|
|
47
|
+
* If omitted, Avalon generates a unique id from the component path plus an instance counter.
|
|
51
48
|
*/
|
|
52
49
|
id?: string;
|
|
53
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Keep this island's live instance across client navigations.
|
|
53
|
+
* The value is a stable key matched against the same key on the next page.
|
|
54
|
+
* `true` uses the component `src` as the key. Qwik islands are never persisted.
|
|
55
|
+
*
|
|
56
|
+
* Equivalent markup: `data-router-persist="key"` on a wrapper element.
|
|
57
|
+
*/
|
|
58
|
+
persist?: string | true;
|
|
59
|
+
|
|
54
60
|
/**
|
|
55
61
|
* Optional argument passed to custom hydration directives.
|
|
56
62
|
* For example, `on:delay` uses this as the delay in milliseconds.
|
|
@@ -61,4 +67,17 @@ export type IslandDirective = {
|
|
|
61
67
|
* ```
|
|
62
68
|
*/
|
|
63
69
|
conditionArg?: string;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Skip server rendering of this component. Avalon emits an empty
|
|
73
|
+
* `<avalon-island>` placeholder and mounts the component in the browser.
|
|
74
|
+
* Props are still serialized. Combines with `condition` for when to mount.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```tsx
|
|
78
|
+
* <Chart island={{ clientOnly: true }} userId={user.id} />
|
|
79
|
+
* <Chart island={{ clientOnly: true, condition: 'on:idle' }} />
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
clientOnly?: boolean;
|
|
64
83
|
};
|
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
declare module "virtual:avalon/page-loader" {
|
|
10
|
-
export function loadPage(
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
export function loadPage(pathname: string): {
|
|
11
|
+
default: unknown;
|
|
12
|
+
metadata?: Record<string, unknown>;
|
|
13
|
+
clientNavigation?: boolean;
|
|
14
|
+
} | null;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
declare module "virtual:avalon/config" {
|
|
@@ -18,6 +20,7 @@ declare module "virtual:avalon/config" {
|
|
|
18
20
|
pagesDir: string;
|
|
19
21
|
layoutsDir: string;
|
|
20
22
|
isDev: boolean;
|
|
23
|
+
clientRouter?: boolean;
|
|
21
24
|
[key: string]: unknown;
|
|
22
25
|
};
|
|
23
26
|
export default config;
|
|
@@ -42,6 +45,10 @@ declare module "virtual:avalon/assets" {
|
|
|
42
45
|
};
|
|
43
46
|
}
|
|
44
47
|
|
|
48
|
+
declare module "virtual:avalon/ssr-dom" {
|
|
49
|
+
// Side-effect module: installs a `document` stub before Lit SSR loads.
|
|
50
|
+
}
|
|
51
|
+
|
|
45
52
|
declare module "virtual:avalon/renderer" {
|
|
46
53
|
const handler: {
|
|
47
54
|
(event: unknown): Promise<Response>;
|
|
@@ -56,7 +63,20 @@ declare module "virtual:avalon/client-entry" {
|
|
|
56
63
|
}
|
|
57
64
|
|
|
58
65
|
declare module "virtual:avalon/integration-loader" {
|
|
59
|
-
export function loadIntegrationModule(framework: string): Promise<
|
|
66
|
+
export function loadIntegrationModule(framework: string): Promise<{
|
|
67
|
+
hydrate?: (
|
|
68
|
+
el: HTMLElement,
|
|
69
|
+
component: unknown,
|
|
70
|
+
props: Record<string, unknown>,
|
|
71
|
+
) => void | Promise<void>;
|
|
72
|
+
mount?: (
|
|
73
|
+
el: HTMLElement,
|
|
74
|
+
component: unknown,
|
|
75
|
+
props: Record<string, unknown>,
|
|
76
|
+
) => void | Promise<void>;
|
|
77
|
+
unmount?: (el: HTMLElement) => void | Promise<void>;
|
|
78
|
+
preLitHydration?: () => Promise<void>;
|
|
79
|
+
}>;
|
|
60
80
|
export function preLitHydration(): Promise<void>;
|
|
61
81
|
export function loadHMRAdapter(framework: string): Promise<unknown>;
|
|
62
82
|
}
|
|
@@ -76,8 +96,8 @@ declare module "virtual:server-island-key" {
|
|
|
76
96
|
}
|
|
77
97
|
|
|
78
98
|
declare module "virtual:server-island-integrations" {
|
|
79
|
-
|
|
80
|
-
|
|
99
|
+
/** Registers framework SSR integrations into the global registry (idempotent). */
|
|
100
|
+
export function ensureServerIslandIntegrations(): void;
|
|
81
101
|
}
|
|
82
102
|
|
|
83
103
|
declare module "virtual:avalon-actions-manifest" {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as e}from"node:fs";import{resolve as t}from"node:path";import n from"node:process";export const DEFAULT_MDX_CONFIG={jsxImportSource:`preact`,syntaxHighlighting:!0,remarkPlugins:[],rehypePlugins:[]};export const DEFAULT_IMAGE_CONFIG={enabled:!0,defaultFormat:`webp`,quality:80,widths:[200,400,600,800,1200],removeMetadata:!0,include:/^[^?]+\.(heif|avif|jpeg|jpg|png|tiff|webp|gif)(\?.*)?$/,exclude:`public/**/*`};export const DEFAULT_CONFIG={core:`preact`,pagesDir:`src/pages`,layoutsDir:`src/layouts`,modules:null,integrations:[],mdx:DEFAULT_MDX_CONFIG,image:DEFAULT_IMAGE_CONFIG,verbose:!1,autoDiscoverIntegrations:!0,validateIntegrations:!0,showWarnings:!0,lazyIntegrations:!0};export const DEFAULT_MODULES_CONFIG={pagesDirName:`pages`,layoutsDirName:`layouts`};function s(e){return e?typeof e==`string`?{dir:e,pagesDirName:DEFAULT_MODULES_CONFIG.pagesDirName,layoutsDirName:DEFAULT_MODULES_CONFIG.layoutsDirName}:{dir:e.dir,pagesDirName:e.pagesDirName??DEFAULT_MODULES_CONFIG.pagesDirName,layoutsDirName:e.layoutsDirName??DEFAULT_MODULES_CONFIG.layoutsDirName}:null}function c(e){return e===!1?{...DEFAULT_IMAGE_CONFIG,enabled:!1}:e===void 0||e===!0?DEFAULT_IMAGE_CONFIG:{enabled:e.enabled??DEFAULT_IMAGE_CONFIG.enabled,defaultFormat:e.defaultFormat??DEFAULT_IMAGE_CONFIG.defaultFormat,quality:e.quality??DEFAULT_IMAGE_CONFIG.quality,widths:e.widths??DEFAULT_IMAGE_CONFIG.widths,removeMetadata:e.removeMetadata??DEFAULT_IMAGE_CONFIG.removeMetadata,include:e.include??DEFAULT_IMAGE_CONFIG.include,exclude:e.exclude??DEFAULT_IMAGE_CONFIG.exclude}}export function resolveConfig(e,t){let n=e??{},i=s(n.modules),o=c(n.image),l=n.core??DEFAULT_CONFIG.core;return{core:l,pagesDir:n.pagesDir??DEFAULT_CONFIG.pagesDir,layoutsDir:n.layoutsDir??DEFAULT_CONFIG.layoutsDir,modules:i,integrations:n.integrations??DEFAULT_CONFIG.integrations,mdx:{jsxImportSource:n.mdx?.jsxImportSource??l,syntaxHighlighting:n.mdx?.syntaxHighlighting??DEFAULT_MDX_CONFIG.syntaxHighlighting,remarkPlugins:n.mdx?.remarkPlugins??DEFAULT_MDX_CONFIG.remarkPlugins,rehypePlugins:n.mdx?.rehypePlugins??DEFAULT_MDX_CONFIG.rehypePlugins},image:o,verbose:n.verbose??DEFAULT_CONFIG.verbose,autoDiscoverIntegrations:n.autoDiscoverIntegrations??DEFAULT_CONFIG.autoDiscoverIntegrations,validateIntegrations:n.validateIntegrations??DEFAULT_CONFIG.validateIntegrations,showWarnings:n.showWarnings??DEFAULT_CONFIG.showWarnings,lazyIntegrations:n.lazyIntegrations??DEFAULT_CONFIG.lazyIntegrations,isDev:t}}export function checkDirectoriesExist(r,i=n.cwd()){let a=[];!r.modules&&r.pagesDir&&a.push({path:r.pagesDir,type:`pages`}),r.layoutsDir&&a.push({path:r.layoutsDir,type:`layouts`});let o=[];for(let{path:n,type:s}of a){let a=t(i,n),c=e(a);o.push({path:n,absolutePath:a,exists:c,type:s}),!c&&r.showWarnings&&console.warn(`⚠️ Avalon: ${s} directory '${n}' does not exist (resolved to: ${a}). This directory will be skipped.`)}return o}export function logDirectoryCheckSummary(e,t){let n=e.filter(e=>!e.exists),r=e.filter(e=>e.exists);t&&(r.length>0&&console.log(` ✅ Found directories: ${r.map(e=>e.path).join(`, `)}`),n.length>0&&console.log(` ⚠️ Missing directories: ${n.map(e=>e.path).join(`, `)}`))}
|
|
1
|
+
import{existsSync as e}from"node:fs";import{resolve as t}from"node:path";import n from"node:process";export const DEFAULT_MDX_CONFIG={jsxImportSource:`preact`,syntaxHighlighting:!0,remarkPlugins:[],rehypePlugins:[]};export const DEFAULT_IMAGE_CONFIG={enabled:!0,defaultFormat:`webp`,quality:80,widths:[200,400,600,800,1200],removeMetadata:!0,include:/^[^?]+\.(heif|avif|jpeg|jpg|png|tiff|webp|gif)(\?.*)?$/,exclude:`public/**/*`};export const DEFAULT_CONFIG={core:`preact`,pagesDir:`src/pages`,layoutsDir:`src/layouts`,modules:null,integrations:[],mdx:DEFAULT_MDX_CONFIG,image:DEFAULT_IMAGE_CONFIG,verbose:!1,autoDiscoverIntegrations:!0,validateIntegrations:!0,showWarnings:!0,lazyIntegrations:!0,clientRouter:!1};export const DEFAULT_MODULES_CONFIG={pagesDirName:`pages`,layoutsDirName:`layouts`};function s(e){return e?typeof e==`string`?{dir:e,pagesDirName:DEFAULT_MODULES_CONFIG.pagesDirName,layoutsDirName:DEFAULT_MODULES_CONFIG.layoutsDirName}:{dir:e.dir,pagesDirName:e.pagesDirName??DEFAULT_MODULES_CONFIG.pagesDirName,layoutsDirName:e.layoutsDirName??DEFAULT_MODULES_CONFIG.layoutsDirName}:null}function c(e){return e===!1?{...DEFAULT_IMAGE_CONFIG,enabled:!1}:e===void 0||e===!0?DEFAULT_IMAGE_CONFIG:{enabled:e.enabled??DEFAULT_IMAGE_CONFIG.enabled,defaultFormat:e.defaultFormat??DEFAULT_IMAGE_CONFIG.defaultFormat,quality:e.quality??DEFAULT_IMAGE_CONFIG.quality,widths:e.widths??DEFAULT_IMAGE_CONFIG.widths,removeMetadata:e.removeMetadata??DEFAULT_IMAGE_CONFIG.removeMetadata,include:e.include??DEFAULT_IMAGE_CONFIG.include,exclude:e.exclude??DEFAULT_IMAGE_CONFIG.exclude}}export function resolveConfig(e,t){let n=e??{},i=s(n.modules),o=c(n.image),l=n.core??DEFAULT_CONFIG.core;return{core:l,pagesDir:n.pagesDir??DEFAULT_CONFIG.pagesDir,layoutsDir:n.layoutsDir??DEFAULT_CONFIG.layoutsDir,modules:i,integrations:n.integrations??DEFAULT_CONFIG.integrations,mdx:{jsxImportSource:n.mdx?.jsxImportSource??l,syntaxHighlighting:n.mdx?.syntaxHighlighting??DEFAULT_MDX_CONFIG.syntaxHighlighting,remarkPlugins:n.mdx?.remarkPlugins??DEFAULT_MDX_CONFIG.remarkPlugins,rehypePlugins:n.mdx?.rehypePlugins??DEFAULT_MDX_CONFIG.rehypePlugins},image:o,verbose:n.verbose??DEFAULT_CONFIG.verbose,autoDiscoverIntegrations:n.autoDiscoverIntegrations??DEFAULT_CONFIG.autoDiscoverIntegrations,validateIntegrations:n.validateIntegrations??DEFAULT_CONFIG.validateIntegrations,showWarnings:n.showWarnings??DEFAULT_CONFIG.showWarnings,lazyIntegrations:n.lazyIntegrations??DEFAULT_CONFIG.lazyIntegrations,clientRouter:n.clientRouter??DEFAULT_CONFIG.clientRouter,isDev:t}}export function checkDirectoriesExist(r,i=n.cwd()){let a=[];!r.modules&&r.pagesDir&&a.push({path:r.pagesDir,type:`pages`}),r.layoutsDir&&a.push({path:r.layoutsDir,type:`layouts`});let o=[];for(let{path:n,type:s}of a){let a=t(i,n),c=e(a);o.push({path:n,absolutePath:a,exists:c,type:s}),!c&&r.showWarnings&&console.warn(`⚠️ Avalon: ${s} directory '${n}' does not exist (resolved to: ${a}). This directory will be skipped.`)}return o}export function logDirectoryCheckSummary(e,t){let n=e.filter(e=>!e.exists),r=e.filter(e=>e.exists);t&&(r.length>0&&console.log(` ✅ Found directories: ${r.map(e=>e.path).join(`, `)}`),n.length>0&&console.log(` ⚠️ Missing directories: ${n.map(e=>e.path).join(`, `)}`))}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev CSS for Nitro SSR is collected from each page's static import graph
|
|
3
|
+
* (plus layouts and global CSS), not by globbing every `.css` file on disk.
|
|
4
|
+
* The layouts virtual module lives in Nitro's realm, so this walk is filesystem
|
|
5
|
+
* based — Vite's module graph is not available there.
|
|
6
|
+
*/
|
|
7
|
+
import { type DevCssRouteEntry, type DevCssRouteTable } from "../render/dev-css-select.ts";
|
|
8
|
+
export { matchPathPattern, selectDevCssHrefs } from "../render/dev-css-select.ts";
|
|
9
|
+
export type { DevCssRouteEntry, DevCssRouteTable };
|
|
10
|
+
export interface LayoutCssEntry {
|
|
11
|
+
prefix: string;
|
|
12
|
+
filePath: string;
|
|
13
|
+
isRoot: boolean;
|
|
14
|
+
skipRoot: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface CssGraphAlias {
|
|
17
|
+
find: string | RegExp;
|
|
18
|
+
replacement: string;
|
|
19
|
+
}
|
|
20
|
+
/** Same fallbacks as `mdx-island-transform` so MDX `import X from '@shared/…'` reaches CSS. */
|
|
21
|
+
export declare function defaultCssGraphAliases(cwd: string): CssGraphAlias[];
|
|
22
|
+
/** Resolve a relative, root-absolute, or aliased specifier to an existing file. */
|
|
23
|
+
export declare function resolveProjectImport(fromFile: string, spec: string, cwd: string, aliases?: CssGraphAlias[]): string | null;
|
|
24
|
+
/**
|
|
25
|
+
* Collect `/`-rooted CSS hrefs reachable from `entryFile` via static imports
|
|
26
|
+
* and CSS `@import`. Skips `node_modules`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function collectCssHrefsFromEntry(entryFile: string, cwd: string, aliases?: CssGraphAlias[]): string[];
|
|
29
|
+
/**
|
|
30
|
+
* Build the per-route CSS table used by `virtual:avalon/layouts` in development.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildDevCssRouteTable(options: {
|
|
33
|
+
cwd: string;
|
|
34
|
+
routes: Array<{
|
|
35
|
+
pattern: string;
|
|
36
|
+
filePath: string;
|
|
37
|
+
}>;
|
|
38
|
+
layouts: LayoutCssEntry[];
|
|
39
|
+
globalCSS: string[];
|
|
40
|
+
aliases?: CssGraphAlias[];
|
|
41
|
+
}): DevCssRouteTable;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFileSync as e,statSync as t}from"node:fs";import{dirname as n,extname as r,join as i,relative as a,resolve as o}from"node:path";import{stripTrailingSlash as s}from"../render/dev-css-select.js";export{matchPathPattern,selectDevCssHrefs}from"../render/dev-css-select.js";const c=[`.tsx`,`.ts`,`.jsx`,`.js`,`.mjs`,`.cjs`,`.vue`,`.svelte`,`.mdx`,`.md`,`.css`,`.scss`,`.sass`,`.less`,`.styl`,`.stylus`],l=new Set([`.css`,`.scss`,`.sass`,`.less`,`.styl`,`.stylus`]),u=c.map(e=>`index${e}`),d=/(?:from|import)\s+['"]([^'"]+)['"]/g,f=/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g,p=/@import\s+(?:url\s*\(\s*)?['"]([^'"]+)['"]/g;function m(e){return l.has(r(e.split(`?`)[0]).toLowerCase())}function h(e,t){let n=a(e,t).replaceAll(`\\`,`/`);return n.startsWith(`/`)?n:`/${n}`}function g(e){let n=e.split(`?`)[0];try{if(!t(n).isFile())return null}catch{return null}return n}function _(e){for(let t of e){let e=g(t);if(e)return e}return null}export function defaultCssGraphAliases(e){return[{find:`@shared`,replacement:i(e,`app/shared`)},{find:`@modules`,replacement:i(e,`app/modules`)},{find:`@/`,replacement:`${i(e,`app`)}/`},{find:`$islands`,replacement:i(e,`src/islands`)},{find:`$components`,replacement:i(e,`src/components`)},{find:`~/`,replacement:`${i(e,`src`)}/`}]}function v(e,t,n){if(e===t)return n;let r=t.endsWith(`/`)?t:`${t}/`;if(!e.startsWith(r))return null;let i=e.slice(r.length);return n.endsWith(`/`)?`${n}${i}`:i?`${n}/${i}`:n}function y(e,t){for(let n of t){let t=n.find;if(typeof t==`string`){let r=v(e,t,n.replacement);if(r)return r;continue}if(t.test(e))return e.replace(t,n.replacement)}return null}function b(e){return g(e)||(r(e.split(`?`)[0])?null:_([...c.map(t=>e+t),...u.map(t=>i(e,t))]))}export function resolveProjectImport(e,t,r,a=[]){let s=t.split(`?`)[0].split(`#`)[0];if(!s||/^(https?:|data:|virtual:)/i.test(s)||s.startsWith(`\0`))return null;let c=y(s,[...a,...defaultCssGraphAliases(r)]);return c?b(c):s.startsWith(`.`)||s.startsWith(`/`)?b(s.startsWith(`/`)?i(r,s.slice(1)):o(n(e),s)):null}function x(e,t){return[...e.matchAll(t)].flatMap(e=>e[1]?[e[1]]:[])}function S(e,t){return t?x(e,p):[...x(e,d),...x(e,f),...x(e,p)]}function C(e){return e.includes(`/node_modules/`)||e.includes(`\\node_modules\\`)}function w(t){try{return e(t,`utf8`)}catch{return null}}export function collectCssHrefsFromEntry(e,t,n=[]){let r=[],i=new Set,a=[o(e)];for(;a.length>0;){let e=a.pop();if(!e||i.has(e)||C(e))continue;i.add(e);let o=w(e);if(o===null)continue;let s=m(e);s&&r.push(h(t,e));for(let r of S(o,s)){let o=resolveProjectImport(e,r,t,n);o&&!i.has(o)&&!C(o)&&a.push(o)}}return T(r)}function T(e){return[...new Set(e)]}function E(...e){return T(e.flat())}function D(e,t){if(t===`/`)return!0;let n=s(e),r=s(t);return n===r||n.startsWith(`${r}/`)}function O(e,t){let n=[];for(let r of t){let t=r.startsWith(`/`)?r:`/${r}`,i=g(o(e,t.slice(1)));if(i){n.push(...collectCssHrefsFromEntry(i,e));continue}n.push(t)}return T(n)}function k(e,t,n,r,i){let a=collectCssHrefsFromEntry(e.filePath,t,i),o=r.filter(t=>D(e.pattern,t.prefix)),s=o.some(e=>!e.isRoot&&e.skipRoot),c=o.filter(e=>!(e.isRoot&&s)).flatMap(e=>e.hrefs);return{pattern:e.pattern,pageHrefs:E(n,a),hrefs:E(n,c,a)}}export function buildDevCssRouteTable(e){let t=e.aliases??[],n=O(e.cwd,e.globalCSS),r=e.layouts.map(n=>({...n,hrefs:collectCssHrefsFromEntry(n.filePath,e.cwd,t)}));return{globalHrefs:n,fallbackHrefs:E(n,E(...r.filter(e=>e.isRoot).map(e=>e.hrefs))),routes:e.routes.map(i=>k(i,e.cwd,n,r,t))}}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev CSS HMR is owned by Avalon, not Vite's link rewriter or Nitro's
|
|
3
|
+
* document reload.
|
|
4
|
+
*
|
|
5
|
+
* Pipeline:
|
|
6
|
+
* 1. SSR injects `<link rel="stylesheet" href="…?direct">`.
|
|
7
|
+
* 2. `hotUpdate` returns [] so Vite does not css-update and Nitro does not
|
|
8
|
+
* treat SSR-only CSS as a document reload.
|
|
9
|
+
* 3. Hard-invalidate the file (and `?direct`) in every environment, then
|
|
10
|
+
* transform with `?direct&v=` — Vite strips `t=<13 digits>`.
|
|
11
|
+
* 4. Send `{ href, css }` on the client hot channel. The client writes `css`
|
|
12
|
+
* into a `<style data-avalon-css>` tag (does not refetch the link).
|
|
13
|
+
*/
|
|
14
|
+
import type { Plugin, ViteDevServer } from "vite";
|
|
15
|
+
export declare function isCssHotFile(file: string): boolean;
|
|
16
|
+
export declare function fileToDevHref(root: string, file: string): string;
|
|
17
|
+
export declare function isBrowserFullReloadPayload(payload: unknown): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Drop only the document reload that is correlated with this CSS save.
|
|
20
|
+
* Unrelated JS / HTML / config reloads in the same 250 ms window still pass.
|
|
21
|
+
*/
|
|
22
|
+
export declare function isCssCorrelatedReload(payload: unknown): boolean;
|
|
23
|
+
/** Drop a document reload that Nitro emits for server-only CSS modules. */
|
|
24
|
+
export declare function shouldDropCssFullReload(cssSaveInFlight: boolean, payload: unknown): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* One in-flight CSS push per file. A save while that push is running is
|
|
27
|
+
* marked pending; when the active push settles, one follow-up runs for
|
|
28
|
+
* the latest CSS.
|
|
29
|
+
*/
|
|
30
|
+
export declare function scheduleCssPush(file: string, pushing: Set<string>, pending: Set<string>, run: (file: string) => Promise<void>): void;
|
|
31
|
+
/** Standalone client listener. Nitro HTML never runs `transformIndexHtml`. */
|
|
32
|
+
export declare function generateDevCssHmrModule(): string;
|
|
33
|
+
export declare function createDevCssHmrPlugin(): Plugin;
|
|
34
|
+
export declare function invalidateCssFile(server: ViteDevServer, file: string): void;
|
|
35
|
+
export declare function cssHotPayload(href: string, css: string): {
|
|
36
|
+
type: "custom";
|
|
37
|
+
event: "avalon:css";
|
|
38
|
+
data: {
|
|
39
|
+
href: string;
|
|
40
|
+
css: string;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{relative as e}from"node:path";import{readDirectCss as t}from"../render/collect-css.js";const n=/\.(css|scss|sass|less|styl|stylus)$/;export function isCssHotFile(e){return n.test(e.split(`?`)[0]??e)}export function fileToDevHref(t,n){let r=e(t,n).replaceAll(`\\`,`/`);return r.startsWith(`/`)?r:`/${r}`}export function isBrowserFullReloadPayload(e){return!e||typeof e!=`object`?!1:e.type===`full-reload`}function o(e){let t=e.path;return typeof t==`string`?t:``}function s(e){let t=e.triggeredBy;return typeof t==`string`?t:``}export function isCssCorrelatedReload(e){if(!e||typeof e!=`object`)return!1;let t=s(e);if(t)return isCssHotFile(t);let n=o(e);return!n||n===`*`?!0:isCssHotFile(n)}export function shouldDropCssFullReload(e,t){return e&&isBrowserFullReloadPayload(t)&&isCssCorrelatedReload(t)}export function scheduleCssPush(e,t,n,r){if(t.has(e)){n.add(e);return}t.add(e),r(e).finally(()=>{t.delete(e),n.has(e)&&(n.delete(e),scheduleCssPush(e,t,n,r))})}export function generateDevCssHmrModule(){return[`if (import.meta.hot) {`,` import.meta.hot.on('avalon:css', function (data) {`,` if (!data || typeof data.href !== 'string') return;`,` var href = data.href.split('?')[0];`,` var id = href.charAt(0) === '/' ? href : '/' + href;`,` var css = typeof data.css === 'string' ? data.css : '';`,` var links = document.querySelectorAll('link[data-avalon-css]');`,` for (var i = 0; i < links.length; i++) {`,` if (links[i].dataset.avalonCss === id) {`,` links[i].href = id + '?direct&v=' + Date.now();`,` }`,` }`,` if (!css || css.indexOf('__vite__updateStyle') !== -1 || css.indexOf('import.meta.hot') !== -1) return;`,` var style;`,` var nodes = document.querySelectorAll('style[data-avalon-css]');`,` for (var j = 0; j < nodes.length; j++) {`,` if (nodes[j].dataset.avalonCss === id) { style = nodes[j]; break; }`,` }`,` if (!style) {`,` style = document.createElement('style');`,` style.setAttribute('type', 'text/css');`,` style.dataset.avalonCss = id;`,` document.head.appendChild(style);`,` }`,` style.textContent = css;`,` });`,`}`,``].join(`
|
|
2
|
+
`)}export function createDevCssHmrPlugin(){let e=!1,t,n=new Set,i=new Set,a=()=>{e=!0,t!==void 0&&clearTimeout(t),t=setTimeout(()=>{e=!1,t=void 0},250)};return{name:`avalon:dev-css-hmr`,apply:`serve`,enforce:`pre`,applyToEnvironment:()=>!0,configureServer(t){return()=>{let n=t.ws.send.bind(t.ws);t.ws.send=((...t)=>{if(!shouldDropCssFullReload(e,t[0]))return n(...t)})}},hotUpdate({file:e,server:t}){if(isCssHotFile(e))return a(),scheduleCssPush(e,n,i,e=>d(t,e)),[]}}}function c(e,t,n){let r=new Set(e.getModulesByFile?.(t)??[]);for(let t of n){let n=e.getModuleById?.(t);n&&r.add(n);let i=e.urlToModuleMap?.get(t);i&&r.add(i)}return r}function l(e,t){let n=e._pendingRequests;if(n)for(let e of t)n.delete(e)}export function invalidateCssFile(e,t){let n=e.config?.root?fileToDevHref(e.config.root,t):``,r=n?[n,`${n}?direct`]:[];for(let n of Object.values(e.environments??{})){let e=n.moduleGraph;for(let n of c(e,t,r))e.invalidateModule(n);l(n,r)}}export function cssHotPayload(e,t){return{type:`custom`,event:`avalon:css`,data:{href:e,css:t}}}function u(e,t,n){let r=cssHotPayload(t,n),i=new Set,a=e=>{!e||i.has(e)||(i.add(e),e.send(r))};a(e.ws),a(e.environments?.client?.hot)}async function d(e,n){invalidateCssFile(e,n);let r=fileToDevHref(e.config.root,n),a=await t(e,r);u(e,r,a),e.config.logger.info(`css hmr ${r} (${a.length}b)`,{timestamp:!0})}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite's default CSS-module hash includes file contents. After a save, the
|
|
3
|
+
* Nitro page still exports the previous class names while `?direct` CSS uses
|
|
4
|
+
* new hashes — the document looks unstyled. Dev hashes only the file path
|
|
5
|
+
* and local name so a value edit keeps the same selectors.
|
|
6
|
+
*/
|
|
7
|
+
export declare function stableDevScopedName(name: string, filename: string): string;
|
|
8
|
+
export declare function shouldSetDevScopedName(modules: unknown): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createHash as e}from"node:crypto";import{basename as t}from"node:path";export function stableDevScopedName(n,r){let i=(r.split(`?`)[0]??r).replaceAll(`\\`,`/`),a=e(`sha1`).update(`${i}\0${n}`).digest(`hex`).slice(0,5);return`_${t(i).replace(/\.module\.\w+$/,``).replace(/[^\w-]/g,`_`)}_${n.replace(/[^\w-]/g,`_`)}_${a}`}export function shouldSetDevScopedName(e){return e===!1?!1:e&&typeof e==`object`&&`generateScopedName`in e?e.generateScopedName==null:!0}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework Vite defaults derived from `core` + `integrations`.
|
|
3
|
+
*
|
|
4
|
+
* Users should not copy aliases, optimizeDeps, or ssr.noExternal into their
|
|
5
|
+
* config — Avalon applies them from the frameworks they actually enabled.
|
|
6
|
+
*/
|
|
7
|
+
import type { SSROptions, UserConfig } from "vite";
|
|
8
|
+
export interface FrameworkViteDefaultsOptions {
|
|
9
|
+
integrations: readonly string[];
|
|
10
|
+
core?: "preact" | "react";
|
|
11
|
+
command: "serve" | "build";
|
|
12
|
+
root?: string;
|
|
13
|
+
nitroPreset?: string;
|
|
14
|
+
/** Override package resolution (tests). Returns an ESM path or undefined. */
|
|
15
|
+
resolvePackage?: (specifier: string) => string | undefined;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Map a bare specifier to the Preact package Avalon should pin, or null
|
|
19
|
+
* when this id is not part of the compat rewrite.
|
|
20
|
+
*/
|
|
21
|
+
export declare function mapPreactCompatId(id: string, core?: "preact" | "react"): string | null;
|
|
22
|
+
/** Absolute Preact pins belong on SSR always, and on the client production build. */
|
|
23
|
+
export declare function shouldResolvePreactCompat(env: string | undefined, command: string | undefined): boolean;
|
|
24
|
+
export declare function asNoExternalList(value: SSROptions["noExternal"]): Array<string | RegExp>;
|
|
25
|
+
/**
|
|
26
|
+
* Vite config fragment Avalon merges in `config()`. Safe to call with a
|
|
27
|
+
* stub `resolvePackage` in tests.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getFrameworkViteDefaults(options: FrameworkViteDefaultsOptions): UserConfig;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createRequire as e}from"node:module";import{join as t}from"node:path";import{getOptimizeDepsForIntegrations as n,getSSRNoExternalForIntegrations as r}from"../build/integration-config.js";const i=`@builder.io/qwik`,a=[`preact`,`preact/hooks`,`preact/compat`,`preact/compat/server`,`preact/compat/client`],o=[{find:/^react$/,spec:`preact/compat`},{find:/^react\/jsx-runtime$/,spec:`preact/jsx-runtime`},{find:/^react\/jsx-dev-runtime$/,spec:`preact/jsx-runtime`},{find:/^react-dom$/,spec:`preact/compat`},{find:/^react-dom\/server$/,spec:`preact/compat/server`},{find:/^react-dom\/client$/,spec:`preact/compat/client`}],s={react:`preact/compat`,"react/jsx-runtime":`preact/jsx-runtime`,"react/jsx-dev-runtime":`preact/jsx-runtime`,"react-dom":`preact/compat`,"react-dom/server":`preact/compat/server`,"react-dom/client":`preact/compat/client`},c=new Set([...a,`preact/jsx-runtime`,`preact/jsx-dev-runtime`]);export function mapPreactCompatId(e,t=`preact`){return c.has(e)?e:t===`react`?null:s[e]??null}export function shouldResolvePreactCompat(e,t){return e===`ssr`||e===`nitro`?!0:t===`build`}const u=[{find:/^vue$/,replacement:`vue/dist/vue.runtime.esm-bundler.js`},{find:/^@vue\/shared$/,replacement:`@vue/shared/dist/shared.esm-bundler.js`},{find:/^@vue\/runtime-core$/,replacement:`@vue/runtime-core/dist/runtime-core.esm-bundler.js`},{find:/^@vue\/runtime-dom$/,replacement:`@vue/runtime-dom/dist/runtime-dom.esm-bundler.js`},{find:/^@vue\/reactivity$/,replacement:`@vue/reactivity/dist/reactivity.esm-bundler.js`},{find:/^@vue\/server-renderer$/,replacement:`@vue/server-renderer/dist/server-renderer.esm-bundler.js`}];function d(e){let t=e.replaceAll(`/`,String.raw`\/`);return RegExp(`^${t}$`)}export function asNoExternalList(e){return Array.isArray(e)?e:!e||e===!0?[]:[e]}function f(n,r){try{return e(t(n,`package.json`)).resolve(r).replace(/\.js$/,`.mjs`)}catch{return}}function p(e,t,n){n&&e.push({find:t,replacement:n})}function m(e,t){let n=new Set(e);return n.add(t),[...n]}function h(e,t,n){let r=[];if(t===`preact`||e.includes(`preact`))for(let e of a)p(r,d(e),n(e));if(t===`preact`)for(let{find:e,spec:t}of o)p(r,e,n(t));return e.includes(`vue`)&&r.push(...u),r}function g(e,t){let n={__DEV__:e===`serve`,__PROD__:e===`build`,global:`globalThis`,"process.env.NODE_ENV":JSON.stringify(e===`serve`?`development`:`production`)};return t.includes(`vue`)?(n.__VUE_OPTIONS_API__=!0,n.__VUE_PROD_DEVTOOLS__=e===`serve`,n):n}function _(e,t){let n=r(e.filter(e=>e!==`solid`));e.includes(`vue`)&&!n.includes(`estree-walker`)&&n.push(`estree-walker`);let i={noExternal:n};e.includes(`solid`)&&(i.resolve={conditions:[`node`]});let a=t?.startsWith(`cloudflare`)??!1;return(e.includes(`solid`)||a)&&(i.target=`webworker`),i}export function getFrameworkViteDefaults(e){let t=e.core??`preact`,r=e.root??process.cwd(),a=e.resolvePackage??(e=>f(r,e)),o=m(e.integrations,t),s=new Set(n(o));t===`react`&&(s.add(`react-dom/server`),s.add(`react/jsx-runtime`));let c=[];o.includes(`qwik`)&&(s.delete(i),s.delete(`${i}/server`),c.push(i));let l=h(e.integrations,t,a);return{define:g(e.command,e.integrations),resolve:l.length>0?{alias:l}:void 0,optimizeDeps:{include:[...s],exclude:c},ssr:_(o,e.nitroPreset)}}
|
|
@@ -19,8 +19,10 @@ export declare const VIRTUAL_MODULE_IDS: {
|
|
|
19
19
|
readonly ASSETS: "virtual:avalon/assets";
|
|
20
20
|
readonly RENDERER: "virtual:avalon/renderer";
|
|
21
21
|
readonly CLIENT_ENTRY: "virtual:avalon/client-entry";
|
|
22
|
+
readonly DEV_CSS_HMR: "virtual:avalon/dev-css-hmr";
|
|
22
23
|
readonly INTEGRATION_LOADER: "virtual:avalon/integration-loader";
|
|
23
24
|
readonly ACTIONS: "virtual:avalon/actions";
|
|
25
|
+
readonly SSR_DOM: "virtual:avalon/ssr-dom";
|
|
24
26
|
};
|
|
25
27
|
export declare const RESOLVED_VIRTUAL_IDS: {
|
|
26
28
|
readonly PAGE_ROUTES: "\0virtual:avalon/page-routes";
|
|
@@ -32,8 +34,10 @@ export declare const RESOLVED_VIRTUAL_IDS: {
|
|
|
32
34
|
readonly ASSETS: "\0virtual:avalon/assets";
|
|
33
35
|
readonly RENDERER: "\0virtual:avalon/renderer";
|
|
34
36
|
readonly CLIENT_ENTRY: "\0virtual:avalon/client-entry";
|
|
37
|
+
readonly DEV_CSS_HMR: "\0virtual:avalon/dev-css-hmr";
|
|
35
38
|
readonly INTEGRATION_LOADER: "\0virtual:avalon/integration-loader";
|
|
36
39
|
readonly ACTIONS: "\0virtual:avalon/actions";
|
|
40
|
+
readonly SSR_DOM: "\0virtual:avalon/ssr-dom";
|
|
37
41
|
};
|
|
38
42
|
export interface NitroIntegrationResult {
|
|
39
43
|
nitroOptions: NitroConfigOutput;
|