feat: add Vue support (#66)
Co-authored-by: Colby McHenry <me@colbymchenry.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Colby McHenry
Claude Opus 4.7
parent
804ab671d4
commit
5ab81746e8
@@ -3080,6 +3080,156 @@ describe('Directory Exclusion', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Vue Extraction', () => {
|
||||
it('should detect Vue files', () => {
|
||||
expect(detectLanguage('App.vue')).toBe('vue');
|
||||
expect(detectLanguage('components/Button.vue')).toBe('vue');
|
||||
expect(isLanguageSupported('vue')).toBe(true);
|
||||
});
|
||||
|
||||
it('should extract component node from a Vue SFC', () => {
|
||||
const code = `<template>
|
||||
<div>{{ message }}</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return { message: 'Hello' };
|
||||
}
|
||||
}
|
||||
</script>
|
||||
`;
|
||||
const result = extractFromSource('HelloWorld.vue', code);
|
||||
|
||||
const componentNode = result.nodes.find((n) => n.kind === 'component');
|
||||
expect(componentNode).toBeDefined();
|
||||
expect(componentNode?.name).toBe('HelloWorld');
|
||||
expect(componentNode?.language).toBe('vue');
|
||||
expect(componentNode?.isExported).toBe(true);
|
||||
});
|
||||
|
||||
it('should extract functions from <script> block', () => {
|
||||
const code = `<template>
|
||||
<button @click="handleClick">Click</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
function handleClick() {
|
||||
console.log('clicked');
|
||||
}
|
||||
|
||||
const count = 0;
|
||||
</script>
|
||||
`;
|
||||
const result = extractFromSource('Button.vue', code);
|
||||
|
||||
const componentNode = result.nodes.find((n) => n.kind === 'component');
|
||||
expect(componentNode).toBeDefined();
|
||||
expect(componentNode?.name).toBe('Button');
|
||||
|
||||
const funcNode = result.nodes.find((n) => n.kind === 'function' && n.name === 'handleClick');
|
||||
expect(funcNode).toBeDefined();
|
||||
expect(funcNode?.language).toBe('vue');
|
||||
});
|
||||
|
||||
it('should extract from <script setup lang="ts"> block', () => {
|
||||
const code = `<template>
|
||||
<div>{{ count }}</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
const count = ref(0);
|
||||
|
||||
function increment(): void {
|
||||
count.value++;
|
||||
}
|
||||
</script>
|
||||
`;
|
||||
const result = extractFromSource('Counter.vue', code);
|
||||
|
||||
const componentNode = result.nodes.find((n) => n.kind === 'component');
|
||||
expect(componentNode).toBeDefined();
|
||||
expect(componentNode?.name).toBe('Counter');
|
||||
|
||||
const funcNode = result.nodes.find((n) => n.kind === 'function' && n.name === 'increment');
|
||||
expect(funcNode).toBeDefined();
|
||||
expect(funcNode?.language).toBe('vue');
|
||||
|
||||
// All nodes should be marked as vue language
|
||||
for (const node of result.nodes) {
|
||||
expect(node.language).toBe('vue');
|
||||
}
|
||||
});
|
||||
|
||||
it('should extract from both <script> and <script setup> blocks', () => {
|
||||
const code = `<template>
|
||||
<div>{{ msg }}</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'DualScript'
|
||||
}
|
||||
</script>
|
||||
|
||||
<script setup>
|
||||
const msg = 'hello';
|
||||
|
||||
function greet() {
|
||||
return msg;
|
||||
}
|
||||
</script>
|
||||
`;
|
||||
const result = extractFromSource('DualScript.vue', code);
|
||||
|
||||
const componentNode = result.nodes.find((n) => n.kind === 'component');
|
||||
expect(componentNode).toBeDefined();
|
||||
|
||||
const greetFunc = result.nodes.find((n) => n.kind === 'function' && n.name === 'greet');
|
||||
expect(greetFunc).toBeDefined();
|
||||
});
|
||||
|
||||
it('should create component node for template-only Vue file', () => {
|
||||
const code = `<template>
|
||||
<div>Static content</div>
|
||||
</template>
|
||||
`;
|
||||
const result = extractFromSource('Static.vue', code);
|
||||
|
||||
const componentNode = result.nodes.find((n) => n.kind === 'component');
|
||||
expect(componentNode).toBeDefined();
|
||||
expect(componentNode?.name).toBe('Static');
|
||||
expect(componentNode?.language).toBe('vue');
|
||||
|
||||
// Only the component node should exist (no script nodes)
|
||||
expect(result.nodes.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should create containment edges from component to script nodes', () => {
|
||||
const code = `<template>
|
||||
<div>{{ value }}</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const value = 42;
|
||||
</script>
|
||||
`;
|
||||
const result = extractFromSource('Contained.vue', code);
|
||||
|
||||
const componentNode = result.nodes.find((n) => n.kind === 'component');
|
||||
expect(componentNode).toBeDefined();
|
||||
|
||||
// Should have containment edges from component to child nodes
|
||||
const containEdges = result.edges.filter(
|
||||
(e) => e.source === componentNode!.id && e.kind === 'contains'
|
||||
);
|
||||
expect(containEdges.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Instantiates + Decorates edge extraction', () => {
|
||||
it('emits an instantiates ref for `new Foo()`', () => {
|
||||
const code = `
|
||||
|
||||
Reference in New Issue
Block a user