agentshield/parser/typescript/
mod.rs1pub(crate) mod ast;
2pub(crate) mod classify;
3pub(crate) mod fallback;
4pub(crate) mod patterns;
5
6#[cfg(test)]
7mod tests;
8
9#[cfg(feature = "typescript")]
10use std::collections::HashSet;
11use std::path::Path;
12#[cfg(feature = "typescript")]
13use std::path::PathBuf;
14
15#[cfg(feature = "typescript")]
16use ast::{collect_params, walk_node};
17#[cfg(feature = "typescript")]
18use classify::detect_sanitizer_assignments;
19#[cfg(not(feature = "typescript"))]
20use fallback::parse_file_fallback;
21
22use crate::error::Result;
23use crate::ir::Language;
24use crate::parser::LanguageParser;
25use crate::parser::ParsedFile;
26
27pub struct TypeScriptParser;
29
30#[cfg(feature = "typescript")]
31impl LanguageParser for TypeScriptParser {
32 fn language(&self) -> Language {
33 Language::TypeScript
34 }
35
36 fn parse_file(&self, path: &Path, content: &str) -> Result<ParsedFile> {
37 let mut parser = tree_sitter::Parser::new();
38 let is_tsx = path
39 .extension()
40 .is_some_and(|ext| ext == "tsx" || ext == "jsx");
41
42 let lang = if is_tsx {
43 tree_sitter_typescript::LANGUAGE_TSX
44 } else {
45 tree_sitter_typescript::LANGUAGE_TYPESCRIPT
46 };
47
48 parser
49 .set_language(&lang.into())
50 .map_err(|e| crate::error::ShieldError::Parse {
51 file: path.display().to_string(),
52 message: format!("Failed to load TypeScript grammar: {e}"),
53 })?;
54
55 let tree = parser
56 .parse(content, None)
57 .ok_or_else(|| crate::error::ShieldError::Parse {
58 file: path.display().to_string(),
59 message: "tree-sitter failed to parse TypeScript".into(),
60 })?;
61
62 let file_path = PathBuf::from(path);
63 let source = content.as_bytes();
64 let mut parsed = ParsedFile::default();
65 let mut param_names = HashSet::new();
66
67 detect_sanitizer_assignments(content, &mut parsed.sanitized_vars);
69
70 collect_params(
72 tree.root_node(),
73 source,
74 &file_path,
75 &mut param_names,
76 &mut parsed,
77 );
78
79 walk_node(
81 tree.root_node(),
82 source,
83 &file_path,
84 ¶m_names,
85 &mut parsed,
86 );
87
88 Ok(parsed)
89 }
90}
91
92#[cfg(not(feature = "typescript"))]
93impl LanguageParser for TypeScriptParser {
94 fn language(&self) -> Language {
95 Language::TypeScript
96 }
97
98 fn parse_file(&self, path: &Path, content: &str) -> Result<ParsedFile> {
99 parse_file_fallback(path, content)
100 }
101}