1use crate::ecmascript;
4use crate::{ContainerBody, Import, Language, LanguageSymbols, Visibility};
5use tree_sitter::Node;
6
7pub struct JavaScript;
9
10impl Language for JavaScript {
11 fn name(&self) -> &'static str {
12 "JavaScript"
13 }
14 fn extensions(&self) -> &'static [&'static str] {
15 &["js", "mjs", "cjs", "jsx"]
16 }
17 fn grammar_name(&self) -> &'static str {
18 "javascript"
19 }
20
21 fn as_symbols(&self) -> Option<&dyn LanguageSymbols> {
22 Some(self)
23 }
24
25 fn signature_suffix(&self) -> &'static str {
26 " {}"
27 }
28
29 fn extract_docstring(&self, node: &Node, content: &str) -> Option<String> {
30 ecmascript::extract_jsdoc(node, content)
31 }
32
33 fn extract_implements(&self, node: &Node, content: &str) -> crate::ImplementsInfo {
34 ecmascript::extract_implements(node, content)
35 }
36
37 fn build_signature(&self, node: &Node, content: &str) -> String {
38 let name = match self.node_name(node, content) {
39 Some(n) => n,
40 None => {
41 return content[node.byte_range()]
42 .lines()
43 .next()
44 .unwrap_or("")
45 .trim()
46 .to_string();
47 }
48 };
49 ecmascript::build_signature(node, content, name)
50 }
51
52 fn extract_imports(&self, node: &Node, content: &str) -> Vec<Import> {
53 ecmascript::extract_imports(node, content)
54 }
55
56 fn format_import(&self, import: &Import, names: Option<&[&str]>) -> String {
57 ecmascript::format_import(import, names)
58 }
59
60 fn is_test_symbol(&self, symbol: &crate::Symbol) -> bool {
61 {
62 let name = symbol.name.as_str();
63 match symbol.kind {
64 crate::SymbolKind::Function | crate::SymbolKind::Method => {
65 name.starts_with("test_")
66 || name.starts_with("Test")
67 || name == "describe"
68 || name == "it"
69 || name == "test"
70 }
71 crate::SymbolKind::Module => {
72 name == "tests" || name == "test" || name == "__tests__"
73 }
74 _ => false,
75 }
76 }
77 }
78
79 fn test_file_globs(&self) -> &'static [&'static str] {
80 &[
81 "**/__tests__/**/*.js",
82 "**/__mocks__/**/*.js",
83 "**/*.test.js",
84 "**/*.spec.js",
85 "**/*.test.jsx",
86 "**/*.spec.jsx",
87 ]
88 }
89
90 fn extract_attributes(&self, node: &Node, content: &str) -> Vec<String> {
91 ecmascript::extract_decorators(node, content)
92 }
93
94 fn container_body<'a>(&self, node: &'a Node<'a>) -> Option<Node<'a>> {
95 node.child_by_field_name("body")
96 }
97
98 fn analyze_container_body(
99 &self,
100 body_node: &Node,
101 content: &str,
102 inner_indent: &str,
103 ) -> Option<ContainerBody> {
104 crate::body::analyze_brace_body(body_node, content, inner_indent)
105 }
106
107 fn get_visibility(&self, node: &Node, content: &str) -> Visibility {
108 ecmascript::get_visibility(node, content)
109 }
110
111 fn extract_module_doc(&self, src: &str) -> Option<String> {
112 ecmascript::extract_js_module_doc(src)
113 }
114}
115
116impl LanguageSymbols for JavaScript {}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use crate::validate_unused_kinds_audit;
122
123 #[test]
126 fn unused_node_kinds_audit() {
127 #[rustfmt::skip]
128 let documented_unused: &[&str] = &[
129 "class_body", "class_heritage", "class_static_block", "formal_parameters", "field_definition", "private_property_identifier", "property_identifier", "shorthand_property_identifier", "shorthand_property_identifier_pattern", "statement_block", "statement_identifier", "switch_body", "else_clause", "finally_clause", "augmented_assignment_expression", "await_expression", "parenthesized_expression","sequence_expression", "subscript_expression", "unary_expression", "update_expression", "yield_expression", "export_clause", "export_specifier", "import", "import_attribute", "import_clause", "import_specifier", "named_imports", "namespace_export", "namespace_import", "debugger_statement", "empty_statement", "expression_statement", "labeled_statement", "using_declaration", "with_statement", "jsx_expression", "break_statement",
180 "while_statement",
181 "throw_statement",
182 "if_statement",
183 "for_statement",
184 "import_statement",
185 "ternary_expression",
186 "catch_clause",
187 "do_statement",
188 "return_statement",
189 "try_statement",
190 "for_in_statement",
191 "continue_statement",
192 "switch_statement",
193 "switch_case",
194 "arrow_function",
195 ];
196
197 validate_unused_kinds_audit(&JavaScript, documented_unused)
198 .expect("JavaScript unused node kinds audit failed");
199 }
200}