@useavalon/avalon 0.1.12 → 0.1.13
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/mod.ts +302 -0
- package/package.json +9 -17
- package/src/build/integration-bundler-plugin.ts +116 -0
- package/src/build/integration-config.ts +168 -0
- package/src/build/integration-detection-plugin.ts +117 -0
- package/src/build/integration-resolver-plugin.ts +90 -0
- package/src/build/island-manifest.ts +269 -0
- package/src/build/island-types-generator.ts +476 -0
- package/src/build/mdx-island-transform.ts +464 -0
- package/src/build/mdx-plugin.ts +98 -0
- package/src/build/page-island-transform.ts +598 -0
- package/src/build/prop-extractors/index.ts +21 -0
- package/src/build/prop-extractors/lit.ts +140 -0
- package/src/build/prop-extractors/qwik.ts +16 -0
- package/src/build/prop-extractors/solid.ts +125 -0
- package/src/build/prop-extractors/svelte.ts +194 -0
- package/src/build/prop-extractors/vue.ts +111 -0
- package/src/build/sidecar-file-manager.ts +104 -0
- package/src/build/sidecar-renderer.ts +30 -0
- package/src/client/adapters/index.ts +21 -0
- package/src/client/components.ts +35 -0
- package/src/client/css-hmr-handler.ts +344 -0
- package/src/client/framework-adapter.ts +462 -0
- package/src/client/hmr-coordinator.ts +396 -0
- package/src/client/hmr-error-overlay.js +533 -0
- package/src/client/main.js +824 -0
- package/src/components/Image.tsx +123 -0
- package/src/components/IslandErrorBoundary.tsx +145 -0
- package/src/components/LayoutDataErrorBoundary.tsx +141 -0
- package/src/components/LayoutErrorBoundary.tsx +127 -0
- package/src/components/PersistentIsland.tsx +52 -0
- package/src/components/StreamingErrorBoundary.tsx +233 -0
- package/src/components/StreamingLayout.tsx +538 -0
- package/src/core/components/component-analyzer.ts +192 -0
- package/src/core/components/component-detection.ts +508 -0
- package/src/core/components/enhanced-framework-detector.ts +500 -0
- package/src/core/components/framework-registry.ts +563 -0
- package/src/core/content/mdx-processor.ts +46 -0
- package/src/core/integrations/index.ts +19 -0
- package/src/core/integrations/loader.ts +125 -0
- package/src/core/integrations/registry.ts +175 -0
- package/src/core/islands/island-persistence.ts +325 -0
- package/src/core/islands/island-state-serializer.ts +258 -0
- package/src/core/islands/persistent-island-context.tsx +80 -0
- package/src/core/islands/use-persistent-state.ts +68 -0
- package/src/core/layout/enhanced-layout-resolver.ts +322 -0
- package/src/core/layout/layout-cache-manager.ts +485 -0
- package/src/core/layout/layout-composer.ts +357 -0
- package/src/core/layout/layout-data-loader.ts +516 -0
- package/src/core/layout/layout-discovery.ts +243 -0
- package/src/core/layout/layout-matcher.ts +299 -0
- package/src/core/layout/layout-types.ts +110 -0
- package/src/core/modules/framework-module-resolver.ts +273 -0
- package/src/islands/component-analysis.ts +213 -0
- package/src/islands/css-utils.ts +565 -0
- package/src/islands/discovery/index.ts +80 -0
- package/src/islands/discovery/registry.ts +340 -0
- package/src/islands/discovery/resolver.ts +477 -0
- package/src/islands/discovery/scanner.ts +386 -0
- package/src/islands/discovery/types.ts +117 -0
- package/src/islands/discovery/validator.ts +544 -0
- package/src/islands/discovery/watcher.ts +368 -0
- package/src/islands/framework-detection.ts +428 -0
- package/src/islands/integration-loader.ts +490 -0
- package/src/islands/island.tsx +565 -0
- package/src/islands/render-cache.ts +550 -0
- package/src/islands/types.ts +80 -0
- package/src/islands/universal-css-collector.ts +157 -0
- package/src/islands/universal-head-collector.ts +137 -0
- package/src/layout-system.ts +218 -0
- package/src/middleware/discovery.ts +268 -0
- package/src/middleware/executor.ts +315 -0
- package/src/middleware/index.ts +76 -0
- package/src/middleware/types.ts +99 -0
- package/src/nitro/build-config.ts +576 -0
- package/src/nitro/config.ts +483 -0
- package/src/nitro/error-handler.ts +636 -0
- package/src/nitro/index.ts +173 -0
- package/src/nitro/island-manifest.ts +584 -0
- package/src/nitro/middleware-adapter.ts +260 -0
- package/src/nitro/renderer.ts +1471 -0
- package/src/nitro/route-discovery.ts +439 -0
- package/src/nitro/types.ts +321 -0
- package/src/render/collect-css.ts +198 -0
- package/src/render/error-pages.ts +79 -0
- package/src/render/isolated-ssr-renderer.ts +654 -0
- package/src/render/ssr.ts +1030 -0
- package/src/schemas/api.ts +30 -0
- package/src/schemas/core.ts +64 -0
- package/src/schemas/index.ts +212 -0
- package/src/schemas/layout.ts +279 -0
- package/src/schemas/routing/index.ts +38 -0
- package/src/schemas/routing.ts +376 -0
- package/src/types/as-island.ts +20 -0
- package/src/types/layout.ts +285 -0
- package/src/types/routing.ts +555 -0
- package/src/types/types.ts +5 -0
- package/src/utils/dev-logger.ts +299 -0
- package/src/utils/fs.ts +151 -0
- package/src/vite-plugin/auto-discover.ts +551 -0
- package/src/vite-plugin/config.ts +266 -0
- package/src/vite-plugin/errors.ts +127 -0
- package/src/vite-plugin/image-optimization.ts +156 -0
- package/src/vite-plugin/integration-activator.ts +126 -0
- package/src/vite-plugin/island-sidecar-plugin.ts +176 -0
- package/src/vite-plugin/module-discovery.ts +189 -0
- package/src/vite-plugin/nitro-integration.ts +1354 -0
- package/src/vite-plugin/plugin.ts +403 -0
- package/src/vite-plugin/types.ts +327 -0
- package/src/vite-plugin/validation.ts +228 -0
- package/dist/mod.js +0 -1
- package/dist/src/build/integration-bundler-plugin.js +0 -1
- package/dist/src/build/integration-config.js +0 -1
- package/dist/src/build/integration-detection-plugin.js +0 -1
- package/dist/src/build/integration-resolver-plugin.js +0 -1
- package/dist/src/build/island-manifest.js +0 -1
- package/dist/src/build/island-types-generator.js +0 -5
- package/dist/src/build/mdx-island-transform.js +0 -2
- package/dist/src/build/mdx-plugin.js +0 -1
- package/dist/src/build/page-island-transform.js +0 -3
- package/dist/src/build/prop-extractors/index.js +0 -1
- package/dist/src/build/prop-extractors/lit.js +0 -1
- package/dist/src/build/prop-extractors/qwik.js +0 -1
- package/dist/src/build/prop-extractors/solid.js +0 -1
- package/dist/src/build/prop-extractors/svelte.js +0 -1
- package/dist/src/build/prop-extractors/vue.js +0 -1
- package/dist/src/build/sidecar-file-manager.js +0 -1
- package/dist/src/build/sidecar-renderer.js +0 -6
- package/dist/src/client/adapters/index.js +0 -1
- package/dist/src/client/components.js +0 -1
- package/dist/src/client/css-hmr-handler.js +0 -1
- package/dist/src/client/framework-adapter.js +0 -13
- package/dist/src/client/hmr-coordinator.js +0 -1
- package/dist/src/client/hmr-error-overlay.js +0 -214
- package/dist/src/client/main.js +0 -39
- package/dist/src/components/Image.js +0 -1
- package/dist/src/components/IslandErrorBoundary.js +0 -1
- package/dist/src/components/LayoutDataErrorBoundary.js +0 -1
- package/dist/src/components/LayoutErrorBoundary.js +0 -1
- package/dist/src/components/PersistentIsland.js +0 -1
- package/dist/src/components/StreamingErrorBoundary.js +0 -1
- package/dist/src/components/StreamingLayout.js +0 -29
- package/dist/src/core/components/component-analyzer.js +0 -1
- package/dist/src/core/components/component-detection.js +0 -5
- package/dist/src/core/components/enhanced-framework-detector.js +0 -1
- package/dist/src/core/components/framework-registry.js +0 -1
- package/dist/src/core/content/mdx-processor.js +0 -1
- package/dist/src/core/integrations/index.js +0 -1
- package/dist/src/core/integrations/loader.js +0 -1
- package/dist/src/core/integrations/registry.js +0 -1
- package/dist/src/core/islands/island-persistence.js +0 -1
- package/dist/src/core/islands/island-state-serializer.js +0 -1
- package/dist/src/core/islands/persistent-island-context.js +0 -1
- package/dist/src/core/islands/use-persistent-state.js +0 -1
- package/dist/src/core/layout/enhanced-layout-resolver.js +0 -1
- package/dist/src/core/layout/layout-cache-manager.js +0 -1
- package/dist/src/core/layout/layout-composer.js +0 -1
- package/dist/src/core/layout/layout-data-loader.js +0 -1
- package/dist/src/core/layout/layout-discovery.js +0 -1
- package/dist/src/core/layout/layout-matcher.js +0 -1
- package/dist/src/core/layout/layout-types.js +0 -1
- package/dist/src/core/modules/framework-module-resolver.js +0 -1
- package/dist/src/islands/component-analysis.js +0 -1
- package/dist/src/islands/css-utils.js +0 -17
- package/dist/src/islands/discovery/index.js +0 -1
- package/dist/src/islands/discovery/registry.js +0 -1
- package/dist/src/islands/discovery/resolver.js +0 -2
- package/dist/src/islands/discovery/scanner.js +0 -1
- package/dist/src/islands/discovery/types.js +0 -1
- package/dist/src/islands/discovery/validator.js +0 -18
- package/dist/src/islands/discovery/watcher.js +0 -1
- package/dist/src/islands/framework-detection.js +0 -1
- package/dist/src/islands/integration-loader.js +0 -1
- package/dist/src/islands/island.js +0 -1
- package/dist/src/islands/render-cache.js +0 -1
- package/dist/src/islands/types.js +0 -1
- package/dist/src/islands/universal-css-collector.js +0 -5
- package/dist/src/islands/universal-head-collector.js +0 -2
- package/dist/src/layout-system.js +0 -1
- package/dist/src/middleware/discovery.js +0 -1
- package/dist/src/middleware/executor.js +0 -1
- package/dist/src/middleware/index.js +0 -1
- package/dist/src/middleware/types.js +0 -1
- package/dist/src/nitro/build-config.js +0 -1
- package/dist/src/nitro/config.js +0 -1
- package/dist/src/nitro/error-handler.js +0 -198
- package/dist/src/nitro/index.js +0 -1
- package/dist/src/nitro/island-manifest.js +0 -2
- package/dist/src/nitro/middleware-adapter.js +0 -1
- package/dist/src/nitro/renderer.js +0 -183
- package/dist/src/nitro/route-discovery.js +0 -1
- package/dist/src/nitro/types.js +0 -1
- package/dist/src/render/collect-css.js +0 -3
- package/dist/src/render/error-pages.js +0 -48
- package/dist/src/render/isolated-ssr-renderer.js +0 -1
- package/dist/src/render/ssr.js +0 -90
- package/dist/src/schemas/api.js +0 -1
- package/dist/src/schemas/core.js +0 -1
- package/dist/src/schemas/index.js +0 -1
- package/dist/src/schemas/layout.js +0 -1
- package/dist/src/schemas/routing/index.js +0 -1
- package/dist/src/schemas/routing.js +0 -1
- package/dist/src/types/as-island.js +0 -1
- package/dist/src/types/layout.js +0 -1
- package/dist/src/types/routing.js +0 -1
- package/dist/src/types/types.js +0 -1
- package/dist/src/utils/dev-logger.js +0 -12
- package/dist/src/utils/fs.js +0 -1
- package/dist/src/vite-plugin/auto-discover.js +0 -1
- package/dist/src/vite-plugin/config.js +0 -1
- package/dist/src/vite-plugin/errors.js +0 -1
- package/dist/src/vite-plugin/image-optimization.js +0 -45
- package/dist/src/vite-plugin/integration-activator.js +0 -1
- package/dist/src/vite-plugin/island-sidecar-plugin.js +0 -1
- package/dist/src/vite-plugin/module-discovery.js +0 -1
- package/dist/src/vite-plugin/nitro-integration.js +0 -42
- package/dist/src/vite-plugin/plugin.js +0 -1
- package/dist/src/vite-plugin/types.js +0 -1
- package/dist/src/vite-plugin/validation.js +0 -2
- /package/{dist/src → src}/client/types/framework-runtime.d.ts +0 -0
- /package/{dist/src → src}/client/types/vite-hmr.d.ts +0 -0
- /package/{dist/src → src}/client/types/vite-virtual-modules.d.ts +0 -0
- /package/{dist/src → src}/layout-system.d.ts +0 -0
- /package/{dist/src → src}/types/image.d.ts +0 -0
- /package/{dist/src → src}/types/index.d.ts +0 -0
- /package/{dist/src → src}/types/island-jsx.d.ts +0 -0
- /package/{dist/src → src}/types/island-prop.d.ts +0 -0
- /package/{dist/src → src}/types/mdx.d.ts +0 -0
- /package/{dist/src → src}/types/urlpattern.d.ts +0 -0
- /package/{dist/src → src}/types/vite-env.d.ts +0 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Resolution for Avalon Vite Plugin
|
|
3
|
+
*
|
|
4
|
+
* This module provides default configuration values and the resolution
|
|
5
|
+
* function that merges user configuration with defaults.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
AvalonPluginConfig,
|
|
10
|
+
ResolvedAvalonConfig,
|
|
11
|
+
ResolvedMDXConfig,
|
|
12
|
+
ResolvedModulesConfig,
|
|
13
|
+
ResolvedImageConfig,
|
|
14
|
+
ModulesConfig,
|
|
15
|
+
ImageConfig,
|
|
16
|
+
} from "./types.ts";
|
|
17
|
+
import { existsSync } from "node:fs";
|
|
18
|
+
import { resolve } from "node:path";
|
|
19
|
+
import process from "node:process";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Default MDX configuration values
|
|
23
|
+
*/
|
|
24
|
+
export const DEFAULT_MDX_CONFIG: ResolvedMDXConfig = {
|
|
25
|
+
jsxImportSource: "preact",
|
|
26
|
+
syntaxHighlighting: true,
|
|
27
|
+
remarkPlugins: [],
|
|
28
|
+
rehypePlugins: [],
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Default image optimization configuration values
|
|
33
|
+
*/
|
|
34
|
+
export const DEFAULT_IMAGE_CONFIG: ResolvedImageConfig = {
|
|
35
|
+
enabled: true,
|
|
36
|
+
defaultFormat: "webp",
|
|
37
|
+
quality: 80,
|
|
38
|
+
widths: [200, 400, 600, 800, 1200],
|
|
39
|
+
removeMetadata: true,
|
|
40
|
+
include: /^[^?]+\.(heif|avif|jpeg|jpg|png|tiff|webp|gif)(\?.*)?$/,
|
|
41
|
+
exclude: "public/**/*",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Default configuration values for the Avalon plugin
|
|
46
|
+
* These are used when the user doesn't provide specific values
|
|
47
|
+
*/
|
|
48
|
+
export const DEFAULT_CONFIG: Omit<ResolvedAvalonConfig, "isDev"> = {
|
|
49
|
+
pagesDir: "src/pages",
|
|
50
|
+
layoutsDir: "src/layouts",
|
|
51
|
+
modules: null,
|
|
52
|
+
integrations: [],
|
|
53
|
+
mdx: DEFAULT_MDX_CONFIG,
|
|
54
|
+
image: DEFAULT_IMAGE_CONFIG,
|
|
55
|
+
verbose: false,
|
|
56
|
+
autoDiscoverIntegrations: true,
|
|
57
|
+
validateIntegrations: true,
|
|
58
|
+
showWarnings: true,
|
|
59
|
+
lazyIntegrations: true,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Default modules configuration values
|
|
64
|
+
*/
|
|
65
|
+
export const DEFAULT_MODULES_CONFIG = {
|
|
66
|
+
pagesDirName: "pages",
|
|
67
|
+
layoutsDirName: "layouts",
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Resolves the modules configuration
|
|
72
|
+
*/
|
|
73
|
+
function resolveModulesConfig(
|
|
74
|
+
modules: string | ModulesConfig | undefined
|
|
75
|
+
): ResolvedModulesConfig | null {
|
|
76
|
+
if (!modules) return null;
|
|
77
|
+
|
|
78
|
+
if (typeof modules === "string") {
|
|
79
|
+
return {
|
|
80
|
+
dir: modules,
|
|
81
|
+
pagesDirName: DEFAULT_MODULES_CONFIG.pagesDirName,
|
|
82
|
+
layoutsDirName: DEFAULT_MODULES_CONFIG.layoutsDirName,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
dir: modules.dir,
|
|
88
|
+
pagesDirName: modules.pagesDirName ?? DEFAULT_MODULES_CONFIG.pagesDirName,
|
|
89
|
+
layoutsDirName: modules.layoutsDirName ?? DEFAULT_MODULES_CONFIG.layoutsDirName,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Resolves the image optimization configuration
|
|
95
|
+
*/
|
|
96
|
+
function resolveImageConfig(
|
|
97
|
+
image: boolean | ImageConfig | undefined
|
|
98
|
+
): ResolvedImageConfig {
|
|
99
|
+
// Explicitly disabled
|
|
100
|
+
if (image === false) {
|
|
101
|
+
return { ...DEFAULT_IMAGE_CONFIG, enabled: false };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Default or explicitly enabled with no options
|
|
105
|
+
if (image === undefined || image === true) {
|
|
106
|
+
return DEFAULT_IMAGE_CONFIG;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Custom config object
|
|
110
|
+
return {
|
|
111
|
+
enabled: image.enabled ?? DEFAULT_IMAGE_CONFIG.enabled,
|
|
112
|
+
defaultFormat: image.defaultFormat ?? DEFAULT_IMAGE_CONFIG.defaultFormat,
|
|
113
|
+
quality: image.quality ?? DEFAULT_IMAGE_CONFIG.quality,
|
|
114
|
+
widths: image.widths ?? DEFAULT_IMAGE_CONFIG.widths,
|
|
115
|
+
removeMetadata: image.removeMetadata ?? DEFAULT_IMAGE_CONFIG.removeMetadata,
|
|
116
|
+
include: image.include ?? DEFAULT_IMAGE_CONFIG.include,
|
|
117
|
+
exclude: image.exclude ?? DEFAULT_IMAGE_CONFIG.exclude,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Resolves user configuration by merging with defaults
|
|
123
|
+
*
|
|
124
|
+
* @param userConfig - Partial configuration provided by the user
|
|
125
|
+
* @param isDev - Whether the application is running in development mode
|
|
126
|
+
* @returns Fully resolved configuration with all defaults applied
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* const resolved = resolveConfig({ integrations: ["react"] }, true);
|
|
131
|
+
* // resolved.pagesDir === "src/pages" (default)
|
|
132
|
+
* // resolved.integrations === ["react"] (user provided)
|
|
133
|
+
* // resolved.isDev === true
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export function resolveConfig(
|
|
137
|
+
userConfig: AvalonPluginConfig | undefined,
|
|
138
|
+
isDev: boolean
|
|
139
|
+
): ResolvedAvalonConfig {
|
|
140
|
+
const config = userConfig ?? {};
|
|
141
|
+
const modules = resolveModulesConfig(config.modules);
|
|
142
|
+
const image = resolveImageConfig(config.image);
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
pagesDir: config.pagesDir ?? DEFAULT_CONFIG.pagesDir,
|
|
146
|
+
layoutsDir: config.layoutsDir ?? DEFAULT_CONFIG.layoutsDir,
|
|
147
|
+
modules,
|
|
148
|
+
integrations: config.integrations ?? DEFAULT_CONFIG.integrations,
|
|
149
|
+
mdx: {
|
|
150
|
+
jsxImportSource:
|
|
151
|
+
config.mdx?.jsxImportSource ?? DEFAULT_MDX_CONFIG.jsxImportSource,
|
|
152
|
+
syntaxHighlighting:
|
|
153
|
+
config.mdx?.syntaxHighlighting ?? DEFAULT_MDX_CONFIG.syntaxHighlighting,
|
|
154
|
+
remarkPlugins:
|
|
155
|
+
config.mdx?.remarkPlugins ?? DEFAULT_MDX_CONFIG.remarkPlugins,
|
|
156
|
+
rehypePlugins:
|
|
157
|
+
config.mdx?.rehypePlugins ?? DEFAULT_MDX_CONFIG.rehypePlugins,
|
|
158
|
+
},
|
|
159
|
+
image,
|
|
160
|
+
verbose: config.verbose ?? DEFAULT_CONFIG.verbose,
|
|
161
|
+
autoDiscoverIntegrations:
|
|
162
|
+
config.autoDiscoverIntegrations ?? DEFAULT_CONFIG.autoDiscoverIntegrations,
|
|
163
|
+
validateIntegrations:
|
|
164
|
+
config.validateIntegrations ?? DEFAULT_CONFIG.validateIntegrations,
|
|
165
|
+
showWarnings: config.showWarnings ?? DEFAULT_CONFIG.showWarnings,
|
|
166
|
+
lazyIntegrations: config.lazyIntegrations ?? DEFAULT_CONFIG.lazyIntegrations,
|
|
167
|
+
isDev,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Result of directory existence check
|
|
174
|
+
*/
|
|
175
|
+
export interface DirectoryCheckResult {
|
|
176
|
+
/** The directory path that was checked */
|
|
177
|
+
path: string;
|
|
178
|
+
/** The resolved absolute path */
|
|
179
|
+
absolutePath: string;
|
|
180
|
+
/** Whether the directory exists */
|
|
181
|
+
exists: boolean;
|
|
182
|
+
/** The type of directory (pages, layouts) */
|
|
183
|
+
type: "pages" | "layouts";
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Check if configured directories exist and log warnings for missing ones
|
|
188
|
+
*
|
|
189
|
+
* This function checks if the configured directories (pagesDir, layoutsDir)
|
|
190
|
+
* exist on the filesystem. If a directory doesn't exist, it logs a warning but
|
|
191
|
+
* does NOT throw an error, allowing the application to continue.
|
|
192
|
+
*
|
|
193
|
+
* @param config - The resolved Avalon configuration
|
|
194
|
+
* @param projectRoot - The root directory of the project (defaults to process.cwd())
|
|
195
|
+
* @returns Array of directory check results
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```ts
|
|
199
|
+
* const results = checkDirectoriesExist(resolvedConfig, '/path/to/project');
|
|
200
|
+
* // Logs warnings for any missing directories
|
|
201
|
+
* // Returns results for programmatic access
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
export function checkDirectoriesExist(
|
|
205
|
+
config: ResolvedAvalonConfig,
|
|
206
|
+
projectRoot: string = process.cwd()
|
|
207
|
+
): DirectoryCheckResult[] {
|
|
208
|
+
const directories: Array<{ path: string; type: DirectoryCheckResult["type"] }> = [];
|
|
209
|
+
|
|
210
|
+
// Only check pagesDir if modules is not configured (traditional architecture)
|
|
211
|
+
// When using modular architecture, pages are discovered from modules
|
|
212
|
+
if (!config.modules && config.pagesDir) {
|
|
213
|
+
directories.push({ path: config.pagesDir, type: "pages" });
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Always check layoutsDir if it's set
|
|
217
|
+
if (config.layoutsDir) {
|
|
218
|
+
directories.push({ path: config.layoutsDir, type: "layouts" });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const results: DirectoryCheckResult[] = [];
|
|
222
|
+
|
|
223
|
+
for (const { path, type } of directories) {
|
|
224
|
+
const absolutePath = resolve(projectRoot, path);
|
|
225
|
+
const exists = existsSync(absolutePath);
|
|
226
|
+
|
|
227
|
+
results.push({
|
|
228
|
+
path,
|
|
229
|
+
absolutePath,
|
|
230
|
+
exists,
|
|
231
|
+
type,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
if (!exists && config.showWarnings) {
|
|
235
|
+
console.warn(
|
|
236
|
+
`⚠️ Avalon: ${type} directory '${path}' does not exist (resolved to: ${absolutePath}). ` +
|
|
237
|
+
`This directory will be skipped.`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return results;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Log a summary of directory check results
|
|
247
|
+
*
|
|
248
|
+
* @param results - The directory check results
|
|
249
|
+
* @param verbose - Whether to log verbose output
|
|
250
|
+
*/
|
|
251
|
+
export function logDirectoryCheckSummary(
|
|
252
|
+
results: DirectoryCheckResult[],
|
|
253
|
+
verbose: boolean
|
|
254
|
+
): void {
|
|
255
|
+
const missing = results.filter((r) => !r.exists);
|
|
256
|
+
const existing = results.filter((r) => r.exists);
|
|
257
|
+
|
|
258
|
+
if (verbose) {
|
|
259
|
+
if (existing.length > 0) {
|
|
260
|
+
console.log(` ✅ Found directories: ${existing.map((r) => r.path).join(", ")}`);
|
|
261
|
+
}
|
|
262
|
+
if (missing.length > 0) {
|
|
263
|
+
console.log(` ⚠️ Missing directories: ${missing.map((r) => r.path).join(", ")}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Classes for Avalon Vite Plugin
|
|
3
|
+
*
|
|
4
|
+
* This module provides custom error classes for configuration and integration errors.
|
|
5
|
+
* These errors include contextual information to help developers quickly identify
|
|
6
|
+
* and fix issues.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Thrown when configuration is invalid
|
|
11
|
+
*
|
|
12
|
+
* This error includes the specific field name and value that caused the error,
|
|
13
|
+
* making it easier to identify and fix configuration issues.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* throw new AvalonConfigError(
|
|
18
|
+
* "must be a valid directory path",
|
|
19
|
+
* "islandsDir",
|
|
20
|
+
* 123 // invalid value - should be a string
|
|
21
|
+
* );
|
|
22
|
+
* // Error: Avalon configuration error in 'islandsDir': must be a valid directory path
|
|
23
|
+
* // Received value: 123
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export class AvalonConfigError extends Error {
|
|
27
|
+
/**
|
|
28
|
+
* The name of the configuration field that caused the error
|
|
29
|
+
*/
|
|
30
|
+
public readonly field: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The invalid value that was provided
|
|
34
|
+
*/
|
|
35
|
+
public readonly value: unknown;
|
|
36
|
+
|
|
37
|
+
constructor(message: string, field: string, value: unknown) {
|
|
38
|
+
const valueStr = formatValue(value);
|
|
39
|
+
super(`Avalon configuration error in '${field}': ${message}\nReceived value: ${valueStr}`);
|
|
40
|
+
this.name = "AvalonConfigError";
|
|
41
|
+
this.field = field;
|
|
42
|
+
this.value = value;
|
|
43
|
+
|
|
44
|
+
// Maintains proper stack trace for where error was thrown (V8 engines)
|
|
45
|
+
if (Error.captureStackTrace) {
|
|
46
|
+
Error.captureStackTrace(this, AvalonConfigError);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Thrown when an integration fails to load or has an invalid name
|
|
53
|
+
*
|
|
54
|
+
* This error includes the integration name and optionally the underlying cause,
|
|
55
|
+
* making it easier to diagnose integration loading issues.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* throw new IntegrationError(
|
|
60
|
+
* "Failed to activate integration. Is @useavalon/react installed?",
|
|
61
|
+
* "react",
|
|
62
|
+
* originalError
|
|
63
|
+
* );
|
|
64
|
+
* // Error: Integration 'react': Failed to activate integration. Is @useavalon/react installed?
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export class IntegrationError extends Error {
|
|
68
|
+
/**
|
|
69
|
+
* The name of the integration that caused the error
|
|
70
|
+
*/
|
|
71
|
+
public readonly integrationName: string;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The original error that caused this error, if any
|
|
75
|
+
*/
|
|
76
|
+
public readonly originalCause?: Error;
|
|
77
|
+
|
|
78
|
+
constructor(message: string, integrationName: string, originalCause?: Error) {
|
|
79
|
+
super(`Integration '${integrationName}': ${message}`, { cause: originalCause });
|
|
80
|
+
this.name = "IntegrationError";
|
|
81
|
+
this.integrationName = integrationName;
|
|
82
|
+
this.originalCause = originalCause;
|
|
83
|
+
|
|
84
|
+
// Maintains proper stack trace for where error was thrown (V8 engines)
|
|
85
|
+
if (Error.captureStackTrace) {
|
|
86
|
+
Error.captureStackTrace(this, IntegrationError);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Format a value for display in error messages
|
|
93
|
+
*
|
|
94
|
+
* @param value - The value to format
|
|
95
|
+
* @returns A string representation of the value
|
|
96
|
+
*/
|
|
97
|
+
function formatValue(value: unknown): string {
|
|
98
|
+
if (value === undefined) {
|
|
99
|
+
return "undefined";
|
|
100
|
+
}
|
|
101
|
+
if (value === null) {
|
|
102
|
+
return "null";
|
|
103
|
+
}
|
|
104
|
+
if (typeof value === "string") {
|
|
105
|
+
return `"${value}"`;
|
|
106
|
+
}
|
|
107
|
+
if (typeof value === "function") {
|
|
108
|
+
return "[Function]";
|
|
109
|
+
}
|
|
110
|
+
if (Array.isArray(value)) {
|
|
111
|
+
if (value.length === 0) {
|
|
112
|
+
return "[]";
|
|
113
|
+
}
|
|
114
|
+
if (value.length <= 3) {
|
|
115
|
+
return `[${value.map(formatValue).join(", ")}]`;
|
|
116
|
+
}
|
|
117
|
+
return `[${value.slice(0, 3).map(formatValue).join(", ")}, ... (${value.length} items)]`;
|
|
118
|
+
}
|
|
119
|
+
if (typeof value === "object") {
|
|
120
|
+
try {
|
|
121
|
+
return JSON.stringify(value);
|
|
122
|
+
} catch {
|
|
123
|
+
return "[Circular]";
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return String(value as string | number | boolean | bigint | symbol);
|
|
127
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Optimization Plugin for Avalon
|
|
3
|
+
*
|
|
4
|
+
* Wraps vite-imagetools to provide automatic image optimization with
|
|
5
|
+
* sensible defaults for responsive images, modern formats, and srcset generation.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Automatic WebP/AVIF conversion
|
|
9
|
+
* - Responsive srcset generation at multiple breakpoints
|
|
10
|
+
* - Metadata stripping for privacy
|
|
11
|
+
* - JSX component output for easy usage in islands
|
|
12
|
+
*
|
|
13
|
+
* Usage in components:
|
|
14
|
+
* ```tsx
|
|
15
|
+
* // Basic - returns optimized URL
|
|
16
|
+
* import heroImg from './hero.jpg?w=800&format=webp';
|
|
17
|
+
*
|
|
18
|
+
* // With srcset for responsive images
|
|
19
|
+
* import heroImg from './hero.jpg?w=400;800;1200&format=webp&as=srcset';
|
|
20
|
+
*
|
|
21
|
+
* // As JSX component (like Qwik's ?jsx)
|
|
22
|
+
* import HeroImage from './hero.jpg?w=1200&jsx';
|
|
23
|
+
* <HeroImage alt="Hero" />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import type { Plugin } from "vite";
|
|
28
|
+
import type { ResolvedImageConfig } from "./types.ts";
|
|
29
|
+
import { createRequire } from "node:module";
|
|
30
|
+
import { join } from "node:path";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Creates the vite-imagetools plugin with Avalon's configuration
|
|
34
|
+
*/
|
|
35
|
+
export async function createImagePlugin(
|
|
36
|
+
config: ResolvedImageConfig,
|
|
37
|
+
verbose: boolean
|
|
38
|
+
): Promise<Plugin[]> {
|
|
39
|
+
if (!config.enabled) {
|
|
40
|
+
if (verbose) {
|
|
41
|
+
console.log(" ⏭️ Image optimization disabled");
|
|
42
|
+
}
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
// Dynamic import to avoid hard dependency if user disables images.
|
|
48
|
+
// Use createRequire from the project root so we resolve the package
|
|
49
|
+
// from the consuming project's node_modules, not avalon's own context.
|
|
50
|
+
const require = createRequire(join(process.cwd(), 'package.json'));
|
|
51
|
+
const { imagetools } = require("vite-imagetools");
|
|
52
|
+
|
|
53
|
+
if (verbose) {
|
|
54
|
+
console.log(" 🖼️ Image optimization enabled");
|
|
55
|
+
console.log(` Format: ${config.defaultFormat}`);
|
|
56
|
+
console.log(` Quality: ${config.quality}`);
|
|
57
|
+
console.log(` Widths: ${config.widths.join(", ")}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const plugin = imagetools({
|
|
61
|
+
include: config.include,
|
|
62
|
+
exclude: config.exclude,
|
|
63
|
+
removeMetadata: config.removeMetadata,
|
|
64
|
+
|
|
65
|
+
// Default directives applied to all images unless overridden
|
|
66
|
+
defaultDirectives: (url) => {
|
|
67
|
+
const params = new URLSearchParams();
|
|
68
|
+
|
|
69
|
+
// Only apply defaults if no format specified
|
|
70
|
+
if (!url.searchParams.has("format")) {
|
|
71
|
+
params.set("format", config.defaultFormat);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Only apply quality if not specified
|
|
75
|
+
if (!url.searchParams.has("quality")) {
|
|
76
|
+
params.set("quality", String(config.quality));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// If ?jsx is used, generate responsive srcset like Qwik
|
|
80
|
+
if (url.searchParams.has("jsx")) {
|
|
81
|
+
// Generate widths for srcset if not specified
|
|
82
|
+
if (!url.searchParams.has("w") && !url.searchParams.has("width")) {
|
|
83
|
+
params.set("w", config.widths.join(";"));
|
|
84
|
+
}
|
|
85
|
+
params.set("as", "picture");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return params;
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return [plugin as Plugin];
|
|
93
|
+
} catch (error) {
|
|
94
|
+
// vite-imagetools not installed
|
|
95
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
96
|
+
|
|
97
|
+
if (message.includes("Cannot find package") || message.includes("MODULE_NOT_FOUND")) {
|
|
98
|
+
console.warn(
|
|
99
|
+
"⚠️ Avalon: Image optimization is enabled but vite-imagetools is not installed.\n" +
|
|
100
|
+
" Install it with: bun add -d vite-imagetools\n" +
|
|
101
|
+
" Or disable image optimization: image: false"
|
|
102
|
+
);
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Type declarations for image imports
|
|
112
|
+
* Users can reference these in their tsconfig.json
|
|
113
|
+
*/
|
|
114
|
+
export const IMAGE_TYPES_DECLARATION = `
|
|
115
|
+
declare module '*.jpg' {
|
|
116
|
+
const src: string;
|
|
117
|
+
export default src;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
declare module '*.jpeg' {
|
|
121
|
+
const src: string;
|
|
122
|
+
export default src;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
declare module '*.png' {
|
|
126
|
+
const src: string;
|
|
127
|
+
export default src;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
declare module '*.webp' {
|
|
131
|
+
const src: string;
|
|
132
|
+
export default src;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
declare module '*.avif' {
|
|
136
|
+
const src: string;
|
|
137
|
+
export default src;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
declare module '*.gif' {
|
|
141
|
+
const src: string;
|
|
142
|
+
export default src;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
declare module '*?jsx' {
|
|
146
|
+
import type { ComponentType } from 'preact';
|
|
147
|
+
const Component: ComponentType<{ alt: string; class?: string; style?: Record<string, string> }>;
|
|
148
|
+
export default Component;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
declare module '*&jsx' {
|
|
152
|
+
import type { ComponentType } from 'preact';
|
|
153
|
+
const Component: ComponentType<{ alt: string; class?: string; style?: Record<string, string> }>;
|
|
154
|
+
export default Component;
|
|
155
|
+
}
|
|
156
|
+
`;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Activator for Avalon Vite Plugin
|
|
3
|
+
*
|
|
4
|
+
* This module handles the activation of framework integrations based on
|
|
5
|
+
* the plugin configuration. It validates integration names and loads
|
|
6
|
+
* the appropriate integration packages.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { IntegrationName, ResolvedAvalonConfig } from "./types.ts";
|
|
10
|
+
import { loadIntegration } from "../islands/integration-loader.ts";
|
|
11
|
+
import { IntegrationError } from "./errors.ts";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Valid integration names that can be activated
|
|
15
|
+
* This array is used for validation and error messages
|
|
16
|
+
*/
|
|
17
|
+
export const VALID_INTEGRATION_NAMES: readonly IntegrationName[] = [
|
|
18
|
+
"react",
|
|
19
|
+
"preact",
|
|
20
|
+
"vue",
|
|
21
|
+
"svelte",
|
|
22
|
+
"solid",
|
|
23
|
+
"lit",
|
|
24
|
+
"qwik",
|
|
25
|
+
] as const;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if a string is a valid integration name
|
|
29
|
+
*
|
|
30
|
+
* @param name - The name to validate
|
|
31
|
+
* @returns True if the name is a valid IntegrationName
|
|
32
|
+
*/
|
|
33
|
+
export function isValidIntegrationName(name: string): name is IntegrationName {
|
|
34
|
+
return VALID_INTEGRATION_NAMES.includes(name as IntegrationName);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Activates the specified integrations
|
|
39
|
+
*
|
|
40
|
+
* Uses the existing integration loader and registry to load and activate
|
|
41
|
+
* framework integrations based on the configuration.
|
|
42
|
+
*
|
|
43
|
+
* @param config - The resolved Avalon configuration
|
|
44
|
+
* @param activeIntegrations - Set to track which integrations have been activated
|
|
45
|
+
* @throws IntegrationError if an invalid integration name is provided or loading fails
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* const activeIntegrations = new Set<IntegrationName>();
|
|
50
|
+
* await activateIntegrations(resolvedConfig, activeIntegrations);
|
|
51
|
+
* // activeIntegrations now contains all successfully loaded integrations
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export async function activateIntegrations(
|
|
55
|
+
config: ResolvedAvalonConfig,
|
|
56
|
+
activeIntegrations: Set<IntegrationName>
|
|
57
|
+
): Promise<void> {
|
|
58
|
+
const { integrations } = config;
|
|
59
|
+
|
|
60
|
+
// Load explicitly specified integrations
|
|
61
|
+
for (const name of integrations) {
|
|
62
|
+
// Validate integration name
|
|
63
|
+
if (!isValidIntegrationName(name)) {
|
|
64
|
+
throw new IntegrationError(
|
|
65
|
+
`Invalid integration name '${name}'. Valid integration names are: ${VALID_INTEGRATION_NAMES.join(", ")}`,
|
|
66
|
+
name
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Skip if already activated
|
|
71
|
+
if (activeIntegrations.has(name)) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
await loadIntegration(name);
|
|
77
|
+
activeIntegrations.add(name);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
throw new IntegrationError(
|
|
80
|
+
`Failed to activate integration. Is @useavalon/${name} installed?`,
|
|
81
|
+
name,
|
|
82
|
+
error as Error
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Activate a single integration by name
|
|
90
|
+
*
|
|
91
|
+
* @param name - The integration name to activate
|
|
92
|
+
* @param activeIntegrations - Set to track which integrations have been activated
|
|
93
|
+
* @param verbose - Whether to log activation messages
|
|
94
|
+
* @returns True if the integration was activated, false if already active
|
|
95
|
+
* @throws IntegrationError if the name is invalid or loading fails
|
|
96
|
+
*/
|
|
97
|
+
export async function activateSingleIntegration(
|
|
98
|
+
name: string,
|
|
99
|
+
activeIntegrations: Set<IntegrationName>,
|
|
100
|
+
_verbose: boolean = false
|
|
101
|
+
): Promise<boolean> {
|
|
102
|
+
// Validate integration name
|
|
103
|
+
if (!isValidIntegrationName(name)) {
|
|
104
|
+
throw new IntegrationError(
|
|
105
|
+
`Invalid integration name '${name}'. Valid integration names are: ${VALID_INTEGRATION_NAMES.join(", ")}`,
|
|
106
|
+
name
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Skip if already activated
|
|
111
|
+
if (activeIntegrations.has(name)) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
await loadIntegration(name);
|
|
117
|
+
activeIntegrations.add(name);
|
|
118
|
+
return true;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
throw new IntegrationError(
|
|
121
|
+
`Failed to activate integration. Is @useavalon/${name} installed?`,
|
|
122
|
+
name,
|
|
123
|
+
error as Error
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|