@useavalon/avalon 0.1.13 → 0.1.14

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,140 +0,0 @@
1
- import { FALLBACK_PROPS, type PropExtractionResult } from "./vue.ts";
2
-
3
- /** Mapping from Lit type constructors to TypeScript type strings */
4
- const LIT_TYPE_MAP: Record<string, string> = {
5
- String: "string",
6
- Number: "number",
7
- Boolean: "boolean",
8
- Array: "unknown[]",
9
- Object: "Record<string, unknown>",
10
- };
11
-
12
- /**
13
- * Extract props from a Lit element source string.
14
- *
15
- * Parses `static properties = { ... }` blocks and maps Lit type
16
- * constructors to TypeScript types. Properties with `state: true`
17
- * are excluded (internal state, not public props).
18
- *
19
- * Never throws — returns fallback on any failure.
20
- */
21
- export function extractLitProps(source: string): PropExtractionResult {
22
- try {
23
- const block = extractStaticPropertiesBlock(source);
24
- if (block === null) {
25
- return { propsType: FALLBACK_PROPS, fallback: true };
26
- }
27
-
28
- const props = parsePropertyEntries(block);
29
- if (props.length === 0) {
30
- return { propsType: FALLBACK_PROPS, fallback: true };
31
- }
32
-
33
- const fields = props.map((p) => p.name + "?: " + p.tsType).join("; ");
34
- const propsType = "{ " + fields + " }";
35
- return { propsType, fallback: false };
36
- } catch {
37
- console.warn(
38
- "[avalon] Failed to extract Lit props — falling back to Record<string, unknown>",
39
- );
40
- return { propsType: FALLBACK_PROPS, fallback: true };
41
- }
42
- }
43
-
44
- /**
45
- * Extract the content inside `static properties = { ... }`.
46
- * Uses brace-counting to handle nested objects.
47
- * Returns the inner content (without outer braces), or null if not found.
48
- */
49
- function extractStaticPropertiesBlock(source: string): string | null {
50
- const marker = /static\s+properties\s*=\s*\{/;
51
- const match = marker.exec(source);
52
- if (!match) {
53
- return null;
54
- }
55
-
56
- // Position of the opening brace
57
- const openBrace = match.index + match[0].length - 1;
58
- let depth = 1;
59
- let i = openBrace + 1;
60
-
61
- while (i < source.length && depth > 0) {
62
- if (source[i] === "{") depth++;
63
- else if (source[i] === "}") depth--;
64
- i++;
65
- }
66
-
67
- if (depth !== 0) {
68
- return null;
69
- }
70
-
71
- // Return content between the outer braces
72
- return source.slice(openBrace + 1, i - 1);
73
- }
74
-
75
- interface ParsedProp {
76
- name: string;
77
- tsType: string;
78
- }
79
-
80
- /**
81
- * Parse individual property entries from the static properties block content.
82
- * Each entry looks like: `propName: { type: Constructor, ... }`
83
- * Filters out entries with `state: true`.
84
- */
85
- function parsePropertyEntries(block: string): ParsedProp[] {
86
- const props: ParsedProp[] = [];
87
-
88
- // Match each property entry: name: { ... }
89
- // We use a regex to find property names followed by `{`, then brace-count
90
- const entryRegex = /(\w+)\s*:\s*\{/g;
91
- let entryMatch: RegExpExecArray | null;
92
-
93
- while ((entryMatch = entryRegex.exec(block)) !== null) {
94
- const name = entryMatch[1];
95
- const openIdx = entryMatch.index + entryMatch[0].length - 1;
96
-
97
- // Extract the balanced { ... } for this entry
98
- const entryBody = extractBalancedBraces(block, openIdx);
99
- if (entryBody === null) continue;
100
-
101
- // Skip state properties
102
- if (/\bstate\s*:\s*true\b/.test(entryBody)) continue;
103
-
104
- // Extract the type constructor
105
- const typeMatch = new RegExp(/\btype\s*:\s*(\w+)/).exec(entryBody);
106
- const litType = typeMatch ? typeMatch[1] : null;
107
- const tsType = litType && litType in LIT_TYPE_MAP
108
- ? LIT_TYPE_MAP[litType]
109
- : "unknown";
110
-
111
- props.push({ name, tsType });
112
-
113
- // Advance regex past this entry to avoid re-matching nested braces
114
- entryRegex.lastIndex = openIdx + (entryBody.length);
115
- }
116
-
117
- return props;
118
- }
119
-
120
- /**
121
- * Extract a balanced `{ ... }` block starting at the given index.
122
- * Returns the content between the braces (excluding outer braces), or null.
123
- */
124
- function extractBalancedBraces(source: string, startIdx: number): string | null {
125
- if (source[startIdx] !== "{") return null;
126
-
127
- let depth = 0;
128
- let i = startIdx;
129
-
130
- while (i < source.length) {
131
- if (source[i] === "{") depth++;
132
- else if (source[i] === "}") depth--;
133
- if (depth === 0) {
134
- return source.slice(startIdx + 1, i);
135
- }
136
- i++;
137
- }
138
-
139
- return null;
140
- }
@@ -1,16 +0,0 @@
1
- import type { PropExtractionResult } from "./vue.ts";
2
- import { FALLBACK_PROPS } from "./vue.ts";
3
-
4
- /**
5
- * Extract props type from a Qwik component file.
6
- * Qwik components use component$(() => ...) — props are typed via the
7
- * generic parameter or a Props interface. For now we fall back to
8
- * Record<string, unknown> since Qwik's JSX types aren't Preact-compatible
9
- * and the sidecar just needs to expose the island prop.
10
- */
11
- export function extractQwikProps(_source: string): PropExtractionResult {
12
- return {
13
- propsType: FALLBACK_PROPS,
14
- fallback: false
15
- };
16
- }
@@ -1,125 +0,0 @@
1
- import { FALLBACK_PROPS, type PropExtractionResult } from "./vue.ts";
2
-
3
- /**
4
- * Extract props from a Solid component source string.
5
- *
6
- * Supports two patterns:
7
- * 1. `export default function Name(props: { ... })` — named function export
8
- * 2. `export default (props: { ... }) =>` — arrow function export
9
- *
10
- * Uses brace-counting to handle nested types in the props parameter.
11
- * Never throws — returns fallback on any failure.
12
- */
13
- export function extractSolidProps(source: string): PropExtractionResult {
14
- try {
15
- // Try named function pattern first
16
- const namedResult = extractFromNamedFunction(source);
17
- if (namedResult !== null) {
18
- return { propsType: namedResult, fallback: false };
19
- }
20
-
21
- // Try arrow function pattern
22
- const arrowResult = extractFromArrowFunction(source);
23
- if (arrowResult !== null) {
24
- return { propsType: arrowResult, fallback: false };
25
- }
26
-
27
- return { propsType: FALLBACK_PROPS, fallback: true };
28
- } catch {
29
- console.warn(
30
- "[avalon] Failed to extract Solid props — falling back to Record<string, unknown>",
31
- );
32
- return { propsType: FALLBACK_PROPS, fallback: true };
33
- }
34
- }
35
-
36
- /**
37
- * Extract props type from `export default function Name(props: { ... })`.
38
- * Returns the type literal string, or null if not found.
39
- */
40
- function extractFromNamedFunction(source: string): string | null {
41
- // Match: export default function <Name>(props:
42
- const regex = /export\s+default\s+function\s+\w+\s*\(\s*props\s*:\s*/;
43
- const match = regex.exec(source);
44
- if (!match) {
45
- return null;
46
- }
47
-
48
- const typeStart = match.index + match[0].length;
49
- return extractPropsType(source, typeStart);
50
- }
51
-
52
- /**
53
- * Extract props type from `export default (props: { ... }) =>`.
54
- * Returns the type literal string, or null if not found.
55
- */
56
- function extractFromArrowFunction(source: string): string | null {
57
- // Match: export default (props:
58
- const regex = /export\s+default\s+\(\s*props\s*:\s*/;
59
- const match = regex.exec(source);
60
- if (!match) {
61
- return null;
62
- }
63
-
64
- const typeStart = match.index + match[0].length;
65
- return extractPropsType(source, typeStart);
66
- }
67
-
68
- /**
69
- * Extract a props type starting at the given index in the source.
70
- * Handles both inline type literals `{ ... }` using brace-counting
71
- * and simple type references like `Props`.
72
- * Returns the trimmed type string, or null if extraction fails.
73
- */
74
- function extractPropsType(source: string, startIdx: number): string | null {
75
- // Skip leading whitespace
76
- let i = startIdx;
77
- while (i < source.length && /\s/.test(source[i])) {
78
- i++;
79
- }
80
-
81
- if (i >= source.length) {
82
- return null;
83
- }
84
-
85
- // If it starts with `{`, use brace-counting for inline type literal
86
- if (source[i] === "{") {
87
- return extractBalancedBraces(source, i);
88
- }
89
-
90
- // Otherwise it's a type reference — read until `)` or `,`
91
- const remaining = source.slice(i);
92
- const refMatch = new RegExp(/^([A-Za-z_$][\w$]*(?:<[^>]*>)?)/).exec(remaining);
93
- if (refMatch) {
94
- return refMatch[1].trim();
95
- }
96
-
97
- return null;
98
- }
99
-
100
- /**
101
- * Extract a balanced `{ ... }` block starting at the given index.
102
- * Returns the full string including the outer braces, or null if unbalanced.
103
- */
104
- function extractBalancedBraces(source: string, startIdx: number): string | null {
105
- if (source[startIdx] !== "{") {
106
- return null;
107
- }
108
-
109
- let depth = 0;
110
- let i = startIdx;
111
-
112
- while (i < source.length) {
113
- if (source[i] === "{") {
114
- depth++;
115
- } else if (source[i] === "}") {
116
- depth--;
117
- if (depth === 0) {
118
- return source.slice(startIdx, i + 1).trim();
119
- }
120
- }
121
- i++;
122
- }
123
-
124
- return null; // unbalanced
125
- }
@@ -1,194 +0,0 @@
1
- import { FALLBACK_PROPS, type PropExtractionResult } from "./vue.ts";
2
-
3
- /**
4
- * Extract props from a Svelte component source string.
5
- *
6
- * Supports three patterns:
7
- * 1. `let { ... }: { ... } = $props()` — inline type literal
8
- * 2. `let { ... }: TypeName = $props()` or `let name: TypeName = $props()`
9
- * — named type resolved from an interface/type declaration in the script block
10
- * 3. `export let name: type` — Svelte 4 style, collected into a type literal
11
- *
12
- * Never throws — returns fallback on any failure.
13
- */
14
- export function extractSvelteProps(source: string): PropExtractionResult {
15
- try {
16
- const scriptContent = extractScriptContent(source);
17
- if (scriptContent === null) {
18
- return { propsType: FALLBACK_PROPS, fallback: true };
19
- }
20
-
21
- // Try $props() patterns first (Svelte 5)
22
- const propsResult = extractFromDollarProps(scriptContent);
23
- if (propsResult !== null) {
24
- return { propsType: propsResult, fallback: false };
25
- }
26
-
27
- // Try export let pattern (Svelte 4)
28
- const exportLetResult = extractFromExportLet(scriptContent);
29
- if (exportLetResult !== null) {
30
- return { propsType: exportLetResult, fallback: false };
31
- }
32
-
33
- return { propsType: FALLBACK_PROPS, fallback: true };
34
- } catch {
35
- console.warn(
36
- "[avalon] Failed to extract Svelte props — falling back to Record<string, unknown>",
37
- );
38
- return { propsType: FALLBACK_PROPS, fallback: true };
39
- }
40
- }
41
-
42
- /**
43
- * Extract the content of the `<script>` or `<script lang="ts">` block.
44
- * Returns `null` if no script block is found.
45
- */
46
- function extractScriptContent(source: string): string | null {
47
- const scriptRegex = /<script\b[^>]*>([\s\S]*?)<\/script>/i;
48
- const match = new RegExp(scriptRegex).exec(source);
49
- return match ? match[1] : null;
50
- }
51
-
52
- /**
53
- * Extract props type from `$props()` call patterns.
54
- *
55
- * Handles:
56
- * - `let { ... }: { ... } = $props()` (inline type literal)
57
- * - `let { ... }: TypeName = $props()` (named type, destructuring)
58
- * - `let name: TypeName = $props()` (named type, non-destructuring)
59
- */
60
- function extractFromDollarProps(scriptContent: string): string | null {
61
- // Match: let <binding> : <type> = $props()
62
- // The binding can be `{ ... }` (destructuring) or a simple identifier
63
- const propsCallRegex =
64
- /let\s+(?:\{[^}]*\}|\w+)\s*:\s*([\s\S]*?)\s*=\s*\$props\s*\(\s*\)/;
65
- const match = new RegExp(propsCallRegex).exec(scriptContent);
66
- if (!match) {
67
- return null;
68
- }
69
-
70
- const typeAnnotation = match[1].trim();
71
- if (typeAnnotation.length === 0) {
72
- return null;
73
- }
74
-
75
- // If it starts with `{`, it's an inline type literal — return as-is
76
- if (typeAnnotation.startsWith("{")) {
77
- // Validate balanced braces
78
- if (!areBracesBalanced(typeAnnotation)) {
79
- console.warn(
80
- "[avalon] Unbalanced braces in Svelte $props() type — falling back",
81
- );
82
- return null;
83
- }
84
- return typeAnnotation;
85
- }
86
-
87
- // Otherwise it's a named type — resolve from interface/type in the script
88
- return resolveNamedType(scriptContent, typeAnnotation);
89
- }
90
-
91
- /**
92
- * Resolve a named type (interface or type alias) from the script content.
93
- * Returns the body as a type literal string, or null if not found.
94
- */
95
- function resolveNamedType(
96
- scriptContent: string,
97
- typeName: string,
98
- ): string | null {
99
- // Try interface first: `interface TypeName { ... }`
100
- const interfaceRegex = new RegExp(
101
- String.raw`interface\s+${escapeRegex(typeName)}\s*\{`,
102
- );
103
- const interfaceMatch = interfaceRegex.exec(scriptContent);
104
- if (interfaceMatch) {
105
- const startIdx = interfaceMatch.index + interfaceMatch[0].length - 1; // position of `{`
106
- const body = extractBalancedBraces(scriptContent, startIdx);
107
- if (body !== null) {
108
- return body;
109
- }
110
- }
111
-
112
- // Try type alias: `type TypeName = { ... }`
113
- const typeAliasRegex = new RegExp(
114
- String.raw`type\s+${escapeRegex(typeName)}\s*=\s*\{`,
115
- );
116
- const typeAliasMatch = typeAliasRegex.exec(scriptContent);
117
- if (typeAliasMatch) {
118
- const startIdx =
119
- typeAliasMatch.index + typeAliasMatch[0].length - 1; // position of `{`
120
- const body = extractBalancedBraces(scriptContent, startIdx);
121
- if (body !== null) {
122
- return body;
123
- }
124
- }
125
-
126
- return null;
127
- }
128
-
129
- /**
130
- * Extract a balanced `{ ... }` block starting at the given index.
131
- * Returns the full string including the outer braces, or null if unbalanced.
132
- */
133
- function extractBalancedBraces(source: string, startIdx: number): string | null {
134
- if (source[startIdx] !== "{") {
135
- return null;
136
- }
137
-
138
- let depth = 0;
139
- let i = startIdx;
140
-
141
- while (i < source.length) {
142
- if (source[i] === "{") {
143
- depth++;
144
- } else if (source[i] === "}") {
145
- depth--;
146
- if (depth === 0) {
147
- return source.slice(startIdx, i + 1).trim();
148
- }
149
- }
150
- i++;
151
- }
152
-
153
- return null; // unbalanced
154
- }
155
-
156
- /**
157
- * Extract props from `export let` declarations (Svelte 4 pattern).
158
- * Collects all `export let name: type` and builds a type literal.
159
- */
160
- function extractFromExportLet(scriptContent: string): string | null {
161
- const exportLetRegex = /export\s+let\s+(\w+)\s*:\s*([^;=]+)/g;
162
- const props: string[] = [];
163
- let match: RegExpExecArray | null;
164
-
165
- while ((match = exportLetRegex.exec(scriptContent)) !== null) {
166
- const name = match[1].trim();
167
- const type = match[2].trim();
168
- if (name && type) {
169
- props.push(`${name}: ${type}`);
170
- }
171
- }
172
-
173
- if (props.length === 0) {
174
- return null;
175
- }
176
-
177
- return `{ ${props.join("; ")} }`;
178
- }
179
-
180
- /** Check if braces are balanced in a string */
181
- function areBracesBalanced(str: string): boolean {
182
- let depth = 0;
183
- for (const ch of str) {
184
- if (ch === "{") depth++;
185
- else if (ch === "}") depth--;
186
- if (depth < 0) return false;
187
- }
188
- return depth === 0;
189
- }
190
-
191
- /** Escape special regex characters in a string */
192
- function escapeRegex(str: string): string {
193
- return str.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
194
- }
@@ -1,111 +0,0 @@
1
- /** Result of prop extraction */
2
- export interface PropExtractionResult {
3
- /** The TypeScript type literal for props, e.g. "{ count?: number }" */
4
- propsType: string;
5
- /** Whether extraction succeeded or fell back */
6
- fallback: boolean;
7
- }
8
-
9
- /** Fallback props type used when extraction fails or no props are found */
10
- export const FALLBACK_PROPS = "Record<string, unknown>";
11
-
12
- /**
13
- * Extract props from a Vue SFC source string.
14
- *
15
- * Looks for `defineProps<{...}>()` inside a `<script setup>` or
16
- * `<script setup lang="ts">` block. Uses brace-counting to handle
17
- * nested types within the angle brackets.
18
- *
19
- * Never throws — returns fallback on any failure.
20
- */
21
- export function extractVueProps(source: string): PropExtractionResult {
22
- try {
23
- // 1. Extract the <script setup ...> block content
24
- const scriptContent = extractScriptSetupContent(source);
25
- if (scriptContent === null) {
26
- return { propsType: FALLBACK_PROPS, fallback: true };
27
- }
28
-
29
- // 2. Find defineProps<...>() and extract the type argument
30
- const propsType = extractDefinePropsType(scriptContent);
31
- if (propsType === null) {
32
- return { propsType: FALLBACK_PROPS, fallback: true };
33
- }
34
-
35
- return { propsType, fallback: false };
36
- } catch {
37
- console.warn(
38
- "[avalon] Failed to extract Vue props — falling back to Record<string, unknown>",
39
- );
40
- return { propsType: FALLBACK_PROPS, fallback: true };
41
- }
42
- }
43
-
44
- /**
45
- * Extract the content of the `<script setup>` block from a Vue SFC source.
46
- * Returns `null` if no `<script setup>` block is found.
47
- */
48
- function extractScriptSetupContent(source: string): string | null {
49
- // Match <script setup> or <script setup lang="ts"> (and other attrs)
50
- // The 's' flag makes . match newlines
51
- const scriptSetupRegex =
52
- /<script\b[^>]*\bsetup\b[^>]*>([\s\S]*?)<\/script>/i;
53
- const match = new RegExp(scriptSetupRegex).exec(source);
54
- return match ? match[1] : null;
55
- }
56
-
57
- /** Check that `{` and `}` are balanced in a string */
58
- function hasBracesBalanced(str: string): boolean {
59
- let depth = 0;
60
- for (const ch of str) {
61
- if (ch === "{") depth++;
62
- else if (ch === "}") depth--;
63
- if (depth < 0) return false;
64
- }
65
- return depth === 0;
66
- }
67
-
68
- /**
69
- * Extract the type argument from `defineProps<TYPE>()` using angle-bracket
70
- * counting to handle nested generics and object types.
71
- *
72
- * After extraction, validates that braces are balanced in the result.
73
- * Returns the trimmed type string, or `null` if not found.
74
- */
75
- function extractDefinePropsType(scriptContent: string): string | null {
76
- const marker = "defineProps<";
77
- const idx = scriptContent.indexOf(marker);
78
- if (idx === -1) {
79
- return null;
80
- }
81
-
82
- const start = idx + marker.length;
83
- let depth = 1;
84
- let i = start;
85
-
86
- while (i < scriptContent.length && depth > 0) {
87
- const ch = scriptContent[i];
88
- if (ch === "<") depth++;
89
- else if (ch === ">") depth--;
90
- if (depth > 0) i++;
91
- }
92
-
93
- if (depth !== 0) {
94
- console.warn("[avalon] Unbalanced angle brackets in defineProps<...> — falling back");
95
- return null;
96
- }
97
-
98
- const typeStr = scriptContent.slice(start, i).trim();
99
- if (typeStr.length === 0) {
100
- return null;
101
- }
102
-
103
- if (!hasBracesBalanced(typeStr)) {
104
- console.warn("[avalon] Unbalanced braces in defineProps type — falling back");
105
- return null;
106
- }
107
-
108
- return typeStr;
109
- }
110
-
111
-
@@ -1,104 +0,0 @@
1
- import path from "node:path";
2
- import { readFile, writeFile, unlink, stat } from "node:fs/promises";
3
-
4
- /**
5
- * Supported compound extensions for island files, ordered longest-first
6
- * so that `.solid.tsx` matches before `.tsx`.
7
- *
8
- * Sidecar naming strategy per extension type:
9
- * - `.vue`, `.svelte` → `Name.d.vue.ts`, `Name.d.svelte.ts`
10
- * (foreign extensions — allowArbitraryExtensions picks these up)
11
- * - `.lit.ts` → `Name.lit.d.ts`
12
- * (TypeScript declaration file lookup: for `Name.lit.ts` TS looks for `Name.lit.d.ts`)
13
- * - `.solid.tsx` → `Name.solid.d.ts`
14
- * (TypeScript declaration file lookup: for `Name.solid.tsx` TS looks for `Name.solid.d.ts`)
15
- * - `.qwik.tsx` → `Name.qwik.d.ts`
16
- * (same as solid.tsx)
17
- */
18
- const COMPOUND_EXTENSIONS = [".solid.tsx", ".qwik.tsx", ".lit.ts", ".svelte", ".vue"];
19
-
20
- /**
21
- * Extensions where TypeScript's native `.d.ts` declaration lookup applies.
22
- * For `Name.lit.ts`, TS looks for `Name.lit.d.ts`.
23
- * For `Name.solid.tsx` / `Name.qwik.tsx`, TS looks for `Name.solid.d.ts` / `Name.qwik.d.ts`.
24
- */
25
- const NATIVE_DECL_EXTENSIONS = new Set([".lit.ts", ".solid.tsx", ".qwik.tsx"]);
26
-
27
- /**
28
- * Compute the sidecar declaration file path for a given island file path.
29
- *
30
- * For simple extensions like `.vue`, the sidecar is `Name.d.vue.ts`.
31
- * For compound extensions like `.lit.ts`, the sidecar is `Name.d.lit.ts`.
32
- * For `.solid.tsx`, the sidecar is `Name.d.solid.tsx.ts`.
33
- */
34
- export function getSidecarPath(islandFilePath: string): string {
35
- const dir = path.dirname(islandFilePath);
36
- const basename = path.basename(islandFilePath);
37
-
38
- for (const ext of COMPOUND_EXTENSIONS) {
39
- if (basename.endsWith(ext)) {
40
- const name = basename.slice(0, -ext.length);
41
- if (NATIVE_DECL_EXTENSIONS.has(ext)) {
42
- // For .lit.ts → Name.lit.d.ts (TS declaration file lookup)
43
- // For .solid.tsx / .qwik.tsx → Name.solid.d.ts / Name.qwik.d.ts
44
- const innerExt = ext.endsWith(".ts") ? ext.slice(0, -3) : ext.slice(0, -4); // strip .ts or .tsx
45
- return path.join(dir, `${name}${innerExt}.d.ts`);
46
- }
47
- // For .vue/.svelte → Name.d.vue.ts / Name.d.svelte.ts (allowArbitraryExtensions)
48
- return path.join(dir, `${name}.d${ext}.ts`);
49
- }
50
- }
51
-
52
- // Fallback
53
- const ext = path.extname(islandFilePath);
54
- const name = basename.slice(0, -ext.length);
55
- return path.join(dir, `${name}.d${ext}.ts`);
56
- }
57
-
58
- /**
59
- * Check if a sidecar file is up-to-date by comparing mtimes.
60
- * Returns `true` if the sidecar exists and is newer than the source file.
61
- */
62
- export async function isSidecarFresh(sourcePath: string, sidecarPath: string): Promise<boolean> {
63
- try {
64
- const [sourceStat, sidecarStat] = await Promise.all([
65
- stat(sourcePath),
66
- stat(sidecarPath),
67
- ]);
68
- return sidecarStat.mtimeMs >= sourceStat.mtimeMs;
69
- } catch {
70
- return false;
71
- }
72
- }
73
-
74
- /**
75
- * Write sidecar content only if it differs from the existing file.
76
- * Returns `true` if a write was performed, `false` if content was already up-to-date.
77
- */
78
- export async function writeSidecarIfChanged(sidecarPath: string, content: string): Promise<boolean> {
79
- try {
80
- const existing = await readFile(sidecarPath, "utf-8");
81
- if (existing === content) {
82
- return false;
83
- }
84
- } catch {
85
- // File doesn't exist yet — that's fine, we'll write it
86
- }
87
-
88
- await writeFile(sidecarPath, content, "utf-8");
89
- return true;
90
- }
91
-
92
- /**
93
- * Delete a sidecar file if it exists.
94
- * Returns `true` if a file was deleted, `false` if it didn't exist.
95
- */
96
- export async function deleteSidecar(sidecarPath: string): Promise<boolean> {
97
- try {
98
- await unlink(sidecarPath);
99
- return true;
100
- } catch {
101
- return false;
102
- }
103
- }
104
-