Skip to main content

code_analyze_core/languages/
javascript.rs

1// SPDX-FileCopyrightText: 2026 code-analyze-mcp contributors
2// SPDX-License-Identifier: Apache-2.0
3/// Tree-sitter query for extracting JavaScript elements (functions, classes, and related).
4#[cfg(feature = "lang-javascript")]
5pub const ELEMENT_QUERY: &str = r"
6(function_declaration) @function
7(class_declaration) @class
8(method_definition) @function
9(generator_function_declaration) @function
10
11";
12
13/// Tree-sitter query for extracting function calls.
14#[cfg(feature = "lang-javascript")]
15pub const CALL_QUERY: &str = r"
16(call_expression
17  function: (identifier) @call)
18(call_expression
19  function: (member_expression property: (property_identifier) @call))
20";
21
22/// Tree-sitter query for extracting JavaScript imports (ESM and CommonJS).
23#[cfg(feature = "lang-javascript")]
24pub const IMPORT_QUERY: &str = r#"
25(import_statement) @import_path
26(call_expression
27  function: (identifier) @_fn (#eq? @_fn "require")
28  arguments: (arguments (string) @import_path))
29"#;
30
31use tree_sitter::Node;
32
33/// Extract inheritance information from a JavaScript class node.
34#[cfg(feature = "lang-javascript")]
35#[must_use]
36pub fn extract_inheritance(node: &Node, source: &str) -> Vec<String> {
37    let mut inherits = Vec::new();
38    // Walk children to find class_heritage node
39    for i in 0..node.named_child_count() {
40        if let Some(child) = node.named_child(u32::try_from(i).unwrap_or(u32::MAX))
41            && child.kind() == "class_heritage"
42        {
43            // Walk class_heritage children for extends_clause
44            for j in 0..child.named_child_count() {
45                if let Some(clause) = child.named_child(u32::try_from(j).unwrap_or(u32::MAX))
46                    && clause.kind() == "extends_clause"
47                    && let Some(value) = clause.child_by_field_name("value")
48                {
49                    let text = &source[value.start_byte()..value.end_byte()];
50                    inherits.push(format!("extends {text}"));
51                }
52            }
53        }
54    }
55    inherits
56}
57
58#[cfg(test)]
59#[cfg(feature = "lang-javascript")]
60mod tests {
61    use tree_sitter::Parser;
62
63    fn parse_js(src: &str) -> tree_sitter::Tree {
64        let mut parser = Parser::new();
65        parser
66            .set_language(&tree_sitter_javascript::LANGUAGE.into())
67            .unwrap();
68        parser.parse(src, None).unwrap()
69    }
70
71    fn find_node_by_kind<'a>(
72        node: tree_sitter::Node<'a>,
73        kind: &str,
74    ) -> Option<tree_sitter::Node<'a>> {
75        if node.kind() == kind {
76            return Some(node);
77        }
78        for i in 0..node.child_count() {
79            if let Some(child) = node.child(u32::try_from(i).unwrap_or(u32::MAX)) {
80                if let Some(found) = find_node_by_kind(child, kind) {
81                    return Some(found);
82                }
83            }
84        }
85        None
86    }
87
88    #[test]
89    fn test_function_declaration() {
90        let src = "function greet() { return 42; }";
91        let tree = parse_js(src);
92        let root = tree.root_node();
93        let func = find_node_by_kind(root, "function_declaration");
94        assert!(func.is_some(), "expected to find function_declaration");
95    }
96
97    #[test]
98    fn test_arrow_function() {
99        let src = "const add = (a, b) => a + b;";
100        let tree = parse_js(src);
101        let root = tree.root_node();
102        let arrow = find_node_by_kind(root, "arrow_function");
103        assert!(arrow.is_some(), "expected to find arrow_function");
104    }
105
106    #[test]
107    fn test_class_declaration() {
108        let src = "class Foo extends Bar { method() {} }";
109        let tree = parse_js(src);
110        let root = tree.root_node();
111        let class = find_node_by_kind(root, "class_declaration");
112        assert!(class.is_some(), "expected to find class_declaration");
113    }
114
115    #[test]
116    fn test_es_import() {
117        let src = "import {x} from 'module';";
118        let tree = parse_js(src);
119        let root = tree.root_node();
120        let import = find_node_by_kind(root, "import_statement");
121        assert!(import.is_some(), "expected to find import_statement");
122    }
123
124    #[test]
125    fn test_commonjs_require() {
126        let src = "const lib = require('lib');";
127        let tree = parse_js(src);
128        let root = tree.root_node();
129        let call = find_node_by_kind(root, "call_expression");
130        assert!(call.is_some(), "expected to find call_expression");
131    }
132}