server/src/parser/index.tsSystem Overview
The complete 5-stage transformation pipeline that parses source code, extracts symbols and scopes, resolves complex relationship bindings, and constructs the canonical in-memory graph.
server/src/parser/index.tsThe 5 Sequential Pipeline Stages
The top-level Parser.parse(repositoryPath) orchestrator executes a strict, sequential pipeline. Each stage depends strictly on the structured output of its predecessor:
| Stage | Name | Input | Output | Key Purpose |
|---|---|---|---|---|
| Step 0 | Path Normalization | Raw path string | Normalized absolute path | Ensures filesystem paths match resolved relative and aliased import paths. |
| Step 1 | Repository Walker | Directory path | RepositoryFiles (4 arrays) | Recursively scans filesystem, filters ignored dirs, and groups source files & configs. |
| Step 2 | Metadata Extraction | Config file paths | RepositoryMetadata | Extracts package.json dependencies and tsconfig/jsconfig baseUrl & path aliases. |
| Step 3 | AST Generation | Source file paths | ParsedFile[] & ParseFailure[] | Parses JS/TS code with Babel into ASTs with modern syntax plugins. |
| Step 4 | Symbol Extraction | ParsedFile (empty symbols) | Populates .symbols & .exports | Pass 1 AST traversal: extracts functions, classes, methods, variables, and types. |
| Step 5 | Relationship Extraction | ParsedFile[] & Metadata | ParsedRelationship[] | Pass 2 AST traversal: resolves calls, extends, implements, instantiates, and imports. |
Orchestrator Implementation
Here is how the Parser class coordinates the stages in server/src/parser/index.ts:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647export class Parser { async parse(repositoryPath: string): Promise<ParsedRepository> { // Step 0: Absolute Path Normalization repositoryPath = path.resolve(repositoryPath); // Step 1: Getting repository files const repositoryFiles = getRepositoryFiles(repositoryPath); if (repositoryFiles.sourceFiles.length === 0) { throw new NoSupportedFileError(); } // Step 2: Extracting metadata (package.json & tsconfig.json) const packageJsonExtractor = new PackageJsonExtractor(); const pathConfigExtractor = new PathConfigExtractor(); const packageJsons = repositoryFiles.packageJsonFiles.map(f => packageJsonExtractor.extract(f)); const pathConfigs = [ ...repositoryFiles.tsconfigJsonFiles, ...repositoryFiles.jsconfigJsonFiles ].map(f => pathConfigExtractor.extract(f)); const metadata: RepositoryMetadata = { packageJsons, pathConfigs }; // Step 3: Parsing files and generating Babel ASTs const parsedFiles: ParsedFile[] = []; const failedFiles: ParseFailure[] = []; for (const file of repositoryFiles.sourceFiles) { try { parsedFiles.push(parseFile(file)); } catch (err: unknown) { failedFiles.push({ filePath: file, message: err instanceof Error ? err.message : "Parse error" }); } } // Step 4: First AST Pass — Symbol Extraction const symbolExtractor = new SymbolExtractor(); for (const parsedFile of parsedFiles) { symbolExtractor.extract(parsedFile); } // Step 5: Second AST Pass — Relationship Extraction const relationshipExtractor = new RelationshipExtractor(); const relationships = relationshipExtractor.extract(parsedFiles, metadata); return { repositoryPath, files: parsedFiles, metadata, failures: failedFiles, relationships }; } }
Architectural Invariant: Why Two AST Passes?
A fundamental requirement of code graph construction is that cross-file relationships cannot be resolved until all symbols and exports across all files are known.
If File A imports { validateUser } from File B, File A's AST visitor cannot link the call site to validateUser's node ID unless File B has already been parsed and its symbols extracted. By separating Symbol Extraction (Pass 1) from Relationship Extraction (Pass 2), CodeGraph guarantees 100% resolution accuracy regardless of file traversal order.
Analysis Coverage & Evolving Syntax Model
CodeGraph analyzes repositories using a static syntax and relationship model. The parser extracts syntactic declarations and explicit binding relationships directly from Abstract Syntax Trees, and analytics algorithms operate deterministically on that extracted graph representation.
JavaScript and TypeScript support an extraordinarily flexible range of language constructs, functional compositions, and coding patterns. Coverage of these syntactic forms is continuously expanding and evolving:
- Direct Bindings: Explicit function calls, class inheritance (
extends), interface contracts (implements), constructor instantiations (new), and ES module imports are fully modeled. - Higher-Order Wrappers & Dynamic Constructs: Complex patterns such as higher-order middleware wrappers (e.g.,
const createUser = asyncHandler(async (req, res) => { ... })) or dynamic property access are completely valid code, though not every indirect relationship may be fully represented by the static extraction model. - Structural Insights: Analysis results provide high-fidelity structural facts and architectural insights across files and symbols, designed as practical engineering telemetry rather than an exhaustive runtime semantic simulation.
Error Containment
failures: ParseFailure[]. The parser never crashes the entire repository run due to a single invalid file; valid files continue through the pipeline.