argyph_graph/resolve/
typescript.rs1use camino::Utf8Path;
2
3use super::{ImportResolver, ModuleTarget};
4use crate::edge::Confidence;
5
6pub struct TypeScriptResolver;
7
8impl ImportResolver for TypeScriptResolver {
9 fn resolve_import(
10 &self,
11 source_file: &Utf8Path,
12 module_path: &[String],
13 _raw: &str,
14 ) -> Option<ModuleTarget> {
15 if module_path.is_empty() {
16 return None;
17 }
18
19 let first = &module_path[0];
20 if first != "." && first != ".." {
21 return None;
22 }
23
24 let source_dir = source_file.parent()?;
25 let joined = module_path.join("/");
26 let resolved = source_dir.join(&joined);
27
28 let candidate = format!("{resolved}.ts");
29 Some(ModuleTarget {
30 file_path: super::normalize_path(&candidate),
31 confidence: Confidence::Heuristic,
32 })
33 }
34}
35
36#[cfg(test)]
37#[allow(clippy::unwrap_used, clippy::expect_used)]
38mod tests {
39 use super::*;
40 use camino::Utf8Path;
41
42 #[test]
43 fn resolve_relative_import() {
44 let resolver = TypeScriptResolver;
45 let path: Vec<String> = [".", "math"].iter().map(|s| s.to_string()).collect();
46 let tgt = resolver
47 .resolve_import(
48 Utf8Path::new("src/components/App.ts"),
49 &path,
50 "import { add } from './math'",
51 )
52 .expect("should resolve ./math");
53
54 assert!(
55 tgt.file_path.contains("components/math"),
56 "got {}",
57 tgt.file_path
58 );
59 }
60
61 #[test]
62 fn resolve_parent_import() {
63 let resolver = TypeScriptResolver;
64 let path: Vec<String> = ["..", "types"].iter().map(|s| s.to_string()).collect();
65 let tgt = resolver
66 .resolve_import(
67 Utf8Path::new("src/deep/nested/file.ts"),
68 &path,
69 "import { User } from '../types'",
70 )
71 .expect("should resolve ../types");
72
73 assert!(
74 tgt.file_path.contains("src/deep/types"),
75 "got {}",
76 tgt.file_path
77 );
78 }
79
80 #[test]
81 fn bare_specifier_returns_none() {
82 let resolver = TypeScriptResolver;
83 let path: Vec<String> = ["react"].iter().map(|s| s.to_string()).collect();
84 assert!(resolver
85 .resolve_import(
86 Utf8Path::new("src/index.ts"),
87 &path,
88 "import { useState } from 'react'",
89 )
90 .is_none());
91 }
92}