@useavalon/avalon 0.1.13 → 0.1.15

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/dist/mod.js +1 -0
  2. package/dist/src/build/integration-bundler-plugin.js +1 -0
  3. package/dist/src/build/integration-config.js +1 -0
  4. package/dist/src/build/integration-detection-plugin.js +1 -0
  5. package/dist/src/build/integration-resolver-plugin.js +1 -0
  6. package/dist/src/build/island-manifest.js +1 -0
  7. package/dist/src/build/island-types-generator.js +5 -0
  8. package/dist/src/build/mdx-island-transform.js +2 -0
  9. package/dist/src/build/mdx-plugin.js +1 -0
  10. package/dist/src/build/page-island-transform.js +3 -0
  11. package/dist/src/build/prop-extractors/index.js +1 -0
  12. package/dist/src/build/prop-extractors/lit.js +1 -0
  13. package/dist/src/build/prop-extractors/qwik.js +1 -0
  14. package/dist/src/build/prop-extractors/solid.js +1 -0
  15. package/dist/src/build/prop-extractors/svelte.js +1 -0
  16. package/dist/src/build/prop-extractors/vue.js +1 -0
  17. package/dist/src/build/sidecar-file-manager.js +1 -0
  18. package/dist/src/build/sidecar-renderer.js +6 -0
  19. package/dist/src/client/adapters/index.js +1 -0
  20. package/dist/src/client/components.js +1 -0
  21. package/dist/src/client/css-hmr-handler.js +1 -0
  22. package/dist/src/client/framework-adapter.js +13 -0
  23. package/dist/src/client/hmr-coordinator.js +1 -0
  24. package/dist/src/client/hmr-error-overlay.js +214 -0
  25. package/dist/src/client/main.js +39 -0
  26. package/dist/src/components/Image.js +1 -0
  27. package/dist/src/components/IslandErrorBoundary.js +1 -0
  28. package/dist/src/components/LayoutDataErrorBoundary.js +1 -0
  29. package/dist/src/components/LayoutErrorBoundary.js +1 -0
  30. package/dist/src/components/PersistentIsland.js +1 -0
  31. package/dist/src/components/StreamingErrorBoundary.js +1 -0
  32. package/dist/src/components/StreamingLayout.js +29 -0
  33. package/dist/src/core/components/component-analyzer.js +1 -0
  34. package/dist/src/core/components/component-detection.js +5 -0
  35. package/dist/src/core/components/enhanced-framework-detector.js +1 -0
  36. package/dist/src/core/components/framework-registry.js +1 -0
  37. package/dist/src/core/content/mdx-processor.js +1 -0
  38. package/dist/src/core/integrations/index.js +1 -0
  39. package/dist/src/core/integrations/loader.js +1 -0
  40. package/dist/src/core/integrations/registry.js +1 -0
  41. package/dist/src/core/islands/island-persistence.js +1 -0
  42. package/dist/src/core/islands/island-state-serializer.js +1 -0
  43. package/dist/src/core/islands/persistent-island-context.js +1 -0
  44. package/dist/src/core/islands/use-persistent-state.js +1 -0
  45. package/dist/src/core/layout/enhanced-layout-resolver.js +1 -0
  46. package/dist/src/core/layout/layout-cache-manager.js +1 -0
  47. package/dist/src/core/layout/layout-composer.js +1 -0
  48. package/dist/src/core/layout/layout-data-loader.js +1 -0
  49. package/dist/src/core/layout/layout-discovery.js +1 -0
  50. package/dist/src/core/layout/layout-matcher.js +1 -0
  51. package/dist/src/core/layout/layout-types.js +1 -0
  52. package/dist/src/core/modules/framework-module-resolver.js +1 -0
  53. package/dist/src/islands/component-analysis.js +1 -0
  54. package/dist/src/islands/css-utils.js +17 -0
  55. package/dist/src/islands/discovery/index.js +1 -0
  56. package/dist/src/islands/discovery/registry.js +1 -0
  57. package/dist/src/islands/discovery/resolver.js +2 -0
  58. package/dist/src/islands/discovery/scanner.js +1 -0
  59. package/dist/src/islands/discovery/types.js +1 -0
  60. package/dist/src/islands/discovery/validator.js +18 -0
  61. package/dist/src/islands/discovery/watcher.js +1 -0
  62. package/dist/src/islands/framework-detection.js +1 -0
  63. package/dist/src/islands/integration-loader.js +1 -0
  64. package/dist/src/islands/island.js +1 -0
  65. package/dist/src/islands/render-cache.js +1 -0
  66. package/dist/src/islands/types.js +1 -0
  67. package/dist/src/islands/universal-css-collector.js +5 -0
  68. package/dist/src/islands/universal-head-collector.js +2 -0
  69. package/dist/src/layout-system.js +1 -0
  70. package/dist/src/middleware/discovery.js +1 -0
  71. package/dist/src/middleware/executor.js +1 -0
  72. package/dist/src/middleware/index.js +1 -0
  73. package/dist/src/middleware/types.js +1 -0
  74. package/dist/src/nitro/build-config.js +1 -0
  75. package/dist/src/nitro/config.js +1 -0
  76. package/dist/src/nitro/error-handler.js +198 -0
  77. package/dist/src/nitro/index.js +1 -0
  78. package/dist/src/nitro/island-manifest.js +2 -0
  79. package/dist/src/nitro/middleware-adapter.js +1 -0
  80. package/dist/src/nitro/renderer.js +183 -0
  81. package/dist/src/nitro/route-discovery.js +1 -0
  82. package/dist/src/nitro/types.js +1 -0
  83. package/dist/src/render/collect-css.js +3 -0
  84. package/dist/src/render/error-pages.js +48 -0
  85. package/dist/src/render/isolated-ssr-renderer.js +1 -0
  86. package/dist/src/render/ssr.js +90 -0
  87. package/dist/src/schemas/api.js +1 -0
  88. package/dist/src/schemas/core.js +1 -0
  89. package/dist/src/schemas/index.js +1 -0
  90. package/dist/src/schemas/layout.js +1 -0
  91. package/dist/src/schemas/routing/index.js +1 -0
  92. package/dist/src/schemas/routing.js +1 -0
  93. package/dist/src/types/as-island.js +1 -0
  94. package/dist/src/types/layout.js +1 -0
  95. package/dist/src/types/routing.js +1 -0
  96. package/dist/src/types/types.js +1 -0
  97. package/dist/src/utils/dev-logger.js +12 -0
  98. package/dist/src/utils/fs.js +1 -0
  99. package/dist/src/vite-plugin/auto-discover.js +1 -0
  100. package/dist/src/vite-plugin/config.js +1 -0
  101. package/dist/src/vite-plugin/errors.js +1 -0
  102. package/dist/src/vite-plugin/image-optimization.js +45 -0
  103. package/dist/src/vite-plugin/integration-activator.js +1 -0
  104. package/dist/src/vite-plugin/island-sidecar-plugin.js +1 -0
  105. package/dist/src/vite-plugin/module-discovery.js +1 -0
  106. package/dist/src/vite-plugin/nitro-integration.js +42 -0
  107. package/dist/src/vite-plugin/plugin.js +1 -0
  108. package/dist/src/vite-plugin/types.js +1 -0
  109. package/dist/src/vite-plugin/validation.js +2 -0
  110. package/package.json +14 -20
  111. package/mod.ts +0 -302
  112. package/src/build/integration-bundler-plugin.ts +0 -116
  113. package/src/build/integration-config.ts +0 -168
  114. package/src/build/integration-detection-plugin.ts +0 -117
  115. package/src/build/integration-resolver-plugin.ts +0 -90
  116. package/src/build/island-manifest.ts +0 -269
  117. package/src/build/island-types-generator.ts +0 -476
  118. package/src/build/mdx-island-transform.ts +0 -464
  119. package/src/build/mdx-plugin.ts +0 -98
  120. package/src/build/page-island-transform.ts +0 -598
  121. package/src/build/prop-extractors/index.ts +0 -21
  122. package/src/build/prop-extractors/lit.ts +0 -140
  123. package/src/build/prop-extractors/qwik.ts +0 -16
  124. package/src/build/prop-extractors/solid.ts +0 -125
  125. package/src/build/prop-extractors/svelte.ts +0 -194
  126. package/src/build/prop-extractors/vue.ts +0 -111
  127. package/src/build/sidecar-file-manager.ts +0 -104
  128. package/src/build/sidecar-renderer.ts +0 -30
  129. package/src/client/adapters/index.ts +0 -21
  130. package/src/client/components.ts +0 -35
  131. package/src/client/css-hmr-handler.ts +0 -344
  132. package/src/client/framework-adapter.ts +0 -462
  133. package/src/client/hmr-coordinator.ts +0 -396
  134. package/src/client/hmr-error-overlay.js +0 -533
  135. package/src/client/main.js +0 -824
  136. package/src/components/Image.tsx +0 -123
  137. package/src/components/IslandErrorBoundary.tsx +0 -145
  138. package/src/components/LayoutDataErrorBoundary.tsx +0 -141
  139. package/src/components/LayoutErrorBoundary.tsx +0 -127
  140. package/src/components/PersistentIsland.tsx +0 -52
  141. package/src/components/StreamingErrorBoundary.tsx +0 -233
  142. package/src/components/StreamingLayout.tsx +0 -538
  143. package/src/core/components/component-analyzer.ts +0 -192
  144. package/src/core/components/component-detection.ts +0 -508
  145. package/src/core/components/enhanced-framework-detector.ts +0 -500
  146. package/src/core/components/framework-registry.ts +0 -563
  147. package/src/core/content/mdx-processor.ts +0 -46
  148. package/src/core/integrations/index.ts +0 -19
  149. package/src/core/integrations/loader.ts +0 -125
  150. package/src/core/integrations/registry.ts +0 -175
  151. package/src/core/islands/island-persistence.ts +0 -325
  152. package/src/core/islands/island-state-serializer.ts +0 -258
  153. package/src/core/islands/persistent-island-context.tsx +0 -80
  154. package/src/core/islands/use-persistent-state.ts +0 -68
  155. package/src/core/layout/enhanced-layout-resolver.ts +0 -322
  156. package/src/core/layout/layout-cache-manager.ts +0 -485
  157. package/src/core/layout/layout-composer.ts +0 -357
  158. package/src/core/layout/layout-data-loader.ts +0 -516
  159. package/src/core/layout/layout-discovery.ts +0 -243
  160. package/src/core/layout/layout-matcher.ts +0 -299
  161. package/src/core/layout/layout-types.ts +0 -110
  162. package/src/core/modules/framework-module-resolver.ts +0 -273
  163. package/src/islands/component-analysis.ts +0 -213
  164. package/src/islands/css-utils.ts +0 -565
  165. package/src/islands/discovery/index.ts +0 -80
  166. package/src/islands/discovery/registry.ts +0 -340
  167. package/src/islands/discovery/resolver.ts +0 -477
  168. package/src/islands/discovery/scanner.ts +0 -386
  169. package/src/islands/discovery/types.ts +0 -117
  170. package/src/islands/discovery/validator.ts +0 -544
  171. package/src/islands/discovery/watcher.ts +0 -368
  172. package/src/islands/framework-detection.ts +0 -428
  173. package/src/islands/integration-loader.ts +0 -490
  174. package/src/islands/island.tsx +0 -565
  175. package/src/islands/render-cache.ts +0 -550
  176. package/src/islands/types.ts +0 -80
  177. package/src/islands/universal-css-collector.ts +0 -157
  178. package/src/islands/universal-head-collector.ts +0 -137
  179. package/src/layout-system.ts +0 -218
  180. package/src/middleware/discovery.ts +0 -268
  181. package/src/middleware/executor.ts +0 -315
  182. package/src/middleware/index.ts +0 -76
  183. package/src/middleware/types.ts +0 -99
  184. package/src/nitro/build-config.ts +0 -576
  185. package/src/nitro/config.ts +0 -483
  186. package/src/nitro/error-handler.ts +0 -636
  187. package/src/nitro/index.ts +0 -173
  188. package/src/nitro/island-manifest.ts +0 -584
  189. package/src/nitro/middleware-adapter.ts +0 -260
  190. package/src/nitro/renderer.ts +0 -1471
  191. package/src/nitro/route-discovery.ts +0 -439
  192. package/src/nitro/types.ts +0 -321
  193. package/src/render/collect-css.ts +0 -198
  194. package/src/render/error-pages.ts +0 -79
  195. package/src/render/isolated-ssr-renderer.ts +0 -654
  196. package/src/render/ssr.ts +0 -1030
  197. package/src/schemas/api.ts +0 -30
  198. package/src/schemas/core.ts +0 -64
  199. package/src/schemas/index.ts +0 -212
  200. package/src/schemas/layout.ts +0 -279
  201. package/src/schemas/routing/index.ts +0 -38
  202. package/src/schemas/routing.ts +0 -376
  203. package/src/types/as-island.ts +0 -20
  204. package/src/types/layout.ts +0 -285
  205. package/src/types/routing.ts +0 -555
  206. package/src/types/types.ts +0 -5
  207. package/src/utils/dev-logger.ts +0 -299
  208. package/src/utils/fs.ts +0 -151
  209. package/src/vite-plugin/auto-discover.ts +0 -551
  210. package/src/vite-plugin/config.ts +0 -266
  211. package/src/vite-plugin/errors.ts +0 -127
  212. package/src/vite-plugin/image-optimization.ts +0 -156
  213. package/src/vite-plugin/integration-activator.ts +0 -126
  214. package/src/vite-plugin/island-sidecar-plugin.ts +0 -176
  215. package/src/vite-plugin/module-discovery.ts +0 -189
  216. package/src/vite-plugin/nitro-integration.ts +0 -1354
  217. package/src/vite-plugin/plugin.ts +0 -403
  218. package/src/vite-plugin/types.ts +0 -327
  219. package/src/vite-plugin/validation.ts +0 -228
  220. /package/{src → dist/src}/client/types/framework-runtime.d.ts +0 -0
  221. /package/{src → dist/src}/client/types/vite-hmr.d.ts +0 -0
  222. /package/{src → dist/src}/client/types/vite-virtual-modules.d.ts +0 -0
  223. /package/{src → dist/src}/layout-system.d.ts +0 -0
  224. /package/{src → dist/src}/types/image.d.ts +0 -0
  225. /package/{src → dist/src}/types/index.d.ts +0 -0
  226. /package/{src → dist/src}/types/island-jsx.d.ts +0 -0
  227. /package/{src → dist/src}/types/island-prop.d.ts +0 -0
  228. /package/{src → dist/src}/types/mdx.d.ts +0 -0
  229. /package/{src → dist/src}/types/urlpattern.d.ts +0 -0
  230. /package/{src → dist/src}/types/vite-env.d.ts +0 -0
