deepwiki-rs 1.2.2

deepwiki-rs(also known as Litho) is a high-performance automatic generation engine for C4 architecture documentation, developed using Rust. It can intelligently analyze project structures, identify core components, parse dependency relationships, and leverage large language models (LLMs) to automatically generate professional architecture documentation.
use super::{Dependency, LanguageProcessor};
use crate::types::code::InterfaceInfo;
use regex::Regex;
use std::path::Path;

#[derive(Debug)]
pub struct ReactProcessor {
    import_regex: Regex,
    hook_regex: Regex,
}

impl ReactProcessor {
    pub fn new() -> Self {
        Self {
            import_regex: Regex::new(r#"^\s*import\s+(?:.*\s+from\s+)?['"]([^'"]+)['"]"#).unwrap(),
            hook_regex: Regex::new(r"use[A-Z][a-zA-Z]*\s*\(").unwrap(),
        }
    }
}

impl LanguageProcessor for ReactProcessor {
    fn supported_extensions(&self) -> Vec<&'static str> {
        vec!["jsx", "tsx"]
    }

    fn extract_dependencies(&self, content: &str, file_path: &Path) -> Vec<Dependency> {
        let mut dependencies = Vec::new();
        let source_file = file_path.to_string_lossy().to_string();

        for (line_num, line) in content.lines().enumerate() {
            // 提取import语句
            if let Some(captures) = self.import_regex.captures(line) {
                if let Some(import_path) = captures.get(1) {
                    let path_str = import_path.as_str();
                    let is_external = !path_str.starts_with('.')
                        && !path_str.starts_with('/')
                        && !path_str.starts_with("@/");

                    let dependency_type = if path_str == "react" || path_str.starts_with("react/") {
                        "react_import"
                    } else {
                        "import"
                    };

                    dependencies.push(Dependency {
                        name: source_file.clone(),
                        path: Some(path_str.to_string()),
                        is_external,
                        line_number: Some(line_num + 1),
                        dependency_type: dependency_type.to_string(),
                        version: None,
                    });
                }
            }
        }

        dependencies
    }

    fn determine_component_type(&self, file_path: &Path, content: &str) -> String {
        let file_name = file_path.file_name().and_then(|n| n.to_str()).unwrap_or("");

        // 检查特殊文件名
        if file_name == "App.jsx" || file_name == "App.tsx" {
            return "react_app".to_string();
        }

        if file_name == "index.jsx" || file_name == "index.tsx" {
            return "react_entry".to_string();
        }

        if file_name.to_lowercase().contains("page")
            || file_path.to_string_lossy().contains("/pages/")
        {
            return "react_page".to_string();
        }

        if file_name.to_lowercase().contains("hook") || file_name.starts_with("use") {
            return "react_hook".to_string();
        }

        // 检查内容模式
        if content.contains("export default")
            && (content.contains("return (") || content.contains("return <"))
        {
            "react_component".to_string()
        } else if self.hook_regex.is_match(content) {
            "react_hook".to_string()
        } else if content.contains("createContext") || content.contains("useContext") {
            "react_context".to_string()
        } else if content.contains("reducer") || content.contains("useReducer") {
            "react_reducer".to_string()
        } else {
            "react_utility".to_string()
        }
    }

    fn is_important_line(&self, line: &str) -> bool {
        let trimmed = line.trim();

        // React组件定义
        if trimmed.starts_with("function ")
            && (trimmed.contains("()") || trimmed.contains("(props"))
            || trimmed.starts_with("const ") && trimmed.contains("= (") && trimmed.contains("=>")
        {
            return true;
        }

        // React Hooks
        if trimmed.contains("useState")
            || trimmed.contains("useEffect")
            || trimmed.contains("useContext")
            || trimmed.contains("useReducer")
            || trimmed.contains("useMemo")
            || trimmed.contains("useCallback")
            || self.hook_regex.is_match(trimmed)
        {
            return true;
        }

        // JSX返回语句
        if trimmed.starts_with("return (") || trimmed.starts_with("return <") {
            return true;
        }

        // 导入导出语句
        if trimmed.starts_with("import ") || trimmed.starts_with("export ") {
            return true;
        }

        // React特有的模式
        if trimmed.contains("createContext")
            || trimmed.contains("forwardRef")
            || trimmed.contains("memo(")
            || trimmed.contains("lazy(")
        {
            return true;
        }

        // 重要注释
        if trimmed.contains("TODO")
            || trimmed.contains("FIXME")
            || trimmed.contains("NOTE")
            || trimmed.contains("HACK")
        {
            return true;
        }

        false
    }

