diff --git a/src/code_type_detector.rs b/src/code_type_detector.rs index 29b42bf..8f1c792 100644 --- a/src/code_type_detector.rs +++ b/src/code_type_detector.rs @@ -254,14 +254,22 @@ impl CodeTypeDetector { } } - fn extract_imports(&self, content: &str, _language: &str) -> Vec { + fn extract_imports(&self, content: &str, language: &str) -> Vec { + let lang = language.to_lowercase(); + if !matches!(lang.as_str(), "javascript" | "typescript" | "tsx" | "jsx" | "js" | "ts") { + return Vec::new(); + } + let mut imports = Vec::new(); - // Extract require() calls + // Extract require() calls and ES import statements (including multiline and side-effect imports). + // The import ... from pattern is constrained to not match across statement boundaries (semicolons). let require_patterns = vec![ regex::Regex::new(r#"require\(\s*['"]([^'"]+)['"]\s*\)"#).unwrap(), - regex::Regex::new(r#"import\s+.*from\s+['"]([^'"]+)['"]"#).unwrap(), + regex::Regex::new(r#"(?s)import\s+(?:\{[^};]*\}|[^;]+?)\s+from\s*['"]([^'"]+)['"]"#) + .unwrap(), regex::Regex::new(r#"import\s*\(\s*['"]([^'"]+)['"]\s*\)"#).unwrap(), + regex::Regex::new(r#"import\s+['"]([^'"]+)['"]"#).unwrap(), ]; for pattern in require_patterns { @@ -513,4 +521,46 @@ mod tests { assert_eq!(detector.detect_from_ast(&tree, source), CodeType::Backend); } + + #[test] + fn detect_code_type_handles_multiline_imports() { + let detector = CodeTypeDetector::new(); + let code = r#" + import { + Injectable, + NestMiddleware, + } from '@nestjs/common'; + + @Injectable() + export class AuthMiddleware {} + "#; + assert_eq!(detector.detect_code_type("auth.ts", code, "typescript"), CodeType::Backend); + } + + #[test] + fn detect_code_type_handles_side_effect_imports() { + let detector = CodeTypeDetector::new(); + let code = r#" + import 'express'; + export const app = {}; + "#; + assert_eq!(detector.detect_code_type("server.ts", code, "typescript"), CodeType::Backend); + } + + #[test] + fn import_regex_does_not_capture_across_statement_boundaries() { + let detector = CodeTypeDetector::new(); + // Semicolon ends the import statement; subsequent `from` in unrelated code must not form a match with `import` + let code = "import foo;\nlet x = calculate(from, 'react');\n"; + let imports = detector.extract_imports(code, "javascript"); + assert!(!imports.contains(&"react".to_string())); + } + + #[test] + fn non_js_imports_do_not_override_backend_default() { + let detector = CodeTypeDetector::new(); + // A Go file importing rxgo should not match JS frontend react signature + let code = "package main\n\nimport \"github.com/reactivex/rxgo\"\n"; + assert_eq!(detector.detect_code_type("main.go", code, "go"), CodeType::Backend); + } }