@useavalon/avalon 0.1.11 → 0.1.12
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/README.md +54 -54
- package/dist/mod.js +1 -0
- package/dist/src/build/integration-bundler-plugin.js +1 -0
- package/dist/src/build/integration-config.js +1 -0
- package/dist/src/build/integration-detection-plugin.js +1 -0
- package/dist/src/build/integration-resolver-plugin.js +1 -0
- package/dist/src/build/island-manifest.js +1 -0
- package/dist/src/build/island-types-generator.js +5 -0
- package/dist/src/build/mdx-island-transform.js +2 -0
- package/dist/src/build/mdx-plugin.js +1 -0
- package/dist/src/build/page-island-transform.js +3 -0
- package/dist/src/build/prop-extractors/index.js +1 -0
- package/dist/src/build/prop-extractors/lit.js +1 -0
- package/dist/src/build/prop-extractors/qwik.js +1 -0
- package/dist/src/build/prop-extractors/solid.js +1 -0
- package/dist/src/build/prop-extractors/svelte.js +1 -0
- package/dist/src/build/prop-extractors/vue.js +1 -0
- package/dist/src/build/sidecar-file-manager.js +1 -0
- package/dist/src/build/sidecar-renderer.js +6 -0
- package/dist/src/client/adapters/index.js +1 -0
- package/dist/src/client/components.js +1 -0
- package/dist/src/client/css-hmr-handler.js +1 -0
- package/dist/src/client/framework-adapter.js +13 -0
- package/dist/src/client/hmr-coordinator.js +1 -0
- package/dist/src/client/hmr-error-overlay.js +214 -0
- package/dist/src/client/main.js +39 -0
- package/{src → dist/src}/client/types/framework-runtime.d.ts +68 -68
- package/{src → dist/src}/client/types/vite-hmr.d.ts +46 -46
- package/dist/src/client/types/vite-virtual-modules.d.ts +70 -0
- package/dist/src/components/Image.js +1 -0
- package/dist/src/components/IslandErrorBoundary.js +1 -0
- package/dist/src/components/LayoutDataErrorBoundary.js +1 -0
- package/dist/src/components/LayoutErrorBoundary.js +1 -0
- package/dist/src/components/PersistentIsland.js +1 -0
- package/dist/src/components/StreamingErrorBoundary.js +1 -0
- package/dist/src/components/StreamingLayout.js +29 -0
- package/dist/src/core/components/component-analyzer.js +1 -0
- package/dist/src/core/components/component-detection.js +5 -0
- package/dist/src/core/components/enhanced-framework-detector.js +1 -0
- package/dist/src/core/components/framework-registry.js +1 -0
- package/dist/src/core/content/mdx-processor.js +1 -0
- package/dist/src/core/integrations/index.js +1 -0
- package/dist/src/core/integrations/loader.js +1 -0
- package/dist/src/core/integrations/registry.js +1 -0
- package/dist/src/core/islands/island-persistence.js +1 -0
- package/dist/src/core/islands/island-state-serializer.js +1 -0
- package/dist/src/core/islands/persistent-island-context.js +1 -0
- package/dist/src/core/islands/use-persistent-state.js +1 -0
- package/dist/src/core/layout/enhanced-layout-resolver.js +1 -0
- package/dist/src/core/layout/layout-cache-manager.js +1 -0
- package/dist/src/core/layout/layout-composer.js +1 -0
- package/dist/src/core/layout/layout-data-loader.js +1 -0
- package/dist/src/core/layout/layout-discovery.js +1 -0
- package/dist/src/core/layout/layout-matcher.js +1 -0
- package/dist/src/core/layout/layout-types.js +1 -0
- package/dist/src/core/modules/framework-module-resolver.js +1 -0
- package/dist/src/islands/component-analysis.js +1 -0
- package/dist/src/islands/css-utils.js +17 -0
- package/dist/src/islands/discovery/index.js +1 -0
- package/dist/src/islands/discovery/registry.js +1 -0
- package/dist/src/islands/discovery/resolver.js +2 -0
- package/dist/src/islands/discovery/scanner.js +1 -0
- package/dist/src/islands/discovery/types.js +1 -0
- package/dist/src/islands/discovery/validator.js +18 -0
- package/dist/src/islands/discovery/watcher.js +1 -0
- package/dist/src/islands/framework-detection.js +1 -0
- package/dist/src/islands/integration-loader.js +1 -0
- package/dist/src/islands/island.js +1 -0
- package/dist/src/islands/render-cache.js +1 -0
- package/dist/src/islands/types.js +1 -0
- package/dist/src/islands/universal-css-collector.js +5 -0
- package/dist/src/islands/universal-head-collector.js +2 -0
- package/{src → dist/src}/layout-system.d.ts +592 -592
- package/dist/src/layout-system.js +1 -0
- package/dist/src/middleware/discovery.js +1 -0
- package/dist/src/middleware/executor.js +1 -0
- package/dist/src/middleware/index.js +1 -0
- package/dist/src/middleware/types.js +1 -0
- package/dist/src/nitro/build-config.js +1 -0
- package/dist/src/nitro/config.js +1 -0
- package/dist/src/nitro/error-handler.js +198 -0
- package/dist/src/nitro/index.js +1 -0
- package/dist/src/nitro/island-manifest.js +2 -0
- package/dist/src/nitro/middleware-adapter.js +1 -0
- package/dist/src/nitro/renderer.js +183 -0
- package/dist/src/nitro/route-discovery.js +1 -0
- package/dist/src/nitro/types.js +1 -0
- package/dist/src/render/collect-css.js +3 -0
- package/{src/render/error-pages.ts → dist/src/render/error-pages.js} +7 -38
- package/dist/src/render/isolated-ssr-renderer.js +1 -0
- package/dist/src/render/ssr.js +90 -0
- package/dist/src/schemas/api.js +1 -0
- package/dist/src/schemas/core.js +1 -0
- package/dist/src/schemas/index.js +1 -0
- package/dist/src/schemas/layout.js +1 -0
- package/dist/src/schemas/routing/index.js +1 -0
- package/dist/src/schemas/routing.js +1 -0
- package/dist/src/types/as-island.js +1 -0
- package/{src → dist/src}/types/image.d.ts +106 -106
- package/{src → dist/src}/types/index.d.ts +22 -22
- package/{src → dist/src}/types/island-jsx.d.ts +33 -33
- package/{src → dist/src}/types/island-prop.d.ts +20 -20
- package/dist/src/types/layout.js +1 -0
- package/{src → dist/src}/types/mdx.d.ts +6 -6
- package/dist/src/types/routing.js +1 -0
- package/dist/src/types/types.js +1 -0
- package/{src → dist/src}/types/urlpattern.d.ts +49 -49
- package/{src → dist/src}/types/vite-env.d.ts +11 -11
- package/dist/src/utils/dev-logger.js +12 -0
- package/dist/src/utils/fs.js +1 -0
- package/dist/src/vite-plugin/auto-discover.js +1 -0
- package/dist/src/vite-plugin/config.js +1 -0
- package/dist/src/vite-plugin/errors.js +1 -0
- package/dist/src/vite-plugin/image-optimization.js +45 -0
- package/dist/src/vite-plugin/integration-activator.js +1 -0
- package/dist/src/vite-plugin/island-sidecar-plugin.js +1 -0
- package/dist/src/vite-plugin/module-discovery.js +1 -0
- package/dist/src/vite-plugin/nitro-integration.js +42 -0
- package/dist/src/vite-plugin/plugin.js +1 -0
- package/dist/src/vite-plugin/types.js +1 -0
- package/dist/src/vite-plugin/validation.js +2 -0
- package/package.json +57 -26
- package/mod.ts +0 -302
- package/src/build/integration-bundler-plugin.ts +0 -116
- package/src/build/integration-config.ts +0 -168
- package/src/build/integration-detection-plugin.ts +0 -117
- package/src/build/integration-resolver-plugin.ts +0 -90
- package/src/build/island-manifest.ts +0 -269
- package/src/build/island-types-generator.ts +0 -476
- package/src/build/mdx-island-transform.ts +0 -464
- package/src/build/mdx-plugin.ts +0 -98
- package/src/build/page-island-transform.ts +0 -598
- package/src/build/prop-extractors/index.ts +0 -21
- package/src/build/prop-extractors/lit.ts +0 -140
- package/src/build/prop-extractors/qwik.ts +0 -16
- package/src/build/prop-extractors/solid.ts +0 -125
- package/src/build/prop-extractors/svelte.ts +0 -194
- package/src/build/prop-extractors/vue.ts +0 -111
- package/src/build/sidecar-file-manager.ts +0 -104
- package/src/build/sidecar-renderer.ts +0 -30
- package/src/client/adapters/index.js +0 -12
- package/src/client/adapters/index.ts +0 -13
- package/src/client/adapters/lit-adapter.js +0 -467
- package/src/client/adapters/lit-adapter.ts +0 -654
- package/src/client/adapters/preact-adapter.js +0 -223
- package/src/client/adapters/preact-adapter.ts +0 -331
- package/src/client/adapters/qwik-adapter.js +0 -259
- package/src/client/adapters/qwik-adapter.ts +0 -345
- package/src/client/adapters/react-adapter.js +0 -220
- package/src/client/adapters/react-adapter.ts +0 -353
- package/src/client/adapters/solid-adapter.js +0 -295
- package/src/client/adapters/solid-adapter.ts +0 -451
- package/src/client/adapters/svelte-adapter.js +0 -368
- package/src/client/adapters/svelte-adapter.ts +0 -524
- package/src/client/adapters/vue-adapter.js +0 -278
- package/src/client/adapters/vue-adapter.ts +0 -467
- package/src/client/components.js +0 -23
- package/src/client/components.ts +0 -35
- package/src/client/css-hmr-handler.js +0 -263
- package/src/client/css-hmr-handler.ts +0 -344
- package/src/client/framework-adapter.js +0 -283
- package/src/client/framework-adapter.ts +0 -462
- package/src/client/hmr-coordinator.js +0 -274
- package/src/client/hmr-coordinator.ts +0 -396
- package/src/client/hmr-error-overlay.js +0 -533
- package/src/client/main.js +0 -816
- package/src/client/types/vite-virtual-modules.d.ts +0 -60
- package/src/components/Image.tsx +0 -123
- package/src/components/IslandErrorBoundary.tsx +0 -145
- package/src/components/LayoutDataErrorBoundary.tsx +0 -141
- package/src/components/LayoutErrorBoundary.tsx +0 -127
- package/src/components/PersistentIsland.tsx +0 -52
- package/src/components/StreamingErrorBoundary.tsx +0 -233
- package/src/components/StreamingLayout.tsx +0 -538
- package/src/core/components/component-analyzer.ts +0 -192
- package/src/core/components/component-detection.ts +0 -508
- package/src/core/components/enhanced-framework-detector.ts +0 -500
- package/src/core/components/framework-registry.ts +0 -563
- package/src/core/content/mdx-processor.ts +0 -46
- package/src/core/integrations/index.ts +0 -19
- package/src/core/integrations/loader.ts +0 -125
- package/src/core/integrations/registry.ts +0 -175
- package/src/core/islands/island-persistence.ts +0 -325
- package/src/core/islands/island-state-serializer.ts +0 -258
- package/src/core/islands/persistent-island-context.tsx +0 -80
- package/src/core/islands/use-persistent-state.ts +0 -68
- package/src/core/layout/enhanced-layout-resolver.ts +0 -322
- package/src/core/layout/layout-cache-manager.ts +0 -485
- package/src/core/layout/layout-composer.ts +0 -357
- package/src/core/layout/layout-data-loader.ts +0 -516
- package/src/core/layout/layout-discovery.ts +0 -243
- package/src/core/layout/layout-matcher.ts +0 -299
- package/src/core/layout/layout-types.ts +0 -110
- package/src/core/modules/framework-module-resolver.ts +0 -273
- package/src/islands/component-analysis.ts +0 -213
- package/src/islands/css-utils.ts +0 -565
- package/src/islands/discovery/index.ts +0 -80
- package/src/islands/discovery/registry.ts +0 -340
- package/src/islands/discovery/resolver.ts +0 -477
- package/src/islands/discovery/scanner.ts +0 -386
- package/src/islands/discovery/types.ts +0 -117
- package/src/islands/discovery/validator.ts +0 -544
- package/src/islands/discovery/watcher.ts +0 -368
- package/src/islands/framework-detection.ts +0 -428
- package/src/islands/integration-loader.ts +0 -490
- package/src/islands/island.tsx +0 -565
- package/src/islands/render-cache.ts +0 -550
- package/src/islands/types.ts +0 -80
- package/src/islands/universal-css-collector.ts +0 -157
- package/src/islands/universal-head-collector.ts +0 -137
- package/src/layout-system.ts +0 -218
- package/src/middleware/discovery.ts +0 -268
- package/src/middleware/executor.ts +0 -315
- package/src/middleware/index.ts +0 -76
- package/src/middleware/types.ts +0 -99
- package/src/nitro/build-config.ts +0 -576
- package/src/nitro/config.ts +0 -483
- package/src/nitro/error-handler.ts +0 -636
- package/src/nitro/index.ts +0 -173
- package/src/nitro/island-manifest.ts +0 -584
- package/src/nitro/middleware-adapter.ts +0 -260
- package/src/nitro/renderer.ts +0 -1471
- package/src/nitro/route-discovery.ts +0 -439
- package/src/nitro/types.ts +0 -321
- package/src/render/collect-css.ts +0 -198
- package/src/render/isolated-ssr-renderer.ts +0 -654
- package/src/render/ssr.ts +0 -1030
- package/src/schemas/api.ts +0 -30
- package/src/schemas/core.ts +0 -64
- package/src/schemas/index.ts +0 -212
- package/src/schemas/layout.ts +0 -279
- package/src/schemas/routing/index.ts +0 -38
- package/src/schemas/routing.ts +0 -376
- package/src/types/as-island.ts +0 -20
- package/src/types/layout.ts +0 -285
- package/src/types/routing.ts +0 -555
- package/src/types/types.ts +0 -5
- package/src/utils/dev-logger.ts +0 -299
- package/src/utils/fs.ts +0 -151
- package/src/vite-plugin/auto-discover.ts +0 -551
- package/src/vite-plugin/config.ts +0 -266
- package/src/vite-plugin/errors.ts +0 -127
- package/src/vite-plugin/image-optimization.ts +0 -156
- package/src/vite-plugin/integration-activator.ts +0 -126
- package/src/vite-plugin/island-sidecar-plugin.ts +0 -176
- package/src/vite-plugin/module-discovery.ts +0 -189
- package/src/vite-plugin/nitro-integration.ts +0 -1354
- package/src/vite-plugin/plugin.ts +0 -409
- package/src/vite-plugin/types.ts +0 -327
- package/src/vite-plugin/validation.ts +0 -228
|
@@ -1,584 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Island Manifest Generation Module for Nitro
|
|
3
|
-
*
|
|
4
|
-
* This module provides island manifest generation during production builds.
|
|
5
|
-
* The manifest contains information about all discovered islands, their
|
|
6
|
-
* compiled asset paths, and framework metadata for client-side hydration.
|
|
7
|
-
*
|
|
8
|
-
* ## Build Output
|
|
9
|
-
*
|
|
10
|
-
* During production build, this module generates:
|
|
11
|
-
* - `island-manifest.json` in the build output directory
|
|
12
|
-
* - Contains metadata for all islands (src, framework, css, preload)
|
|
13
|
-
* - Includes build timestamp and content hashes for cache busting
|
|
14
|
-
* - Generates preload hints for critical assets
|
|
15
|
-
*
|
|
16
|
-
* ## Nitro Integration
|
|
17
|
-
*
|
|
18
|
-
* The manifest is used by Nitro's renderer to:
|
|
19
|
-
* - Resolve compiled island paths for hydration scripts
|
|
20
|
-
* - Inject CSS assets for islands used on a page
|
|
21
|
-
* - Generate preload tags for critical resources
|
|
22
|
-
*
|
|
23
|
-
* ## Asset Metadata
|
|
24
|
-
*
|
|
25
|
-
* Each island entry includes:
|
|
26
|
-
* - `src`: Compiled JavaScript path (e.g., `/islands/Counter.abc123.js`)
|
|
27
|
-
* - `framework`: Detected framework (react, preact, vue, svelte, solid, lit)
|
|
28
|
-
* - `css`: Associated CSS files
|
|
29
|
-
* - `contentHash`: Hash for cache busting
|
|
30
|
-
* - `preloadDeps`: Dependencies to preload
|
|
31
|
-
*
|
|
32
|
-
* @module nitro/island-manifest
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
import { readFile } from "node:fs/promises";
|
|
36
|
-
import type { Plugin } from "vite";
|
|
37
|
-
import type { ResolvedAvalonConfig } from "../vite-plugin/types.ts";
|
|
38
|
-
import type { IslandManifest, IslandEntry } from "./types.ts";
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Extended island entry with build-time metadata
|
|
42
|
-
*/
|
|
43
|
-
export interface BuildIslandEntry extends IslandEntry {
|
|
44
|
-
/** Original source file path */
|
|
45
|
-
sourcePath: string;
|
|
46
|
-
/** Compiled JavaScript chunk name */
|
|
47
|
-
chunkName: string;
|
|
48
|
-
/** Content hash for cache busting */
|
|
49
|
-
contentHash: string;
|
|
50
|
-
/** Dependencies that need to be preloaded */
|
|
51
|
-
preloadDeps: string[];
|
|
52
|
-
/** Whether the island uses streaming */
|
|
53
|
-
usesStreaming: boolean;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Asset metadata for Nitro's asset manifest format
|
|
58
|
-
* This matches Nitro's expected asset metadata structure
|
|
59
|
-
*/
|
|
60
|
-
export interface AssetMetadata {
|
|
61
|
-
/** MIME type of the asset */
|
|
62
|
-
type: string;
|
|
63
|
-
/** ETag for cache validation */
|
|
64
|
-
etag: string;
|
|
65
|
-
/** Last modification time (ISO string) */
|
|
66
|
-
mtime: string;
|
|
67
|
-
/** File size in bytes */
|
|
68
|
-
size: number;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Build-time island manifest with additional metadata
|
|
73
|
-
*/
|
|
74
|
-
export interface BuildIslandManifest extends IslandManifest {
|
|
75
|
-
/** Build timestamp */
|
|
76
|
-
buildTime: number;
|
|
77
|
-
/** Build version/hash */
|
|
78
|
-
buildHash: string;
|
|
79
|
-
/** Avalon version */
|
|
80
|
-
avalonVersion: string;
|
|
81
|
-
/** All CSS assets to inject */
|
|
82
|
-
cssAssets: string[];
|
|
83
|
-
/** Preload hints for critical assets */
|
|
84
|
-
preloadHints: PreloadHint[];
|
|
85
|
-
/** Framework-specific bundles */
|
|
86
|
-
frameworkBundles: Record<string, string>;
|
|
87
|
-
/** Asset metadata for cache headers (Nitro format) */
|
|
88
|
-
assetMetadata?: Record<string, AssetMetadata>;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Preload hint for resource optimization
|
|
93
|
-
*/
|
|
94
|
-
export interface PreloadHint {
|
|
95
|
-
/** Resource URL */
|
|
96
|
-
href: string;
|
|
97
|
-
/** Resource type (script, style, font, etc.) */
|
|
98
|
-
as: "script" | "style" | "font" | "image";
|
|
99
|
-
/** MIME type */
|
|
100
|
-
type?: string;
|
|
101
|
-
/** Cross-origin setting */
|
|
102
|
-
crossorigin?: "anonymous" | "use-credentials";
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Options for island manifest generation
|
|
107
|
-
*/
|
|
108
|
-
export interface IslandManifestOptions {
|
|
109
|
-
/** Output path for the manifest file */
|
|
110
|
-
outputPath?: string;
|
|
111
|
-
/** Include source maps in manifest */
|
|
112
|
-
includeSourceMaps?: boolean;
|
|
113
|
-
/** Generate preload hints */
|
|
114
|
-
generatePreloadHints?: boolean;
|
|
115
|
-
/** Verbose logging */
|
|
116
|
-
verbose?: boolean;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Default manifest generation options
|
|
121
|
-
*/
|
|
122
|
-
export const DEFAULT_MANIFEST_OPTIONS: Required<IslandManifestOptions> = {
|
|
123
|
-
outputPath: "dist/island-manifest.json",
|
|
124
|
-
includeSourceMaps: false,
|
|
125
|
-
generatePreloadHints: true,
|
|
126
|
-
verbose: false,
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Framework detection patterns for island files
|
|
131
|
-
*/
|
|
132
|
-
const FRAMEWORK_PATTERNS: Record<string, RegExp[]> = {
|
|
133
|
-
react: [
|
|
134
|
-
/from\s+['"]react['"]/,
|
|
135
|
-
/from\s+['"]react-dom['"]/,
|
|
136
|
-
/@jsxImportSource\s+react/,
|
|
137
|
-
],
|
|
138
|
-
preact: [
|
|
139
|
-
/from\s+['"]preact['"]/,
|
|
140
|
-
/from\s+['"]preact\/hooks['"]/,
|
|
141
|
-
/@jsxImportSource\s+preact/,
|
|
142
|
-
],
|
|
143
|
-
vue: [
|
|
144
|
-
/from\s+['"]vue['"]/,
|
|
145
|
-
/\.vue$/,
|
|
146
|
-
],
|
|
147
|
-
svelte: [
|
|
148
|
-
/from\s+['"]svelte['"]/,
|
|
149
|
-
/\.svelte$/,
|
|
150
|
-
],
|
|
151
|
-
solid: [
|
|
152
|
-
/from\s+['"]solid-js['"]/,
|
|
153
|
-
/\.solid\.(tsx|jsx)$/,
|
|
154
|
-
],
|
|
155
|
-
lit: [
|
|
156
|
-
/from\s+['"]lit['"]/,
|
|
157
|
-
/from\s+['"]@lit['"]/,
|
|
158
|
-
/\.lit\.(ts|js)$/,
|
|
159
|
-
],
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Detects the framework used by an island based on file content and extension
|
|
164
|
-
*
|
|
165
|
-
* @param filePath - Path to the island file
|
|
166
|
-
* @param content - File content
|
|
167
|
-
* @returns Detected framework name
|
|
168
|
-
*/
|
|
169
|
-
export function detectIslandFramework(
|
|
170
|
-
filePath: string,
|
|
171
|
-
content: string
|
|
172
|
-
): string {
|
|
173
|
-
// Check file extension first
|
|
174
|
-
if (filePath.endsWith(".vue")) return "vue";
|
|
175
|
-
if (filePath.endsWith(".svelte")) return "svelte";
|
|
176
|
-
if (filePath.includes(".solid.")) return "solid";
|
|
177
|
-
if (filePath.includes(".lit.")) return "lit";
|
|
178
|
-
|
|
179
|
-
// Check content patterns
|
|
180
|
-
for (const [framework, patterns] of Object.entries(FRAMEWORK_PATTERNS)) {
|
|
181
|
-
for (const pattern of patterns) {
|
|
182
|
-
if (pattern.test(content) || pattern.test(filePath)) {
|
|
183
|
-
return framework;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Default to preact for JSX/TSX files
|
|
189
|
-
if (filePath.endsWith(".tsx") || filePath.endsWith(".jsx")) {
|
|
190
|
-
return "preact";
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return "unknown";
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Generates a content hash for cache busting
|
|
198
|
-
*
|
|
199
|
-
* @param content - Content to hash
|
|
200
|
-
* @returns Short hash string
|
|
201
|
-
*/
|
|
202
|
-
export async function generateContentHash(content: string): Promise<string> {
|
|
203
|
-
const encoder = new TextEncoder();
|
|
204
|
-
const data = encoder.encode(content);
|
|
205
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
206
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
207
|
-
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
208
|
-
return hashHex.slice(0, 8);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Extracts dependencies from island file content
|
|
213
|
-
*
|
|
214
|
-
* @param content - File content
|
|
215
|
-
* @returns Array of dependency names
|
|
216
|
-
*/
|
|
217
|
-
export function extractIslandDependencies(content: string): string[] {
|
|
218
|
-
const deps: string[] = [];
|
|
219
|
-
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
220
|
-
const dynamicImportRegex = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
221
|
-
|
|
222
|
-
let match;
|
|
223
|
-
|
|
224
|
-
// Static imports
|
|
225
|
-
while ((match = importRegex.exec(content)) !== null) {
|
|
226
|
-
const importPath = match[1];
|
|
227
|
-
if (!importPath.startsWith(".") && !importPath.startsWith("/")) {
|
|
228
|
-
deps.push(importPath);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Dynamic imports
|
|
233
|
-
while ((match = dynamicImportRegex.exec(content)) !== null) {
|
|
234
|
-
const importPath = match[1];
|
|
235
|
-
if (!importPath.startsWith(".") && !importPath.startsWith("/")) {
|
|
236
|
-
deps.push(importPath);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return [...new Set(deps)];
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Creates an island entry from discovered island information
|
|
245
|
-
*
|
|
246
|
-
* @param name - Island name
|
|
247
|
-
* @param sourcePath - Source file path
|
|
248
|
-
* @param content - File content
|
|
249
|
-
* @param compiledPath - Compiled asset path
|
|
250
|
-
* @returns Island entry
|
|
251
|
-
*/
|
|
252
|
-
export async function createIslandEntry(
|
|
253
|
-
name: string,
|
|
254
|
-
sourcePath: string,
|
|
255
|
-
content: string,
|
|
256
|
-
compiledPath: string
|
|
257
|
-
): Promise<BuildIslandEntry> {
|
|
258
|
-
const framework = detectIslandFramework(sourcePath, content);
|
|
259
|
-
const contentHash = await generateContentHash(content);
|
|
260
|
-
const deps = extractIslandDependencies(content);
|
|
261
|
-
|
|
262
|
-
return {
|
|
263
|
-
src: compiledPath,
|
|
264
|
-
framework,
|
|
265
|
-
css: [], // Will be populated during build
|
|
266
|
-
preload: deps.filter((d) => !d.includes("/")), // Only top-level packages
|
|
267
|
-
sourcePath,
|
|
268
|
-
chunkName: name,
|
|
269
|
-
contentHash,
|
|
270
|
-
preloadDeps: [],
|
|
271
|
-
usesStreaming: false,
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Generates preload hints for critical assets
|
|
277
|
-
*
|
|
278
|
-
* @param manifest - Island manifest
|
|
279
|
-
* @returns Array of preload hints
|
|
280
|
-
*/
|
|
281
|
-
export function generatePreloadHints(
|
|
282
|
-
manifest: Partial<BuildIslandManifest>
|
|
283
|
-
): PreloadHint[] {
|
|
284
|
-
const hints: PreloadHint[] = [];
|
|
285
|
-
|
|
286
|
-
// Add client entry script
|
|
287
|
-
if (manifest.clientEntry) {
|
|
288
|
-
hints.push({
|
|
289
|
-
href: manifest.clientEntry,
|
|
290
|
-
as: "script",
|
|
291
|
-
type: "text/javascript",
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Add CSS assets
|
|
296
|
-
for (const css of manifest.css ?? []) {
|
|
297
|
-
hints.push({
|
|
298
|
-
href: css,
|
|
299
|
-
as: "style",
|
|
300
|
-
type: "text/css",
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Add framework bundles
|
|
305
|
-
for (const bundle of Object.values(manifest.frameworkBundles ?? {})) {
|
|
306
|
-
hints.push({
|
|
307
|
-
href: bundle,
|
|
308
|
-
as: "script",
|
|
309
|
-
type: "text/javascript",
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return hints;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Creates a Vite plugin for island manifest generation
|
|
318
|
-
*
|
|
319
|
-
* @param avalonConfig - Resolved Avalon configuration
|
|
320
|
-
* @param options - Manifest generation options
|
|
321
|
-
* @returns Vite plugin
|
|
322
|
-
*/
|
|
323
|
-
export function createIslandManifestPlugin(
|
|
324
|
-
avalonConfig: ResolvedAvalonConfig,
|
|
325
|
-
options: IslandManifestOptions = {}
|
|
326
|
-
): Plugin {
|
|
327
|
-
const opts = { ...DEFAULT_MANIFEST_OPTIONS, ...options };
|
|
328
|
-
const islands: Map<string, BuildIslandEntry> = new Map();
|
|
329
|
-
const cssAssets: Set<string> = new Set();
|
|
330
|
-
let clientEntry = "";
|
|
331
|
-
|
|
332
|
-
return {
|
|
333
|
-
name: "avalon:island-manifest",
|
|
334
|
-
enforce: "post",
|
|
335
|
-
|
|
336
|
-
// Track island chunks during build
|
|
337
|
-
generateBundle(_outputOptions, bundle) {
|
|
338
|
-
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
339
|
-
// Track CSS assets
|
|
340
|
-
if (fileName.endsWith(".css")) {
|
|
341
|
-
cssAssets.add(`/${fileName}`);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Track client entry
|
|
345
|
-
if (
|
|
346
|
-
chunk.type === "chunk" &&
|
|
347
|
-
(chunk.name === "client" || chunk.name === "main")
|
|
348
|
-
) {
|
|
349
|
-
clientEntry = `/${fileName}`;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// Track island chunks
|
|
353
|
-
if (
|
|
354
|
-
chunk.type === "chunk" &&
|
|
355
|
-
(chunk.facadeModuleId?.includes("/islands/") ||
|
|
356
|
-
chunk.name?.startsWith("islands/"))
|
|
357
|
-
) {
|
|
358
|
-
const name = chunk.name?.replace("islands/", "") ?? fileName;
|
|
359
|
-
const existingEntry = islands.get(name);
|
|
360
|
-
|
|
361
|
-
if (existingEntry) {
|
|
362
|
-
// Update with compiled path
|
|
363
|
-
existingEntry.src = `/${fileName}`;
|
|
364
|
-
} else {
|
|
365
|
-
// Create new entry
|
|
366
|
-
islands.set(name, {
|
|
367
|
-
src: `/${fileName}`,
|
|
368
|
-
framework: detectFrameworkFromChunk(chunk),
|
|
369
|
-
css: [],
|
|
370
|
-
preload: [],
|
|
371
|
-
sourcePath: chunk.facadeModuleId ?? "",
|
|
372
|
-
chunkName: name,
|
|
373
|
-
contentHash: /\.([a-f0-9]+)\.js$/.exec(fileName)?.[1] ?? "",
|
|
374
|
-
preloadDeps: chunk.imports ?? [],
|
|
375
|
-
usesStreaming: false,
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
},
|
|
381
|
-
|
|
382
|
-
// Write manifest after build
|
|
383
|
-
async writeBundle(_options: unknown, bundle: Record<string, { type: string; source?: string | Uint8Array; code?: string }>) {
|
|
384
|
-
// Generate asset metadata for Nitro's format (type, etag, mtime, size)
|
|
385
|
-
const assetMetadata: Record<string, AssetMetadata> = {};
|
|
386
|
-
const buildTime = new Date().toISOString();
|
|
387
|
-
|
|
388
|
-
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
389
|
-
const entry = await buildAssetMetadataEntry(fileName, chunk, buildTime);
|
|
390
|
-
if (entry) assetMetadata[`/${fileName}`] = entry;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
const manifest: BuildIslandManifest = {
|
|
394
|
-
islands: Object.fromEntries(islands),
|
|
395
|
-
clientEntry,
|
|
396
|
-
css: Array.from(cssAssets),
|
|
397
|
-
buildTime: Date.now(),
|
|
398
|
-
buildHash: await generateContentHash(JSON.stringify(Object.fromEntries(islands))),
|
|
399
|
-
avalonVersion: "1.0.0",
|
|
400
|
-
cssAssets: Array.from(cssAssets),
|
|
401
|
-
preloadHints: opts.generatePreloadHints
|
|
402
|
-
? generatePreloadHints({
|
|
403
|
-
clientEntry,
|
|
404
|
-
css: Array.from(cssAssets),
|
|
405
|
-
islands: Object.fromEntries(islands),
|
|
406
|
-
})
|
|
407
|
-
: [],
|
|
408
|
-
frameworkBundles: {},
|
|
409
|
-
assetMetadata,
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
// Write manifest file
|
|
413
|
-
const manifestJson = JSON.stringify(manifest, null, 2);
|
|
414
|
-
|
|
415
|
-
if (opts.verbose) {
|
|
416
|
-
console.log("📋 Island manifest generated:");
|
|
417
|
-
console.log(` Islands: ${islands.size}`);
|
|
418
|
-
console.log(` CSS assets: ${cssAssets.size}`);
|
|
419
|
-
console.log(` Client entry: ${clientEntry}`);
|
|
420
|
-
console.log(` Asset metadata entries: ${Object.keys(assetMetadata).length}`);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Emit the manifest as an asset
|
|
424
|
-
this.emitFile({
|
|
425
|
-
type: "asset",
|
|
426
|
-
fileName: "island-manifest.json",
|
|
427
|
-
source: manifestJson,
|
|
428
|
-
});
|
|
429
|
-
},
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
/**
|
|
434
|
-
* Generates asset metadata entry for a single bundle chunk
|
|
435
|
-
*/
|
|
436
|
-
async function buildAssetMetadataEntry(
|
|
437
|
-
fileName: string,
|
|
438
|
-
chunk: { type: string; source?: string | Uint8Array; code?: string },
|
|
439
|
-
buildTime: string
|
|
440
|
-
): Promise<AssetMetadata | null> {
|
|
441
|
-
let content: string | Uint8Array | undefined;
|
|
442
|
-
if (chunk.type === "asset" && chunk.source) {
|
|
443
|
-
content = chunk.source;
|
|
444
|
-
} else if (chunk.type === "chunk" && chunk.code) {
|
|
445
|
-
content = chunk.code;
|
|
446
|
-
}
|
|
447
|
-
if (!content) return null;
|
|
448
|
-
|
|
449
|
-
const size = typeof content === "string"
|
|
450
|
-
? new TextEncoder().encode(content).length
|
|
451
|
-
: content.length;
|
|
452
|
-
const contentStr = typeof content === "string"
|
|
453
|
-
? content
|
|
454
|
-
: new TextDecoder().decode(content);
|
|
455
|
-
const etag = await generateContentHash(contentStr);
|
|
456
|
-
|
|
457
|
-
const ext = fileName.substring(fileName.lastIndexOf("."));
|
|
458
|
-
const mimeTypes: Record<string, string> = {
|
|
459
|
-
".js": "application/javascript",
|
|
460
|
-
".mjs": "application/javascript",
|
|
461
|
-
".css": "text/css",
|
|
462
|
-
".json": "application/json",
|
|
463
|
-
".html": "text/html",
|
|
464
|
-
".map": "application/json",
|
|
465
|
-
};
|
|
466
|
-
|
|
467
|
-
return {
|
|
468
|
-
type: mimeTypes[ext] ?? "application/octet-stream",
|
|
469
|
-
etag: `"${etag}"`,
|
|
470
|
-
mtime: buildTime,
|
|
471
|
-
size,
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
/**
|
|
476
|
-
* @param chunk - Rollup output chunk
|
|
477
|
-
* @returns Detected framework name
|
|
478
|
-
*/
|
|
479
|
-
function detectFrameworkFromChunk(chunk: {
|
|
480
|
-
facadeModuleId?: string | null;
|
|
481
|
-
code?: string;
|
|
482
|
-
}): string {
|
|
483
|
-
const filePath = chunk.facadeModuleId ?? "";
|
|
484
|
-
const content = chunk.code ?? "";
|
|
485
|
-
|
|
486
|
-
return detectIslandFramework(filePath, content);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* Loads an existing island manifest from disk
|
|
491
|
-
*
|
|
492
|
-
* @param manifestPath - Path to the manifest file
|
|
493
|
-
* @returns Loaded manifest or null if not found
|
|
494
|
-
*/
|
|
495
|
-
export async function loadIslandManifest(
|
|
496
|
-
manifestPath: string = "dist/island-manifest.json"
|
|
497
|
-
): Promise<BuildIslandManifest | null> {
|
|
498
|
-
try {
|
|
499
|
-
const content = await readFile(manifestPath, "utf-8");
|
|
500
|
-
return JSON.parse(content) as BuildIslandManifest;
|
|
501
|
-
} catch {
|
|
502
|
-
return null;
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* Gets the compiled asset path for an island
|
|
508
|
-
*
|
|
509
|
-
* @param islandName - Name of the island
|
|
510
|
-
* @param manifest - Island manifest
|
|
511
|
-
* @returns Compiled asset path or null if not found
|
|
512
|
-
*/
|
|
513
|
-
export function getIslandAssetPath(
|
|
514
|
-
islandName: string,
|
|
515
|
-
manifest: BuildIslandManifest | null
|
|
516
|
-
): string | null {
|
|
517
|
-
if (!manifest) return null;
|
|
518
|
-
|
|
519
|
-
const entry = manifest.islands[islandName];
|
|
520
|
-
return entry?.src ?? null;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
/**
|
|
524
|
-
* Gets all CSS assets that should be injected for a page
|
|
525
|
-
*
|
|
526
|
-
* @param islandNames - Names of islands used on the page
|
|
527
|
-
* @param manifest - Island manifest
|
|
528
|
-
* @returns Array of CSS asset paths
|
|
529
|
-
*/
|
|
530
|
-
export function getPageCssAssets(
|
|
531
|
-
islandNames: string[],
|
|
532
|
-
manifest: BuildIslandManifest | null
|
|
533
|
-
): string[] {
|
|
534
|
-
if (!manifest) return [];
|
|
535
|
-
|
|
536
|
-
const cssAssets = new Set<string>();
|
|
537
|
-
|
|
538
|
-
// Add global CSS
|
|
539
|
-
for (const css of manifest.css) {
|
|
540
|
-
cssAssets.add(css);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
// Add island-specific CSS
|
|
544
|
-
for (const name of islandNames) {
|
|
545
|
-
const entry = manifest.islands[name];
|
|
546
|
-
if (entry?.css) {
|
|
547
|
-
for (const css of entry.css) {
|
|
548
|
-
cssAssets.add(css);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
return Array.from(cssAssets);
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
/**
|
|
557
|
-
* Generates HTML preload tags for critical assets
|
|
558
|
-
*
|
|
559
|
-
* @param manifest - Island manifest
|
|
560
|
-
* @returns HTML string with preload tags
|
|
561
|
-
*/
|
|
562
|
-
export function generatePreloadTags(manifest: BuildIslandManifest): string {
|
|
563
|
-
const tags: string[] = [];
|
|
564
|
-
|
|
565
|
-
for (const hint of manifest.preloadHints) {
|
|
566
|
-
const attrs = [
|
|
567
|
-
`rel="preload"`,
|
|
568
|
-
`href="${hint.href}"`,
|
|
569
|
-
`as="${hint.as}"`,
|
|
570
|
-
];
|
|
571
|
-
|
|
572
|
-
if (hint.type) {
|
|
573
|
-
attrs.push(`type="${hint.type}"`);
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
if (hint.crossorigin) {
|
|
577
|
-
attrs.push(`crossorigin="${hint.crossorigin}"`);
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
tags.push(`<link ${attrs.join(" ")}>`);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
return tags.join("\n");
|
|
584
|
-
}
|