@useavalon/avalon 0.1.0
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 -0
- package/mod.ts +301 -0
- package/package.json +85 -0
- package/src/build/README.md +310 -0
- package/src/build/integration-bundler-plugin.ts +116 -0
- package/src/build/integration-config.ts +168 -0
- package/src/build/integration-detection-plugin.ts +117 -0
- package/src/build/integration-resolver-plugin.ts +90 -0
- package/src/build/island-manifest.ts +269 -0
- package/src/build/island-types-generator.ts +476 -0
- package/src/build/mdx-island-transform.ts +464 -0
- package/src/build/mdx-plugin.ts +98 -0
- package/src/build/page-island-transform.ts +598 -0
- package/src/build/prop-extractors/index.ts +21 -0
- package/src/build/prop-extractors/lit.ts +140 -0
- package/src/build/prop-extractors/qwik.ts +16 -0
- package/src/build/prop-extractors/solid.ts +125 -0
- package/src/build/prop-extractors/svelte.ts +194 -0
- package/src/build/prop-extractors/vue.ts +111 -0
- package/src/build/sidecar-file-manager.ts +104 -0
- package/src/build/sidecar-renderer.ts +30 -0
- package/src/client/adapters/index.ts +13 -0
- package/src/client/adapters/lit-adapter.ts +654 -0
- package/src/client/adapters/preact-adapter.ts +331 -0
- package/src/client/adapters/qwik-adapter.ts +345 -0
- package/src/client/adapters/react-adapter.ts +353 -0
- package/src/client/adapters/solid-adapter.ts +451 -0
- package/src/client/adapters/svelte-adapter.ts +524 -0
- package/src/client/adapters/vue-adapter.ts +467 -0
- package/src/client/components.ts +35 -0
- package/src/client/css-hmr-handler.ts +344 -0
- package/src/client/framework-adapter.ts +462 -0
- package/src/client/hmr-coordinator.ts +396 -0
- package/src/client/hmr-error-overlay.js +533 -0
- package/src/client/main.js +816 -0
- package/src/client/tests/css-hmr-handler.test.ts +360 -0
- package/src/client/tests/framework-adapter.test.ts +519 -0
- package/src/client/tests/hmr-coordinator.test.ts +176 -0
- package/src/client/tests/hydration-option-parsing.test.ts +107 -0
- package/src/client/tests/lit-adapter.test.ts +427 -0
- package/src/client/tests/preact-adapter.test.ts +353 -0
- package/src/client/tests/qwik-adapter.test.ts +343 -0
- package/src/client/tests/react-adapter.test.ts +317 -0
- package/src/client/tests/solid-adapter.test.ts +396 -0
- package/src/client/tests/svelte-adapter.test.ts +387 -0
- package/src/client/tests/vue-adapter.test.ts +407 -0
- package/src/client/types/framework-runtime.d.ts +68 -0
- package/src/client/types/vite-hmr.d.ts +46 -0
- package/src/client/types/vite-virtual-modules.d.ts +60 -0
- package/src/components/Image.tsx +123 -0
- package/src/components/IslandErrorBoundary.tsx +145 -0
- package/src/components/LayoutDataErrorBoundary.tsx +141 -0
- package/src/components/LayoutErrorBoundary.tsx +127 -0
- package/src/components/PersistentIsland.tsx +52 -0
- package/src/components/StreamingErrorBoundary.tsx +233 -0
- package/src/components/StreamingLayout.tsx +538 -0
- package/src/components/tests/component-analyzer.test.ts +96 -0
- package/src/components/tests/component-detection.test.ts +347 -0
- package/src/components/tests/persistent-islands.test.ts +398 -0
- package/src/core/components/component-analyzer.ts +192 -0
- package/src/core/components/component-detection.ts +508 -0
- package/src/core/components/enhanced-framework-detector.ts +500 -0
- package/src/core/components/framework-registry.ts +563 -0
- package/src/core/components/tests/enhanced-framework-detector.test.ts +577 -0
- package/src/core/components/tests/framework-registry.test.ts +465 -0
- package/src/core/content/mdx-processor.ts +46 -0
- package/src/core/integrations/README.md +282 -0
- package/src/core/integrations/index.ts +19 -0
- package/src/core/integrations/loader.ts +125 -0
- package/src/core/integrations/registry.ts +195 -0
- package/src/core/islands/island-persistence.ts +325 -0
- package/src/core/islands/island-state-serializer.ts +258 -0
- package/src/core/islands/persistent-island-context.tsx +80 -0
- package/src/core/islands/use-persistent-state.ts +68 -0
- package/src/core/layout/enhanced-layout-resolver.ts +322 -0
- package/src/core/layout/layout-cache-manager.ts +485 -0
- package/src/core/layout/layout-composer.ts +357 -0
- package/src/core/layout/layout-data-loader.ts +516 -0
- package/src/core/layout/layout-discovery.ts +243 -0
- package/src/core/layout/layout-matcher.ts +299 -0
- package/src/core/layout/layout-types.ts +110 -0
- package/src/core/layout/tests/enhanced-layout-resolver.test.ts +477 -0
- package/src/core/layout/tests/layout-cache-optimization.test.ts +149 -0
- package/src/core/layout/tests/layout-composer.test.ts +486 -0
- package/src/core/layout/tests/layout-data-loader.test.ts +443 -0
- package/src/core/layout/tests/layout-discovery.test.ts +253 -0
- package/src/core/layout/tests/layout-matcher.test.ts +480 -0
- package/src/core/modules/framework-module-resolver.ts +273 -0
- package/src/core/modules/tests/framework-module-resolver.test.ts +263 -0
- package/src/core/modules/tests/module-resolution-integration.test.ts +117 -0
- package/src/islands/component-analysis.ts +213 -0
- package/src/islands/css-utils.ts +565 -0
- package/src/islands/discovery/index.ts +80 -0
- package/src/islands/discovery/registry.ts +340 -0
- package/src/islands/discovery/resolver.ts +477 -0
- package/src/islands/discovery/scanner.ts +386 -0
- package/src/islands/discovery/tests/island-discovery.test.ts +881 -0
- package/src/islands/discovery/types.ts +117 -0
- package/src/islands/discovery/validator.ts +544 -0
- package/src/islands/discovery/watcher.ts +368 -0
- package/src/islands/framework-detection.ts +428 -0
- package/src/islands/integration-loader.ts +490 -0
- package/src/islands/island.tsx +565 -0
- package/src/islands/render-cache.ts +550 -0
- package/src/islands/types.ts +80 -0
- package/src/islands/universal-css-collector.ts +157 -0
- package/src/islands/universal-head-collector.ts +137 -0
- package/src/layout-system.d.ts +592 -0
- package/src/layout-system.ts +218 -0
- package/src/middleware/__tests__/discovery.test.ts +107 -0
- package/src/middleware/discovery.ts +268 -0
- package/src/middleware/executor.ts +315 -0
- package/src/middleware/index.ts +76 -0
- package/src/middleware/types.ts +99 -0
- package/src/nitro/build-config.ts +576 -0
- package/src/nitro/config.ts +483 -0
- package/src/nitro/error-handler.ts +636 -0
- package/src/nitro/index.ts +173 -0
- package/src/nitro/island-manifest.ts +584 -0
- package/src/nitro/middleware-adapter.ts +260 -0
- package/src/nitro/renderer.ts +1458 -0
- package/src/nitro/route-discovery.ts +439 -0
- package/src/nitro/types.ts +321 -0
- package/src/render/collect-css.ts +198 -0
- package/src/render/error-pages.ts +79 -0
- package/src/render/isolated-ssr-renderer.ts +654 -0
- package/src/render/ssr.ts +1030 -0
- package/src/schemas/api.ts +30 -0
- package/src/schemas/core.ts +64 -0
- package/src/schemas/index.ts +212 -0
- package/src/schemas/layout.ts +279 -0
- package/src/schemas/routing/index.ts +38 -0
- package/src/schemas/routing.ts +376 -0
- package/src/types/as-island.ts +20 -0
- package/src/types/image.d.ts +106 -0
- package/src/types/index.d.ts +22 -0
- package/src/types/island-jsx.d.ts +33 -0
- package/src/types/island-prop.d.ts +20 -0
- package/src/types/layout.ts +285 -0
- package/src/types/mdx.d.ts +6 -0
- package/src/types/routing.ts +555 -0
- package/src/types/tests/layout-types.test.ts +197 -0
- package/src/types/types.ts +5 -0
- package/src/types/urlpattern.d.ts +49 -0
- package/src/types/vite-env.d.ts +11 -0
- package/src/utils/dev-logger.ts +299 -0
- package/src/utils/fs.ts +151 -0
- package/src/vite-plugin/auto-discover.ts +551 -0
- package/src/vite-plugin/config.ts +266 -0
- package/src/vite-plugin/errors.ts +127 -0
- package/src/vite-plugin/image-optimization.ts +151 -0
- package/src/vite-plugin/integration-activator.ts +126 -0
- package/src/vite-plugin/island-sidecar-plugin.ts +176 -0
- package/src/vite-plugin/module-discovery.ts +189 -0
- package/src/vite-plugin/nitro-integration.ts +1334 -0
- package/src/vite-plugin/plugin.ts +329 -0
- package/src/vite-plugin/tests/image-optimization.test.ts +54 -0
- package/src/vite-plugin/types.ts +327 -0
- package/src/vite-plugin/validation.ts +228 -0
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Framework Detector Tests
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive test suite for the enhanced framework detection system,
|
|
5
|
+
* including edge cases, performance tests, and confidence scoring validation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import {
|
|
10
|
+
EnhancedFrameworkDetector,
|
|
11
|
+
type FrameworkDetectionResult,
|
|
12
|
+
type FrameworkConfig,
|
|
13
|
+
} from '../enhanced-framework-detector.ts';
|
|
14
|
+
|
|
15
|
+
describe('EnhancedFrameworkDetector - Preact Detection', () => {
|
|
16
|
+
const detector = new EnhancedFrameworkDetector();
|
|
17
|
+
|
|
18
|
+
it('should detect Preact with JSX import source', () => {
|
|
19
|
+
const content = `
|
|
20
|
+
/** @jsxImportSource preact */
|
|
21
|
+
import { useState } from 'preact/hooks';
|
|
22
|
+
|
|
23
|
+
export default function Counter() {
|
|
24
|
+
const [count, setCount] = useState(0);
|
|
25
|
+
return <button onClick={() => setCount(count + 1)}>{count}</button>;
|
|
26
|
+
}`;
|
|
27
|
+
|
|
28
|
+
const result = detector.detectFramework('Counter.tsx', content);
|
|
29
|
+
|
|
30
|
+
expect(result.framework).toEqual('preact');
|
|
31
|
+
expect(result.confidence).toEqual('high');
|
|
32
|
+
expect(result.evidence.some(e => e.includes('JSX import source'))).toBeTruthy();
|
|
33
|
+
expect(result.evidence.some(e => e.includes('preact'))).toBeTruthy();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should detect Preact with import statements', () => {
|
|
37
|
+
const content = `
|
|
38
|
+
import { render } from 'preact';
|
|
39
|
+
import { useState, useEffect } from 'preact/hooks';
|
|
40
|
+
|
|
41
|
+
export default function App() {
|
|
42
|
+
const [data, setData] = useState(null);
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
fetch('/api/data').then(r => r.json()).then(setData);
|
|
46
|
+
}, []);
|
|
47
|
+
|
|
48
|
+
return <div>{data?.message}</div>;
|
|
49
|
+
}`;
|
|
50
|
+
|
|
51
|
+
const result = detector.detectFramework('App.tsx', content);
|
|
52
|
+
|
|
53
|
+
expect(result.framework).toEqual('preact');
|
|
54
|
+
expect(result.confidence === 'high' || result.confidence === 'medium').toBeTruthy();
|
|
55
|
+
expect(result.evidence.some(e => e.includes('Framework import'))).toBeTruthy();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should detect Preact with content patterns', () => {
|
|
59
|
+
const content = `
|
|
60
|
+
export default function Component() {
|
|
61
|
+
const [state, setState] = useState(0);
|
|
62
|
+
const memoized = useMemo(() => state * 2, [state]);
|
|
63
|
+
|
|
64
|
+
return <div>{memoized}</div>;
|
|
65
|
+
}`;
|
|
66
|
+
|
|
67
|
+
const result = detector.detectFramework('Component.tsx', content);
|
|
68
|
+
|
|
69
|
+
// Should detect based on content patterns even without explicit imports
|
|
70
|
+
expect(result.framework === 'preact' || result.framework === 'solid').toBeTruthy();
|
|
71
|
+
expect(result.evidence.length > 0).toBeTruthy();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('EnhancedFrameworkDetector - Solid Detection', () => {
|
|
76
|
+
const detector = new EnhancedFrameworkDetector();
|
|
77
|
+
|
|
78
|
+
it('should detect Solid with JSX import source', () => {
|
|
79
|
+
const content = `
|
|
80
|
+
/** @jsxImportSource solid-js */
|
|
81
|
+
import { createSignal } from 'solid-js';
|
|
82
|
+
|
|
83
|
+
export default function Counter() {
|
|
84
|
+
const [count, setCount] = createSignal(0);
|
|
85
|
+
return <button onClick={() => setCount(count() + 1)}>{count()}</button>;
|
|
86
|
+
}`;
|
|
87
|
+
|
|
88
|
+
const result = detector.detectFramework('Counter.tsx', content);
|
|
89
|
+
|
|
90
|
+
expect(result.framework).toEqual('solid');
|
|
91
|
+
expect(result.confidence).toEqual('high');
|
|
92
|
+
expect(result.evidence.some(e => e.includes('JSX import source'))).toBeTruthy();
|
|
93
|
+
expect(result.evidence.some(e => e.includes('solid-js'))).toBeTruthy();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should detect Solid with import statements', () => {
|
|
97
|
+
const content = `
|
|
98
|
+
import { render } from 'solid-js/web';
|
|
99
|
+
import { createSignal, createEffect } from 'solid-js';
|
|
100
|
+
|
|
101
|
+
export default function App() {
|
|
102
|
+
const [data, setData] = createSignal(null);
|
|
103
|
+
|
|
104
|
+
createEffect(() => {
|
|
105
|
+
fetch('/api/data').then(r => r.json()).then(setData);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return <div>{data()?.message}</div>;
|
|
109
|
+
}`;
|
|
110
|
+
|
|
111
|
+
const result = detector.detectFramework('App.tsx', content);
|
|
112
|
+
|
|
113
|
+
expect(result.framework).toEqual('solid');
|
|
114
|
+
expect(result.confidence === 'high' || result.confidence === 'medium').toBeTruthy();
|
|
115
|
+
expect(result.evidence.some(e => e.includes('Framework import'))).toBeTruthy();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should detect Solid with content patterns', () => {
|
|
119
|
+
const content = `
|
|
120
|
+
export default function Component() {
|
|
121
|
+
const [signal, setSignal] = createSignal(0);
|
|
122
|
+
const memo = createMemo(() => signal() * 2);
|
|
123
|
+
|
|
124
|
+
onMount(() => {
|
|
125
|
+
console.log('Component mounted');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return <div>{memo()}</div>;
|
|
129
|
+
}`;
|
|
130
|
+
|
|
131
|
+
const result = detector.detectFramework('Component.tsx', content);
|
|
132
|
+
|
|
133
|
+
expect(result.framework).toEqual('solid');
|
|
134
|
+
expect(result.evidence.some(e => e.includes('content pattern'))).toBeTruthy();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('EnhancedFrameworkDetector - Vue Detection', () => {
|
|
139
|
+
const detector = new EnhancedFrameworkDetector();
|
|
140
|
+
|
|
141
|
+
it('should detect Vue with template syntax', () => {
|
|
142
|
+
const content = `
|
|
143
|
+
<template>
|
|
144
|
+
<div>
|
|
145
|
+
<h1>{{ title }}</h1>
|
|
146
|
+
<button @click="increment">{{ count }}</button>
|
|
147
|
+
</div>
|
|
148
|
+
</template>
|
|
149
|
+
|
|
150
|
+
<script>
|
|
151
|
+
import { ref } from 'vue';
|
|
152
|
+
|
|
153
|
+
export default {
|
|
154
|
+
setup() {
|
|
155
|
+
const count = ref(0);
|
|
156
|
+
const title = ref('Vue Component');
|
|
157
|
+
|
|
158
|
+
const increment = () => {
|
|
159
|
+
count.value++;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
return { count, title, increment };
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
</script>`;
|
|
166
|
+
|
|
167
|
+
const result = detector.detectFramework('Component.vue', content);
|
|
168
|
+
|
|
169
|
+
expect(result.framework).toEqual('vue');
|
|
170
|
+
expect(result.confidence).toEqual('high');
|
|
171
|
+
expect(result.evidence.some(e => e.includes('File extension'))).toBeTruthy();
|
|
172
|
+
expect(result.evidence.some(e => e.includes('content pattern'))).toBeTruthy();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should detect Vue with composition API', () => {
|
|
176
|
+
const content = `
|
|
177
|
+
<template>
|
|
178
|
+
<div>{{ computedValue }}</div>
|
|
179
|
+
</template>
|
|
180
|
+
|
|
181
|
+
<script setup>
|
|
182
|
+
import { ref, computed, watchEffect } from 'vue';
|
|
183
|
+
|
|
184
|
+
const data = ref(null);
|
|
185
|
+
const computedValue = computed(() => data.value?.message || 'Loading...');
|
|
186
|
+
|
|
187
|
+
watchEffect(() => {
|
|
188
|
+
fetch('/api/data').then(r => r.json()).then(d => data.value = d);
|
|
189
|
+
});
|
|
190
|
+
</script>`;
|
|
191
|
+
|
|
192
|
+
const result = detector.detectFramework('Component.vue', content);
|
|
193
|
+
|
|
194
|
+
expect(result.framework).toEqual('vue');
|
|
195
|
+
expect(result.evidence.some(e => e.includes('Framework import'))).toBeTruthy();
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe('EnhancedFrameworkDetector - Svelte Detection', () => {
|
|
200
|
+
const detector = new EnhancedFrameworkDetector();
|
|
201
|
+
|
|
202
|
+
it('should detect Svelte with reactive statements', () => {
|
|
203
|
+
const content = `
|
|
204
|
+
<script>
|
|
205
|
+
import { onMount } from 'svelte';
|
|
206
|
+
|
|
207
|
+
let count = 0;
|
|
208
|
+
$: doubled = count * 2;
|
|
209
|
+
|
|
210
|
+
onMount(() => {
|
|
211
|
+
console.log('Component mounted');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
function increment() {
|
|
215
|
+
count += 1;
|
|
216
|
+
}
|
|
217
|
+
</script>
|
|
218
|
+
|
|
219
|
+
<button on:click={increment}>
|
|
220
|
+
Count: {count} (doubled: {doubled})
|
|
221
|
+
</button>
|
|
222
|
+
|
|
223
|
+
<style>
|
|
224
|
+
button {
|
|
225
|
+
background: blue;
|
|
226
|
+
color: white;
|
|
227
|
+
}
|
|
228
|
+
</style>`;
|
|
229
|
+
|
|
230
|
+
const result = detector.detectFramework('Component.svelte', content);
|
|
231
|
+
|
|
232
|
+
expect(result.framework).toEqual('svelte');
|
|
233
|
+
expect(result.confidence).toEqual('high');
|
|
234
|
+
expect(result.evidence.some(e => e.includes('File extension'))).toBeTruthy();
|
|
235
|
+
expect(result.evidence.some(e => e.includes('content pattern'))).toBeTruthy();
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should detect Svelte with stores', () => {
|
|
239
|
+
const content = `
|
|
240
|
+
<script>
|
|
241
|
+
import { writable } from 'svelte/store';
|
|
242
|
+
|
|
243
|
+
const store = writable(0);
|
|
244
|
+
|
|
245
|
+
function increment() {
|
|
246
|
+
store.update(n => n + 1);
|
|
247
|
+
}
|
|
248
|
+
</script>
|
|
249
|
+
|
|
250
|
+
<button on:click={increment}>
|
|
251
|
+
Click me
|
|
252
|
+
</button>`;
|
|
253
|
+
|
|
254
|
+
const result = detector.detectFramework('Component.svelte', content);
|
|
255
|
+
|
|
256
|
+
expect(result.framework).toEqual('svelte');
|
|
257
|
+
expect(result.evidence.some(e => e.includes('Framework import'))).toBeTruthy();
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('EnhancedFrameworkDetector - Edge Cases', () => {
|
|
262
|
+
const detector = new EnhancedFrameworkDetector();
|
|
263
|
+
|
|
264
|
+
it('should handle missing JSX import source', () => {
|
|
265
|
+
const content = `
|
|
266
|
+
export default function Component() {
|
|
267
|
+
return <div>Hello World</div>;
|
|
268
|
+
}`;
|
|
269
|
+
|
|
270
|
+
const result = detector.detectFramework('Component.tsx', content);
|
|
271
|
+
|
|
272
|
+
// Should still attempt detection based on other evidence
|
|
273
|
+
expect(result.framework).toBeDefined();
|
|
274
|
+
expect(result.warnings.length > 0).toBeTruthy();
|
|
275
|
+
expect(result.warnings.some(w => w.includes('low confidence'))).toBeTruthy();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('should handle ambiguous content', () => {
|
|
279
|
+
const content = `
|
|
280
|
+
// This could be either Preact or Solid
|
|
281
|
+
export default function Component() {
|
|
282
|
+
const [state, setState] = useState(0);
|
|
283
|
+
return <div>{state}</div>;
|
|
284
|
+
}`;
|
|
285
|
+
|
|
286
|
+
const result = detector.detectFramework('Component.tsx', content);
|
|
287
|
+
|
|
288
|
+
// Should detect something but with warnings about ambiguity
|
|
289
|
+
expect(result.framework).toBeDefined();
|
|
290
|
+
if (result.confidence === 'low') {
|
|
291
|
+
expect(result.warnings.length > 0).toBeTruthy();
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('should handle empty content', () => {
|
|
296
|
+
const content = '';
|
|
297
|
+
|
|
298
|
+
const result = detector.detectFramework('Component.tsx', content);
|
|
299
|
+
|
|
300
|
+
// With .tsx extension, it might detect as preact or solid with low confidence
|
|
301
|
+
expect(result.framework === 'unknown' || result.framework === 'preact' || result.framework === 'solid').toBeTruthy();
|
|
302
|
+
expect(result.confidence).toEqual('low');
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('should handle non-component files', () => {
|
|
306
|
+
const content = `
|
|
307
|
+
export const API_URL = 'https://api.example.com';
|
|
308
|
+
export const VERSION = '1.0.0';
|
|
309
|
+
`;
|
|
310
|
+
|
|
311
|
+
const result = detector.detectFramework('constants.ts', content);
|
|
312
|
+
|
|
313
|
+
// .ts extension matches lit's file extensions, so it detects as lit with low confidence
|
|
314
|
+
expect(result.framework).toEqual('lit');
|
|
315
|
+
expect(result.confidence).toEqual('low');
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('should handle mixed framework imports', () => {
|
|
319
|
+
const content = `
|
|
320
|
+
// This is problematic - mixing frameworks
|
|
321
|
+
import { useState } from 'preact/hooks';
|
|
322
|
+
import { createSignal } from 'solid-js';
|
|
323
|
+
|
|
324
|
+
export default function Component() {
|
|
325
|
+
const [preactState] = useState(0);
|
|
326
|
+
const [solidSignal] = createSignal(0);
|
|
327
|
+
return <div>{preactState} {solidSignal()}</div>;
|
|
328
|
+
}`;
|
|
329
|
+
|
|
330
|
+
const result = detector.detectFramework('Component.tsx', content);
|
|
331
|
+
|
|
332
|
+
// Should detect one framework (likely the one with higher score)
|
|
333
|
+
expect(result.framework).toBeDefined();
|
|
334
|
+
expect(result.framework !== 'unknown').toBeTruthy();
|
|
335
|
+
// May have warnings or low confidence due to mixed imports
|
|
336
|
+
expect(result.warnings.length >= 0).toBeTruthy(); // Allow no warnings if detection is confident
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
describe('EnhancedFrameworkDetector - Confidence Scoring', () => {
|
|
341
|
+
const detector = new EnhancedFrameworkDetector();
|
|
342
|
+
|
|
343
|
+
it('should give high confidence for JSX import source + imports + content', () => {
|
|
344
|
+
const content = `
|
|
345
|
+
/** @jsxImportSource preact */
|
|
346
|
+
import { useState } from 'preact/hooks';
|
|
347
|
+
|
|
348
|
+
export default function Component() {
|
|
349
|
+
const [state, setState] = useState(0);
|
|
350
|
+
return <div>{state}</div>;
|
|
351
|
+
}`;
|
|
352
|
+
|
|
353
|
+
const result = detector.detectFramework('Component.tsx', content);
|
|
354
|
+
|
|
355
|
+
expect(result.confidence).toEqual('high');
|
|
356
|
+
expect(result.evidence.length >= 3).toBeTruthy(); // JSX source + import + content
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('should give medium confidence for imports + content', () => {
|
|
360
|
+
const content = `
|
|
361
|
+
import { createSignal } from 'solid-js';
|
|
362
|
+
|
|
363
|
+
export default function Component() {
|
|
364
|
+
const [signal, setSignal] = createSignal(0);
|
|
365
|
+
return <div>{signal()}</div>;
|
|
366
|
+
}`;
|
|
367
|
+
|
|
368
|
+
const result = detector.detectFramework('Component.tsx', content);
|
|
369
|
+
|
|
370
|
+
expect(result.confidence === 'medium' || result.confidence === 'high').toBeTruthy();
|
|
371
|
+
expect(result.evidence.length >= 2).toBeTruthy();
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('should give low confidence for extension only', () => {
|
|
375
|
+
const content = `
|
|
376
|
+
export default function Component() {
|
|
377
|
+
return <div>Static content</div>;
|
|
378
|
+
}`;
|
|
379
|
+
|
|
380
|
+
const result = detector.detectFramework('Component.tsx', content);
|
|
381
|
+
|
|
382
|
+
// Should have low confidence since only file extension provides evidence
|
|
383
|
+
expect(result.confidence).toEqual('low');
|
|
384
|
+
// Evidence might include file extension for multiple frameworks
|
|
385
|
+
expect(result.evidence.length >= 0).toBeTruthy();
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
describe('EnhancedFrameworkDetector - Performance Tests', () => {
|
|
390
|
+
const detector = new EnhancedFrameworkDetector();
|
|
391
|
+
|
|
392
|
+
it('should handle large files efficiently', () => {
|
|
393
|
+
// Generate a large file with repeated patterns
|
|
394
|
+
const imports = Array(100)
|
|
395
|
+
.fill(0)
|
|
396
|
+
.map((_, i) => `import { Component${i} } from './component${i}';`)
|
|
397
|
+
.join('\n');
|
|
398
|
+
|
|
399
|
+
const components = Array(100)
|
|
400
|
+
.fill(0)
|
|
401
|
+
.map((_, i) => `const component${i} = createSignal(${i});`)
|
|
402
|
+
.join('\n');
|
|
403
|
+
|
|
404
|
+
const content = `
|
|
405
|
+
/** @jsxImportSource solid-js */
|
|
406
|
+
import { createSignal } from 'solid-js';
|
|
407
|
+
${imports}
|
|
408
|
+
|
|
409
|
+
export default function LargeComponent() {
|
|
410
|
+
${components}
|
|
411
|
+
return <div>Large component</div>;
|
|
412
|
+
}`;
|
|
413
|
+
|
|
414
|
+
const startTime = performance.now();
|
|
415
|
+
const result = detector.detectFramework('LargeComponent.tsx', content);
|
|
416
|
+
const endTime = performance.now();
|
|
417
|
+
|
|
418
|
+
// Should complete within reasonable time (< 100ms for large files)
|
|
419
|
+
expect(endTime - startTime < 100).toBeTruthy();
|
|
420
|
+
expect(result.framework).toEqual('solid');
|
|
421
|
+
expect(result.confidence).toEqual('high');
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('should handle multiple detections efficiently', () => {
|
|
425
|
+
const files = [
|
|
426
|
+
{ path: 'Preact.tsx', content: '/** @jsxImportSource preact */\nimport { useState } from "preact/hooks";' },
|
|
427
|
+
{ path: 'Solid.tsx', content: '/** @jsxImportSource solid-js */\nimport { createSignal } from "solid-js";' },
|
|
428
|
+
{ path: 'Vue.vue', content: '<template><div></div></template>\n<script>import { ref } from "vue";</script>' },
|
|
429
|
+
{ path: 'Svelte.svelte', content: '<script>import { onMount } from "svelte";</script>' },
|
|
430
|
+
];
|
|
431
|
+
|
|
432
|
+
const startTime = performance.now();
|
|
433
|
+
|
|
434
|
+
const results = files.map(file => detector.detectFramework(file.path, file.content));
|
|
435
|
+
|
|
436
|
+
const endTime = performance.now();
|
|
437
|
+
|
|
438
|
+
// Should complete all detections quickly
|
|
439
|
+
expect(endTime - startTime < 50).toBeTruthy();
|
|
440
|
+
|
|
441
|
+
// Verify all detections are correct
|
|
442
|
+
expect(results[0].framework).toEqual('preact');
|
|
443
|
+
expect(results[1].framework).toEqual('solid');
|
|
444
|
+
expect(results[2].framework).toEqual('vue');
|
|
445
|
+
expect(results[3].framework).toEqual('svelte');
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
describe('EnhancedFrameworkDetector - Custom Framework Configuration', () => {
|
|
450
|
+
it('should work with custom framework configurations', () => {
|
|
451
|
+
const customConfig: FrameworkConfig = {
|
|
452
|
+
name: 'custom',
|
|
453
|
+
fileExtensions: ['.custom'],
|
|
454
|
+
jsxImportSources: ['custom-framework'],
|
|
455
|
+
ssrModules: ['custom-framework/server'],
|
|
456
|
+
hydrationModules: ['custom-framework/client'],
|
|
457
|
+
detectionPatterns: {
|
|
458
|
+
imports: [/^custom-framework$/],
|
|
459
|
+
content: [/\bcustomHook\b/],
|
|
460
|
+
jsxPragmas: ['@jsxImportSource custom-framework'],
|
|
461
|
+
},
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
const detector = new EnhancedFrameworkDetector({ custom: customConfig });
|
|
465
|
+
|
|
466
|
+
const content = `
|
|
467
|
+
/** @jsxImportSource custom-framework */
|
|
468
|
+
import { customHook } from 'custom-framework';
|
|
469
|
+
|
|
470
|
+
export default function Component() {
|
|
471
|
+
const value = customHook();
|
|
472
|
+
return <div>{value}</div>;
|
|
473
|
+
}`;
|
|
474
|
+
|
|
475
|
+
const result = detector.detectFramework('Component.custom', content);
|
|
476
|
+
|
|
477
|
+
expect(result.framework).toEqual('custom');
|
|
478
|
+
expect(result.confidence).toEqual('high');
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
describe('EnhancedFrameworkDetector - JSX Import Source Parsing', () => {
|
|
483
|
+
const detector = new EnhancedFrameworkDetector();
|
|
484
|
+
|
|
485
|
+
it('should parse JSX import source from comment', () => {
|
|
486
|
+
const content = `
|
|
487
|
+
/** @jsxImportSource preact */
|
|
488
|
+
export default function Component() {
|
|
489
|
+
return <div>Hello</div>;
|
|
490
|
+
}`;
|
|
491
|
+
|
|
492
|
+
const result = detector.detectFramework('Component.tsx', content);
|
|
493
|
+
|
|
494
|
+
expect(result.framework).toEqual('preact');
|
|
495
|
+
expect(result.evidence.some(e => e.includes('JSX import source: @jsxImportSource preact'))).toBeTruthy();
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it('should parse JSX import source from single-line comment', () => {
|
|
499
|
+
const content = `
|
|
500
|
+
// @jsxImportSource solid-js
|
|
501
|
+
export default function Component() {
|
|
502
|
+
return <div>Hello</div>;
|
|
503
|
+
}`;
|
|
504
|
+
|
|
505
|
+
const result = detector.detectFramework('Component.tsx', content);
|
|
506
|
+
|
|
507
|
+
// Note: Current implementation looks for /** */ comments, not // comments
|
|
508
|
+
// This test documents current behavior
|
|
509
|
+
expect(result.framework === 'solid' || result.framework === 'unknown').toBeTruthy();
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
it('should handle malformed JSX import source', () => {
|
|
513
|
+
const content = `
|
|
514
|
+
/** @jsxImportSource */
|
|
515
|
+
export default function Component() {
|
|
516
|
+
return <div>Hello</div>;
|
|
517
|
+
}`;
|
|
518
|
+
|
|
519
|
+
const result = detector.detectFramework('Component.tsx', content);
|
|
520
|
+
|
|
521
|
+
// Should not crash and should fall back to other detection methods
|
|
522
|
+
expect(result.framework).toBeDefined();
|
|
523
|
+
expect(result.confidence).toEqual('low');
|
|
524
|
+
});
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
describe('EnhancedFrameworkDetector - Import Statement Extraction', () => {
|
|
528
|
+
const detector = new EnhancedFrameworkDetector();
|
|
529
|
+
|
|
530
|
+
it('should extract ES6 imports', () => {
|
|
531
|
+
const content = `
|
|
532
|
+
import React from 'react';
|
|
533
|
+
import { useState, useEffect } from 'preact/hooks';
|
|
534
|
+
import * as Utils from './utils';
|
|
535
|
+
import type { Props } from './types';
|
|
536
|
+
`;
|
|
537
|
+
|
|
538
|
+
const result = detector.detectFramework('Component.tsx', content);
|
|
539
|
+
|
|
540
|
+
// Both react and preact imports are present; react scores higher due to
|
|
541
|
+
// its import patterns and many shared content patterns (useState, useEffect)
|
|
542
|
+
expect(result.framework).toEqual('react');
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it('should extract require statements', () => {
|
|
546
|
+
const content = `
|
|
547
|
+
const { createSignal } = require('solid-js');
|
|
548
|
+
const render = require('solid-js/web').render;
|
|
549
|
+
|
|
550
|
+
module.exports = function Component() {
|
|
551
|
+
const [signal] = createSignal(0);
|
|
552
|
+
return <div>{signal()}</div>;
|
|
553
|
+
};`;
|
|
554
|
+
|
|
555
|
+
const result = detector.detectFramework('Component.tsx', content);
|
|
556
|
+
|
|
557
|
+
expect(result.framework).toEqual('solid');
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it('should handle mixed import styles', () => {
|
|
561
|
+
const content = `
|
|
562
|
+
import { ref } from 'vue';
|
|
563
|
+
const { computed } = require('vue');
|
|
564
|
+
|
|
565
|
+
export default {
|
|
566
|
+
setup() {
|
|
567
|
+
const state = ref(0);
|
|
568
|
+
const doubled = computed(() => state.value * 2);
|
|
569
|
+
return { state, doubled };
|
|
570
|
+
}
|
|
571
|
+
};`;
|
|
572
|
+
|
|
573
|
+
const result = detector.detectFramework('Component.vue', content);
|
|
574
|
+
|
|
575
|
+
expect(result.framework).toEqual('vue');
|
|
576
|
+
});
|
|
577
|
+
});
|