CodeGraph/docs
Section 2.1

System 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.

Source: server/src/parser/index.ts

The 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:

StageNameInputOutputKey Purpose
Step 0Path NormalizationRaw path stringNormalized absolute pathEnsures filesystem paths match resolved relative and aliased import paths.
Step 1Repository WalkerDirectory pathRepositoryFiles (4 arrays)Recursively scans filesystem, filters ignored dirs, and groups source files & configs.
Step 2Metadata ExtractionConfig file pathsRepositoryMetadataExtracts package.json dependencies and tsconfig/jsconfig baseUrl & path aliases.
Step 3AST GenerationSource file pathsParsedFile[] & ParseFailure[]Parses JS/TS code with Babel into ASTs with modern syntax plugins.
Step 4Symbol ExtractionParsedFile (empty symbols)Populates .symbols & .exportsPass 1 AST traversal: extracts functions, classes, methods, variables, and types.
Step 5Relationship ExtractionParsedFile[] & MetadataParsedRelationship[]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:

server/src/parser/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
export 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
Individual file syntax errors are caught and appended to failures: ParseFailure[]. The parser never crashes the entire repository run due to a single invalid file; valid files continue through the pipeline.