@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.
- package/README.md +54 -54
- package/mod.ts +302 -302
- package/package.json +49 -26
- package/src/build/integration-bundler-plugin.ts +116 -116
- package/src/build/integration-config.ts +168 -168
- package/src/build/integration-detection-plugin.ts +117 -117
- package/src/build/integration-resolver-plugin.ts +90 -90
- package/src/build/island-manifest.ts +269 -269
- package/src/build/island-types-generator.ts +476 -476
- package/src/build/mdx-island-transform.ts +464 -464
- package/src/build/mdx-plugin.ts +98 -98
- package/src/build/page-island-transform.ts +598 -598
- package/src/build/prop-extractors/index.ts +21 -21
- package/src/build/prop-extractors/lit.ts +140 -140
- package/src/build/prop-extractors/qwik.ts +16 -16
- package/src/build/prop-extractors/solid.ts +125 -125
- package/src/build/prop-extractors/svelte.ts +194 -194
- package/src/build/prop-extractors/vue.ts +111 -111
- package/src/build/sidecar-file-manager.ts +104 -104
- package/src/build/sidecar-renderer.ts +30 -30
- package/src/client/adapters/index.ts +21 -13
- package/src/client/components.ts +35 -35
- package/src/client/css-hmr-handler.ts +344 -344
- package/src/client/framework-adapter.ts +462 -462
- package/src/client/hmr-coordinator.ts +396 -396
- package/src/client/hmr-error-overlay.js +533 -533
- package/src/client/main.js +824 -816
- package/src/client/types/framework-runtime.d.ts +68 -68
- package/src/client/types/vite-hmr.d.ts +46 -46
- package/src/client/types/vite-virtual-modules.d.ts +70 -60
- package/src/components/Image.tsx +123 -123
- package/src/components/IslandErrorBoundary.tsx +145 -145
- package/src/components/LayoutDataErrorBoundary.tsx +141 -141
- package/src/components/LayoutErrorBoundary.tsx +127 -127
- package/src/components/PersistentIsland.tsx +52 -52
- package/src/components/StreamingErrorBoundary.tsx +233 -233
- package/src/components/StreamingLayout.tsx +538 -538
- package/src/core/components/component-analyzer.ts +192 -192
- package/src/core/components/component-detection.ts +508 -508
- package/src/core/components/enhanced-framework-detector.ts +500 -500
- package/src/core/components/framework-registry.ts +563 -563
- package/src/core/content/mdx-processor.ts +46 -46
- package/src/core/integrations/index.ts +19 -19
- package/src/core/integrations/loader.ts +125 -125
- package/src/core/integrations/registry.ts +175 -175
- package/src/core/islands/island-persistence.ts +325 -325
- package/src/core/islands/island-state-serializer.ts +258 -258
- package/src/core/islands/persistent-island-context.tsx +80 -80
- package/src/core/islands/use-persistent-state.ts +68 -68
- package/src/core/layout/enhanced-layout-resolver.ts +322 -322
- package/src/core/layout/layout-cache-manager.ts +485 -485
- package/src/core/layout/layout-composer.ts +357 -357
- package/src/core/layout/layout-data-loader.ts +516 -516
- package/src/core/layout/layout-discovery.ts +243 -243
- package/src/core/layout/layout-matcher.ts +299 -299
- package/src/core/layout/layout-types.ts +110 -110
- package/src/core/modules/framework-module-resolver.ts +273 -273
- package/src/islands/component-analysis.ts +213 -213
- package/src/islands/css-utils.ts +565 -565
- package/src/islands/discovery/index.ts +80 -80
- package/src/islands/discovery/registry.ts +340 -340
- package/src/islands/discovery/resolver.ts +477 -477
- package/src/islands/discovery/scanner.ts +386 -386
- package/src/islands/discovery/types.ts +117 -117
- package/src/islands/discovery/validator.ts +544 -544
- package/src/islands/discovery/watcher.ts +368 -368
- package/src/islands/framework-detection.ts +428 -428
- package/src/islands/integration-loader.ts +490 -490
- package/src/islands/island.tsx +565 -565
- package/src/islands/render-cache.ts +550 -550
- package/src/islands/types.ts +80 -80
- package/src/islands/universal-css-collector.ts +157 -157
- package/src/islands/universal-head-collector.ts +137 -137
- package/src/layout-system.d.ts +592 -592
- package/src/layout-system.ts +218 -218
- package/src/middleware/discovery.ts +268 -268
- package/src/middleware/executor.ts +315 -315
- package/src/middleware/index.ts +76 -76
- package/src/middleware/types.ts +99 -99
- package/src/nitro/build-config.ts +575 -575
- package/src/nitro/config.ts +483 -483
- package/src/nitro/error-handler.ts +636 -636
- package/src/nitro/index.ts +173 -173
- package/src/nitro/island-manifest.ts +584 -584
- package/src/nitro/middleware-adapter.ts +260 -260
- package/src/nitro/renderer.ts +1471 -1471
- package/src/nitro/route-discovery.ts +439 -439
- package/src/nitro/types.ts +321 -321
- package/src/render/collect-css.ts +198 -198
- package/src/render/error-pages.ts +79 -79
- package/src/render/isolated-ssr-renderer.ts +654 -654
- package/src/render/ssr.ts +1030 -1030
- package/src/schemas/api.ts +30 -30
- package/src/schemas/core.ts +64 -64
- package/src/schemas/index.ts +212 -212
- package/src/schemas/layout.ts +279 -279
- package/src/schemas/routing/index.ts +38 -38
- package/src/schemas/routing.ts +376 -376
- package/src/types/as-island.ts +20 -20
- package/src/types/image.d.ts +106 -106
- package/src/types/index.d.ts +22 -22
- package/src/types/island-jsx.d.ts +33 -33
- package/src/types/island-prop.d.ts +20 -20
- package/src/types/layout.ts +285 -285
- package/src/types/mdx.d.ts +6 -6
- package/src/types/routing.ts +555 -555
- package/src/types/types.ts +5 -5
- package/src/types/urlpattern.d.ts +49 -49
- package/src/types/vite-env.d.ts +11 -11
- package/src/utils/dev-logger.ts +299 -299
- package/src/utils/fs.ts +151 -151
- package/src/vite-plugin/auto-discover.ts +551 -551
- package/src/vite-plugin/config.ts +266 -266
- package/src/vite-plugin/errors.ts +127 -127
- package/src/vite-plugin/image-optimization.ts +156 -156
- package/src/vite-plugin/integration-activator.ts +126 -126
- package/src/vite-plugin/island-sidecar-plugin.ts +176 -176
- package/src/vite-plugin/module-discovery.ts +189 -189
- package/src/vite-plugin/nitro-integration.ts +1354 -1354
- package/src/vite-plugin/plugin.ts +403 -409
- package/src/vite-plugin/types.ts +327 -327
- package/src/vite-plugin/validation.ts +228 -228
- package/src/client/adapters/index.js +0 -12
- package/src/client/adapters/lit-adapter.js +0 -467
- package/src/client/adapters/lit-adapter.ts +0 -654
- package/src/client/adapters/preact-adapter.js +0 -223
- package/src/client/adapters/preact-adapter.ts +0 -331
- package/src/client/adapters/qwik-adapter.js +0 -259
- package/src/client/adapters/qwik-adapter.ts +0 -345
- package/src/client/adapters/react-adapter.js +0 -220
- package/src/client/adapters/react-adapter.ts +0 -353
- package/src/client/adapters/solid-adapter.js +0 -295
- package/src/client/adapters/solid-adapter.ts +0 -451
- package/src/client/adapters/svelte-adapter.js +0 -368
- package/src/client/adapters/svelte-adapter.ts +0 -524
- package/src/client/adapters/vue-adapter.js +0 -278
- package/src/client/adapters/vue-adapter.ts +0 -467
- package/src/client/components.js +0 -23
- package/src/client/css-hmr-handler.js +0 -263
- package/src/client/framework-adapter.js +0 -283
- package/src/client/hmr-coordinator.js +0 -274
|
@@ -1,194 +1,194 @@
|
|
|
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
|
+
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 +1,111 @@
|
|
|
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
|
+
/** 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
|
+
|