    fn language_name(&self) -> &'static str {
        "React"
    }

    fn extract_interfaces(&self, content: &str, _file_path: &Path) -> Vec<InterfaceInfo> {
        let mut interfaces = Vec::new();
        let lines: Vec<&str> = content.lines().collect();

        // React组件的接口分析主要关注组件定义和Hook使用
        for (i, line) in lines.iter().enumerate() {
            let trimmed = line.trim();

            // 提取函数组件定义
            if let Some(component_name) = self.extract_function_component(trimmed) {
                interfaces.push(InterfaceInfo {
                    name: component_name,
                    interface_type: "react_component".to_string(),
                    visibility: "public".to_string(),
                    parameters: Vec::new(),
                    return_type: Some("JSX.Element".to_string()),
                    description: self.extract_component_comment(&lines, i),
                });
            }

            // 提取类组件定义
            if let Some(component_name) = self.extract_class_component(trimmed) {
                interfaces.push(InterfaceInfo {
                    name: component_name,
                    interface_type: "react_class_component".to_string(),
                    visibility: "public".to_string(),
                    parameters: Vec::new(),
                    return_type: Some("JSX.Element".to_string()),
                    description: self.extract_component_comment(&lines, i),
                });
            }

            // 提取自定义Hook定义
            if let Some(hook_name) = self.extract_custom_hook(trimmed) {
                interfaces.push(InterfaceInfo {
                    name: hook_name,
                    interface_type: "react_hook".to_string(),
                    visibility: "public".to_string(),
                    parameters: Vec::new(),
                    return_type: None,
                    description: self.extract_component_comment(&lines, i),
                });
            }
        }

        interfaces
    }
}

impl ReactProcessor {
    /// 提取函数组件名称
    fn extract_function_component(&self, line: &str) -> Option<String> {
        // 匹配: function ComponentName, const ComponentName = (), export function ComponentName
        if line.contains("function") && (line.contains("return") || line.contains("=>")) {
            if let Some(start) = line.find("function") {
                let after_function = &line[start + 8..].trim();
                if let Some(space_pos) = after_function.find(' ') {
                    let name = after_function[..space_pos].trim();
                    if name.chars().next().map_or(false, |c| c.is_uppercase()) {
                        return Some(name.to_string());
                    }
                }
            }
        }

        // 匹配: const ComponentName = () => 或 const ComponentName: React.FC
        if line.starts_with("const") || line.starts_with("export const") {
            if let Some(eq_pos) = line.find('=') {
                let before_eq = &line[..eq_pos];
                if let Some(name_start) = before_eq.rfind(' ') {
                    let name = before_eq[name_start + 1..].trim().trim_end_matches(':');
                    if name.chars().next().map_or(false, |c| c.is_uppercase()) {
                        return Some(name.to_string());
                    }
                }
            }
        }

        None
    }

    /// 提取类组件名称
    fn extract_class_component(&self, line: &str) -> Option<String> {
        if line.contains("class")
            && (line.contains("extends React.Component") || line.contains("extends Component"))
        {
            if let Some(class_pos) = line.find("class") {
                let after_class = &line[class_pos + 5..].trim();
                if let Some(space_pos) = after_class.find(' ') {
                    let name = after_class[..space_pos].trim();
                    if name.chars().next().map_or(false, |c| c.is_uppercase()) {
                        return Some(name.to_string());
                    }
                }
            }
        }
        None
    }

    /// 提取自定义Hook名称
    fn extract_custom_hook(&self, line: &str) -> Option<String> {
        // 匹配: function useCustomHook, const useCustomHook =
        if line.contains("function use") || (line.contains("const use") && line.contains('=')) {
            if line.contains("function") {
                if let Some(start) = line.find("function") {
                    let after_function = &line[start + 8..].trim();
                    if let Some(space_pos) = after_function.find(' ') {
                        let name = after_function[..space_pos].trim();
                        if name.starts_with("use") && name.len() > 3 {
                            return Some(name.to_string());
                        }
                    }
                }
            } else if line.contains("const") {
                if let Some(eq_pos) = line.find('=') {
                    let before_eq = &line[..eq_pos];
                    if let Some(name_start) = before_eq.rfind(' ') {
                        let name = before_eq[name_start + 1..].trim();
                        if name.starts_with("use") && name.len() > 3 {
                            return Some(name.to_string());
                        }
                    }
                }
            }
        }
        None
    }

    /// 提取组件注释
    fn extract_component_comment(&self, lines: &[&str], current_line: usize) -> Option<String> {
        let mut doc_lines = Vec::new();

        // 向上查找注释
        for i in (0..current_line).rev() {
            let line = lines[i].trim();

            if line.starts_with("//") {
                doc_lines.insert(0, line.trim_start_matches("//").trim().to_string());
            } else if line.starts_with("/*") && line.ends_with("*/") {
                let content = line.trim_start_matches("/*").trim_end_matches("*/").trim();
                doc_lines.insert(0, content.to_string());
                break;
            } else if !line.is_empty() {
                break;
            }
        }

        if doc_lines.is_empty() {
            None
        } else {
            Some(doc_lines.join(" "))
        }
    }
}