fob_graph/analysis/extractors/mod.rs
1//! Framework script extractors.
2//!
3//! This module provides extractors for extracting JavaScript/TypeScript from
4//! framework-specific file formats (Astro, Svelte, Vue).
5//!
6//! # Usage
7//!
8//! ```rust,no_run
9//! use super::extractors::{extract_scripts, ExtractedScript};
10//! use std::path::Path;
11//!
12//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
13//! let path = Path::new("component.astro");
14//! let content = std::fs::read_to_string(path)?;
15//! let scripts = extract_scripts(path, &content)?;
16//!
17//! for script in scripts {
18//! println!("Found script: {}", script.source_text);
19//! }
20//! # Ok(()) }
21//! ```
22
23mod astro;
24mod common;
25mod svelte;
26mod vue;
27
28pub use astro::AstroExtractor;
29pub use common::{ExtractedScript, Extractor, ExtractorError, ScriptContext};
30pub use svelte::SvelteExtractor;
31pub use vue::VueExtractor;
32
33// Re-export constants for convenience
34pub use common::{MAX_FILE_SIZE, MAX_SCRIPT_TAGS};
35
36use std::path::Path;
37
38/// Extract scripts from a framework file, auto-detecting the framework by extension.
39///
40/// # Arguments
41///
42/// * `path` - The file path (used to determine framework by extension)
43/// * `content` - The file content
44///
45/// # Returns
46///
47/// A vector of extracted scripts, or an error if extraction fails.
48///
49/// # Supported Extensions
50///
51/// - `.astro` - Astro components
52/// - `.svelte` - Svelte components
53/// - `.vue` - Vue Single File Components
54///
55/// # Example
56///
57/// ```rust,no_run
58/// use super::extractors::extract_scripts;
59/// use std::path::Path;
60///
61/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
62/// let path = Path::new("component.astro");
63/// let content = r#"---
64/// const title = 'My Page'
65/// ---
66/// <script>
67/// console.log(title)
68/// </script>"#;
69///
70/// let scripts = extract_scripts(path, content)?;
71/// assert_eq!(scripts.len(), 2); // Frontmatter + script tag
72/// # Ok(()) }
73/// ```
74pub fn extract_scripts<'a>(
75 path: &Path,
76 content: &'a str,
77) -> Result<Vec<ExtractedScript<'a>>, ExtractorError> {
78 let extension = path.extension().and_then(|ext| ext.to_str()).unwrap_or("");
79
80 match extension {
81 "astro" => AstroExtractor.extract(content),
82 "svelte" => SvelteExtractor.extract(content),
83 "vue" => VueExtractor.extract(content),
84 _ => Ok(vec![]), // Not a framework file, return empty
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn test_extract_scripts_astro() {
94 let path = Path::new("test.astro");
95 let content = r#"---
96const x = 1
97---
98<script>console.log(x)</script>"#;
99 let scripts = extract_scripts(path, content).unwrap();
100 assert_eq!(scripts.len(), 2);
101 }
102
103 #[test]
104 fn test_extract_scripts_svelte() {
105 let path = Path::new("test.svelte");
106 let content = r#"<script>let x = 1</script>"#;
107 let scripts = extract_scripts(path, content).unwrap();
108 assert_eq!(scripts.len(), 1);
109 }
110
111 #[test]
112 fn test_extract_scripts_vue() {
113 let path = Path::new("test.vue");
114 let content = r#"<script setup>const x = 1</script>"#;
115 let scripts = extract_scripts(path, content).unwrap();
116 assert_eq!(scripts.len(), 1);
117 }
118
119 #[test]
120 fn test_extract_scripts_unknown_extension() {
121 let path = Path::new("test.js");
122 let content = "const x = 1";
123 let scripts = extract_scripts(path, content).unwrap();
124 assert_eq!(scripts.len(), 0);
125 }
126}