@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,403 @@
1
+ /**
2
+ * Avalon Vite Plugin
3
+ *
4
+ * This module provides the main `avalon()` function that creates a unified Vite plugin
5
+ * for the Avalon framework. It handles configuration resolution, integration activation,
6
+ * Nitro server integration, and wires up all the necessary Vite hooks.
7
+ *
8
+ * ISLAND DETECTION:
9
+ * Islands are detected by usage - any component used with an `island` prop in pages
10
+ * or layouts is automatically treated as an island. No fixed islands directory required.
11
+ */
12
+
13
+ import type { Plugin, PluginOption, ResolvedConfig, ViteDevServer } from 'vite';
14
+ import type { AvalonPluginConfig, IntegrationName, ResolvedAvalonConfig } from './types.ts';
15
+ import { createRequire } from 'node:module';
16
+ import { dirname, join } from 'node:path';
17
+ import { resolveConfig, checkDirectoriesExist } from './config.ts';
18
+ import { activateIntegrations, activateSingleIntegration } from './integration-activator.ts';
19
+ import { discoverIntegrationsFromIslandUsage } from './auto-discover.ts';
20
+ import { validateActiveIntegrations, formatValidationResults } from './validation.ts';
21
+ import { createMDXPlugin } from '../build/mdx-plugin.ts';
22
+ import { mdxIslandTransform } from '../build/mdx-island-transform.ts';
23
+ import { pageIslandTransform } from '../build/page-island-transform.ts';
24
+ import { registry } from '../core/integrations/registry.ts';
25
+ import { createNitroIntegration } from './nitro-integration.ts';
26
+ import { islandSidecarPlugin } from './island-sidecar-plugin.ts';
27
+ import { createImagePlugin } from './image-optimization.ts';
28
+ import type { NitroConfigOutput } from '../nitro/config.ts';
29
+ declare global {
30
+ var __avalonConfig: ResolvedAvalonConfig | undefined;
31
+ var __viteDevServer: ViteDevServer | undefined;
32
+ var __nitroConfig: NitroConfigOutput | undefined;
33
+ }
34
+
35
+ /**
36
+ * Collects Vite plugins from all activated integrations.
37
+ *
38
+ * This function iterates through the activated integrations and calls their
39
+ * vitePlugin() method if implemented. The returned plugins are collected and
40
+ * flattened into a single array.
41
+ *
42
+ * Plugin ordering is handled to ensure correct application:
43
+ * - Lit plugins come first (DOM shim requirement)
44
+ * - Other framework plugins follow
45
+ *
46
+ * @param activeIntegrations - Set of activated integration names
47
+ * @param verbose - Whether to log detailed information
48
+ * @returns Promise resolving to an array of Vite plugins from integrations
49
+ */
50
+ export async function collectIntegrationPlugins(
51
+ activeIntegrations: Set<IntegrationName>,
52
+ verbose: boolean = false,
53
+ ): Promise<Plugin[]> {
54
+ const plugins: Plugin[] = [];
55
+ const litPlugins: Plugin[] = [];
56
+
57
+ for (const name of activeIntegrations) {
58
+ const validPlugins = await loadPluginsForIntegration(name, verbose);
59
+ if (name === 'lit') {
60
+ litPlugins.push(...validPlugins);
61
+ } else {
62
+ plugins.push(...validPlugins);
63
+ }
64
+ }
65
+
66
+ return [...litPlugins, ...plugins];
67
+ }
68
+
69
+ async function loadPluginsForIntegration(name: IntegrationName, _verbose: boolean): Promise<Plugin[]> {
70
+ const integration = registry.get(name);
71
+ if (!integration) return [];
72
+ if (typeof integration.vitePlugin !== 'function') return [];
73
+
74
+ try {
75
+ const result = await integration.vitePlugin();
76
+ const pluginArray = Array.isArray(result) ? result : [result];
77
+ return pluginArray.filter((p): p is Plugin => p != null);
78
+ } catch (error) {
79
+ console.warn(`[avalon] Failed to load vite plugin for ${name}:`, error instanceof Error ? error.message : error);
80
+ return [];
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Discovers which integrations are actually needed by scanning pages/layouts for island prop usage.
86
+ * This enables lazy loading - only load Vite plugins for frameworks that are actually used.
87
+ *
88
+ * @param config - The resolved Avalon configuration
89
+ * @param projectRoot - The project root directory (defaults to cwd)
90
+ * @returns Set of integration names that are actually needed
91
+ */
92
+ async function discoverNeededIntegrations(
93
+ config: ResolvedAvalonConfig,
94
+ projectRoot?: string,
95
+ ): Promise<Set<IntegrationName>> {
96
+ const needed = new Set<IntegrationName>();
97
+
98
+ try {
99
+ // Scan pages, layouts, and modules for components used with island prop
100
+ const discovered = await discoverIntegrationsFromIslandUsage(
101
+ config.pagesDir,
102
+ config.layoutsDir,
103
+ projectRoot,
104
+ config.modules?.dir,
105
+ );
106
+
107
+ // Only include integrations that are both discovered AND configured
108
+ for (const integration of discovered) {
109
+ if (config.integrations.includes(integration)) {
110
+ needed.add(integration);
111
+ }
112
+ }
113
+ } catch {
114
+ // If discovery fails, fall back to all configured integrations
115
+ for (const integration of config.integrations) {
116
+ needed.add(integration);
117
+ }
118
+ }
119
+
120
+ return needed;
121
+ }
122
+
123
+ async function resolveIntegrationsToLoad(preResolvedConfig: ResolvedAvalonConfig): Promise<IntegrationName[]> {
124
+ if (!preResolvedConfig.lazyIntegrations || preResolvedConfig.integrations.length === 0) {
125
+ return [...preResolvedConfig.integrations];
126
+ }
127
+
128
+ const needed = await discoverNeededIntegrations(preResolvedConfig);
129
+ if (needed.size === 0) {
130
+ return [...preResolvedConfig.integrations];
131
+ }
132
+
133
+ return Array.from(needed);
134
+ }
135
+
136
+ async function setupMDXPlugins(preResolvedConfig: ResolvedAvalonConfig): Promise<Plugin[]> {
137
+ try {
138
+ const mdxPlugins = await createMDXPlugin({
139
+ jsxImportSource: preResolvedConfig.mdx.jsxImportSource,
140
+ syntaxHighlighting: preResolvedConfig.mdx.syntaxHighlighting,
141
+ remarkPlugins: preResolvedConfig.mdx.remarkPlugins as import('unified').Pluggable[],
142
+ rehypePlugins: preResolvedConfig.mdx.rehypePlugins as import('unified').Pluggable[],
143
+ development: true,
144
+ });
145
+ mdxPlugins.push(mdxIslandTransform({ verbose: preResolvedConfig.verbose }));
146
+ return mdxPlugins;
147
+ } catch (error) {
148
+ if (preResolvedConfig.showWarnings) {
149
+ console.warn('⚠️ Could not configure MDX plugin:', error);
150
+ }
151
+ return [];
152
+ }
153
+ }
154
+
155
+ function setupNitroPlugins(
156
+ preResolvedConfig: ResolvedAvalonConfig,
157
+ nitroConfig: NonNullable<AvalonPluginConfig['nitro']>,
158
+ _verbose?: boolean,
159
+ ): { plugins: Plugin[]; options: NitroConfigOutput } {
160
+ const { plugins, nitroOptions } = createNitroIntegration(preResolvedConfig, nitroConfig);
161
+ globalThis.__nitroConfig = nitroOptions;
162
+ return { plugins, options: nitroOptions };
163
+ }
164
+
165
+ async function runAutoDiscovery(
166
+ resolvedConfig: ResolvedAvalonConfig,
167
+ viteRoot: string,
168
+ activeIntegrations: Set<IntegrationName>,
169
+ ): Promise<void> {
170
+ if (!resolvedConfig.autoDiscoverIntegrations) return;
171
+
172
+ try {
173
+ const discovered = await discoverIntegrationsFromIslandUsage(
174
+ resolvedConfig.pagesDir,
175
+ resolvedConfig.layoutsDir,
176
+ viteRoot,
177
+ resolvedConfig.modules?.dir,
178
+ );
179
+ for (const name of discovered) {
180
+ if (activeIntegrations.has(name)) continue;
181
+ try {
182
+ await activateSingleIntegration(name, activeIntegrations, resolvedConfig.verbose);
183
+ } catch (error) {
184
+ if (resolvedConfig.showWarnings) console.warn(` ⚠️ Could not auto-load integration: ${name}`, error);
185
+ }
186
+ }
187
+ } catch (error) {
188
+ if (resolvedConfig.showWarnings) console.warn(' ⚠️ Auto-discovery failed:', error);
189
+ }
190
+ }
191
+
192
+ function runValidation(resolvedConfig: ResolvedAvalonConfig, activeIntegrations: Set<IntegrationName>): void {
193
+ if (!resolvedConfig.validateIntegrations || activeIntegrations.size === 0) return;
194
+
195
+ const validationSummary = validateActiveIntegrations(activeIntegrations, resolvedConfig.showWarnings);
196
+ if (!validationSummary.allValid) {
197
+ console.error(formatValidationResults(validationSummary));
198
+ if (resolvedConfig.showWarnings) console.warn(' ⚠️ Some integrations have validation issues.');
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Creates the Avalon Vite plugin array
204
+ *
205
+ * @param config - Avalon configuration options
206
+ * @returns A promise that resolves to an array of Vite plugins that handle all Avalon functionality.
207
+ * Returns PluginOption[] to avoid TypeScript's excessive stack depth issues
208
+ * when comparing Plugin<any> arrays in Vite 8's complex type system.
209
+ */
210
+ export async function avalon(config?: AvalonPluginConfig): Promise<PluginOption[]> {
211
+ // Resolved configuration with defaults applied
212
+ let resolvedConfig: ResolvedAvalonConfig;
213
+
214
+ // Reference to Vite's resolved config
215
+ let viteConfig: ResolvedConfig;
216
+
217
+ // Track which integrations are activated
218
+ const activeIntegrations = new Set<IntegrationName>();
219
+
220
+ // Pre-resolve config to get MDX settings and integration list
221
+ // We use isDev=true as a default; the actual value will be set in configResolved
222
+ const preResolvedConfig = resolveConfig(config, true);
223
+
224
+ const integrationsToLoad = await resolveIntegrationsToLoad(preResolvedConfig);
225
+
226
+ if (integrationsToLoad.length > 0) {
227
+ await activateIntegrations({ ...preResolvedConfig, integrations: integrationsToLoad }, activeIntegrations);
228
+ }
229
+ const mdxPlugins = await setupMDXPlugins(preResolvedConfig);
230
+
231
+ // Image optimization plugins (vite-imagetools wrapper)
232
+ const imagePlugins = await createImagePlugin(preResolvedConfig.image, preResolvedConfig.verbose);
233
+
234
+ let integrationPlugins: Plugin[] = [];
235
+ if (activeIntegrations.size > 0) {
236
+ integrationPlugins = await collectIntegrationPlugins(activeIntegrations, preResolvedConfig.verbose);
237
+ }
238
+
239
+ let nitroPlugins: Plugin[] = [];
240
+ if (config?.nitro) {
241
+ const { plugins } = setupNitroPlugins(preResolvedConfig, config.nitro, preResolvedConfig.verbose);
242
+ nitroPlugins = plugins;
243
+ }
244
+
245
+ // Sidecar plugin for Vue/Svelte/Solid type declarations
246
+ const sidecarPlugin = islandSidecarPlugin({
247
+ verbose: preResolvedConfig.verbose,
248
+ });
249
+
250
+ // Pre-resolve paths for standalone projects.
251
+ // In the monorepo www/ project these are handled by manual resolve.alias.
252
+ const require = createRequire(import.meta.url);
253
+
254
+ let clientMainResolved: string | null = null;
255
+ try {
256
+ const clientEntry = require.resolve('@useavalon/avalon/client');
257
+ clientMainResolved = join(dirname(clientEntry), 'main.js');
258
+ } catch {
259
+ // Monorepo — www/ sets its own alias
260
+ }
261
+
262
+ // Resolve /@useavalon/*/client and /@useavalon/*/client/hmr virtual imports
263
+ // used by main.js. These are resolved dynamically in the resolveId hook
264
+ // using Vite's resolver.
265
+ const integrationVirtualIds = new Set(
266
+ ['preact', 'react', 'vue', 'svelte', 'solid', 'lit', 'qwik'].flatMap(name => [
267
+ `/@useavalon/${name}/client`,
268
+ `/@useavalon/${name}/client/hmr`,
269
+ ]),
270
+ );
271
+
272
+ // The main Avalon plugin
273
+ const avalonPlugin: Plugin = {
274
+ name: 'avalon',
275
+ enforce: 'pre',
276
+
277
+ config() {
278
+ // @useavalon packages ship raw .ts source for SSR and pre-compiled
279
+ // .js for client-side code.
280
+ //
281
+ // oxc.exclude: Prevents Vite's built-in OXC from processing @useavalon
282
+ // .ts files. Without this, OXC applies integration plugins' global
283
+ // jsx: 'automatic' config to plain .ts files, causing errors.
284
+ // Our transform hook below handles TS stripping for SSR instead.
285
+ // Client-side loads .js files which OXC skips by default (/\.js$/).
286
+ //
287
+ // ssr.noExternal: Ensures Vite processes @useavalon packages through
288
+ // the SSR transform pipeline instead of treating them as external CJS.
289
+ return {
290
+ oxc: {
291
+ exclude: [/node_modules\/@useavalon\/.*\.tsx?$/],
292
+ },
293
+ ssr: {
294
+ noExternal: [/^@useavalon\//],
295
+ },
296
+ };
297
+ },
298
+
299
+ configResolved(resolvedViteConfig: ResolvedConfig) {
300
+ viteConfig = resolvedViteConfig;
301
+ const isDev = resolvedViteConfig.command === 'serve';
302
+ resolvedConfig = resolveConfig(config, isDev);
303
+
304
+ globalThis.__avalonConfig = resolvedConfig;
305
+
306
+ checkDirectoriesExist(resolvedConfig, resolvedViteConfig.root);
307
+ },
308
+
309
+ async resolveId(id: string) {
310
+ if (id === '/src/client/main.js' && clientMainResolved) {
311
+ return clientMainResolved;
312
+ }
313
+ // /@useavalon/*/client and /@useavalon/*/client/hmr — resolve through
314
+ // Vite's pipeline so it finds workspace-linked or npm-installed
315
+ // integration packages from the consuming project's node_modules,
316
+ // not from avalon's own context.
317
+ if (integrationVirtualIds.has(id)) {
318
+ const packageId = id.slice(1); // strip leading /
319
+ const resolved = await this.resolve(packageId);
320
+ return resolved?.id ?? null;
321
+ }
322
+ return null;
323
+ },
324
+
325
+ async transform(code: string, id: string) {
326
+ // For SSR: strip TypeScript from @useavalon packages ourselves.
327
+ // Integration plugins (react, preact) set jsx: 'automatic' which Vite's
328
+ // OXC applies to all files — causing "Invalid jsx option" errors on
329
+ // plain .ts files during SSR. We intercept and strip TS without JSX config.
330
+ // For client-side: main.js imports pre-compiled .js files that OXC
331
+ // skips entirely (default exclude: /\.js$/), avoiding the jsx conflict.
332
+ if (this.environment?.config?.consumer === 'server' && id.includes('@useavalon/') && /\.tsx?$/.test(id)) {
333
+ const { transform: oxcTransform } = await import('oxc-transform');
334
+ const result = await oxcTransform(id, code, {
335
+ sourcemap: true,
336
+ typescript: { onlyRemoveTypeImports: false },
337
+ });
338
+ return { code: result.code, map: result.map, moduleType: 'js' };
339
+ }
340
+ },
341
+
342
+ async buildStart() {
343
+ await runAutoDiscovery(resolvedConfig, viteConfig?.root, activeIntegrations);
344
+ runValidation(resolvedConfig, activeIntegrations);
345
+ },
346
+
347
+ configureServer(server: ViteDevServer) {
348
+ (globalThis as any).__viteDevServer = server;
349
+ },
350
+ };
351
+
352
+ // Extract Lit plugins for proper ordering
353
+ const litPlugins = integrationPlugins.filter(p => p.name?.includes('lit'));
354
+ const otherIntegrationPlugins = integrationPlugins.filter(p => !p.name?.includes('lit'));
355
+
356
+ // Page island transform: auto-wraps components with `island` prop
357
+ const pageTransformPlugin = pageIslandTransform({
358
+ pagesDir: preResolvedConfig.pagesDir,
359
+ layoutsDir: preResolvedConfig.layoutsDir,
360
+ modules: preResolvedConfig.modules,
361
+ verbose: preResolvedConfig.verbose,
362
+ });
363
+
364
+ return [
365
+ pageTransformPlugin,
366
+ ...imagePlugins,
367
+ ...litPlugins,
368
+ ...mdxPlugins,
369
+ avalonPlugin,
370
+ sidecarPlugin,
371
+ ...nitroPlugins,
372
+ ...otherIntegrationPlugins,
373
+ ] as PluginOption[];
374
+ }
375
+
376
+ export function getResolvedConfig(): ResolvedAvalonConfig | undefined {
377
+ return globalThis.__avalonConfig;
378
+ }
379
+
380
+ export function getPagesDir(): string {
381
+ return globalThis.__avalonConfig?.pagesDir ?? 'src/pages';
382
+ }
383
+
384
+ export function getLayoutsDir(): string {
385
+ return globalThis.__avalonConfig?.layoutsDir ?? 'src/layouts';
386
+ }
387
+
388
+ export function getNitroConfig(): NitroConfigOutput | undefined {
389
+ return globalThis.__nitroConfig;
390
+ }
391
+
392
+ export function isNitroEnabled(): boolean {
393
+ return globalThis.__nitroConfig !== undefined;
394
+ }
395
+
396
+ export type {
397
+ AvalonPluginConfig,
398
+ IntegrationName,
399
+ ResolvedAvalonConfig,
400
+ ImageConfig,
401
+ ResolvedImageConfig,
402
+ } from './types.ts';
403
+ export type { AvalonNitroConfig, NitroConfigOutput } from '../nitro/config.ts';