codesynapse_core/ts_extract/
vue.rs1use 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 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 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 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
128impl 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}