| 577 | |
| 578 | |
| 579 | class JavaScriptParser(LanguageParser): # This is the old regex-based parser |
| 580 | def __init__(self): |
| 581 | # Regex for functions, classes, and basic imports/exports. |
| 582 | # WARNING: Regex-based parsing for JS/TS is very limited and error-prone. |
| 583 | # A proper AST parser (e.g., esprima, acorn, or tree-sitter) is highly recommended for robust analysis. |
| 584 | |
| 585 | # function funcName(...) or const funcName = (...) => { ... } or let funcName = function(...) |
| 586 | self.function_pattern = re.compile( |
| 587 | r"^\s*(?:async\s+)?function\s+([a-zA-Z0-9_]+)\s*\(|" # function foo() |
| 588 | r"^\s*(?:const|let|var)\s+([a-zA-Z0-9_]+)\s*=\s*(?:async\s*)?\(?[^)]*\)?\s*=>|" # const foo = () => |
| 589 | r"^\s*(?:const|let|var)\s+([a-zA-Z0-9_]+)\s*=\s*function\s*\(", # const foo = function() |
| 590 | re.MULTILINE, |
| 591 | ) |
| 592 | |
| 593 | self.class_pattern = re.compile( |
| 594 | r"^\s*class\s+([a-zA-Z0-9_]+)(?:\s+extends\s+([a-zA-Z0-9_]+))?", |
| 595 | re.MULTILINE, |
| 596 | ) |
| 597 | |
| 598 | # import defaultExport from 'module'; import { namedExport } from 'module'; import * as name from 'module'; |
| 599 | self.import_pattern = re.compile( |
| 600 | r"import\s+(?:(.+?)\s+from\s+)?['\"]([^'\"]+)['\"]", re.MULTILINE |
| 601 | ) |
| 602 | pass |
| 603 | |
| 604 | def parse( |
| 605 | self, files_data: List[Dict[str, Any]], all_files_content: Dict[str, str] |
| 606 | ) -> Tuple[List[GraphNodeData], List[GraphEdgeData]]: |
| 607 | nodes_data: List[GraphNodeData] = [] |
| 608 | edges_data: List[GraphEdgeData] = [] |
| 609 | |
| 610 | for file_data in files_data: |
| 611 | path = file_data["path"] |
| 612 | # Ensure the parser only processes files it's supposed to handle, |
| 613 | # although GraphGenerator._get_parser should already filter. |
| 614 | if not (path.endswith((".js", ".jsx", ".ts", ".tsx"))): |
| 615 | continue |
| 616 | |
| 617 | content = file_data["content"] |
| 618 | # Module ID from path, similar to Python |
| 619 | module_id = path.replace("/", ".").rsplit(".", 1)[0] |
| 620 | if ( |
| 621 | Path(path).name == Path(path).stem and "." not in Path(path).stem |
| 622 | ): # for files like 'index' without extension in id |
| 623 | module_id = path.replace("/", ".") |
| 624 | |
| 625 | # Create module node |
| 626 | module_node: GraphNodeData = { |
| 627 | "id": module_id, |
| 628 | "name": Path(path).name, # Use full filename as name for module |
| 629 | "category": "module", |
| 630 | "file": path, |
| 631 | "start_line": 1, |
| 632 | "end_line": len(content.splitlines()), |
| 633 | "code": None, |
| 634 | "parent_id": str(Path(path).parent), # Set parent to directory path |
| 635 | "imports": [], |
| 636 | } |