Skip to main content

qex_core/chunk/languages/
javascript.rs

1use super::{extract_preceding_comments, find_child_text, find_name, LanguageChunker, NodeMetadata};
2use crate::chunk::ChunkType;
3
4pub struct JavaScriptChunker;
5
6impl LanguageChunker for JavaScriptChunker {
7    fn tree_sitter_language(&self) -> tree_sitter::Language {
8        tree_sitter_javascript::LANGUAGE.into()
9    }
10
11    fn language_name(&self) -> &str {
12        "javascript"
13    }
14
15    fn file_extensions(&self) -> &[&str] {
16        &["js", "jsx", "mjs", "cjs"]
17    }
18
19    fn is_splittable(&self, node_type: &str) -> bool {
20        matches!(
21            node_type,
22            "function_declaration"
23                | "class_declaration"
24                | "method_definition"
25                | "arrow_function"
26                | "generator_function"
27                | "generator_function_declaration"
28                | "export_statement"
29                | "lexical_declaration"
30        )
31    }
32
33    fn has_nested_chunks(&self, node_type: &str) -> bool {
34        matches!(node_type, "class_declaration" | "export_statement")
35    }
36
37    fn classify_node(&self, node_type: &str, parent_name: Option<&str>) -> ChunkType {
38        match node_type {
39            "class_declaration" => ChunkType::Class,
40            "method_definition" => ChunkType::Method,
41            "function_declaration" | "generator_function_declaration" => {
42                if parent_name.is_some() {
43                    ChunkType::Method
44                } else {
45                    ChunkType::Function
46                }
47            }
48            "arrow_function" | "generator_function" => ChunkType::Function,
49            "export_statement" | "lexical_declaration" => ChunkType::ModuleLevel,
50            _ => ChunkType::ModuleLevel,
51        }
52    }
53
54    fn extract_metadata(&self, node: tree_sitter::Node, source: &str) -> NodeMetadata {
55        let mut meta = NodeMetadata::default();
56
57        match node.kind() {
58            "function_declaration" | "generator_function_declaration" => {
59                meta.name = find_name(node, source);
60                meta.is_async = source[node.start_byte()..node.end_byte()].starts_with("async ");
61                meta.is_generator = node.kind().contains("generator");
62                meta.docstring = extract_preceding_comments(node, source);
63            }
64            "class_declaration" => {
65                meta.name = find_name(node, source);
66                meta.docstring = extract_preceding_comments(node, source);
67            }
68            "method_definition" => {
69                meta.name = find_child_text(node, source, "property_identifier");
70                meta.docstring = extract_preceding_comments(node, source);
71            }
72            "arrow_function" => {
73                // Arrow functions are often assigned: const foo = () => {}
74                // Name comes from parent variable_declarator
75                meta.docstring = extract_preceding_comments(node, source);
76            }
77            "export_statement" => {
78                // Look for the declaration inside
79                let mut cursor = node.walk();
80                for child in node.children(&mut cursor) {
81                    if child.kind() == "function_declaration" || child.kind() == "class_declaration" {
82                        meta.name = find_name(child, source);
83                    } else if child.kind() == "lexical_declaration" {
84                        // const/let export
85                        let mut inner = child.walk();
86                        for decl in child.children(&mut inner) {
87                            if decl.kind() == "variable_declarator" {
88                                meta.name = find_name(decl, source);
89                                break;
90                            }
91                        }
92                    }
93                }
94                meta.docstring = extract_preceding_comments(node, source);
95            }
96            "lexical_declaration" => {
97                let mut cursor = node.walk();
98                for child in node.children(&mut cursor) {
99                    if child.kind() == "variable_declarator" {
100                        meta.name = find_name(child, source);
101                        // Check if value is arrow function
102                        let mut inner = child.walk();
103                        for val in child.children(&mut inner) {
104                            if val.kind() == "arrow_function" {
105                                meta.is_async = source[val.start_byte()..val.end_byte()].starts_with("async ");
106                            }
107                        }
108                        break;
109                    }
110                }
111                meta.docstring = extract_preceding_comments(node, source);
112            }
113            _ => {
114                meta.name = find_name(node, source);
115                meta.docstring = extract_preceding_comments(node, source);
116            }
117        }
118
119        meta
120    }
121}