@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.
Files changed (230) hide show
  1. package/mod.ts +302 -0
  2. package/package.json +9 -17
  3. package/src/build/integration-bundler-plugin.ts +116 -0
  4. package/src/build/integration-config.ts +168 -0
  5. package/src/build/integration-detection-plugin.ts +117 -0
  6. package/src/build/integration-resolver-plugin.ts +90 -0
  7. package/src/build/island-manifest.ts +269 -0
  8. package/src/build/island-types-generator.ts +476 -0
  9. package/src/build/mdx-island-transform.ts +464 -0
  10. package/src/build/mdx-plugin.ts +98 -0
  11. package/src/build/page-island-transform.ts +598 -0
  12. package/src/build/prop-extractors/index.ts +21 -0
  13. package/src/build/prop-extractors/lit.ts +140 -0
  14. package/src/build/prop-extractors/qwik.ts +16 -0
  15. package/src/build/prop-extractors/solid.ts +125 -0
  16. package/src/build/prop-extractors/svelte.ts +194 -0
  17. package/src/build/prop-extractors/vue.ts +111 -0
  18. package/src/build/sidecar-file-manager.ts +104 -0
  19. package/src/build/sidecar-renderer.ts +30 -0
  20. package/src/client/adapters/index.ts +21 -0
  21. package/src/client/components.ts +35 -0
  22. package/src/client/css-hmr-handler.ts +344 -0
  23. package/src/client/framework-adapter.ts +462 -0
  24. package/src/client/hmr-coordinator.ts +396 -0
  25. package/src/client/hmr-error-overlay.js +533 -0
  26. package/src/client/main.js +824 -0
  27. package/src/components/Image.tsx +123 -0
  28. package/src/components/IslandErrorBoundary.tsx +145 -0
  29. package/src/components/LayoutDataErrorBoundary.tsx +141 -0
  30. package/src/components/LayoutErrorBoundary.tsx +127 -0
  31. package/src/components/PersistentIsland.tsx +52 -0
  32. package/src/components/StreamingErrorBoundary.tsx +233 -0
  33. package/src/components/StreamingLayout.tsx +538 -0
  34. package/src/core/components/component-analyzer.ts +192 -0
  35. package/src/core/components/component-detection.ts +508 -0
  36. package/src/core/components/enhanced-framework-detector.ts +500 -0
  37. package/src/core/components/framework-registry.ts +563 -0
  38. package/src/core/content/mdx-processor.ts +46 -0
  39. package/src/core/integrations/index.ts +19 -0
  40. package/src/core/integrations/loader.ts +125 -0
  41. package/src/core/integrations/registry.ts +175 -0
  42. package/src/core/islands/island-persistence.ts +325 -0
  43. package/src/core/islands/island-state-serializer.ts +258 -0
  44. package/src/core/islands/persistent-island-context.tsx +80 -0
  45. package/src/core/islands/use-persistent-state.ts +68 -0
  46. package/src/core/layout/enhanced-layout-resolver.ts +322 -0
  47. package/src/core/layout/layout-cache-manager.ts +485 -0
  48. package/src/core/layout/layout-composer.ts +357 -0
  49. package/src/core/layout/layout-data-loader.ts +516 -0
  50. package/src/core/layout/layout-discovery.ts +243 -0
  51. package/src/core/layout/layout-matcher.ts +299 -0
  52. package/src/core/layout/layout-types.ts +110 -0
  53. package/src/core/modules/framework-module-resolver.ts +273 -0
  54. package/src/islands/component-analysis.ts +213 -0
  55. package/src/islands/css-utils.ts +565 -0
  56. package/src/islands/discovery/index.ts +80 -0
  57. package/src/islands/discovery/registry.ts +340 -0
  58. package/src/islands/discovery/resolver.ts +477 -0
  59. package/src/islands/discovery/scanner.ts +386 -0
  60. package/src/islands/discovery/types.ts +117 -0
  61. package/src/islands/discovery/validator.ts +544 -0
  62. package/src/islands/discovery/watcher.ts +368 -0
  63. package/src/islands/framework-detection.ts +428 -0
  64. package/src/islands/integration-loader.ts +490 -0
  65. package/src/islands/island.tsx +565 -0
  66. package/src/islands/render-cache.ts +550 -0
  67. package/src/islands/types.ts +80 -0
  68. package/src/islands/universal-css-collector.ts +157 -0
  69. package/src/islands/universal-head-collector.ts +137 -0
  70. package/src/layout-system.ts +218 -0
  71. package/src/middleware/discovery.ts +268 -0
  72. package/src/middleware/executor.ts +315 -0
  73. package/src/middleware/index.ts +76 -0
  74. package/src/middleware/types.ts +99 -0
  75. package/src/nitro/build-config.ts +576 -0
  76. package/src/nitro/config.ts +483 -0
  77. package/src/nitro/error-handler.ts +636 -0
  78. package/src/nitro/index.ts +173 -0
  79. package/src/nitro/island-manifest.ts +584 -0
  80. package/src/nitro/middleware-adapter.ts +260 -0
  81. package/src/nitro/renderer.ts +1471 -0
  82. package/src/nitro/route-discovery.ts +439 -0
  83. package/src/nitro/types.ts +321 -0
  84. package/src/render/collect-css.ts +198 -0
  85. package/src/render/error-pages.ts +79 -0
  86. package/src/render/isolated-ssr-renderer.ts +654 -0
  87. package/src/render/ssr.ts +1030 -0
  88. package/src/schemas/api.ts +30 -0
  89. package/src/schemas/core.ts +64 -0
  90. package/src/schemas/index.ts +212 -0
  91. package/src/schemas/layout.ts +279 -0
  92. package/src/schemas/routing/index.ts +38 -0
  93. package/src/schemas/routing.ts +376 -0
  94. package/src/types/as-island.ts +20 -0
  95. package/src/types/layout.ts +285 -0
  96. package/src/types/routing.ts +555 -0
  97. package/src/types/types.ts +5 -0
  98. package/src/utils/dev-logger.ts +299 -0
  99. package/src/utils/fs.ts +151 -0
  100. package/src/vite-plugin/auto-discover.ts +551 -0
  101. package/src/vite-plugin/config.ts +266 -0
  102. package/src/vite-plugin/errors.ts +127 -0
  103. package/src/vite-plugin/image-optimization.ts +156 -0
  104. package/src/vite-plugin/integration-activator.ts +126 -0
  105. package/src/vite-plugin/island-sidecar-plugin.ts +176 -0
  106. package/src/vite-plugin/module-discovery.ts +189 -0
  107. package/src/vite-plugin/nitro-integration.ts +1354 -0
  108. package/src/vite-plugin/plugin.ts +403 -0
  109. package/src/vite-plugin/types.ts +327 -0
  110. package/src/vite-plugin/validation.ts +228 -0
  111. package/dist/mod.js +0 -1
  112. package/dist/src/build/integration-bundler-plugin.js +0 -1
  113. package/dist/src/build/integration-config.js +0 -1
  114. package/dist/src/build/integration-detection-plugin.js +0 -1
  115. package/dist/src/build/integration-resolver-plugin.js +0 -1
  116. package/dist/src/build/island-manifest.js +0 -1
  117. package/dist/src/build/island-types-generator.js +0 -5
  118. package/dist/src/build/mdx-island-transform.js +0 -2
  119. package/dist/src/build/mdx-plugin.js +0 -1
  120. package/dist/src/build/page-island-transform.js +0 -3
  121. package/dist/src/build/prop-extractors/index.js +0 -1
  122. package/dist/src/build/prop-extractors/lit.js +0 -1
  123. package/dist/src/build/prop-extractors/qwik.js +0 -1
  124. package/dist/src/build/prop-extractors/solid.js +0 -1
  125. package/dist/src/build/prop-extractors/svelte.js +0 -1
  126. package/dist/src/build/prop-extractors/vue.js +0 -1
  127. package/dist/src/build/sidecar-file-manager.js +0 -1
  128. package/dist/src/build/sidecar-renderer.js +0 -6
  129. package/dist/src/client/adapters/index.js +0 -1
  130. package/dist/src/client/components.js +0 -1
  131. package/dist/src/client/css-hmr-handler.js +0 -1
  132. package/dist/src/client/framework-adapter.js +0 -13
  133. package/dist/src/client/hmr-coordinator.js +0 -1
  134. package/dist/src/client/hmr-error-overlay.js +0 -214
  135. package/dist/src/client/main.js +0 -39
  136. package/dist/src/components/Image.js +0 -1
  137. package/dist/src/components/IslandErrorBoundary.js +0 -1
  138. package/dist/src/components/LayoutDataErrorBoundary.js +0 -1
  139. package/dist/src/components/LayoutErrorBoundary.js +0 -1
  140. package/dist/src/components/PersistentIsland.js +0 -1
  141. package/dist/src/components/StreamingErrorBoundary.js +0 -1
  142. package/dist/src/components/StreamingLayout.js +0 -29
  143. package/dist/src/core/components/component-analyzer.js +0 -1
  144. package/dist/src/core/components/component-detection.js +0 -5
  145. package/dist/src/core/components/enhanced-framework-detector.js +0 -1
  146. package/dist/src/core/components/framework-registry.js +0 -1
  147. package/dist/src/core/content/mdx-processor.js +0 -1
  148. package/dist/src/core/integrations/index.js +0 -1
  149. package/dist/src/core/integrations/loader.js +0 -1
  150. package/dist/src/core/integrations/registry.js +0 -1
  151. package/dist/src/core/islands/island-persistence.js +0 -1
  152. package/dist/src/core/islands/island-state-serializer.js +0 -1
  153. package/dist/src/core/islands/persistent-island-context.js +0 -1
  154. package/dist/src/core/islands/use-persistent-state.js +0 -1
  155. package/dist/src/core/layout/enhanced-layout-resolver.js +0 -1
  156. package/dist/src/core/layout/layout-cache-manager.js +0 -1
  157. package/dist/src/core/layout/layout-composer.js +0 -1
  158. package/dist/src/core/layout/layout-data-loader.js +0 -1
  159. package/dist/src/core/layout/layout-discovery.js +0 -1
  160. package/dist/src/core/layout/layout-matcher.js +0 -1
  161. package/dist/src/core/layout/layout-types.js +0 -1
  162. package/dist/src/core/modules/framework-module-resolver.js +0 -1
  163. package/dist/src/islands/component-analysis.js +0 -1
  164. package/dist/src/islands/css-utils.js +0 -17
  165. package/dist/src/islands/discovery/index.js +0 -1
  166. package/dist/src/islands/discovery/registry.js +0 -1
  167. package/dist/src/islands/discovery/resolver.js +0 -2
  168. package/dist/src/islands/discovery/scanner.js +0 -1
  169. package/dist/src/islands/discovery/types.js +0 -1
  170. package/dist/src/islands/discovery/validator.js +0 -18
  171. package/dist/src/islands/discovery/watcher.js +0 -1
  172. package/dist/src/islands/framework-detection.js +0 -1
  173. package/dist/src/islands/integration-loader.js +0 -1
  174. package/dist/src/islands/island.js +0 -1
  175. package/dist/src/islands/render-cache.js +0 -1
  176. package/dist/src/islands/types.js +0 -1
  177. package/dist/src/islands/universal-css-collector.js +0 -5
  178. package/dist/src/islands/universal-head-collector.js +0 -2
  179. package/dist/src/layout-system.js +0 -1
  180. package/dist/src/middleware/discovery.js +0 -1
  181. package/dist/src/middleware/executor.js +0 -1
  182. package/dist/src/middleware/index.js +0 -1
  183. package/dist/src/middleware/types.js +0 -1
  184. package/dist/src/nitro/build-config.js +0 -1
  185. package/dist/src/nitro/config.js +0 -1
  186. package/dist/src/nitro/error-handler.js +0 -198
  187. package/dist/src/nitro/index.js +0 -1
  188. package/dist/src/nitro/island-manifest.js +0 -2
  189. package/dist/src/nitro/middleware-adapter.js +0 -1
  190. package/dist/src/nitro/renderer.js +0 -183
  191. package/dist/src/nitro/route-discovery.js +0 -1
  192. package/dist/src/nitro/types.js +0 -1
  193. package/dist/src/render/collect-css.js +0 -3
  194. package/dist/src/render/error-pages.js +0 -48
  195. package/dist/src/render/isolated-ssr-renderer.js +0 -1
  196. package/dist/src/render/ssr.js +0 -90
  197. package/dist/src/schemas/api.js +0 -1
  198. package/dist/src/schemas/core.js +0 -1
  199. package/dist/src/schemas/index.js +0 -1
  200. package/dist/src/schemas/layout.js +0 -1
  201. package/dist/src/schemas/routing/index.js +0 -1
  202. package/dist/src/schemas/routing.js +0 -1
  203. package/dist/src/types/as-island.js +0 -1
  204. package/dist/src/types/layout.js +0 -1
  205. package/dist/src/types/routing.js +0 -1
  206. package/dist/src/types/types.js +0 -1
  207. package/dist/src/utils/dev-logger.js +0 -12
  208. package/dist/src/utils/fs.js +0 -1
  209. package/dist/src/vite-plugin/auto-discover.js +0 -1
  210. package/dist/src/vite-plugin/config.js +0 -1
  211. package/dist/src/vite-plugin/errors.js +0 -1
  212. package/dist/src/vite-plugin/image-optimization.js +0 -45
  213. package/dist/src/vite-plugin/integration-activator.js +0 -1
  214. package/dist/src/vite-plugin/island-sidecar-plugin.js +0 -1
  215. package/dist/src/vite-plugin/module-discovery.js +0 -1
  216. package/dist/src/vite-plugin/nitro-integration.js +0 -42
  217. package/dist/src/vite-plugin/plugin.js +0 -1
  218. package/dist/src/vite-plugin/types.js +0 -1
  219. package/dist/src/vite-plugin/validation.js +0 -2
  220. /package/{dist/src → src}/client/types/framework-runtime.d.ts +0 -0
  221. /package/{dist/src → src}/client/types/vite-hmr.d.ts +0 -0
  222. /package/{dist/src → src}/client/types/vite-virtual-modules.d.ts +0 -0
  223. /package/{dist/src → src}/layout-system.d.ts +0 -0
  224. /package/{dist/src → src}/types/image.d.ts +0 -0
  225. /package/{dist/src → src}/types/index.d.ts +0 -0
  226. /package/{dist/src → src}/types/island-jsx.d.ts +0 -0
  227. /package/{dist/src → src}/types/island-prop.d.ts +0 -0
  228. /package/{dist/src → src}/types/mdx.d.ts +0 -0
  229. /package/{dist/src → src}/types/urlpattern.d.ts +0 -0
  230. /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
+ }