Skip to main content

sdivi_lang_typescript/
lib.rs

1//! TypeScript language adapter for sdivi-rust.
2//!
3//! Implements [`sdivi_parsing::adapter::LanguageAdapter`] for `.ts` and `.tsx`
4//! source files using the `tree-sitter-typescript` grammar.
5//!
6//! # Thread safety
7//!
8//! `tree_sitter::Parser` is not `Send`. Parsers are stored in `thread_local!`
9//! storage so that `TypeScriptAdapter` itself can be `Send + Sync` and
10//! participate in rayon parallel parsing.
11
12mod extract;
13
14use std::cell::RefCell;
15use std::path::Path;
16
17use sdivi_parsing::adapter::LanguageAdapter;
18use sdivi_parsing::feature_record::FeatureRecord;
19
20use extract::{collect_hints, extract_exports, extract_imports, extract_signatures};
21
22thread_local! {
23    static TS_PARSER: RefCell<tree_sitter::Parser> = RefCell::new({
24        let mut p = tree_sitter::Parser::new();
25        p.set_language(&tree_sitter_typescript::language_typescript())
26            .expect("tree-sitter-typescript grammar failed to load");
27        p
28    });
29
30    static TSX_PARSER: RefCell<tree_sitter::Parser> = RefCell::new({
31        let mut p = tree_sitter::Parser::new();
32        p.set_language(&tree_sitter_typescript::language_tsx())
33            .expect("tree-sitter-typescript (TSX) grammar failed to load");
34        p
35    });
36}
37
38/// Language adapter for TypeScript source files (`.ts` and `.tsx`).
39///
40/// Parses `.ts` and `.tsx` files with the `tree-sitter-typescript` grammar and
41/// extracts:
42/// - `imports` from `import` statements
43/// - `exports` from `export` statements at module scope
44/// - `signatures` from function and method declarations
45/// - `pattern_hints` for the patterns stage
46///
47/// # Examples
48///
49/// ```rust
50/// use sdivi_lang_typescript::TypeScriptAdapter;
51/// use sdivi_parsing::adapter::LanguageAdapter;
52///
53/// let adapter = TypeScriptAdapter;
54/// assert_eq!(adapter.language_name(), "typescript");
55/// assert!(adapter.file_extensions().contains(&".ts"));
56/// assert!(adapter.file_extensions().contains(&".tsx"));
57/// ```
58pub struct TypeScriptAdapter;
59
60impl LanguageAdapter for TypeScriptAdapter {
61    fn language_name(&self) -> &'static str {
62        "typescript"
63    }
64
65    fn file_extensions(&self) -> &[&'static str] {
66        &[".ts", ".tsx"]
67    }
68
69    /// Parses `content` and returns a [`FeatureRecord`].
70    ///
71    /// The tree-sitter CST is created, traversed, and **dropped** before this
72    /// method returns. No tree-sitter type escapes into the returned record.
73    fn parse_file(&self, path: &Path, content: String) -> FeatureRecord {
74        let source = content.as_bytes();
75        let is_tsx = path.extension().is_some_and(|e| e == "tsx");
76
77        let (imports, exports, signatures, pattern_hints) = if is_tsx {
78            TSX_PARSER.with(|cell| {
79                let mut parser = cell.borrow_mut();
80                let tree = parser
81                    .parse(source, None)
82                    .expect("tree-sitter-typescript (TSX) failed to parse");
83                let root = tree.root_node();
84                let imports = extract_imports(root, source);
85                let exports = extract_exports(root, source);
86                let signatures = extract_signatures(root, source);
87                let hints = collect_hints(root, source);
88                (imports, exports, signatures, hints)
89            })
90        } else {
91            TS_PARSER.with(|cell| {
92                let mut parser = cell.borrow_mut();
93                let tree = parser
94                    .parse(source, None)
95                    .expect("tree-sitter-typescript failed to parse");
96                let root = tree.root_node();
97                let imports = extract_imports(root, source);
98                let exports = extract_exports(root, source);
99                let signatures = extract_signatures(root, source);
100                let hints = collect_hints(root, source);
101                (imports, exports, signatures, hints)
102            })
103        };
104
105        FeatureRecord {
106            path: path.to_path_buf(),
107            language: "typescript".to_string(),
108            imports,
109            exports,
110            signatures,
111            pattern_hints,
112        }
113    }
114}