@useavalon/avalon 0.1.11 → 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 (141) hide show
  1. package/README.md +54 -54
  2. package/mod.ts +302 -302
  3. package/package.json +49 -26
  4. package/src/build/integration-bundler-plugin.ts +116 -116
  5. package/src/build/integration-config.ts +168 -168
  6. package/src/build/integration-detection-plugin.ts +117 -117
  7. package/src/build/integration-resolver-plugin.ts +90 -90
  8. package/src/build/island-manifest.ts +269 -269
  9. package/src/build/island-types-generator.ts +476 -476
  10. package/src/build/mdx-island-transform.ts +464 -464
  11. package/src/build/mdx-plugin.ts +98 -98
  12. package/src/build/page-island-transform.ts +598 -598
  13. package/src/build/prop-extractors/index.ts +21 -21
  14. package/src/build/prop-extractors/lit.ts +140 -140
  15. package/src/build/prop-extractors/qwik.ts +16 -16
  16. package/src/build/prop-extractors/solid.ts +125 -125
  17. package/src/build/prop-extractors/svelte.ts +194 -194
  18. package/src/build/prop-extractors/vue.ts +111 -111
  19. package/src/build/sidecar-file-manager.ts +104 -104
  20. package/src/build/sidecar-renderer.ts +30 -30
  21. package/src/client/adapters/index.ts +21 -13
  22. package/src/client/components.ts +35 -35
  23. package/src/client/css-hmr-handler.ts +344 -344
  24. package/src/client/framework-adapter.ts +462 -462
  25. package/src/client/hmr-coordinator.ts +396 -396
  26. package/src/client/hmr-error-overlay.js +533 -533
  27. package/src/client/main.js +824 -816
  28. package/src/client/types/framework-runtime.d.ts +68 -68
  29. package/src/client/types/vite-hmr.d.ts +46 -46
  30. package/src/client/types/vite-virtual-modules.d.ts +70 -60
  31. package/src/components/Image.tsx +123 -123
  32. package/src/components/IslandErrorBoundary.tsx +145 -145
  33. package/src/components/LayoutDataErrorBoundary.tsx +141 -141
  34. package/src/components/LayoutErrorBoundary.tsx +127 -127
  35. package/src/components/PersistentIsland.tsx +52 -52
  36. package/src/components/StreamingErrorBoundary.tsx +233 -233
  37. package/src/components/StreamingLayout.tsx +538 -538
  38. package/src/core/components/component-analyzer.ts +192 -192
  39. package/src/core/components/component-detection.ts +508 -508
  40. package/src/core/components/enhanced-framework-detector.ts +500 -500
  41. package/src/core/components/framework-registry.ts +563 -563
  42. package/src/core/content/mdx-processor.ts +46 -46
  43. package/src/core/integrations/index.ts +19 -19
  44. package/src/core/integrations/loader.ts +125 -125
  45. package/src/core/integrations/registry.ts +175 -175
  46. package/src/core/islands/island-persistence.ts +325 -325
  47. package/src/core/islands/island-state-serializer.ts +258 -258
  48. package/src/core/islands/persistent-island-context.tsx +80 -80
  49. package/src/core/islands/use-persistent-state.ts +68 -68
  50. package/src/core/layout/enhanced-layout-resolver.ts +322 -322
  51. package/src/core/layout/layout-cache-manager.ts +485 -485
  52. package/src/core/layout/layout-composer.ts +357 -357
  53. package/src/core/layout/layout-data-loader.ts +516 -516
  54. package/src/core/layout/layout-discovery.ts +243 -243
  55. package/src/core/layout/layout-matcher.ts +299 -299
  56. package/src/core/layout/layout-types.ts +110 -110
  57. package/src/core/modules/framework-module-resolver.ts +273 -273
  58. package/src/islands/component-analysis.ts +213 -213
  59. package/src/islands/css-utils.ts +565 -565
  60. package/src/islands/discovery/index.ts +80 -80
  61. package/src/islands/discovery/registry.ts +340 -340
  62. package/src/islands/discovery/resolver.ts +477 -477
  63. package/src/islands/discovery/scanner.ts +386 -386
  64. package/src/islands/discovery/types.ts +117 -117
  65. package/src/islands/discovery/validator.ts +544 -544
  66. package/src/islands/discovery/watcher.ts +368 -368
  67. package/src/islands/framework-detection.ts +428 -428
  68. package/src/islands/integration-loader.ts +490 -490
  69. package/src/islands/island.tsx +565 -565
  70. package/src/islands/render-cache.ts +550 -550
  71. package/src/islands/types.ts +80 -80
  72. package/src/islands/universal-css-collector.ts +157 -157
  73. package/src/islands/universal-head-collector.ts +137 -137
  74. package/src/layout-system.d.ts +592 -592
  75. package/src/layout-system.ts +218 -218
  76. package/src/middleware/discovery.ts +268 -268
  77. package/src/middleware/executor.ts +315 -315
  78. package/src/middleware/index.ts +76 -76
  79. package/src/middleware/types.ts +99 -99
  80. package/src/nitro/build-config.ts +575 -575
  81. package/src/nitro/config.ts +483 -483
  82. package/src/nitro/error-handler.ts +636 -636
  83. package/src/nitro/index.ts +173 -173
  84. package/src/nitro/island-manifest.ts +584 -584
  85. package/src/nitro/middleware-adapter.ts +260 -260
  86. package/src/nitro/renderer.ts +1471 -1471
  87. package/src/nitro/route-discovery.ts +439 -439
  88. package/src/nitro/types.ts +321 -321
  89. package/src/render/collect-css.ts +198 -198
  90. package/src/render/error-pages.ts +79 -79
  91. package/src/render/isolated-ssr-renderer.ts +654 -654
  92. package/src/render/ssr.ts +1030 -1030
  93. package/src/schemas/api.ts +30 -30
  94. package/src/schemas/core.ts +64 -64
  95. package/src/schemas/index.ts +212 -212
  96. package/src/schemas/layout.ts +279 -279
  97. package/src/schemas/routing/index.ts +38 -38
  98. package/src/schemas/routing.ts +376 -376
  99. package/src/types/as-island.ts +20 -20
  100. package/src/types/image.d.ts +106 -106
  101. package/src/types/index.d.ts +22 -22
  102. package/src/types/island-jsx.d.ts +33 -33
  103. package/src/types/island-prop.d.ts +20 -20
  104. package/src/types/layout.ts +285 -285
  105. package/src/types/mdx.d.ts +6 -6
  106. package/src/types/routing.ts +555 -555
  107. package/src/types/types.ts +5 -5
  108. package/src/types/urlpattern.d.ts +49 -49
  109. package/src/types/vite-env.d.ts +11 -11
  110. package/src/utils/dev-logger.ts +299 -299
  111. package/src/utils/fs.ts +151 -151
  112. package/src/vite-plugin/auto-discover.ts +551 -551
  113. package/src/vite-plugin/config.ts +266 -266
  114. package/src/vite-plugin/errors.ts +127 -127
  115. package/src/vite-plugin/image-optimization.ts +156 -156
  116. package/src/vite-plugin/integration-activator.ts +126 -126
  117. package/src/vite-plugin/island-sidecar-plugin.ts +176 -176
  118. package/src/vite-plugin/module-discovery.ts +189 -189
  119. package/src/vite-plugin/nitro-integration.ts +1354 -1354
  120. package/src/vite-plugin/plugin.ts +403 -409
  121. package/src/vite-plugin/types.ts +327 -327
  122. package/src/vite-plugin/validation.ts +228 -228
  123. package/src/client/adapters/index.js +0 -12
  124. package/src/client/adapters/lit-adapter.js +0 -467
  125. package/src/client/adapters/lit-adapter.ts +0 -654
  126. package/src/client/adapters/preact-adapter.js +0 -223
  127. package/src/client/adapters/preact-adapter.ts +0 -331
  128. package/src/client/adapters/qwik-adapter.js +0 -259
  129. package/src/client/adapters/qwik-adapter.ts +0 -345
  130. package/src/client/adapters/react-adapter.js +0 -220
  131. package/src/client/adapters/react-adapter.ts +0 -353
  132. package/src/client/adapters/solid-adapter.js +0 -295
  133. package/src/client/adapters/solid-adapter.ts +0 -451
  134. package/src/client/adapters/svelte-adapter.js +0 -368
  135. package/src/client/adapters/svelte-adapter.ts +0 -524
  136. package/src/client/adapters/vue-adapter.js +0 -278
  137. package/src/client/adapters/vue-adapter.ts +0 -467
  138. package/src/client/components.js +0 -23
  139. package/src/client/css-hmr-handler.js +0 -263
  140. package/src/client/framework-adapter.js +0 -283
  141. package/src/client/hmr-coordinator.js +0 -274