@@ -1,464 +0,0 @@
1
- /**
2
- * MDX Island Transform Plugin
3
- *
4
- * Transforms island component usage in MDX files into renderIsland() calls.
5
- *
6
- * Supports two patterns:
7
- * 1. Imports from islands/ directories (legacy pattern)
8
- * 2. Components used with the `island` prop (preferred pattern)
9
- *
10
- * Problem: MDX files import island components directly and render them as raw JSX.
11
- * After MDX compilation, these become jsxDEV(ComponentName, ...) calls. Preact's
12
- * renderToString doesn't support async components, so we can't use async wrappers.
13
- *
14
- * Solution: Replaces the compiled JSX calls (_jsxDEV(Component, { island: ... }))
15
- * with await expressions that call renderIsland() directly. This allows proper
16
- * async SSR rendering of islands in MDX files.
17
- */
18
-
19
- import type { Plugin } from 'vite';
20
- import { dirname } from 'node:path';
21
-
22
- export interface MDXIslandTransformOptions {
23
- islandPathPatterns?: RegExp[];
24
- verbose?: boolean;
25
- }
26
-
27
- const DEFAULT_ISLAND_PATTERNS = [
28
- /['"]\.\.\/islands\//,
29
- /['"]\.\/islands\//,
30
- /['"]\.\.\/\.\.\/islands\//,
31
- /['"]\$islands\//,
32
- /['"]@\/islands\//,
33
- /['"]\/src\/islands\//,
34
- ];
35
-
36
- interface IslandImport {
37
- localName: string;
38
- importPath: string;
39
- islandPropUsage: boolean;
40
- }
41
-
42
- /**
43
- * Find all default imports in the code
44
- */
45
- function findAllDefaultImports(code: string): Map<string, string> {
46
- const imports = new Map<string, string>();
47
- const re = /import\s+([A-Z]\w*)\s+from\s+(['"][^'"]+['"])/g;
48
- let m;
49
- while ((m = re.exec(code)) !== null) {
50
- const localName = m[1];
51
- const importPath = m[2].slice(1, -1);
52
- imports.set(localName, importPath);
53
- }
54
- return imports;
55
- }
56
-
57
- /**
58
- * Find components used with the island prop in the code
59
- * Handles both raw JSX (<Component island={...}) and compiled JSX (_jsxDEV(Component, { island:)
60
- */
61
- function findIslandPropUsage(code: string): Set<string> {
62
- const components = new Set<string>();
63
-
64
- // Match raw JSX: <ComponentName ... island={...} or <ComponentName ... island ...
65
- const rawJsxRe = /<([A-Z]\w*)\s+[^>]*\bisland\s*[={]/g;
66
- let m;
67
- while ((m = rawJsxRe.exec(code)) !== null) {
68
- components.add(m[1]);
69
- }
70
-
71
- // Match compiled JSX: _jsxDEV(ComponentName, { island: or jsxDEV(ComponentName, { island:
72
- // Also handles jsx() and jsxs() variants
73
- const compiledJsxRe = /(?:_?jsxs?(?:DEV)?)\s*\(\s*([A-Z]\w*)\s*,\s*\{[^}]*\bisland\s*:/g;
74
- while ((m = compiledJsxRe.exec(code)) !== null) {
75
- components.add(m[1]);
76
- }
77
-
78
- return components;
79
- }
80
-
81
- function findIslandImports(code: string, patterns: RegExp[]): IslandImport[] {
82
- const imports: IslandImport[] = [];
83
- const allImports = findAllDefaultImports(code);
84
- const islandPropComponents = findIslandPropUsage(code);
85
-
86
- for (const [localName, importPath] of allImports) {
87
- const quotedPath = `"${importPath}"`;
88
- const isFromIslandsDir = patterns.some(p => p.test(quotedPath));
89
- const hasIslandProp = islandPropComponents.has(localName);
90
-
91
- if (isFromIslandsDir || hasIslandProp) {
92
- imports.push({ localName, importPath, islandPropUsage: hasIslandProp });
93
- }
94
- }
95
-
96
- return imports;
97
- }
98
-
99
- /**
100
- * Resolve an import path to an absolute src path for renderIsland
101
- */
102
- function resolveIslandSrc(importPath: string, fileId: string): string {
103
- // Already absolute
104
- if (importPath.startsWith('/src/')) return importPath;
105
- if (importPath.startsWith('/app/')) return importPath;
106
- if (importPath.startsWith('/')) return importPath;
107
-
108
- // Handle aliases - convert to absolute paths
109
- if (importPath.startsWith('@/')) {
110
- return '/app/' + importPath.slice(2);
111
- }
112
- if (importPath.startsWith('@shared/')) {
113
- return '/app/shared/' + importPath.slice(8);
114
- }
115
- if (importPath.startsWith('@modules/')) {
116
- return '/app/modules/' + importPath.slice(9);
117
- }
118
- if (importPath.startsWith('$components/')) {
119
- return '/src/components/' + importPath.slice(12);
120
- }
121
- if (importPath.startsWith('$islands/')) {
122
- return '/src/islands/' + importPath.slice(9);
123
- }
124
- if (importPath.startsWith('~/')) {
125
- return '/src/' + importPath.slice(2);
126
- }
127
-
128
- // Relative import - resolve relative to the file
129
- if (importPath.startsWith('.')) {
130
- const normalized = fileId.replaceAll('\\', '/');
131
-
132
- // Try to find /app/ or /src/ in the path
133
- let baseIndex = normalized.indexOf('/app/');
134
- if (baseIndex === -1) baseIndex = normalized.indexOf('/src/');
135
-
136
- if (baseIndex !== -1) {
137
- const fileDir = dirname(normalized.slice(baseIndex));
138
- // Simple path resolution
139
- const parts = fileDir.split('/');
140
- const importParts = importPath.split('/');
141
-
142
- for (const part of importParts) {
143
- if (part === '..') {
144
- parts.pop();
145
- } else if (part !== '.') {
146
- parts.push(part);
147
- }
148
- }
149
-
150
- return parts.join('/');
151
- }
152
- }
153
-
154
- // Fallback for islands directory pattern
155
- if (importPath.includes('/islands/')) {
156
- const parts = importPath.split('/');
157
- return '/src/islands/' + parts.at(-1);
158
- }
159
-
160
- // Fallback: return as-is with /src/ prefix
161
- return '/src/' + importPath.split('/').pop();
162
- }
163
-
164
- function detectFramework(src: string): string | undefined {
165
- if (src.endsWith('.vue')) return 'vue';
166
- if (src.endsWith('.svelte')) return 'svelte';
167
- if (src.includes('.solid.')) return 'solid';
168
- if (src.includes('.lit.')) return 'lit';
169
- if (src.includes('.qwik.')) return 'qwik';
170
- if (src.endsWith('.tsx') || src.endsWith('.jsx')) return 'preact';
171
- return undefined;
172
- }
173
-
174
- /**
175
- * Skip a brace-delimited expression `{...}`, handling nested braces and strings.
176
- * Returns the index after the closing brace.
177
- */
178
- function skipBracedExpression(code: string, openBraceIdx: number): number {
179
- let pos = openBraceIdx + 1;
180
- let depth = 1;
181
- while (pos < code.length && depth > 0) {
182
- const ch = code[pos];
183
- if (ch === '{') {
184
- depth++;
185
- pos++;
186
- } else if (ch === '}') {
187
- depth--;
188
- if (depth > 0) pos++;
189
- } else if (ch === "'" || ch === '"' || ch === '`') {
190
- pos = skipStringLiteral(code, pos);
191
- } else {
192
- pos++;
193
- }
194
- }
195
- return pos < code.length ? pos + 1 : pos;
196
- }
197
-
198
- /**
199
- * Skip a string literal (single, double, or backtick).
200
- * Returns index after closing quote.
201
- */
202
- function skipStringLiteral(code: string, pos: number): number {
203
- const quote = code[pos];
204
- pos++;
205
- while (pos < code.length && code[pos] !== quote) {
206
- if (code[pos] === '\\') pos++; // skip escaped char
207
- pos++;
208
- }
209
- return pos < code.length ? pos + 1 : pos;
210
- }
211
-
212
- /**
213
- * Find the end of a JSX function call: _jsxDEV(Component, {...}, ...)
214
- * Returns the index after the closing parenthesis.
215
- */
216
- function findJsxCallEnd(code: string, startIdx: number): number {
217
- let pos = startIdx;
218
- let depth = 0;
219
-
220
- // Find the opening parenthesis
221
- while (pos < code.length && code[pos] !== '(') pos++;
222
- if (pos >= code.length) return startIdx;
223
-
224
- // Now track parentheses depth
225
- while (pos < code.length) {
226
- const ch = code[pos];
227
- if (ch === '(') {
228
- depth++;
229
- pos++;
230
- } else if (ch === ')') {
231
- depth--;
232
- pos++;
233
- if (depth === 0) return pos;
234
- } else if (ch === '{') {
235
- pos = skipBracedExpression(code, pos);
236
- } else if (ch === "'" || ch === '"' || ch === '`') {
237
- pos = skipStringLiteral(code, pos);
238
- } else {
239
- pos++;
240
- }
241
- }
242
- return pos;
243
- }
244
-
245
- /**
246
- * Extract the island prop value from a JSX props object.
247
- * Given `{ island: { condition: 'on:interaction' }, other: 1 }`, returns `{ condition: 'on:interaction' }`
248
- */
249
- function extractIslandProp(propsStr: string): { islandValue: string; otherProps: string } | null {
250
- // Find `island:` or `island :` in the props
251
- const islandMatch = propsStr.match(/\bisland\s*:\s*/);
252
- if (!islandMatch) return null;
253
-
254
- const islandStart = islandMatch.index! + islandMatch[0].length;
255
-
256
- // The island value could be:
257
- // 1. An object literal: { condition: 'on:interaction' }
258
- // 2. A variable reference: islandOpts
259
- // 3. A more complex expression
260
-
261
- let islandEnd: number;
262
- if (propsStr[islandStart] === '{') {
263
- // Object literal - find matching closing brace
264
- islandEnd = skipBracedExpression(propsStr, islandStart);
265
- } else {
266
- // Find the next comma or closing brace
267
- let pos = islandStart;
268
- let depth = 0;
269
- while (pos < propsStr.length) {
270
- const ch = propsStr[pos];
271
- if (ch === '{' || ch === '[' || ch === '(') {
272
- depth++;
273
- pos++;
274
- } else if (ch === '}' || ch === ']' || ch === ')') {
275
- if (depth === 0) break;
276
- depth--;
277
- pos++;
278
- } else if (ch === ',' && depth === 0) {
279
- break;
280
- } else {
281
- pos++;
282
- }
283
- }
284
- islandEnd = pos;
285
- }
286
-
287
- const islandValue = propsStr.slice(islandStart, islandEnd).trim();
288
-
289
- // Build other props by removing the island prop
290
- const beforeIsland = propsStr.slice(0, islandMatch.index!).trim();
291
- const afterIsland = propsStr.slice(islandEnd).trim();
292
-
293
- // Clean up: remove trailing/leading commas
294
- let otherProps = beforeIsland;
295
- if (afterIsland.startsWith(',')) {
296
- otherProps += afterIsland.slice(1);
297
- } else {
298
- otherProps += afterIsland;
299
- }
300
-
301
- // Remove trailing comma before closing brace
302
- otherProps = otherProps.replace(/,\s*}$/, '}').replace(/{\s*,/, '{');
303
-
304
- return { islandValue, otherProps };
305
- }
306
-
307
- /**
308
- * Replace JSX calls for a component with renderIsland await expressions.
309
- * Transforms: _jsxDEV(Component, { island: {...}, prop: 1 }, ...)
310
- * Into: (await __AvalonRenderIsland({ src: "...", ...island, props: { prop: 1 } }))
311
- */
312
- function replaceJsxCalls(
313
- code: string,
314
- componentName: string,
315
- srcPath: string,
316
- framework: string | undefined,
317
- ): string {
318
- // Match patterns like: _jsxDEV(ComponentName, or jsxDEV(ComponentName, or jsx(ComponentName,
319
- const jsxCallPattern = new RegExp(
320
- '(_?jsxs?(?:DEV)?)\\s*\\(\\s*' + componentName + '\\s*,',
321
- 'g'
322
- );
323
-
324
- let result = '';
325
- let lastIndex = 0;
326
- let match;
327
-
328
- while ((match = jsxCallPattern.exec(code)) !== null) {
329
- const matchStart = match.index;
330
- const jsxFn = match[1];
331
-
332
- // Find the end of this JSX call
333
- const callEnd = findJsxCallEnd(code, matchStart);
334
- const fullCall = code.slice(matchStart, callEnd);
335
-
336
- // Check if this call has an island prop
337
- if (!fullCall.includes('island')) {
338
- // No island prop, keep as-is
339
- result += code.slice(lastIndex, callEnd);
340
- lastIndex = callEnd;
341
- continue;
342
- }
343
-
344
- // Extract the props object - it's the second argument
345
- // Pattern: jsxFn(Component, { props }, key, isStatic, source, self)
346
- const propsStart = fullCall.indexOf('{');
347
- if (propsStart === -1) {
348
- result += code.slice(lastIndex, callEnd);
349
- lastIndex = callEnd;
350
- continue;
351
- }
352
-
353
- const propsEnd = skipBracedExpression(fullCall, propsStart);
354
- const propsStr = fullCall.slice(propsStart, propsEnd);
355
-
356
- const extracted = extractIslandProp(propsStr);
357
- if (!extracted) {
358
- result += code.slice(lastIndex, callEnd);
359
- lastIndex = callEnd;
360
- continue;
361
- }
362
-
363
- const { islandValue, otherProps } = extracted;
364
- const fwArg = framework ? `framework: "${framework}",` : '';
365
-
366
- // Check if otherProps is empty (just `{}`)
367
- const hasOtherProps = otherProps.trim() !== '{}' && otherProps.trim() !== '';
368
- const propsArg = hasOtherProps ? `props: ${otherProps},` : '';
369
-
370
- // Build the renderIsland call
371
- // We spread the island value to get condition, ssr, etc.
372
- const renderCall = `(await __AvalonRenderIsland({ src: "${srcPath}", ${fwArg} ...(${islandValue}), ${propsArg} ssr: (${islandValue}).ssr !== undefined ? (${islandValue}).ssr : true }))`;
373
-
374
- result += code.slice(lastIndex, matchStart) + renderCall;
375
- lastIndex = callEnd;
376
- }
377
-
378
- result += code.slice(lastIndex);
379
- return result;
380
- }
381
-
382
- export function mdxIslandTransform(options: MDXIslandTransformOptions = {}): Plugin {
383
- const { islandPathPatterns = DEFAULT_ISLAND_PATTERNS, verbose = false } = options;
384
-
385
- return {
386
- name: 'avalon:mdx-island-transform',
387
- enforce: 'post',
388
-
389
- transform(code: string, id: string) {
390
- if (!id.endsWith('.mdx') && !id.includes('.mdx?')) {
391
- return null;
392
- }
393
-
394
- const islandImports = findIslandImports(code, islandPathPatterns);
395
- if (islandImports.length === 0) {
396
- return null;
397
- }
398
-
399
- if (verbose) {
400
- console.log('[mdx-island-transform] Found ' + islandImports.length + ' island import(s) in ' + id);
401
- for (const imp of islandImports) {
402
- console.log(' - ' + imp.localName + ' from ' + imp.importPath + (imp.islandPropUsage ? ' (island prop)' : ' (islands dir)'));
403
- }
404
- }
405
-
406
- let transformed = code;
407
-
408
- // Add the renderIsland import for async SSR
409
- const hasAvalonImport =
410
- transformed.includes('from "@useavalon/avalon"') || transformed.includes("from '@useavalon/avalon'");
411
-
412
- if (!hasAvalonImport) {
413
- const firstImport = /^(import\s.+?from\s+.+?\n)/m.exec(transformed);
414
- if (firstImport) {
415
- const pos = transformed.indexOf(firstImport[0]) + firstImport[0].length;
416
- const line = 'import { renderIsland as __AvalonRenderIsland } from "@useavalon/avalon";\n';
417
- transformed = transformed.slice(0, pos) + line + transformed.slice(pos);
418
- }
419
- }
420
-
421
- // Replace JSX calls with renderIsland await expressions
422
- for (const island of islandImports) {
423
- const srcPath = resolveIslandSrc(island.importPath, id);
424
- const fw = detectFramework(srcPath);
425
-
426
- transformed = replaceJsxCalls(transformed, island.localName, srcPath, fw);
427
- }
428
-
429
- // Comment out the original imports (keep for CSS graph but don't use the binding)
430
- for (const island of islandImports) {
431
- const importRe = new RegExp(
432
- `import\\s+${island.localName}\\s+from\\s+(['"][^'"]+['"])`,
433
- 'g'
434
- );
435
- transformed = transformed.replace(
436
- importRe,
437
- `import $1; // [mdx-island-transform] kept for CSS: ${island.localName}`
438
- );
439
- }
440
-
441
- // Make the MDX content function async so we can use await
442
- // Transform: function _createMdxContent(props) {
443
- // Into: async function _createMdxContent(props) {
444
- transformed = transformed.replace(
445
- /function\s+_createMdxContent\s*\(/g,
446
- 'async function _createMdxContent('
447
- );
448
-
449
- // Also make the default export async if it wraps _createMdxContent
450
- // Transform: export default function MDXContent(props = {}) {
451
- // Into: export default async function MDXContent(props = {}) {
452
- transformed = transformed.replace(
453
- /export\s+default\s+function\s+MDXContent\s*\(/g,
454
- 'export default async function MDXContent('
455
- );
456
-
457
- if (verbose) {
458
- console.log('[mdx-island-transform] Transformed ' + id);
459
- }
460
-
461
- return { code: transformed, map: null };
462
- },
463
- };
464
- }
@@ -1,98 +0,0 @@
1
- import type { Plugin } from 'vite';
2
- import type { Pluggable } from 'unified';
3
-
4
- export interface MDXPluginOptions {
5
- remarkPlugins?: Pluggable[];
6
- rehypePlugins?: Pluggable[];
7
- development?: boolean;
8
- jsxImportSource?: string;
9
- /** Enable syntax highlighting via rehype-highlight (default: true) */
10
- syntaxHighlighting?: boolean;
11
- }
12
-
13
- /**
14
- * Creates and configures the MDX Vite plugin
15
- *
16
- * This function sets up the MDX plugin with:
17
- * - Frontmatter processing (remark-frontmatter, remark-mdx-frontmatter)
18
- * - GitHub Flavored Markdown support (remark-gfm)
19
- * - Syntax highlighting (rehype-highlight) - enabled by default
20
- * - Custom remark/rehype plugins
21
- * - JSX runtime configuration
22
- *
23
- * @param options - MDX plugin configuration options
24
- * @returns Array of Vite plugins (empty if MDX dependencies are not available)
25
- *
26
- * @example
27
- * ```ts
28
- * // Basic usage with defaults
29
- * const plugins = await createMDXPlugin();
30
- *
31
- * // With React JSX runtime
32
- * const plugins = await createMDXPlugin({ jsxImportSource: 'react' });
33
- *
34
- * // Disable syntax highlighting
35
- * const plugins = await createMDXPlugin({ syntaxHighlighting: false });
36
- * ```
37
- */
38
- export async function createMDXPlugin(options: MDXPluginOptions = {}): Promise<Plugin[]> {
39
- const {
40
- remarkPlugins = [],
41
- rehypePlugins = [],
42
- development = false,
43
- jsxImportSource = 'preact',
44
- syntaxHighlighting = true,
45
- } = options;
46
-
47
- try {
48
- // Load the core MDX plugin
49
- const { default: mdx } = await import('@mdx-js/rollup');
50
-
51
- // Load remark plugins for frontmatter processing and GFM support
52
- const { default: remarkFrontmatter } = await import('remark-frontmatter');
53
- const { default: remarkMdxFrontmatter } = await import('remark-mdx-frontmatter');
54
- const { default: remarkGfm } = await import('remark-gfm');
55
-
56
- // Build rehype plugins array based on options
57
- const finalRehypePlugins: Pluggable[] = [];
58
-
59
- // Add syntax highlighting if enabled
60
- if (syntaxHighlighting) {
61
- try {
62
- const { default: rehypeHighlight } = await import('rehype-highlight');
63
- finalRehypePlugins.push(rehypeHighlight);
64
- } catch {
65
- console.warn('[avalon:mdx] rehype-highlight not available, syntax highlighting disabled');
66
- }
67
- }
68
-
69
- // Add user-provided rehype plugins
70
- finalRehypePlugins.push(...rehypePlugins);
71
-
72
- // Configure MDX plugin with frontmatter processing, GFM support, and optional syntax highlighting
73
- const mdxPlugin = mdx({
74
- // Plugin chains - frontmatter must come first, then export as named exports
75
- remarkPlugins: [remarkFrontmatter, remarkMdxFrontmatter, remarkGfm, ...remarkPlugins],
76
- rehypePlugins: finalRehypePlugins,
77
-
78
- // JSX configuration for the specified runtime
79
- jsxImportSource: jsxImportSource,
80
-
81
- // Development vs production optimizations
82
- development,
83
-
84
- // Ensure proper module format
85
- format: 'mdx',
86
- });
87
-
88
- return [mdxPlugin];
89
- } catch (error) {
90
- const errorMessage = error instanceof Error ? error.message : String(error);
91
- console.error('❌ Failed to configure MDX plugin:', errorMessage);
92
- console.warn('💡 Install missing dependencies or check import map');
93
-
94
- // Always return empty array to allow server to start without MDX
95
- console.warn('⚠️ MDX plugin disabled - .mdx files will not be processed');
96
- return [];
97
- }
98
- }