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}