dumpling 0.1.0

A fast JavaScript runtime and bundler in Rust
Documentation
use crate::error::Result;
use std::path::PathBuf;

#[derive(Debug, Clone)]
pub struct TypeScriptConfig {
    pub target: String,
    pub module: String,
    pub source_map: bool,
    pub declaration: bool,
    pub strict: bool,
}

impl Default for TypeScriptConfig {
    fn default() -> Self {
        Self {
            target: "ES2020".to_string(),
            module: "ESNext".to_string(),
            source_map: true,
            declaration: false,
            strict: true,
        }
    }
}

pub struct TypeScriptTranspiler {
    config: TypeScriptConfig,
}

impl TypeScriptTranspiler {
    pub fn new(config: TypeScriptConfig) -> Self {
        Self { config }
    }

    pub fn transpile(&self, path: &PathBuf, source: &str) -> Result<String> {
        // For now, we'll just return the source as-is
        // In a real implementation, this would transform TypeScript to JavaScript
        // The SWC integration was causing compilation issues, so we'll simplify for now

        // Add a comment to indicate the file was processed
        let mut result = String::new();

        if self.config.strict {
            result.push_str("'use strict';\n");
        }

        // Simple TypeScript to JavaScript transformations
        let processed = self.simple_transform(source)?;
        result.push_str(&processed);

        Ok(result)
    }

    pub fn generate_declaration(&self, path: &PathBuf, source: &str) -> Result<String> {
        // For now, just return the source as a declaration file
        // In a real implementation, this would extract type information
        let file_name = path
            .file_name()
            .and_then(|n| n.to_str())
            .unwrap_or("unknown.ts");

        Ok(format!(
            "// Declaration file for {}\n// Type extraction not implemented yet\n",
            file_name
        ))
    }

    fn simple_transform(&self, source: &str) -> Result<String> {
        // Very basic TypeScript transformations:
        // 1. Remove type annotations
        // 2. Handle interfaces (convert to comments for now)
        // 3. Remove exports that don't have values

        let mut result = String::new();
        let mut in_interface = false;
        let mut in_type_alias = false;
        let mut brace_count = 0;

        for line in source.lines() {
            let trimmed = line.trim();

            // Skip interface declarations
            if trimmed.starts_with("interface ") {
                in_interface = true;
                continue;
            }

            // Skip type aliases
            if trimmed.starts_with("type ") && trimmed.contains("= ") {
                in_type_alias = true;
                continue;
            }

            // Track braces for interfaces/types
            if in_interface || in_type_alias {
                for c in line.chars() {
                    if c == '{' {
                        brace_count += 1;
                    } else if c == '}' {
                        brace_count -= 1;
                    }
                }

                if brace_count == 0 {
                    in_interface = false;
                    in_type_alias = false;
                }
                continue;
            }

            // Remove type annotations from function parameters
            let processed_line = self.remove_type_annotations(trimmed);

            // Remove type annotations from variable declarations
            let processed_line = self.remove_var_type_annotations(&processed_line);

            // Handle export types (convert to comments)
            let processed_line = if trimmed.starts_with("export type ") {
                format!("// {}", trimmed)
            } else {
                processed_line.to_string()
            };

            result.push_str(&processed_line);
            result.push('\n');
        }

        Ok(result)
    }

    fn remove_type_annotations(&self, line: &str) -> String {
        // Simple regex-like pattern matching to remove type annotations
        let mut result = String::new();
        let mut chars = line.chars().peekable();

        while let Some(c) = chars.next() {
            match c {
                ':' => {
                    // Check if this is a type annotation
                    if let Some(&next_char) = chars.peek() {
                        if next_char.is_whitespace() || next_char.is_alphabetic() {
                            // Skip until we find a non-type character
                            let mut depth = 0;
                            while let Some(&next) = chars.peek() {
                                match next {
                                    '{' | '(' | '<' => depth += 1,
                                    '}' | ')' | '>' => {
                                        if depth > 0 {
                                            depth -= 1;
                                        }
                                    }
                                    '=' | ',' | ')' | '}' | ';' => {
                                        if depth == 0 {
                                            break;
                                        }
                                    }
                                    _ => {}
                                }
                                chars.next();
                            }
                            continue;
                        }
                    }
                    result.push(c);
                }
                _ => result.push(c),
            }
        }

        result
    }

    fn remove_var_type_annotations(&self, line: &str) -> String {
        // Remove type annotations from variable declarations
        // Pattern: "const/let/var name: type = value" -> "const/let/var name = value"
        let line = line.replace(" as any", "");

        // This is a simplified approach - a real implementation would need proper parsing
        if line.trim().starts_with("const ")
            || line.trim().starts_with("let ")
            || line.trim().starts_with("var ")
        {
            // Split on the first ':' to remove type annotation
            if let Some(colon_pos) = line.find(':') {
                let before = &line[..colon_pos];

                // Find the '=' after the colon
                if let Some(eq_pos) = line[colon_pos..].find('=') {
                    let after = &line[colon_pos + eq_pos..];
                    return format!("{} {}", before.trim_end(), after);
                }
            }
        }

        line.to_string()
    }
}

pub fn is_typescript_file(path: &PathBuf) -> bool {
    if let Some(ext) = path.extension() {
        if let Some(ext_str) = ext.to_str() {
            return ext_str == "ts" || ext_str == "tsx";
        }
    }
    false
}

pub fn get_declaration_path(path: &PathBuf) -> PathBuf {
    let mut declaration_path = path.clone();
    if let Some(ext) = declaration_path.extension() {
        let ext_str = ext.to_string_lossy();
        let new_ext: String = if ext_str == "ts" || ext_str == "tsx" {
            "d.ts".to_string()
        } else {
            format!("{}.d.ts", ext_str)
        };
        declaration_path.set_extension(new_ext.as_str());
    } else {
        declaration_path.set_extension("d.ts");
    }
    declaration_path
}