code_analyze_core/languages/
javascript.rs1pub const ELEMENT_QUERY: &str = r"
5(function_declaration) @function
6(class_declaration) @class
7(method_definition) @function
8(generator_function_declaration) @function
9
10";
11
12pub const CALL_QUERY: &str = r"
14(call_expression
15 function: (identifier) @call)
16(call_expression
17 function: (member_expression property: (property_identifier) @call))
18";
19
20pub const IMPORT_QUERY: &str = r#"
22(import_statement) @import_path
23(call_expression
24 function: (identifier) @_fn (#eq? @_fn "require")
25 arguments: (arguments (string) @import_path))
26"#;
27
28pub const DEFUSE_QUERY: &str = r"
30(variable_declarator name: (identifier) @write.declarator)
31(assignment_expression left: (identifier) @write.assign)
32(augmented_assignment_expression left: (identifier) @writeread.augmented)
33(update_expression argument: (identifier) @writeread.update)
34(identifier) @read.usage
35";
36
37use tree_sitter::Node;
44
45#[must_use]
47pub fn extract_inheritance(node: &Node, source: &str) -> Vec<String> {
48 let mut inherits = Vec::new();
49 for i in 0..node.named_child_count() {
51 if let Some(child) = node.named_child(u32::try_from(i).unwrap_or(u32::MAX))
52 && child.kind() == "class_heritage"
53 {
54 for j in 0..child.named_child_count() {
56 if let Some(clause) = child.named_child(u32::try_from(j).unwrap_or(u32::MAX))
57 && clause.kind() == "extends_clause"
58 && let Some(value) = clause.child_by_field_name("value")
59 {
60 let text = &source[value.start_byte()..value.end_byte()];
61 inherits.push(format!("extends {text}"));
62 }
63 }
64 }
65 }
66 inherits
67}
68
69#[cfg(all(test, feature = "lang-javascript"))]
70mod tests {
71 use crate::DefUseKind;
72 use crate::parser::SemanticExtractor;
73 use tree_sitter::Parser;
74
75 fn parse_js(src: &str) -> tree_sitter::Tree {
76 let mut parser = Parser::new();
77 parser
78 .set_language(&tree_sitter_javascript::LANGUAGE.into())
79 .unwrap();
80 parser.parse(src, None).unwrap()
81 }
82
83 fn find_node_by_kind<'a>(
84 node: tree_sitter::Node<'a>,
85 kind: &str,
86 ) -> Option<tree_sitter::Node<'a>> {
87 if node.kind() == kind {
88 return Some(node);
89 }
90 for i in 0..node.child_count() {
91 if let Some(child) = node.child(u32::try_from(i).unwrap_or(u32::MAX)) {
92 if let Some(found) = find_node_by_kind(child, kind) {
93 return Some(found);
94 }
95 }
96 }
97 None
98 }
99
100 #[test]
101 fn test_function_declaration() {
102 let src = "function greet() { return 42; }";
103 let tree = parse_js(src);
104 let root = tree.root_node();
105 let func = find_node_by_kind(root, "function_declaration");
106 assert!(func.is_some(), "expected to find function_declaration");
107 }
108
109 #[test]
110 fn test_arrow_function() {
111 let src = "const add = (a, b) => a + b;";
112 let tree = parse_js(src);
113 let root = tree.root_node();
114 let arrow = find_node_by_kind(root, "arrow_function");
115 assert!(arrow.is_some(), "expected to find arrow_function");
116 }
117
118 #[test]
119 fn test_class_declaration() {
120 let src = "class Foo extends Bar { method() {} }";
121 let tree = parse_js(src);
122 let root = tree.root_node();
123 let class = find_node_by_kind(root, "class_declaration");
124 assert!(class.is_some(), "expected to find class_declaration");
125 }
126
127 #[test]
128 fn test_es_import() {
129 let src = "import {x} from 'module';";
130 let tree = parse_js(src);
131 let root = tree.root_node();
132 let import = find_node_by_kind(root, "import_statement");
133 assert!(import.is_some(), "expected to find import_statement");
134 }
135
136 #[test]
137 fn test_commonjs_require() {
138 let src = "const lib = require('lib');";
139 let tree = parse_js(src);
140 let root = tree.root_node();
141 let call = find_node_by_kind(root, "call_expression");
142 assert!(call.is_some(), "expected to find call_expression");
143 }
144
145 #[test]
146 fn test_defuse_query_write_site() {
147 let src = "let y = 10;\n";
149 let sites =
150 SemanticExtractor::extract_def_use_for_file(src, "javascript", "y", "test.js", None);
151 assert!(!sites.is_empty(), "defuse sites should not be empty");
152 let has_write = sites.iter().any(|s| matches!(s.kind, DefUseKind::Write));
153 assert!(has_write, "should contain a Write DefUseSite");
154 }
155}