Skip to main content

codesynapse_core/ts_extract/
vue.rs

1use super::{add_contains_edge, add_node_if_missing, make_file_node};
2use crate::error::Result;
3use crate::extract::{make_id, ImportNode, LanguageExtractor};
4use crate::types::{Edge, ExtractionFragment, Node};
5use std::collections::HashMap;
6use std::path::Path;
7
8pub struct TsVueExtractor;
9
10impl TsVueExtractor {
11    pub fn extract(source: &[u8], path: &Path) -> Result<ExtractionFragment> {
12        let (file_id, _, file_node) = make_file_node(path);
13        let mut fragment = ExtractionFragment {
14            nodes: vec![file_node],
15            edges: vec![],
16        };
17
18        let content = std::str::from_utf8(source).unwrap_or("");
19
20        // Extract script section
21        let mut script_content = String::new();
22        let mut template_content = String::new();
23        let mut in_script = false;
24        let mut in_template = false;
25
26        for line in content.lines() {
27            if line.trim().starts_with("<script") {
28                in_script = true;
29                continue;
30            }
31            if line.trim() == "</script>" {
32                in_script = false;
33                continue;
34            }
35            if line.trim().starts_with("<template") {
36                in_template = true;
37                continue;
38            }
39            if line.trim() == "</template>" {
40                in_template = false;
41                continue;
42            }
43            if in_script {
44                script_content.push_str(line);
45                script_content.push('\n');
46            }
47            if in_template {
48                template_content.push_str(line);
49                template_content.push('\n');
50            }
51        }
52
53        // Extract component name from script
54        if script_content.contains("name:") {
55            for line in script_content.lines() {
56                if line.contains("name:") {
57                    let name_part = line.split("name:").nth(1).unwrap_or("").trim();
58                    let comp_name = name_part
59                        .trim_matches(',')
60                        .trim()
61                        .trim_matches('\'')
62                        .trim_matches('"')
63                        .trim();
64                    if !comp_name.is_empty() {
65                        let comp_id = make_id(&[&file_id, comp_name]);
66                        fragment.nodes.push(Node {
67                            id: comp_id.clone(),
68                            label: comp_name.to_string(),
69                            file_type: "class".to_string(),
70                            source_file: path.to_string_lossy().to_string(),
71                            source_location: None,
72                            community: None,
73                            rationale: None,
74                            docstring: None,
75                            metadata: {
76                                let mut m = HashMap::new();
77                                m.insert("kind".to_string(), "component".to_string());
78                                m
79                            },
80                        });
81                        add_contains_edge(&mut fragment, &file_id, comp_id, path);
82                    }
83                }
84            }
85        }
86
87        // Track template refs
88        for line in template_content.lines() {
89            if line.contains("ref=\"") || line.contains("ref='") {
90                let ref_name = line
91                    .split("ref=\"")
92                    .nth(1)
93                    .or_else(|| line.split("ref='").nth(1))
94                    .and_then(|s| s.split('"').next().or_else(|| s.split('\'').next()))
95                    .unwrap_or("")
96                    .trim();
97                if !ref_name.is_empty() {
98                    let ref_id = make_id(&[&file_id, ref_name, "ref"]);
99                    let ref_node = Node {
100                        id: ref_id.clone(),
101                        label: format!("ref:{}", ref_name),
102                        file_type: "code".to_string(),
103                        source_file: path.to_string_lossy().to_string(),
104                        source_location: None,
105                        community: None,
106                        rationale: None,
107                        docstring: None,
108                        metadata: HashMap::new(),
109                    };
110                    add_node_if_missing(&mut fragment, ref_node);
111                    fragment.edges.push(Edge {
112                        source: file_id.clone(),
113                        target: ref_id,
114                        relation: "contains".to_string(),
115                        confidence: "EXTRACTED".to_string(),
116                        source_file: Some(path.to_string_lossy().to_string()),
117                        weight: 1.0,
118                        context: None,
119                    });
120                }
121            }
122        }
123
124        Ok(fragment)
125    }
126}
127
128/// Svelte SFC extractor
129impl LanguageExtractor for TsVueExtractor {
130    fn file_extensions(&self) -> Vec<&'static str> {
131        vec!["vue"]
132    }
133    fn extract(&self, source: &[u8], path: &Path) -> Result<ExtractionFragment> {
134        Self::extract(source, path)
135    }
136    fn resolve_imports(&self, _imports: &[ImportNode]) -> Vec<Edge> {
137        vec![]
138    }
139    fn collect_type_refs(&self, _fragment: &mut ExtractionFragment) {}
140}