@@ -1,21 +1,21 @@
1
- export { type PropExtractionResult, FALLBACK_PROPS, extractVueProps } from "./vue.ts";
2
- export { extractSvelteProps } from "./svelte.ts";
3
- export { extractLitProps } from "./lit.ts";
4
- export { extractSolidProps } from "./solid.ts";
5
- export { extractQwikProps } from "./qwik.ts";
6
-
7
- import type { PropExtractionResult } from "./vue.ts";
8
- import { extractVueProps } from "./vue.ts";
9
- import { extractSvelteProps } from "./svelte.ts";
10
- import { extractLitProps } from "./lit.ts";
11
- import { extractSolidProps } from "./solid.ts";
12
- import { extractQwikProps } from "./qwik.ts";
13
-
14
- /** Maps framework name to its prop extractor function */
15
- export const EXTRACTOR_MAP: Record<string, (source: string) => PropExtractionResult> = {
16
- vue: extractVueProps,
17
- svelte: extractSvelteProps,
18
- lit: extractLitProps,
19
- solid: extractSolidProps,
20
- qwik: extractQwikProps,
21
- };
1
+ export { type PropExtractionResult, FALLBACK_PROPS, extractVueProps } from "./vue.ts";
2
+ export { extractSvelteProps } from "./svelte.ts";
3
+ export { extractLitProps } from "./lit.ts";
4
+ export { extractSolidProps } from "./solid.ts";
5
+ export { extractQwikProps } from "./qwik.ts";
6
+
7
+ import type { PropExtractionResult } from "./vue.ts";
8
+ import { extractVueProps } from "./vue.ts";
9
+ import { extractSvelteProps } from "./svelte.ts";
10
+ import { extractLitProps } from "./lit.ts";
11
+ import { extractSolidProps } from "./solid.ts";
12
+ import { extractQwikProps } from "./qwik.ts";
13
+
14
+ /** Maps framework name to its prop extractor function */
15
+ export const EXTRACTOR_MAP: Record<string, (source: string) => PropExtractionResult> = {
16
+ vue: extractVueProps,
17
+ svelte: extractSvelteProps,
18
+ lit: extractLitProps,
19
+ solid: extractSolidProps,
20
+ qwik: extractQwikProps,
21
+ };
@@ -1,140 +1,140 @@
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
+ 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 +1,16 @@
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
+ 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 +1,125 @@
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
+ 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
+ }