@useavalon/avalon 0.1.88 → 0.1.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/build/integration-bundler-plugin.d.ts +1 -1
- package/dist/src/build/integration-detection-plugin.d.ts +1 -1
- package/dist/src/build/integration-detection-plugin.js +1 -1
- package/dist/src/build/integration-resolver-plugin.d.ts +1 -1
- package/dist/src/build/island-code-splitting.d.ts +1 -1
- package/dist/src/build/island-manifest.d.ts +2 -2
- package/dist/src/build/island-manifest.js +1 -1
- package/dist/src/build/island-types-generator.js +4 -4
- package/dist/src/build/mdx-island-transform.js +1 -1
- package/dist/src/build/mdx-plugin.d.ts +2 -2
- package/dist/src/build/prop-extractors/index.d.ts +3 -3
- package/dist/src/build/prop-extractors/index.js +1 -1
- package/dist/src/build/prop-extractors/lit.js +1 -1
- package/dist/src/build/prop-extractors/svelte.js +1 -1
- package/dist/src/build/sidecar-file-manager.js +1 -1
- package/dist/src/client/adapters/index.d.ts +1 -1
- package/dist/src/client/adapters/index.js +1 -1
- package/dist/src/client/css-hmr-handler.d.ts +2 -2
- package/dist/src/client/framework-adapter.d.ts +2 -2
- package/dist/src/client/hmr-coordinator.d.ts +6 -6
- package/dist/src/client/main.js +1 -1
- package/dist/src/client/types/framework-runtime.d.ts +54 -55
- package/dist/src/client/types/vite-hmr.d.ts +35 -35
- package/dist/src/client/types/vite-virtual-modules.d.ts +56 -28
- package/dist/src/core/components/component-analyzer.d.ts +3 -3
- package/dist/src/core/components/component-analyzer.js +1 -1
- package/dist/src/core/components/component-detection.d.ts +8 -8
- package/dist/src/core/components/component-detection.js +1 -1
- package/dist/src/core/components/enhanced-framework-detector.d.ts +2 -2
- package/dist/src/core/components/enhanced-framework-detector.js +1 -1
- package/dist/src/core/components/framework-registry.d.ts +1 -1
- package/dist/src/core/integrations/index.d.ts +1 -1
- package/dist/src/core/integrations/index.js +1 -1
- package/dist/src/core/integrations/registry.js +1 -1
- package/dist/src/core/layout/enhanced-layout-resolver.d.ts +8 -8
- package/dist/src/core/layout/enhanced-layout-resolver.js +1 -1
- package/dist/src/core/layout/layout-cache-manager.d.ts +1 -1
- package/dist/src/core/layout/layout-composer.d.ts +2 -2
- package/dist/src/core/layout/layout-composer.js +1 -1
- package/dist/src/core/layout/layout-data-loader.d.ts +2 -2
- package/dist/src/core/layout/layout-discovery.d.ts +2 -2
- package/dist/src/core/layout/layout-discovery.js +1 -1
- package/dist/src/core/layout/layout-matcher.d.ts +1 -1
- package/dist/src/core/layout/layout-matcher.js +1 -1
- package/dist/src/core/layout/layout-types.d.ts +3 -3
- package/dist/src/islands/component-analysis.d.ts +4 -4
- package/dist/src/islands/component-analysis.js +1 -1
- package/dist/src/islands/critical-css.js +3 -3
- package/dist/src/islands/css-utils.js +1 -1
- package/dist/src/islands/discovery/index.d.ts +9 -9
- package/dist/src/islands/discovery/index.js +1 -1
- package/dist/src/islands/discovery/registry.d.ts +1 -1
- package/dist/src/islands/discovery/resolver.d.ts +1 -1
- package/dist/src/islands/discovery/resolver.js +2 -2
- package/dist/src/islands/discovery/scanner.d.ts +1 -1
- package/dist/src/islands/discovery/scanner.js +1 -1
- package/dist/src/islands/discovery/validator.d.ts +1 -1
- package/dist/src/islands/discovery/validator.js +9 -9
- package/dist/src/islands/discovery/watcher.d.ts +1 -1
- package/dist/src/islands/discovery/watcher.js +1 -1
- package/dist/src/islands/framework-base-css.d.ts +36 -0
- package/dist/src/islands/framework-base-css.js +1 -0
- package/dist/src/islands/framework-detection.d.ts +3 -3
- package/dist/src/islands/framework-detection.js +1 -1
- package/dist/src/islands/island.js +1 -1
- package/dist/src/islands/per-island-script.js +1 -1
- package/dist/src/islands/universal-head-collector.d.ts +2 -2
- package/dist/src/middleware/discovery.d.ts +1 -1
- package/dist/src/middleware/discovery.js +1 -1
- package/dist/src/middleware/executor.d.ts +2 -2
- package/dist/src/middleware/index.d.ts +3 -3
- package/dist/src/middleware/index.js +1 -1
- package/dist/src/middleware/types.d.ts +2 -2
- package/dist/src/nitro/error-handler.d.ts +2 -2
- package/dist/src/nitro/error-handler.js +2 -2
- package/dist/src/nitro/index.d.ts +8 -8
- package/dist/src/nitro/index.js +1 -1
- package/dist/src/nitro/island-manifest.d.ts +1 -1
- package/dist/src/nitro/island-manifest.js +1 -1
- package/dist/src/nitro/middleware-adapter.d.ts +2 -2
- package/dist/src/nitro/types.d.ts +4 -4
- package/dist/src/render/isolated-ssr-renderer.d.ts +2 -2
- package/dist/src/render/isolated-ssr-renderer.js +1 -1
- package/dist/src/render/ssr.d.ts +5 -5
- package/dist/src/render/ssr.js +4 -4
- package/dist/src/schemas/api.d.ts +1 -1
- package/dist/src/schemas/core.d.ts +1 -1
- package/dist/src/schemas/index.d.ts +8 -8
- package/dist/src/schemas/index.js +1 -1
- package/dist/src/schemas/routing/index.d.ts +1 -1
- package/dist/src/schemas/routing/index.js +1 -1
- package/dist/src/schemas/routing.d.ts +4 -4
- package/dist/src/schemas/routing.js +1 -1
- package/dist/src/types/image.d.ts +38 -38
- package/dist/src/types/island-jsx.d.ts +16 -16
- package/dist/src/types/mdx.d.ts +2 -2
- package/dist/src/types/routing.d.ts +7 -7
- package/dist/src/types/routing.js +1 -1
- package/dist/src/types/virtual-modules.d.ts +1 -1
- package/dist/src/types/vite-env.d.ts +1 -1
- package/dist/src/vite-plugin/auto-discover.js +1 -1
- package/dist/src/vite-plugin/config.d.ts +1 -1
- package/dist/src/vite-plugin/image-optimization.d.ts +2 -2
- package/dist/src/vite-plugin/image-optimization.js +1 -1
- package/dist/src/vite-plugin/island-sidecar-plugin.js +1 -1
- package/dist/src/vite-plugin/module-discovery.js +1 -1
- package/dist/src/vite-plugin/nitro-integration.js +1 -1
- package/dist/src/vite-plugin/plugin.d.ts +2 -2
- package/dist/src/vite-plugin/plugin.js +1 -1
- package/dist/src/vite-plugin/types.d.ts +1 -2
- package/package.json +3 -2
- package/dist/src/client/main-slim.js +0 -1
- package/dist/src/client/strategies.js +0 -1
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
* Exports all types and functions for discovering island components
|
|
5
5
|
* in nested directory structures.
|
|
6
6
|
*/
|
|
7
|
-
export
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
11
|
-
export type {
|
|
12
|
-
export {
|
|
13
|
-
export type {
|
|
14
|
-
export {
|
|
7
|
+
export { createIslandRegistry, IslandRegistry, } from "./registry.ts";
|
|
8
|
+
export type { ImportPathOptions, ResolutionResult, } from "./resolver.ts";
|
|
9
|
+
export { createIslandResolver, IslandResolver, } from "./resolver.ts";
|
|
10
|
+
export { discoverAllIslands, discoverIslandDirectories, discoverIslandsInDirectory, getDefaultIslandsPath, getQualifiedIslandName, hasDefaultIslandsDirectory, isIslandsDirectory, parseQualifiedIslandName, } from "./scanner.ts";
|
|
11
|
+
export type { DiscoveredIsland, IslandChangeEvent, IslandCollision, IslandDirectory, IslandDiscoveryConfig, IslandFileExtension, } from "./types.ts";
|
|
12
|
+
export { DEFAULT_DISCOVERY_CONFIG, ISLAND_FILE_EXTENSIONS, isSupportedIslandExtension, } from "./types.ts";
|
|
13
|
+
export type { CircularDependency, ValidationError, ValidationResult, ValidationWarning, } from "./validator.ts";
|
|
14
|
+
export { createIslandValidator, formatCircularDependency, formatValidationError, formatValidationResult, formatValidationWarning, IslandValidator, validateAllIslands, } from "./validator.ts";
|
|
15
15
|
export type { IslandChangeCallback, IslandWatcherOptions, } from "./watcher.ts";
|
|
16
|
-
export {
|
|
16
|
+
export { createIslandWatcher, IslandWatcher, } from "./watcher.ts";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{
|
|
1
|
+
export{createIslandRegistry,IslandRegistry}from"./registry.js";export{createIslandResolver,IslandResolver}from"./resolver.js";export{discoverAllIslands,discoverIslandDirectories,discoverIslandsInDirectory,getDefaultIslandsPath,getQualifiedIslandName,hasDefaultIslandsDirectory,isIslandsDirectory,parseQualifiedIslandName}from"./scanner.js";export{DEFAULT_DISCOVERY_CONFIG,ISLAND_FILE_EXTENSIONS,isSupportedIslandExtension}from"./types.js";export{createIslandValidator,formatCircularDependency,formatValidationError,formatValidationResult,formatValidationWarning,IslandValidator,validateAllIslands}from"./validator.js";export{createIslandWatcher,IslandWatcher}from"./watcher.js";
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Central registry for all discovered islands with resolution capabilities.
|
|
5
5
|
* Handles registration, resolution by name/namespace, and collision detection.
|
|
6
6
|
*/
|
|
7
|
-
import type { DiscoveredIsland,
|
|
7
|
+
import type { DiscoveredIsland, IslandCollision, IslandDirectory, IslandDiscoveryConfig } from "./types.ts";
|
|
8
8
|
/**
|
|
9
9
|
* Central registry for all discovered islands.
|
|
10
10
|
* Provides registration, resolution, and collision detection capabilities.
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
* - The resolver returns `ambiguous: true` with alternatives
|
|
39
39
|
* - Default directory always has priority for simple name resolution
|
|
40
40
|
*/
|
|
41
|
-
import type { DiscoveredIsland } from "./types.ts";
|
|
42
41
|
import type { IslandRegistry } from "./registry.ts";
|
|
42
|
+
import type { DiscoveredIsland } from "./types.ts";
|
|
43
43
|
/**
|
|
44
44
|
* Result of resolving an island reference
|
|
45
45
|
*/
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{relative as e}from"node:path";import{
|
|
2
|
-
`)}`),Error(n)}return t}findSimilarNames(e){let
|
|
1
|
+
import{relative as e}from"node:path";import{getQualifiedIslandName as t,parseQualifiedIslandName as n}from"./scanner.js";const r={isDevelopment:!0,basePath:`/`,projectRoot:``,absolute:!1};export class IslandResolver{_registry;_projectRoot;constructor(e,t){this._registry=e,this._projectRoot=t}get registry(){return this._registry}get projectRoot(){return this._projectRoot}resolve(e){let t=this.normalizeReference(e),n=this.resolveExplicitPath(t);if(n)return n;if(t.includes(`/`)){let e=this.resolveQualifiedName(t);if(e)return e}return this.resolveByName(t)}normalizeReference(e){let t=e.trim().replace(/^\/+|\/+$/g,``);t=t.replace(/\\/g,`/`);for(let e of[/\.solid\.(tsx|jsx)$/,/\.react\.(tsx|jsx)$/,/\.lit\.(ts|js)$/,/\.preact\.(tsx|jsx)$/,/\.(tsx|ts|jsx|js|vue|svelte)$/])if(e.test(t)){t=t.replace(e,``);break}return t}resolveExplicitPath(e){if(!e.includes(`islands/`))return null;let t=this._registry.getAllIslands();for(let n of t){let t=n.relativePath.replace(/\.[^.]+$/,``),r=[/\.solid$/,/\.react$/,/\.lit$/,/\.preact$/],i=t;for(let e of r)i=i.replace(e,``);if(i===e||i.endsWith(`/`+e)||t===e||t.endsWith(`/`+e))return{island:n,importPath:this.generateImportPath(n),ambiguous:!1}}return null}resolveQualifiedName(e){let{namespace:t,name:r}=n(e),i=this._registry.resolve(r,t);if(!i)return null;let a=this._registry.findByName(r),o=a.length>1;return{island:i,importPath:this.generateImportPath(i),ambiguous:o,alternatives:o?a.filter(e=>e!==i):void 0}}resolveByName(e){let t=this._registry.findByName(e);if(t.length===0)return null;let n=[...t].sort((e,t)=>e.directory.isDefault&&!t.directory.isDefault?-1:!e.directory.isDefault&&t.directory.isDefault?1:e.namespace.localeCompare(t.namespace)),r=n[0],i=t.length>1;return{island:r,importPath:this.generateImportPath(r),ambiguous:i,alternatives:i?n.slice(1):void 0}}generateImportPath(t,n={}){let i={...r,...n},a=i.projectRoot||this._projectRoot;if(i.absolute)return t.filePath;if(i.isDevelopment){let n=e(a,t.filePath).replace(/\\/g,`/`);return`${i.basePath}${n}`}return`${i.basePath}${t.relativePath}`}generateQualifiedImportPath(e){return t(e)}getResolutionOrder(){return[`Island Resolution Order:`,`1. Explicit path-based references (e.g., 'src/modules/auth/islands/Counter')`,`2. Qualified name matches (e.g., 'modules/auth/Counter')`,`3. Default /src/islands/ directory (highest priority for unqualified names)`,`4. Nested directories in alphabetical order by namespace`,``,`Discovered directories (in priority order):`,...this._registry.directories.map((e,t)=>` ${t+1}. ${e.relativePath}${e.isDefault?` (default)`:``}`)]}isAmbiguous(e){return this.resolve(e)?.ambiguous??!1}getAllMatches(e){let t=this.normalizeReference(e);if(t.includes(`/`)&&!t.includes(`islands/`)){let{namespace:e,name:r}=n(t),i=this._registry.resolve(r,e);return i?[i]:[]}return this._registry.findByName(t)}suggestQualifiedNames(e){return this._registry.findByName(e).map(e=>t(e))}resolveOrThrow(e){let t=this.resolve(e);if(!t){let t=this.findSimilarNames(e),n=`Island not found: "${e}"`;throw t.length>0&&(n+=`\n\nDid you mean one of these?\n${t.map(e=>` - ${e}`).join(`
|
|
2
|
+
`)}`),Error(n)}return t}findSimilarNames(e){let n=this._registry.getAllIslands(),r=e.toLowerCase();return n.filter(e=>{let n=e.name.toLowerCase(),i=t(e).toLowerCase();return n.includes(r)||r.includes(n)||i.includes(r)||this.levenshteinDistance(n,r)<=3}).map(e=>t(e)).slice(0,5)}levenshteinDistance(e,t){let n=[];for(let e=0;e<=t.length;e++)n[e]=[e];for(let t=0;t<=e.length;t++)n[0][t]=t;for(let r=1;r<=t.length;r++)for(let i=1;i<=e.length;i++)t.charAt(r-1)===e.charAt(i-1)?n[r][i]=n[r-1][i-1]:n[r][i]=Math.min(n[r-1][i-1]+1,n[r][i-1]+1,n[r-1][i]+1);return n[t.length][e.length]}}export function createIslandResolver(e,t){return new IslandResolver(e,t)}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Recursively scans the source directory to discover all islands directories.
|
|
5
5
|
* Supports nested patterns like /src/modules/[module]/islands/.
|
|
6
6
|
*/
|
|
7
|
-
import type { IslandDirectory, IslandDiscoveryConfig
|
|
7
|
+
import type { DiscoveredIsland, IslandDirectory, IslandDiscoveryConfig } from "./types.ts";
|
|
8
8
|
/**
|
|
9
9
|
* Discover all island directories within the source directory.
|
|
10
10
|
* Uses recursive scanning to find all directories named "islands".
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{stat as e,readdir as t}from"node:fs/promises";import{basename as n,dirname as r,extname as i,relative as a,resolve as o}from"node:path";import{DEFAULT_DISCOVERY_CONFIG as s,isSupportedIslandExtension as c}from"./types.js";export async function discoverIslandDirectories(t,n={}){let r={...s,...n},i=o(t,r.rootDir),a=[];try{if(!(await e(i)).isDirectory())return a}catch{return a}return await u(i,t,r,a),a.sort((e,t)=>e.isDefault&&!t.isDefault?-1:!e.isDefault&&t.isDefault?1:e.relativePath.localeCompare(t.relativePath)),a}async function u(e,n,r,i){try{let s=await t(e,{withFileTypes:!0});for(let t of s){if(!t.isDirectory())continue;let s=o(e,t.name);if(!d(a(n,s),r.exclude)){if(t.name===`islands`){let e=f(s,n,r);i.push(e);continue}await u(s,n,r,i)}}}catch(t){(!(t instanceof Error)||t.code!==`EACCES`)&&console.warn(`Warning: Could not scan directory ${e}:`,t)}}function d(e,t){let n=e.replace(/\\/g,`/`);for(let e of t)if(n===e||n.startsWith(e+`/`)||n.includes(`/`+e+`/`)||n.endsWith(`/`+e))return!0;return!1}function f(e,t,n){let i=a(o(t,n.rootDir),e),s=i===`islands`,c=``;return s||(c=r(i).replace(/\\/g,`/`),n.namespaces[c]&&(c=n.namespaces[c])),{path:e,relativePath:i.replace(/\\/g,`/`),namespace:c,isDefault:s}}export function isIslandsDirectory(e){return n(e)===`islands`}export function getDefaultIslandsPath(e,t=`src`){return o(e,t,`islands`)}export async function hasDefaultIslandsDirectory(t,n=`src`){let r=getDefaultIslandsPath(t,n);try{return(await e(r)).isDirectory()}catch{return!1}}export async function discoverIslandsInDirectory(e,n){let r=[];try{let a=await t(e.path,{withFileTypes:!0});for(let t of a){if(!t.isFile()||!c(i(t.name)))continue;let a=p(t.name,e,n);r.push(a)}}catch(t){console.warn(`Warning: Could not scan islands directory ${e.path}:`,t)}return r.sort((e,t)=>e.name.localeCompare(t.name)),r}export async function discoverAllIslands(e,t={}){let n=await discoverIslandDirectories(e,t),r=[];for(let t of n){let n=await discoverIslandsInDirectory(t,e);r.push(...n)}return r}function p(e,t,n){let r=i(e),s=m(e),c=o(t.path,e),l=a(n,c).replace(/\\/g,`/`),u=h(e);return{name:s,filePath:c,relativePath:l,namespace:t.namespace,framework:u,extension:r,directory:t}}function m(e){let t=e;for(let e of[`.solid.tsx`,`.solid.jsx`,`.react.tsx`,`.react.jsx`,`.lit.ts`,`.lit.js`,`.preact.tsx`,`.preact.jsx`])if(t.endsWith(e))return t.slice(0,-e.length);for(let e of[`.tsx`,`.ts`,`.jsx`,`.js`,`.vue`,`.svelte`])if(t.endsWith(e))return t.slice(0,-e.length);return t}function h(e){let t=e.toLowerCase();if(t.includes(`.solid.`))return`solid`;if(t.includes(`.react.`))return`react`;if(t.includes(`.lit.`))return`lit`;if(t.includes(`.preact.`))return`preact`;if(t.includes(`.qwik.`))return`qwik`;if(e.endsWith(`.vue`))return`vue`;if(e.endsWith(`.svelte`))return`svelte`;if((e.endsWith(`.ts`)||e.endsWith(`.js`))&&!e.endsWith(`.d.ts`)){let t=m(e);if(/^[A-Z]/.test(t))return`lit`}return e.endsWith(`.tsx`)||e.endsWith(`.jsx`)?`preact`:`unknown`}export function getQualifiedIslandName(e){return e.namespace===``?e.name:`${e.namespace}/${e.name}`}export function parseQualifiedIslandName(e){let t=e.lastIndexOf(`/`);return t===-1?{namespace:``,name:e}:{namespace:e.slice(0,t),name:e.slice(t+1)}}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Validates island components and directory structure.
|
|
5
5
|
* Provides validation for exports, naming conventions, and circular dependencies.
|
|
6
6
|
*/
|
|
7
|
-
import type {
|
|
7
|
+
import type { DiscoveredIsland, IslandDirectory } from "./types.ts";
|
|
8
8
|
/**
|
|
9
9
|
* Result of validating an island or directory
|
|
10
10
|
*/
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import{
|
|
2
|
-
→ `)}`}async validateComponent(
|
|
1
|
+
import{stat as e,readdir as t,readFile as n}from"node:fs/promises";import{basename as r,extname as i,relative as a,resolve as o}from"node:path";import{isSupportedIslandExtension as s}from"./types.js";const c={pascalCase:/^[A-Z][a-zA-Z0-9]*$/,validFileName:/^[a-zA-Z][a-zA-Z0-9._-]*$/,frameworkSuffixes:[`.solid`,`.react`,`.lit`,`.preact`]},l=`https://avalon.dev/docs/islands`;export class IslandValidator{_projectRoot;constructor(e){this._projectRoot=e}get projectRoot(){return this._projectRoot}extractComponentName(e){let t=r(e);for(let e of c.frameworkSuffixes)if(t.includes(e)){let n=t.indexOf(e);return t.slice(0,n)}let n=i(t);return t.slice(0,-n.length)}toPascalCase(e){return e.split(/[-_\s]+/).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(``)}hasValidJsExport(e){let t=/export\s+default\s+/.test(e)||/export\s*\{\s*[^}]*\s+as\s+default\s*[,}]/.test(e),n=/export\s+(function|class|const)\s+[A-Z]/.test(e),r=/@customElement\s*\(/.test(e)||/customElements\.define\s*\(/.test(e);return t||n||r}isValidVueComponent(e){return/<template[\s>]/.test(e)||/<script[\s>]/.test(e)}isValidSvelteComponent(e){return e.trim().length>0}createExportError(e,t){let n=this.extractComponentName(e),r;switch(t){case`vue`:r=`Add a <template> or <script> section to your Vue component`;break;case`svelte`:r=`Add component markup to your Svelte file`;break;default:r=`Add a default export: export default function ${n}() { return <div>...</div>; }`}return{type:`invalid-export`,message:`Island component "${n}" does not export a valid component`,filePath:e,line:1,column:1,suggestion:`${r}\n\nSee: ${l}#component-exports`}}async validateExports(e){let t=[],r=[];try{let r=await n(e,`utf-8`),a=i(e).toLowerCase();a===`.vue`?this.isValidVueComponent(r)||t.push(this.createExportError(e,`vue`)):a===`.svelte`?this.isValidSvelteComponent(r)||t.push(this.createExportError(e,`svelte`)):this.hasValidJsExport(r)||t.push(this.createExportError(e,`js`))}catch{t.push({type:`invalid-export`,message:`Cannot read file: ${e}`,filePath:e,suggestion:`Check file permissions and encoding`})}return{valid:t.length===0,errors:t,warnings:r}}extractJsImports(e){let t=[],n=/import\s+(?:[\w\s{},*]+\s+from\s+)?['"]([^'"]+)['"]/g,r;for(r=n.exec(e);r!==null;r=n.exec(e))t.push(r[1]);let i=/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;for(r=i.exec(e);r!==null;r=i.exec(e))t.push(r[1]);let a=/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;for(r=a.exec(e);r!==null;r=a.exec(e))t.push(r[1]);return t}extractImports(e,t){let n=[],r=i(t).toLowerCase();if(r===`.vue`||r===`.svelte`){let t=e.match(/<script[^>]*>([\s\S]*?)<\/script>/gi);if(t)for(let e of t)n.push(...this.extractJsImports(e))}else n.push(...this.extractJsImports(e));return n}resolveImportToIsland(e,t,n,a){if(!e.startsWith(`.`)&&!e.startsWith(`/`))return null;if(e.startsWith(`.`)){let r=o(o(t.filePath,`..`),e);if(!i(r)){for(let e of[`.tsx`,`.ts`,`.jsx`,`.js`,`.vue`,`.svelte`])if(a.has(r+e)){for(let t of n.values())if(t.filePath===r+e)return t}}if(a.has(r)){for(let e of n.values())if(e.filePath===r)return e}}let s=r(e).replace(/\.[^.]+$/,``);return n.get(s)||null}async buildImportGraph(e){let t=new Map,r=new Set(e.map(e=>e.filePath)),i=new Map;for(let t of e){i.set(t.name,t);let e=t.relativePath.replace(/\.[^.]+$/,``);i.set(e,t)}for(let a of e){let e=[];try{let t=await n(a.filePath,`utf-8`),o=this.extractImports(t,a.filePath);for(let t of o){let n=this.resolveImportToIsland(t,a,i,r);n&&e.push(n.filePath)}}catch{}t.set(a.filePath,e)}return t}cycleExists(e,t){let n=new Set(t);for(let r of e){if(r.length!==t.length-1)continue;let e=new Set(r),i=!0;for(let t of n)if(!e.has(t)){i=!1;break}if(i)return!0}return!1}findCycles(e){let t=[],n=new Set,r=new Set,i=[],a=o=>{n.add(o),r.add(o),i.push(o);let s=e.get(o)||[];for(let e of s)if(!n.has(e))a(e);else if(r.has(e)){let n=i.indexOf(e);if(n!==-1){let r=[...i.slice(n),e];this.cycleExists(t,r)||t.push(r)}}i.pop(),r.delete(o)};for(let t of e.keys())n.has(t)||a(t);return t}formatCycleDescription(e){return`Circular dependency detected:\n ${e.map(e=>a(this._projectRoot,e)).join(`
|
|
2
|
+
→ `)}`}async validateComponent(t){let n=[],r=[];try{if(!(await e(t)).isFile())return n.push({type:`invalid-export`,message:`Path is not a file: ${t}`,filePath:t}),{valid:!1,errors:n,warnings:r}}catch{return n.push({type:`invalid-export`,message:`File not found: ${t}`,filePath:t}),{valid:!1,errors:n,warnings:r}}let a=i(t);if(!s(a))return n.push({type:`invalid-export`,message:`Unsupported file extension: ${a}`,filePath:t,suggestion:`Use one of: .tsx, .ts, .jsx, .js, .vue, .svelte`}),{valid:!1,errors:n,warnings:r};let o=this.validateNamingConvention(this.extractComponentName(t),t);n.push(...o.errors),r.push(...o.warnings);let c=await this.validateExports(t);return n.push(...c.errors),r.push(...c.warnings),{valid:n.length===0,errors:n,warnings:r}}async validateDirectory(e){let n=[],r=[],a=!1;try{let c=await t(e.path,{withFileTypes:!0});for(let t of c){if(!t.isFile()||!s(i(t.name)))continue;a=!0;let c=o(e.path,t.name),l=await this.validateComponent(c);n.push(...l.errors),r.push(...l.warnings)}}catch{return n.push({type:`invalid-export`,message:`Cannot read directory: ${e.path}`,filePath:e.path,suggestion:`Check directory permissions`}),{valid:!1,errors:n,warnings:r}}return a||r.push({type:`empty-directory`,message:`Islands directory is empty: ${e.relativePath}`,filePath:e.path,suggestion:`Add island components or remove the empty directory`}),{valid:n.length===0,errors:n,warnings:r}}validateNamingConvention(e,t){let n=[],r=[];return c.pascalCase.test(e)||(e.length===0?n.push({type:`naming-convention`,message:`Invalid component name: empty name`,filePath:t,suggestion:`Use PascalCase naming (e.g., "Counter", "UserProfile")`}):/^[a-z]/.test(e)?r.push({type:`deprecated-pattern`,message:`Component name "${e}" should use PascalCase`,filePath:t,suggestion:`Rename to "${this.toPascalCase(e)}"`}):/[^a-zA-Z0-9]/.test(e)&&r.push({type:`deprecated-pattern`,message:`Component name "${e}" contains special characters`,filePath:t,suggestion:`Use only letters and numbers in component names`})),{valid:n.length===0,errors:n,warnings:r}}async detectCircularDependencies(e){let t=await this.buildImportGraph(e);return this.findCycles(t).map(e=>({cycle:e,description:this.formatCycleDescription(e)}))}}export function formatValidationError(e,t){let n=a(t,e.filePath),r=e.line?`${n}:${e.line}${e.column?`:${e.column}`:``}`:n,i=`Error: ${e.message}\n\n`;return i+=` File: ${r}\n`,e.suggestion&&(i+=`\n ${e.suggestion}\n`),i}export function formatValidationWarning(e,t){let n=`Warning: ${e.message}\n`;if(e.filePath){let r=a(t,e.filePath);n+=` File: ${r}\n`}return e.suggestion&&(n+=` Suggestion: ${e.suggestion}\n`),n}export function formatCircularDependency(e,t){let n=e.cycle.map(e=>a(t,e)),r=`Error: Circular dependency detected
|
|
3
3
|
|
|
4
|
-
`;
|
|
5
|
-
`;for(let e=0;e<
|
|
6
|
-
`)}return
|
|
4
|
+
`;r+=` Dependency chain:
|
|
5
|
+
`;for(let e=0;e<n.length;e++){let t=e===n.length-1;r+=`${t?` └─`:` ├─`} ${n[e]}\n`,t||(r+=` │ ↓
|
|
6
|
+
`)}return r+=`
|
|
7
7
|
Suggestion: Break the cycle by:
|
|
8
|
-
`,
|
|
9
|
-
`,
|
|
10
|
-
`,
|
|
11
|
-
`,
|
|
8
|
+
`,r+=` - Moving shared code to a separate module
|
|
9
|
+
`,r+=` - Using dynamic imports for one of the dependencies
|
|
10
|
+
`,r+=` - Restructuring the component hierarchy
|
|
11
|
+
`,r+=`\n See: ${l}#circular-dependencies\n`,r}export function formatValidationResult(e,t){let n=[];if(e.errors.length>0){n.push(`Found ${e.errors.length} error(s):\n`);for(let r of e.errors)n.push(formatValidationError(r,t))}if(e.warnings.length>0){n.length>0&&n.push(`
|
|
12
12
|
`),n.push(`Found ${e.warnings.length} warning(s):\n`);for(let r of e.warnings)n.push(formatValidationWarning(r,t))}return e.valid&&e.warnings.length===0?n.push(`✓ Validation passed
|
|
13
13
|
`):e.valid?n.push(`
|
|
14
14
|
✓ Validation passed with warnings
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* change events with affected island information. Supports HMR for
|
|
6
6
|
* nested island directories.
|
|
7
7
|
*/
|
|
8
|
+
import type { IslandRegistry } from "./registry.ts";
|
|
8
9
|
import type { IslandChangeEvent, IslandDiscoveryConfig } from "./types.ts";
|
|
9
|
-
import { IslandRegistry } from "./registry.ts";
|
|
10
10
|
/**
|
|
11
11
|
* Callback type for island change events
|
|
12
12
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{watch as e}from"node:fs";import{basename as t,extname as n,relative as r,resolve as i}from"node:path";import{discoverIslandsInDirectory as a}from"./scanner.js";import{isSupportedIslandExtension as o}from"./types.js";const s={debounceMs:100,emitInitial:!1};export class IslandWatcher{_projectRoot;_config;_options;_registry;_watchers=[];_callbacks=new Set;_isWatching=!1;_debounceTimers=new Map;constructor(e,t,n={},r={}){this._projectRoot=e,this._registry=t,this._config=n,this._options={...s,...r}}get isWatching(){return this._isWatching}get callbackCount(){return this._callbacks.size}async watch(e){return this._callbacks.add(e),this._isWatching||await this._startWatching(),()=>{this._callbacks.delete(e),this._callbacks.size===0&&this.stop()}}stop(){this._isWatching=!1;for(let e of this._watchers)try{e.close()}catch{}this._watchers=[];for(let e of this._debounceTimers.values())clearTimeout(e);this._debounceTimers.clear()}async _startWatching(){if(this._isWatching)return;this._isWatching=!0;let e=this._registry.directories;for(let t of e)try{this._watchDirectory(t)}catch(e){console.warn(`⚠️ Failed to watch island directory ${t.relativePath}:`,e)}}_watchDirectory(t){try{let r=e(t.path,{recursive:!1},(e,r)=>{if(!this._isWatching||!r)return;let a=i(t.path,r);if(!o(n(r)))return;let s=e;this._debounceEvent(a,s,t)});this._watchers.push(r)}catch(e){if(e instanceof Error&&e.code===`ENOENT`)console.warn(`⚠️ Island directory not found: ${t.relativePath}`);else throw e}}_debounceEvent(e,t,n){let r=this._debounceTimers.get(e);r&&clearTimeout(r);let i=setTimeout(()=>{this._debounceTimers.delete(e),this._handleFileChange(e,t,n)},this._options.debounceMs);this._debounceTimers.set(e,i)}async _handleFileChange(e,t,n){let i=r(this._projectRoot,e).replace(/\\/g,`/`),o;if(t===`change`)o=`change`;else try{let{stat:t}=await import(`node:fs/promises`);await t(e),o=`add`}catch{o=`remove`}let s=null;if(o===`remove`){let t=this._getQualifiedNameFromPath(e,n);s=this._registry.resolve(t)||null,s&&this._registry.unregister(t)}else try{s=(await a(n,this._projectRoot)).find(t=>t.filePath===e)||null,o===`add`&&s&&this._registry.register(s)}catch{s=null}let c={type:o,island:s,filePath:i,timestamp:Date.now()};this._emitEvent(c)}_getQualifiedNameFromPath(e,n){let r=t(e),i=this._extractComponentName(r);return n.namespace===``?i:`${n.namespace}/${i}`}_extractComponentName(e){for(let t of[`.solid.tsx`,`.solid.jsx`,`.react.tsx`,`.react.jsx`,`.lit.ts`,`.lit.js`,`.preact.tsx`,`.preact.jsx`])if(e.endsWith(t))return e.slice(0,-t.length);for(let t of[`.tsx`,`.ts`,`.jsx`,`.js`,`.vue`,`.svelte`])if(e.endsWith(t))return e.slice(0,-t.length);return e}_emitEvent(e){for(let t of this._callbacks)try{t(e)}catch(e){console.error(`Error in island change callback:`,e)}}async refresh(){if(!this._isWatching)return;for(let e of this._watchers)try{e.close()}catch{}this._watchers=[];let e=this._registry.directories;for(let t of e)try{this._watchDirectory(t)}catch(e){console.warn(`⚠️ Failed to watch island directory ${t.relativePath}:`,e)}}}export function createIslandWatcher(e,t,n={},r={}){return new IslandWatcher(e,t,n,r)}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework Baseline CSS
|
|
3
|
+
*
|
|
4
|
+
* Provides default styling for Avalon's framework-emitted custom elements
|
|
5
|
+
* (`<avalon-island>`, `<avalon-page>`, `<avalon-page-content>`).
|
|
6
|
+
*
|
|
7
|
+
* These elements are used as DOM anchors for hydration and content
|
|
8
|
+
* placement. They must be transparent to layout — otherwise an island
|
|
9
|
+
* placed inside a flex/grid container would force the children into a
|
|
10
|
+
* separate inline-level box, breaking the layout.
|
|
11
|
+
*
|
|
12
|
+
* The rule is `display: contents`, which makes the element invisible to
|
|
13
|
+
* the layout engine while keeping it queryable from JS and visible in
|
|
14
|
+
* devtools. This mirrors the pattern Astro, Lit, and Elder.js use for
|
|
15
|
+
* island-style hydration anchors.
|
|
16
|
+
*
|
|
17
|
+
* The CSS is intentionally low-specificity (single-element selectors)
|
|
18
|
+
* so any user CSS that sets a different `display` value naturally wins.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Inject the framework baseline `<style>` tag into the document head.
|
|
22
|
+
*
|
|
23
|
+
* Idempotent — if the marker is already present (e.g. the layout already
|
|
24
|
+
* rendered the tag, or this function is called twice), the HTML is
|
|
25
|
+
* returned unchanged.
|
|
26
|
+
*
|
|
27
|
+
* The tag is inserted as the first child of `<head>` so subsequent
|
|
28
|
+
* stylesheets and inline styles can override it via the cascade.
|
|
29
|
+
*
|
|
30
|
+
* @param html - The rendered HTML string
|
|
31
|
+
* @returns HTML with the baseline `<style>` tag injected, or unchanged
|
|
32
|
+
* if no `<head>` is present or the tag is already there.
|
|
33
|
+
*/
|
|
34
|
+
export declare function injectFrameworkBaseCSS(html: string): string;
|
|
35
|
+
/** Exposed for tests */
|
|
36
|
+
export declare const __FRAMEWORK_BASE_CSS = "avalon-island,avalon-page,avalon-page-content{display:contents}";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=`avalon-island,avalon-page,avalon-page-content{display:contents}`,t=`<style data-avalon-base="true">${e}</style>`;export function injectFrameworkBaseCSS(e){return e.includes(`data-avalon-base="true"`)||!e.includes(`<head>`)&&!e.includes(`<head `)?e:e.replace(/<head(\s[^>]*)?>/i,e=>`${e}${t}`)}export const __FRAMEWORK_BASE_CSS=e;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import {
|
|
1
|
+
import { type IslandRegistry } from "./discovery/index.ts";
|
|
2
|
+
import type { Framework } from "./types.ts";
|
|
3
3
|
/** Known synchronous framework types (excludes 'unknown') */
|
|
4
|
-
type SyncFramework =
|
|
4
|
+
type SyncFramework = "solid" | "vue" | "svelte" | "preact" | "react" | "lit" | "qwik";
|
|
5
5
|
/**
|
|
6
6
|
* Resolve Island component path for Vite SSR loading
|
|
7
7
|
* Converts /islands/* paths to /src/islands/* for proper resolution
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{stat as e,readFile as t}from"node:fs/promises";import{registry as n}from"../core/integrations/registry.js";import{createIslandRegistry as r}from"./discovery/index.js";import{getCachedPath as i,setCachedPath as a}from"./render-cache.js";function o(e,t){let n=s(e);if(!n)return null;let r=t.resolve(n);if(r)return`/`+r.relativePath;if(e.includes(`/`)){let r=c(e);if(r){let e=t.resolve(n,r);if(e)return`/`+e.relativePath}}return null}function s(e){let t=e.split(`/`).filter(Boolean);if(t.length===0)return null;let n=t.at(-1);for(let e of[/\.solid\.(tsx|jsx)$/,/\.react\.(tsx|jsx)$/,/\.lit\.(ts|js)$/,/\.preact\.(tsx|jsx)$/])if(e.test(n)){n=n.replace(e,``);break}return n=n.replace(/\.(tsx|ts|jsx|js|vue|svelte)$/,``),n||null}function c(e){let t=new RegExp(/(?:\/src)?\/(.+?)\/islands\//).exec(e);return t?t[1]:``}async function l(t){try{return await e(t),!0}catch{return!1}}function u(e){if(e.includes(`/islands/`)&&!e.startsWith(`/src/`)){if(/^\/(?:modules\/)?[^/]+\/islands\//.test(e))return`/src`+e;if(e.startsWith(`/islands/`))return e.replace(`/islands/`,`/src/islands/`)}return e}async function d(e){let t=n.getAll(),r=[],i=e.replace(`.tsx`,``);for(let e of t){let t=e.config();for(let e of t.fileExtensions)e===`.tsx`||e===`.jsx`?r.push(`${i}.${t.name}${e}`):r.push(`${i}${e}`)}r.push(e);for(let e of r)if(await l(e.startsWith(`/`)?e.substring(1):e))return e;return null}export async function resolveIslandPath(e){let t=i(e);if(t!==null)return t;let n=u(e.replaceAll(`\\`,`/`)),r=f.__islandRegistry;if(r){let t=o(n,r);if(t)return a(e,t),t}if(n.endsWith(`.tsx`)&&!n.includes(`.solid.`)&&!n.includes(`.preact.`)){let t=await d(n);if(t)return a(e,t),t}return a(e,n),n}export function isNestedIslandPath(e){let t=e.replaceAll(`\\`,`/`);if(!t.includes(`/islands/`))return!1;for(let e of[/^\/islands\//,/^\/src\/islands\//,/^src\/islands\//,/^islands\//])if(e.test(t))return!1;return!0}const f=globalThis;export async function getOrCreateIslandRegistry(e=process.cwd()){return f.__islandRegistry??=await r(e),f.__islandRegistry}export function setIslandRegistry(e){f.__islandRegistry=e}export function clearIslandRegistry(){f.__islandRegistry=void 0}export function detectFrameworkFromSrc(e){let t=e.replaceAll(`\\`,`/`),r=n.getAll();for(let e of r){let n=e.config();if(t.includes(`.${n.name}.`))return n.name}for(let e of r){let n=e.config();for(let e of n.fileExtensions)if(t.endsWith(e))return n.name}return p(t)}function p(e){return e.endsWith(`.vue`)?`vue`:e.endsWith(`.svelte`)?`svelte`:e.includes(`.solid.`)||e.toLowerCase().includes(`solid`)?`solid`:e.includes(`.qwik.`)||e.toLowerCase().includes(`qwik`)?`qwik`:e.includes(`react`)||e.toLowerCase().includes(`react`)?`react`:`preact`}function m(e,t){for(let n of t){let t=n.config();for(let n of t.detectionPatterns.imports)if(n.test(e))return t.name;for(let n of t.detectionPatterns.content)if(n.test(e))return t.name}return null}function h(e){for(let t of[{pattern:/solid-js|@jsxImportSource solid-js/,framework:`solid`},{pattern:/@builder\.io\/qwik|@jsxImportSource @builder\.io\/qwik/,framework:`qwik`},{pattern:/vue|Vue/,framework:`vue`},{pattern:/svelte/,framework:`svelte`},{pattern:/react/,framework:`react`},{pattern:/preact/,framework:`preact`}])if(t.pattern.test(e))return t.framework;return`preact`}async function g(e){try{return await t((await resolveIslandPath(e)).replace(/^\//,``),`utf-8`)}catch{let t=f.__viteDevServer;if(t){let n=await resolveIslandPath(e),r=await t.ssrLoadModule(n);return JSON.stringify(r)}return null}}export async function detectFramework(e){let t=n.getAll();for(let n of t){let t=n.config();for(let n of t.fileExtensions)if(e.endsWith(n))return t.name;if(e.includes(`.${t.name}.`))return t.name}try{let n=await g(e);return n?m(n,t)||(t.length===0?h(n):`preact`):`unknown`}catch{return`unknown`}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Fragment as e,h as t}from"preact";import{getIslandBundlePath as n}from"../build/island-manifest.js";import{devError as r,devLog as i,devWarn as a,isDev as o,logRenderTiming as s}from"../utils/dev-logger.js";import{analyzeComponentFile as c,renderComponentSSROnly as l}from"./component-analysis.js";import{detectFramework as u}from"./framework-detection.js";import{isCustomDirective as d,serializeDirectiveScript as f}from"./hydration-directives.js";import{detectFrameworkFromPath as p,loadIntegration as m}from"./integration-loader.js";import{
|
|
1
|
+
import{Fragment as e,h as t}from"preact";import{getIslandBundlePath as n}from"../build/island-manifest.js";import{devError as r,devLog as i,devWarn as a,isDev as o,logRenderTiming as s}from"../utils/dev-logger.js";import{analyzeComponentFile as c,renderComponentSSROnly as l}from"./component-analysis.js";import{detectFramework as u}from"./framework-detection.js";import{isCustomDirective as d,serializeDirectiveScript as f}from"./hydration-directives.js";import{detectFrameworkFromPath as p,loadIntegration as m}from"./integration-loader.js";import{addModulepreload as h}from"./modulepreload-collector.js";import{generatePerIslandScript as g}from"./per-island-script.js";import{addUniversalCSS as _}from"./universal-css-collector.js";import{addUniversalHead as v}from"./universal-head-collector.js";function y(e){return`island-${e.replaceAll(/[^a-zA-Z0-9]/g,`-`)}`}function b(){return globalThis.__avalonHydrationMode===void 0?globalThis.__viteDevServer?!1:typeof __AVALON_PER_ISLAND__<`u`?__AVALON_PER_ISLAND__:!o():globalThis.__avalonHydrationMode===`per-island`}function x(r,i){if(!b()||i.shouldSkipHydration)return r;let a=n(i.src);i.condition===`on:client`&&h(a);let o=d(i.condition),s=o?f(i.condition):void 0;return t(e,null,r,t(`div`,{dangerouslySetInnerHTML:{__html:g({islandId:i.islandId,componentSrc:a,framework:i.framework,condition:i.condition,conditionArg:i.conditionArg,propsJson:JSON.stringify(i.props),isCustomDirective:o,directiveScript:s??void 0})},"data-island-script":``}))}function S(e){let t={};e.renderId&&(t[`data-solid-render-id`]=e.renderId);let n=e.metadata;return n?.tagName&&(t[`data-tag-name`]=n.tagName),t}function C(e,t,r,i,a){let o={"data-condition":t,"data-src":n(e),"data-props":JSON.stringify(r),"data-render-strategy":`hydrate`,...S(i)};if(d(t)){o[`data-custom-directive`]=t;let e=f(t);e&&(o[`data-directive-script`]=e)}return a&&(o[`data-condition-arg`]=a),o}function w(e){return e.startsWith(`<script`)?`script`:e.startsWith(`<style`)?`style`:e.startsWith(`<meta`)?`meta`:e.startsWith(`<link`)?`link`:e.includes(`window._$HY`)||e.includes(`_$HY=`)?`script`:`other`}function T(e){let t=e.match(/<style[^>]*>([\s\S]*?)<\/style>/i);return t?t[1].trim():null}function E(e,t,n,r){if(e.css&&_(e.css,t,n,e.scopeId),e.head){let a=e.head.trim(),o=w(a);if(o===`style`){let o=T(a);o&&(i(`${r} Extracting CSS from head <style> tag`),_(o,t,n,e.scopeId));return}v(e.head,t,n,o)}}function D(e){let{islandId:n,detectedFramework:r,shouldSkipHydration:a,src:o,condition:s,conditionArg:c,props:l,hydrationData:u,children:d}=e,f={id:n,"data-framework":r},p=a?{"data-render-strategy":`ssr-only`}:C(o,s,l,u,c);r===`lit`&&i(`🔍 [Island Component] ${o} - Lit hydration data:`,{hydrationDataKeys:Object.keys(u),metadata:u.metadata});let m={...f,...p},h;return h=typeof d==`string`?t(`avalon-island`,{...m,dangerouslySetInnerHTML:{__html:d}}):t(`avalon-island`,m,d),x(h,{islandId:n,src:o,condition:s,conditionArg:c,props:l,framework:r,shouldSkipHydration:a})}function O(e){let{islandId:r,detectedFramework:i,shouldSkipHydration:a,src:o,condition:s,props:c,hydrationData:l,conditionArg:u}=e;if(a)return t(`avalon-island`,{id:r,"data-render-strategy":`ssr-only`,"data-framework":i});let p={id:r,"data-condition":s,"data-src":n(o),"data-props":JSON.stringify(c),"data-render-strategy":`hydrate`,"data-framework":i,...S(l)};if(d(s)){p[`data-custom-directive`]=s;let e=f(s);e&&(p[`data-directive-script`]=e)}return u&&(p[`data-condition-arg`]=u),x(t(`avalon-island`,p),{islandId:r,src:o,condition:s,conditionArg:u,props:c,framework:i,shouldSkipHydration:a})}export default function k({src:e,condition:t=`on:client`,conditionArg:n,props:r={},children:o,ssr:s=t!==`on:client`,framework:c,ssrOnly:l=!1,renderOptions:u={},hydrationData:d={}}){let f=y(e),m=l||!!u.forceSSROnly,h=c||p(e),g=o!=null&&o!==``;return i(`🔍 [Island Component] ${e}`,{ssr:s,ssrOnly:l,hasChildren:g,framework:c,condition:t}),s&&g?D({islandId:f,detectedFramework:h,shouldSkipHydration:m,src:e,condition:t,conditionArg:n,props:r,hydrationData:d,children:o}):(s&&!g&&m&&a(`${e}: SSR-only component has no rendered content. This may indicate a rendering error.`),O({islandId:f,detectedFramework:h,shouldSkipHydration:m,src:e,condition:t,props:r,hydrationData:d,conditionArg:n}))}function A(e,i){let a=i instanceof Error?i.message:String(i);return r(`🚨 Island SSR failed for ${e}:`,i),i instanceof Error&&i.stack&&r(`Stack trace:`,i.stack),t(`avalon-island`,{id:y(e),"data-src":n(e),"data-ssr-error":a,"data-render-strategy":`client-only`})}async function j({src:e,condition:t,conditionArg:n,props:i,children:a,ssr:s,framework:c,ssrOnly:l,renderOptions:u,component:d}){let f=`🏝️ [${e}]`;if(!s||a)return k({src:e,condition:t,conditionArg:n,props:i,children:a,ssr:s,framework:c,ssrOnly:l,renderOptions:u});let p;try{p=await m(c)}catch(a){return r(`${f} Failed to load ${c} integration:`,a),k({src:e,condition:t,conditionArg:n,props:i,ssr:!1,framework:c,ssrOnly:l,renderOptions:u})}try{let r=await p.render({component:d??null,props:i,src:e,condition:t,ssrOnly:l,viteServer:globalThis.__viteDevServer,isDev:o()});return E(r,e,c,f),k({src:e,condition:t,conditionArg:n,props:i,children:r.html,ssr:!0,framework:c,ssrOnly:l,renderOptions:u,hydrationData:l?void 0:r.hydrationData})}catch(a){return r(`${f} Fast path SSR failed:`,a),k({src:e,condition:t,conditionArg:n,props:i,ssr:!1,framework:c,ssrOnly:l,renderOptions:u})}}async function M(e,t,n,r){if(t||n.detectScripts===!1)return t;try{let t=await c(e,n);if(t.decision.warnings?.length)for(let e of t.decision.warnings)a(`${r} Analysis warning: ${e}`);return!t.decision.shouldHydrate}catch(e){return a(`${r} Component analysis failed:`,e),t}}async function N(e){return e.endsWith(`.vue`)?`vue`:e.endsWith(`.svelte`)?`svelte`:e.endsWith(`.tsx`)||e.endsWith(`.jsx`)||e.endsWith(`.ts`)||e.endsWith(`.js`)?u(e):`unknown`}async function P(e,t,n,r,i,a,s){let c=await N(e),l=c,u=await(await F(c,a)).render({component:s??null,props:n,src:e,condition:t,ssrOnly:r,viteServer:globalThis.__viteDevServer,isDev:o()});return E(u,e,c,a),k({src:e,condition:t,props:n,children:u.html,ssr:!0,framework:l,ssrOnly:r,renderOptions:i,hydrationData:r?void 0:u.hydrationData})}async function F(e,t){try{i(`${t} Loading integration for framework: ${e}`);let n=await m(e);return i(`${t} ✅ Integration loaded successfully`),n}catch(n){throw r(`${t} Failed to load ${e} integration:`,n),Error(`Failed to load integration for framework '${e}'. Make sure @useavalon/${e} is installed.\nInstall it with: deno add @useavalon/${e}`,{cause:n})}}export async function renderIsland({src:e,condition:t=`on:client`,conditionArg:n,props:r={},children:i,ssr:a=t!==`on:client`,framework:c,ssrOnly:l=!1,renderOptions:u={},component:d}){let f=o()?performance.now():0,p=`🏝️ [${e}]`;try{return l&&!a&&(a=!0),c?await j({src:e,condition:t,conditionArg:n,props:r,children:i,ssr:a,framework:c,ssrOnly:l,renderOptions:u,component:d}):await I({src:e,condition:t,conditionArg:n,props:r,children:i,ssr:a,ssrOnly:l,renderOptions:u,logPrefix:p,component:d})}catch(t){return A(e,t)}finally{o()&&s(e,performance.now()-f)}}async function I(e){let{src:t,condition:n,conditionArg:a,props:o,children:s,ssr:c,ssrOnly:l,renderOptions:u,logPrefix:d,component:f}=e;if(i(`🔍 [renderIsland] ${t} - Starting render (slow path)`,{ssr:c,ssrOnly:l,hasChildren:!!s,condition:n}),await M(t,l,u,d))return L(t,n,o,s,c,u,d);if(!c||s)return k({src:t,condition:n,conditionArg:a,props:o,children:s,ssr:c,renderOptions:u});try{return await P(t,n,o,l,u,d,f)}catch(e){let i=await N(t);return r(`${d} Framework rendering failed:`,e),k({src:t,condition:n,conditionArg:a,props:o,ssr:!1,framework:i,renderOptions:u})}}function L(e,t,n,i,a,o,s){return a&&!i?l({src:e,condition:t,props:n,renderOptions:o}).catch(i=>(r(`${s} SSR failed for SSR-only component:`,i),k({src:e,condition:t,props:n,ssr:!1,ssrOnly:!0,renderOptions:o}))):k({src:e,condition:t,props:n,children:i,ssr:a,ssrOnly:!0,renderOptions:o})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function generatePerIslandScript(e){let{islandId:r,componentSrc:i,framework:a,condition:o,conditionArg:s,propsJson:c,isCustomDirective:l,directiveScript:u}=e;return`<script type="module">${n(r,o,t(r,i,a,c),s,l,u)}<\/script>`}function t(e,t,n,r){return[`async function h(){`,`var e=document.getElementById(${JSON.stringify(e)});`,`if(!e||e.dataset.hydrated)return;`,`try{`,`var p=${r};`,`var m=await import(${JSON.stringify(t)});`,`var C=m.default||Object.values(m).find(function(v){return typeof v==="function"&&v.prototype})||m;`,`if(m.__hydrateIsland){await m.__hydrateIsland(e,C,p)}`,`else if(m.loadIntegrationModule){var i=await m.loadIntegrationModule(${JSON.stringify(n)});if(i.hydrate)await i.hydrate(e,C,p)}`,`e.dataset.hydrated="true";`,`}catch(err){console.error("Hydration error:",err)}`,`}`].join(``)}function n(e,t,n,r,i,a){if(t===`on:client`)return`${n}h();`;if(t===`on:visible`)return[n,`var e=document.getElementById(${JSON.stringify(e)});`,`if(e){try{var o=new IntersectionObserver(function(n){`,`if(n[0].isIntersecting){h();o.disconnect()}`,`},{rootMargin:"50px",threshold:0});o.observe(
|
|
1
|
+
export function generatePerIslandScript(e){let{islandId:r,componentSrc:i,framework:a,condition:o,conditionArg:s,propsJson:c,isCustomDirective:l,directiveScript:u}=e;return`<script type="module">${n(r,o,t(r,i,a,c),s,l,u)}<\/script>`}function t(e,t,n,r){return[`async function h(){`,`var e=document.getElementById(${JSON.stringify(e)});`,`if(!e||e.dataset.hydrated)return;`,`try{`,`var p=${r};`,`var m=await import(${JSON.stringify(t)});`,`var C=m.default||Object.values(m).find(function(v){return typeof v==="function"&&v.prototype})||m;`,`if(m.__hydrateIsland){await m.__hydrateIsland(e,C,p)}`,`else if(m.loadIntegrationModule){var i=await m.loadIntegrationModule(${JSON.stringify(n)});if(i.hydrate)await i.hydrate(e,C,p)}`,`e.dataset.hydrated="true";`,`}catch(err){console.error("Hydration error:",err)}`,`}`].join(``)}function n(e,t,n,r,i,a){if(t===`on:client`)return`${n}h();`;if(t===`on:visible`)return[n,`var e=document.getElementById(${JSON.stringify(e)});`,`if(e){try{var t=e.firstElementChild||e;var o=new IntersectionObserver(function(n){`,`if(n[0].isIntersecting){h();o.disconnect()}`,`},{rootMargin:"50px",threshold:0});o.observe(t)}catch(_){h()}}`].join(``);if(t===`on:idle`)return[n,`if("requestIdleCallback" in globalThis){`,`globalThis.requestIdleCallback(function(){h()},{timeout:5000})`,`}else if(document.readyState==="complete"){`,`setTimeout(function(){h()},200)`,`}else{globalThis.addEventListener("load",function(){setTimeout(function(){h()},200)},{once:true})}`].join(``);if(t===`on:interaction`)return[n,`var e=document.getElementById(${JSON.stringify(e)});`,`if(e){var t=e.firstElementChild||e;var d=false;var ev=["click","touchstart","mouseenter","focusin"];`,`var fn=function(){if(d)return;d=true;ev.forEach(function(n){t.removeEventListener(n,fn)});h()};`,`ev.forEach(function(n){t.addEventListener(n,fn,{once:true,passive:true})})}`].join(``);if(t.startsWith(`media:`)){let e=t.slice(6);return[n,`try{var mq=globalThis.matchMedia(${JSON.stringify(e)});`,`if(mq.matches){h()}else{mq.addEventListener("change",function x(ev){`,`if(ev.matches){h();mq.removeEventListener("change",x)}},{once:true})}}catch(_){h()}`].join(``)}return i&&a?[n,`var e=document.getElementById(${JSON.stringify(e)});`,`if(e){var dir=(${a});`,`dir(e,function(){h()}${r?`,${JSON.stringify(r)}`:``})}`].join(``):`${n}h();`}
|
|
@@ -10,7 +10,7 @@ interface HeadEntry {
|
|
|
10
10
|
content: string;
|
|
11
11
|
src: string;
|
|
12
12
|
framework: string;
|
|
13
|
-
type:
|
|
13
|
+
type: "script" | "meta" | "link" | "other";
|
|
14
14
|
}
|
|
15
15
|
declare global {
|
|
16
16
|
var __universalSSRHead: Map<string, HeadEntry>;
|
|
@@ -23,7 +23,7 @@ declare global {
|
|
|
23
23
|
* @param framework - Framework name
|
|
24
24
|
* @param type - Type of head content
|
|
25
25
|
*/
|
|
26
|
-
export declare function addUniversalHead(content: string, src: string, framework: string, type?:
|
|
26
|
+
export declare function addUniversalHead(content: string, src: string, framework: string, type?: "script" | "meta" | "link" | "other"): void;
|
|
27
27
|
/**
|
|
28
28
|
* Get all collected head content formatted for injection into HTML head
|
|
29
29
|
*
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* Requirements: 3.1, 3.2, 3.3, 3.4
|
|
15
15
|
*/
|
|
16
|
-
import type {
|
|
16
|
+
import type { MiddlewareDiscoveryOptions, MiddlewareRoute } from "./types.ts";
|
|
17
17
|
/**
|
|
18
18
|
* Discovers route-scoped middleware files in the project
|
|
19
19
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
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(){}
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Requirements: 1.2, 1.3, 1.4, 2.1, 2.2
|
|
16
16
|
*/
|
|
17
|
-
import type { H3Event } from
|
|
18
|
-
import type {
|
|
17
|
+
import type { H3Event } from "h3";
|
|
18
|
+
import type { MiddlewareExecutorOptions, MiddlewareRoute } from "./types.ts";
|
|
19
19
|
/**
|
|
20
20
|
* Executes route-scoped middleware chain for a request
|
|
21
21
|
*
|
|
@@ -38,6 +38,6 @@
|
|
|
38
38
|
*
|
|
39
39
|
* Requirements: 4.3
|
|
40
40
|
*/
|
|
41
|
-
export type {
|
|
42
|
-
export { discoverScopedMiddleware, getMatchingMiddleware,
|
|
43
|
-
export {
|
|
41
|
+
export type { MiddlewareDiscoveryOptions, MiddlewareExecutorOptions, MiddlewareFileExport, MiddlewareHandler, MiddlewareRoute, } from "./types.ts";
|
|
42
|
+
export { clearDiscoveryCache, discoverScopedMiddleware, getMatchingMiddleware, } from "./discovery.ts";
|
|
43
|
+
export { clearMiddlewareCache, executeScopedMiddleware, getContextValue, getMiddlewareCacheSize, hasContextValue, invalidateMiddleware, setContextValue, } from "./executor.ts";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{discoverScopedMiddleware,getMatchingMiddleware
|
|
1
|
+
export{clearDiscoveryCache,discoverScopedMiddleware,getMatchingMiddleware}from"./discovery.js";export{clearMiddlewareCache,executeScopedMiddleware,getContextValue,getMiddlewareCacheSize,hasContextValue,invalidateMiddleware,setContextValue}from"./executor.js";
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Requirements: 1.1
|
|
13
13
|
*/
|
|
14
|
-
import type { H3Event } from
|
|
14
|
+
import type { H3Event } from "h3";
|
|
15
15
|
/**
|
|
16
16
|
* Nitro-aligned middleware handler signature
|
|
17
17
|
*
|
|
@@ -63,7 +63,7 @@ export interface MiddlewareRoute {
|
|
|
63
63
|
/** Execution priority (lower numbers execute first) */
|
|
64
64
|
priority: number;
|
|
65
65
|
/** Middleware type - determines which routes it applies to */
|
|
66
|
-
type:
|
|
66
|
+
type: "global" | "pages";
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
69
|
* Middleware discovery options
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Requirements: 10.1, 10.2, 10.3, 10.4, 10.5
|
|
13
13
|
*/
|
|
14
|
-
import type { PageModule, NitroRenderContext, AvalonRuntimeConfig } from "./types.ts";
|
|
15
14
|
import type { H3Event } from "h3";
|
|
16
|
-
import {
|
|
15
|
+
import type { AvalonRuntimeConfig, NitroRenderContext, PageModule } from "./types.ts";
|
|
16
|
+
import { type HttpError } from "./types.ts";
|
|
17
17
|
/**
|
|
18
18
|
* Error page props passed to custom error page components
|
|
19
19
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{createRenderContext as e,getRequestURL as t}from"./renderer.js";import{createInternalError as n,createNotFoundError as r,isHttpError as i}from"./types.js";let a={initialized:!1};export function clearErrorPageCache(){a={initialized:!1}}export async function discoverErrorPages(e){if(a.initialized&&!e.isDev)return a;let{loadPageModule:t,pagesDir:n=`src/pages`}=e;if(!t)return a.initialized=!0,a;try{a.notFound=await t(`${n}/404.tsx`)}catch{try{a.notFound=await t(`${n}/404.jsx`)}catch{a.notFound=null}}try{a.serverError=await t(`${n}/500.tsx`)}catch{try{a.serverError=await t(`${n}/500.jsx`)}catch{a.serverError=null}}try{a.genericError=await t(`${n}/_error.tsx`)}catch{try{a.genericError=await t(`${n}/_error.jsx`)}catch{a.genericError=null}}return a.initialized=!0,a}export function getErrorPageModule(e,t){return e===404&&t.notFound?t.notFound:e===500&&t.serverError?t.serverError:t.genericError?t.genericError:null}export function createErrorPageProps(e,t,n){let r={statusCode:i(e)?e.statusCode:500,message:e.message,url:t};return n&&(r.error=e,r.stack=e.stack),r}export async function renderErrorPage(e,t,n,r){if(typeof e.default!=`function`)return generateDefaultErrorPage(t.statusCode,t.message,r,t.stack);try{let n=e.metadata||{};return`<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
@@ -195,4 +195,4 @@ import{isHttpError as e,createNotFoundError as t,createInternalError as n}from".
|
|
|
195
195
|
<p><a href="/">Return to home</a></p>
|
|
196
196
|
</div>
|
|
197
197
|
</body>
|
|
198
|
-
</html>`}function l(e){return{400:`Bad Request`,401:`Unauthorized`,403:`Forbidden`,404:`Page Not Found`,405:`Method Not Allowed`,408:`Request Timeout`,410:`Gone`,429:`Too Many Requests`,500:`Internal Server Error`,502:`Bad Gateway`,503:`Service Unavailable`,504:`Gateway Timeout`}[e]||`Error`}function u(e){return e.replaceAll(`&`,`&`).replaceAll(`<`,`<`).replaceAll(`>`,`>`).replaceAll(`"`,`"`).replaceAll(`'`,`'`)}export async function handleRenderError(
|
|
198
|
+
</html>`}function l(e){return{400:`Bad Request`,401:`Unauthorized`,403:`Forbidden`,404:`Page Not Found`,405:`Method Not Allowed`,408:`Request Timeout`,410:`Gone`,429:`Too Many Requests`,500:`Internal Server Error`,502:`Bad Gateway`,503:`Service Unavailable`,504:`Gateway Timeout`}[e]||`Error`}function u(e){return e.replaceAll(`&`,`&`).replaceAll(`<`,`<`).replaceAll(`>`,`>`).replaceAll(`"`,`"`).replaceAll(`'`,`'`)}export async function handleRenderError(n,r,a){let{isDev:o=!1}=a,s=i(n)?n.statusCode:500,c=t(r);console.error(`[Render Error] ${s} - ${n.message}`,{url:c.pathname,stack:o?n.stack:void 0});let l=getErrorPageModule(s,await discoverErrorPages(a)),u;return u=l?await renderErrorPage(l,createErrorPageProps(n,c.pathname,o),e(r,{}),o):generateDefaultErrorPage(s,n.message,o,o?n.stack:void 0),new Response(u,{status:s,headers:{"Content-Type":`text/html; charset=utf-8`}})}export function handleApiError(e,t){let{isDev:n=!1}=t,r=i(e)?e.statusCode:500;console.error(`[API Error] ${r} - ${e.message}`,{stack:n?e.stack:void 0});let a=n?{error:e.message,statusCode:r,stack:e.stack}:{error:r>=500?`Internal Server Error`:e.message,statusCode:r};return new Response(JSON.stringify(a),{status:r,headers:{"Content-Type":`application/json`}})}export async function handleNotFound(e,t,n){return handleRenderError(r(`Page not found: ${e}`),t,n)}export async function handleInternalError(e,t,r){let i=n(e.message);return i.stack=e.stack,handleRenderError(i,t,r)}
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
* enabling universal deployment through Nitro presets while maintaining
|
|
6
6
|
* Avalon's multi-framework SSR and islands architecture.
|
|
7
7
|
*/
|
|
8
|
-
export {
|
|
9
|
-
export { type
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
12
|
-
export {
|
|
13
|
-
export {
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
8
|
+
export { type AvalonBuildConfig, type BuildMode, createClientBuildConfig, createCombinedBuildConfig, createNitroBuildPlugin, createServerBuildConfig, createSourceMapConfig, createSourceMapPlugin, DEFAULT_BUILD_CONFIG, DEFAULT_SOURCEMAP_CONFIG, getPresetOutputConfig, getViteSourceMapOption, PRESET_OUTPUT_CONFIGS, type PresetOutputConfig, presetSupportsStreaming, type SourceMapConfig, validateBuildConfig, } from "./build-config.ts";
|
|
9
|
+
export { type AvalonNitroConfig, type AvalonRuntimeConfig, type CacheOptions, createDefaultStaticAssetRouteRules, createNitroConfig, DEFAULT_NITRO_CONFIG, DEFAULT_STATIC_ASSETS_CONFIG, isValidPreset, mergeRouteRules, type NitroConfigOutput, type RouteRule, type StaticAssetsConfig, } from "./config.ts";
|
|
10
|
+
export { clearErrorPageCache, createErrorPageProps, discoverErrorPages, type ErrorHandlerOptions, type ErrorPageProps, generateDefaultErrorPage, getErrorPageModule, handleApiError, handleInternalError, handleNotFound, handleRenderError, renderErrorPage, } from "./error-handler.ts";
|
|
11
|
+
export { type AssetMetadata, type BuildIslandEntry, type BuildIslandManifest, createIslandEntry, createIslandManifestPlugin, DEFAULT_MANIFEST_OPTIONS, detectIslandFramework, extractIslandDependencies, generateContentHash, generatePreloadHints, generatePreloadTags, getIslandAssetPath, getPageCssAssets, type IslandManifestOptions, loadIslandManifest, type PreloadHint, } from "./island-manifest.ts";
|
|
12
|
+
export { createMiddlewareContext, ensureAvalonContext, getMiddlewareContext, getMiddlewareLocal, getMiddlewareState, getOrCreateMiddlewareContext, getRequestHeaders as getMiddlewareRequestHeaders, getRequestURL as getMiddlewareRequestURL, getRouterParams as getMiddlewareRouterParams, hasAvalonContext, type MiddlewareContextOptions, setMiddlewareLocal, setMiddlewareState, storeMiddlewareContext, toRequest as middlewareToRequest, } from "./middleware-adapter.ts";
|
|
13
|
+
export { createErrorResponse, createNitroCatchAllRenderer, createNitroRenderer, createRenderContext, createStreamingResponse, ensureHydrationMarkers, extractIslandMarkers, getRequestURL as getRendererRequestURL, type IslandMarker, injectHydrationScript, type NitroCatchAllOptions, processHydrationRequirements, type RenderHandlerOptions, type ResolvedPageRoute, renderPage, renderPageStream, type StreamingSSROptions, setResponseHeader, toRequest as rendererToRequest, validateHydrationMarkers, } from "./renderer.ts";
|
|
14
|
+
export { calculateRouteSpecificity, discoverPageRoutes, extractParamsFromPattern, type FilePathPatternResult, filePathToPattern, isPrivateFile, matchRoutePattern, PAGE_EXTENSIONS, type PageDiscoveryOptions, sortRoutesBySpecificity, validateRoutePattern, } from "./route-discovery.ts";
|
|
15
|
+
export { type AvalonEventContext, type BuildOptions, createInternalError, createMethodNotAllowedError, createNotFoundError, type DevServerOptions, type DiscoveredRoute, type ErrorResponse, type H3Event, HttpError, type IslandEntry, type IslandManifest, isHttpError, type LayoutContext, type NitroRenderContext, type NitroRouteConfig, type PageMetadata, type PageModule, type SSRRenderOptions, type SSRRenderResult, } from "./types.ts";
|
package/dist/src/nitro/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{
|
|
1
|
+
export{createClientBuildConfig,createCombinedBuildConfig,createNitroBuildPlugin,createServerBuildConfig,createSourceMapConfig,createSourceMapPlugin,DEFAULT_BUILD_CONFIG,DEFAULT_SOURCEMAP_CONFIG,getPresetOutputConfig,getViteSourceMapOption,PRESET_OUTPUT_CONFIGS,presetSupportsStreaming,validateBuildConfig}from"./build-config.js";export{createDefaultStaticAssetRouteRules,createNitroConfig,DEFAULT_NITRO_CONFIG,DEFAULT_STATIC_ASSETS_CONFIG,isValidPreset,mergeRouteRules}from"./config.js";export{clearErrorPageCache,createErrorPageProps,discoverErrorPages,generateDefaultErrorPage,getErrorPageModule,handleApiError,handleInternalError,handleNotFound,handleRenderError,renderErrorPage}from"./error-handler.js";export{createIslandEntry,createIslandManifestPlugin,DEFAULT_MANIFEST_OPTIONS,detectIslandFramework,extractIslandDependencies,generateContentHash,generatePreloadHints,generatePreloadTags,getIslandAssetPath,getPageCssAssets,loadIslandManifest}from"./island-manifest.js";export{createMiddlewareContext,ensureAvalonContext,getMiddlewareContext,getMiddlewareLocal,getMiddlewareState,getOrCreateMiddlewareContext,getRequestHeaders as getMiddlewareRequestHeaders,getRequestURL as getMiddlewareRequestURL,getRouterParams as getMiddlewareRouterParams,hasAvalonContext,setMiddlewareLocal,setMiddlewareState,storeMiddlewareContext,toRequest as middlewareToRequest}from"./middleware-adapter.js";export{createErrorResponse,createNitroCatchAllRenderer,createNitroRenderer,createRenderContext,createStreamingResponse,ensureHydrationMarkers,extractIslandMarkers,getRequestURL as getRendererRequestURL,injectHydrationScript,processHydrationRequirements,renderPage,renderPageStream,setResponseHeader,toRequest as rendererToRequest,validateHydrationMarkers}from"./renderer.js";export{calculateRouteSpecificity,discoverPageRoutes,extractParamsFromPattern,filePathToPattern,isPrivateFile,matchRoutePattern,PAGE_EXTENSIONS,sortRoutesBySpecificity,validateRoutePattern}from"./route-discovery.js";export{createInternalError,createMethodNotAllowedError,createNotFoundError,HttpError,isHttpError}from"./types.js";
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
*/
|
|
34
34
|
import type { Plugin } from "vite";
|
|
35
35
|
import type { ResolvedAvalonConfig } from "../vite-plugin/types.ts";
|
|
36
|
-
import type {
|
|
36
|
+
import type { IslandEntry, IslandManifest } from "./types.ts";
|
|
37
37
|
/**
|
|
38
38
|
* Extended island entry with build-time metadata
|
|
39
39
|
*/
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{readFile as e}from"node:fs/promises";export const DEFAULT_MANIFEST_OPTIONS={outputPath:`dist/island-manifest.json`,includeSourceMaps:!1,generatePreloadHints:!0,verbose:!1};const n={react:[/from\s+['"]react['"]/,/from\s+['"]react-dom['"]/,/@jsxImportSource\s+react/],preact:[/from\s+['"]preact['"]/,/from\s+['"]preact\/hooks['"]/,/@jsxImportSource\s+preact/],vue:[/from\s+['"]vue['"]/,/\.vue$/],svelte:[/from\s+['"]svelte['"]/,/\.svelte$/],solid:[/from\s+['"]solid-js['"]/,/\.solid\.(tsx|jsx)$/],lit:[/from\s+['"]lit['"]/,/from\s+['"]@lit['"]/,/\.lit\.(ts|js)$/]};export function detectIslandFramework(e,t){if(e.endsWith(`.vue`))return`vue`;if(e.endsWith(`.svelte`))return`svelte`;if(e.includes(`.solid.`))return`solid`;if(e.includes(`.lit.`))return`lit`;for(let[r,i]of Object.entries(n))for(let n of i)if(n.test(t)||n.test(e))return r;return e.endsWith(`.tsx`)||e.endsWith(`.jsx`)?`preact`:`unknown`}export async function generateContentHash(e){let t=new TextEncoder().encode(e),n=await crypto.subtle.digest(`SHA-256`,t);return Array.from(new Uint8Array(n)).map(e=>e.toString(16).padStart(2,`0`)).join(``).slice(0,8)}export function extractIslandDependencies(e){let t=[],n=/import\s+.*?\s+from\s+['"]([^'"]+)['"]/g,r=/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g,i;for(
|
|
1
|
+
import{readFile as e}from"node:fs/promises";export const DEFAULT_MANIFEST_OPTIONS={outputPath:`dist/island-manifest.json`,includeSourceMaps:!1,generatePreloadHints:!0,verbose:!1};const n={react:[/from\s+['"]react['"]/,/from\s+['"]react-dom['"]/,/@jsxImportSource\s+react/],preact:[/from\s+['"]preact['"]/,/from\s+['"]preact\/hooks['"]/,/@jsxImportSource\s+preact/],vue:[/from\s+['"]vue['"]/,/\.vue$/],svelte:[/from\s+['"]svelte['"]/,/\.svelte$/],solid:[/from\s+['"]solid-js['"]/,/\.solid\.(tsx|jsx)$/],lit:[/from\s+['"]lit['"]/,/from\s+['"]@lit['"]/,/\.lit\.(ts|js)$/]};export function detectIslandFramework(e,t){if(e.endsWith(`.vue`))return`vue`;if(e.endsWith(`.svelte`))return`svelte`;if(e.includes(`.solid.`))return`solid`;if(e.includes(`.lit.`))return`lit`;for(let[r,i]of Object.entries(n))for(let n of i)if(n.test(t)||n.test(e))return r;return e.endsWith(`.tsx`)||e.endsWith(`.jsx`)?`preact`:`unknown`}export async function generateContentHash(e){let t=new TextEncoder().encode(e),n=await crypto.subtle.digest(`SHA-256`,t);return Array.from(new Uint8Array(n)).map(e=>e.toString(16).padStart(2,`0`)).join(``).slice(0,8)}export function extractIslandDependencies(e){let t=[],n=/import\s+.*?\s+from\s+['"]([^'"]+)['"]/g,r=/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g,i;for(i=n.exec(e);i!==null;i=n.exec(e)){let e=i[1];!e.startsWith(`.`)&&!e.startsWith(`/`)&&t.push(e)}for(i=r.exec(e);i!==null;i=r.exec(e)){let e=i[1];!e.startsWith(`.`)&&!e.startsWith(`/`)&&t.push(e)}return[...new Set(t)]}export async function createIslandEntry(e,t,n,o){let s=detectIslandFramework(t,n),c=await generateContentHash(n);return{src:o,framework:s,css:[],preload:extractIslandDependencies(n).filter(e=>!e.includes(`/`)),sourcePath:t,chunkName:e,contentHash:c,preloadDeps:[],usesStreaming:!1}}export function generatePreloadHints(e){let t=[];e.clientEntry&&t.push({href:e.clientEntry,as:`script`,type:`text/javascript`});for(let n of e.css??[])t.push({href:n,as:`style`,type:`text/css`});for(let n of Object.values(e.frameworkBundles??{}))t.push({href:n,as:`script`,type:`text/javascript`});return t}export function createIslandManifestPlugin(e,n={}){let r={...DEFAULT_MANIFEST_OPTIONS,...n},a=new Map,o=new Set,c=``;return{name:`avalon:island-manifest`,enforce:`post`,generateBundle(e,t){for(let[e,n]of Object.entries(t))if(e.endsWith(`.css`)&&o.add(`/${e}`),n.type===`chunk`&&(n.name===`client`||n.name===`main`)&&(c=`/${e}`),n.type===`chunk`&&(n.facadeModuleId?.includes(`/islands/`)||n.name?.startsWith(`islands/`))){let t=n.name?.replace(`islands/`,``)??e,r=a.get(t);r?r.src=`/${e}`:a.set(t,{src:`/${e}`,framework:u(n),css:[],preload:[],sourcePath:n.facadeModuleId??``,chunkName:t,contentHash:/\.([a-f0-9]+)\.js$/.exec(e)?.[1]??``,preloadDeps:n.imports??[],usesStreaming:!1})}},async writeBundle(e,t){let n={},u=new Date().toISOString();for(let[e,r]of Object.entries(t)){let t=await l(e,r,u);t&&(n[`/${e}`]=t)}let d={islands:Object.fromEntries(a),clientEntry:c,css:Array.from(o),buildTime:Date.now(),buildHash:await generateContentHash(JSON.stringify(Object.fromEntries(a))),avalonVersion:`1.0.0`,cssAssets:Array.from(o),preloadHints:r.generatePreloadHints?generatePreloadHints({clientEntry:c,css:Array.from(o),islands:Object.fromEntries(a)}):[],frameworkBundles:{},assetMetadata:n},f=JSON.stringify(d,null,2);r.verbose&&(console.log(`📋 Island manifest generated:`),console.log(` Islands: ${a.size}`),console.log(` CSS assets: ${o.size}`),console.log(` Client entry: ${c}`),console.log(` Asset metadata entries: ${Object.keys(n).length}`)),this.emitFile({type:`asset`,fileName:`island-manifest.json`,source:f})}}}async function l(e,t,n){let r;if(t.type===`asset`&&t.source?r=t.source:t.type===`chunk`&&t.code&&(r=t.code),!r)return null;let a=typeof r==`string`?new TextEncoder().encode(r).length:r.length,o=await generateContentHash(typeof r==`string`?r:new TextDecoder().decode(r)),s=e.substring(e.lastIndexOf(`.`));return{type:{".js":`application/javascript`,".mjs":`application/javascript`,".css":`text/css`,".json":`application/json`,".html":`text/html`,".map":`application/json`}[s]??`application/octet-stream`,etag:`"${o}"`,mtime:n,size:a}}function u(e){return detectIslandFramework(e.facadeModuleId??``,e.code??``)}export async function loadIslandManifest(t=`dist/island-manifest.json`){try{let n=await e(t,`utf-8`);return JSON.parse(n)}catch{return null}}export function getIslandAssetPath(e,t){return t?t.islands[e]?.src??null:null}export function getPageCssAssets(e,t){if(!t)return[];let n=new Set;for(let e of t.css)n.add(e);for(let r of e){let e=t.islands[r];if(e?.css)for(let t of e.css)n.add(t)}return Array.from(n)}export function generatePreloadTags(e){let t=[];for(let n of e.preloadHints){let e=[`rel="preload"`,`href="${n.href}"`,`as="${n.as}"`];n.type&&e.push(`type="${n.type}"`),n.crossorigin&&e.push(`crossorigin="${n.crossorigin}"`),t.push(`<link ${e.join(` `)}>`)}return t.join(`
|
|
2
2
|
`)}
|