Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 79 additions & 19 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,17 @@ The main logic for code generation resides in the <mcfile name="ts-morph-codegen

The code generation process includes sophisticated import resolution and dependency management to handle complex type hierarchies and multi-level import chains:

#### DependencyCollector
#### DependencyTraversal

The <mcfile name="dependency-collector.ts" path="src/traverse/dependency-collector.ts"></mcfile> module implements a `DependencyCollector` class that:
The <mcfile name="dependency-traversal.ts" path="src/traverse/dependency-traversal.ts"></mcfile> module implements a unified `DependencyTraversal` class that exclusively uses Graphology for all dependency management:

- **Graphology-Only Architecture**: Uses Graphology's DirectedGraph exclusively for all dependency tracking, eliminating Map-based and other tracking mechanisms
- **Unified Data Management**: All dependency information is stored and managed through the Graphology graph structure with node attributes
- **Topological Sorting**: Employs graphology-dag for robust dependency ordering with circular dependency detection and preference-based sorting
- **Traverses Import Chains**: Recursively follows import declarations to collect all type dependencies from external files
- **Builds Dependency Graph**: Creates a comprehensive map of type dependencies, tracking which types depend on which other types
- **Topological Sorting**: Performs topological sorting to ensure types are generated in the correct dependency order
- **Handles Multi-level Imports**: Supports complex scenarios with 3+ levels of nested imports (e.g., `TypeA` imports `TypeB` which imports `TypeC`)
- **Graph-Based Caching**: Uses Graphology node attributes for caching type information and dependency relationships
- **Export-Aware Ordering**: Provides specialized ordering logic for `exportEverything=false` scenarios to ensure proper dependency resolution

#### Key Features

Expand All @@ -93,19 +96,58 @@ The <mcfile name="dependency-collector.ts" path="src/traverse/dependency-collect

#### Implementation Details

The import resolution process works in two phases:
The import resolution process works in three phases using the unified architecture:

1. **Collection Phase**:
- `DependencyCollector.collectFromImports()` traverses all import declarations
- `DependencyCollector.addLocalTypes()` adds local type aliases
- Dependencies are tracked in a `Map<string, TypeInfo>` structure
- `DependencyTraversal.collectFromImports()` traverses all import declarations using integrated AST traversal
- `DependencyTraversal.addLocalTypes()` adds local type aliases with unified type reference extraction
- Dependencies are tracked using Graphology's DirectedGraph with nodes and edges representing type relationships

2. **Generation Phase**:
- `DependencyCollector.getTopologicallySortedTypes()` returns types in dependency order
2. **Analysis Phase**:
- `DependencyTraversal.getTopologicallySortedTypesWithPreference()` performs dependency ordering with export-aware preferences
- Uses graphology-dag for circular dependency detection and topological sorting
- Handles complex dependency scenarios including imported vs. local type ordering based on `exportEverything` flag

3. **Generation Phase**:
- `DependencyTraversal.getTopologicallySortedTypes()` returns types in dependency order
- `TypeAliasParser.parseWithImportFlag()` generates code with appropriate export handling
- Types are processed sequentially in the sorted order

This approach ensures that complex import scenarios work correctly and generated code compiles without dependency errors.
This unified approach ensures robust handling of complex import scenarios, circular dependencies, and generates code that compiles without dependency errors while maintaining optimal performance through reduced module boundaries.

### Unified Dependency Management

The dependency management system is built on a unified architecture that integrates all dependency-related functionality:

#### DependencyTraversal Integration

The <mcfile name="dependency-traversal.ts" path="src/traverse/dependency-traversal.ts"></mcfile> module provides comprehensive dependency management:

- **Integrated AST Traversal**: Combines AST traversal logic with dependency collection for optimal performance
- **Direct Graphology Usage**: Uses Graphology's DirectedGraph directly for dependency tracking without abstraction layers
- **Unified Type Reference Extraction**: Consolidates type reference extraction logic within the main traversal module
- **Export-Aware Processing**: Implements specialized logic for handling `exportEverything=false` scenarios with proper dependency ordering
- **Performance Optimization**: Eliminates module boundaries and reduces function call overhead through unified architecture

#### Graph-Based Dependency Resolution

The unified module leverages Graphology's ecosystem for robust dependency management:

- **DirectedGraph**: Uses Graphology's optimized graph data structure for dependency relationships
- **Topological Sorting**: Employs `topologicalSort` from `graphology-dag` for dependency ordering with circular dependency detection
- **Preference-Based Ordering**: Implements `getTopologicallySortedTypesWithPreference()` for export-aware type ordering
- **Memory Efficiency**: Direct Graphology usage provides optimal memory management for large dependency graphs
- **Type Safety**: Full TypeScript support through graphology-types package

#### Simplified Architecture Benefits

The Graphology-only approach provides several advantages:

- **Simplified Architecture**: Eliminates multiple tracking mechanisms (Map-based dependencies, visitedFiles, various caches) in favor of a single graph-based solution
- **Enhanced Performance**: Direct Graphology operations provide optimized graph algorithms and data structures
- **Improved Maintainability**: Single dependency tracking mechanism reduces complexity and potential inconsistencies
- **Better Memory Management**: Graphology's optimized memory handling for large dependency graphs
- **Unified Data Model**: All dependency information stored consistently in graph nodes and edges

## Interface Inheritance Support

Expand All @@ -115,13 +157,13 @@ The codebase provides comprehensive support for TypeScript interface inheritance

The main codegen logic in <mcfile name="ts-morph-codegen.ts" path="src/ts-morph-codegen.ts"></mcfile> implements sophisticated processing order management:

1. **Dependency Analysis**: Uses `InterfaceTypeDependencyAnalyzer` to analyze complex relationships between interfaces and type aliases
2. **Conditional Processing**: Handles three scenarios:
- Interfaces depending on type aliases only
- Type aliases depending on interfaces only
- Both dependencies present (three-phase processing)
3. **Topological Sorting**: Ensures types are processed in correct dependency order to prevent "type not found" errors
4. **Circular Dependency Detection**: The algorithm detects and handles circular inheritance scenarios gracefully
1. **Unified Dependency Analysis**: Uses <mcfile name="dependency-traversal.ts" path="src/traverse/dependency-traversal.ts"></mcfile> with integrated graph-based architecture to analyze complex relationships between interfaces and type aliases
2. **Direct Graph Processing**: Leverages Graphology's DirectedGraph and topological sorting for robust dependency ordering without abstraction layers
3. **Export-Aware Processing**: Handles dependency ordering based on `exportEverything` flag:
- `exportEverything=true`: Prioritizes imported types for consistent ordering
- `exportEverything=false`: Ensures dependency-aware ordering while respecting local type preferences
4. **Topological Sorting**: Uses `getTopologicallySortedTypesWithPreference()` to ensure types are processed in correct dependency order to prevent "type not found" errors
5. **Circular Dependency Detection**: The graph-based algorithm detects and handles circular inheritance scenarios gracefully with detailed error reporting

### TypeBox Composite Generation

Expand Down Expand Up @@ -277,7 +319,25 @@ When implementing new type handlers or modifying existing ones, it is crucial to

## Performance Optimizations

Several optimizations have been implemented to improve the performance of the code generation process, particularly for import resolution:
Several optimizations have been implemented to improve the performance of the code generation process, particularly for import resolution and dependency management:

### Unified Dependency Management with Graphology

The project uses **Graphology** through a unified architecture for all dependency graph operations, providing:

- **Production-Ready Graph Library**: Leverages Graphology's battle-tested graph data structures and algorithms
- **Optimized Performance**: Benefits from Graphology's highly optimized internal implementations for graph operations
- **Advanced Graph Algorithms**: Direct access to specialized algorithms through Graphology ecosystem (graphology-dag, graphology-traversal)
- **Type Safety**: Full TypeScript support through graphology-types package
- **Memory Efficiency**: Graphology's optimized memory management for large graphs
- **Unified Architecture**: Single module eliminates abstraction layers and reduces complexity

#### Core Architecture

- **DependencyTraversal**: Uses Graphology's `DirectedGraph` exclusively for all dependency tracking, with no fallback to Map-based structures
- **Integrated Topological Sorting**: Leverages `topologicalSort` from `graphology-dag` for ordering dependencies with export-aware preferences
- **Graph-Based Data Storage**: All dependency information, visited files, and type metadata stored as Graphology node attributes
- **Export-Aware Processing**: Implements specialized ordering logic for different export scenarios using graph-based algorithms

### TypeBox Type Handler Optimization

Expand Down
60 changes: 42 additions & 18 deletions bun.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,33 @@
"workspaces": {
"": {
"name": "new-bun-project",
"dependencies": {
"graphology": "^0.26.0",
"graphology-dag": "^0.4.1",
"graphology-traversal": "^0.3.1",
},
"devDependencies": {
"@eslint/js": "latest",
"@prettier/sync": "latest",
"@sinclair/typebox-codegen": "latest",
"@types/bun": "latest",
"@typescript-eslint/eslint-plugin": "latest",
"@typescript-eslint/parser": "latest",
"eslint": "latest",
"eslint-config-prettier": "latest",
"eslint-plugin-no-relative-import-paths": "latest",
"eslint-plugin-prettier": "latest",
"globals": "latest",
"jiti": "latest",
"prettier": "latest",
"prettier-plugin-organize-imports": "latest",
"ts-morph": "latest",
"typescript-eslint": "latest",
"wikibase-sdk": "latest",
"@eslint/js": "^9.34.0",
"@prettier/sync": "^0.6.1",
"@sinclair/typebox-codegen": "^0.11.1",
"@types/bun": "^1.2.21",
"@typescript-eslint/eslint-plugin": "^8.41.0",
"@typescript-eslint/parser": "^8.41.0",
"eslint": "^9.34.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-no-relative-import-paths": "^1.6.1",
"eslint-plugin-prettier": "^5.5.4",
"globals": "^16.3.0",
"graphology-types": "^0.24.8",
"jiti": "^2.5.1",
"prettier": "^3.6.2",
"prettier-plugin-organize-imports": "^4.2.0",
"ts-morph": "^26.0.0",
"typescript-eslint": "^8.41.0",
"wikibase-sdk": "^10.2.3",
},
"peerDependencies": {
"typescript": "latest",
"typescript": "~5.9.2",
},
},
},
Expand Down Expand Up @@ -166,6 +172,8 @@

"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],

"events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],

"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],

"fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="],
Expand Down Expand Up @@ -194,6 +202,18 @@

"graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],

"graphology": ["graphology@0.26.0", "", { "dependencies": { "events": "^3.3.0" }, "peerDependencies": { "graphology-types": ">=0.24.0" } }, "sha512-8SSImzgUUYC89Z042s+0r/vMibY7GX/Emz4LDO5e7jYXhuoWfHISPFJYjpRLUSJGq6UQ6xlenvX1p/hJdfXuXg=="],

"graphology-dag": ["graphology-dag@0.4.1", "", { "dependencies": { "graphology-utils": "^2.4.1", "mnemonist": "^0.39.0" }, "peerDependencies": { "graphology-types": ">=0.19.0" } }, "sha512-3ch9oOAnHZDoT043vyg7ukmSkKJ505nFzaHaYOn0IF2PgGo5VtIavyVK4UpbIa4tli3hhGm1ZTdBsubTmaxu/w=="],

"graphology-indices": ["graphology-indices@0.17.0", "", { "dependencies": { "graphology-utils": "^2.4.2", "mnemonist": "^0.39.0" }, "peerDependencies": { "graphology-types": ">=0.20.0" } }, "sha512-A7RXuKQvdqSWOpn7ZVQo4S33O0vCfPBnUSf7FwE0zNCasqwZVUaCXePuWo5HBpWw68KJcwObZDHpFk6HKH6MYQ=="],

"graphology-traversal": ["graphology-traversal@0.3.1", "", { "dependencies": { "graphology-indices": "^0.17.0", "graphology-utils": "^2.0.0" }, "peerDependencies": { "graphology-types": ">=0.20.0" } }, "sha512-lGLrLKEDKtNgAKgHVhVftKf3cb/nuWwuVPQZHXRnN90JWn0RSjco/s+NB2ARSlMapEMlbnPgv6j++427yTnU3Q=="],

"graphology-types": ["graphology-types@0.24.8", "", {}, "sha512-hDRKYXa8TsoZHjgEaysSRyPdT6uB78Ci8WnjgbStlQysz7xR52PInxNsmnB7IBOM1BhikxkNyCVEFgmPKnpx3Q=="],

"graphology-utils": ["graphology-utils@2.5.2", "", { "peerDependencies": { "graphology-types": ">=0.23.0" } }, "sha512-ckHg8MXrXJkOARk56ZaSCM1g1Wihe2d6iTmz1enGOz4W/l831MBCKSayeFQfowgF8wd+PQ4rlch/56Vs/VZLDQ=="],

"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],

"ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
Expand Down Expand Up @@ -236,10 +256,14 @@

"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],

"mnemonist": ["mnemonist@0.39.8", "", { "dependencies": { "obliterator": "^2.0.1" } }, "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ=="],

"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],

"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],

"obliterator": ["obliterator@2.0.5", "", {}, "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw=="],

"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],

"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
Expand Down
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"eslint-plugin-no-relative-import-paths": "^1.6.1",
"eslint-plugin-prettier": "^5.5.4",
"globals": "^16.3.0",
"graphology-types": "^0.24.8",
"jiti": "^2.5.1",
"prettier": "^3.6.2",
"prettier-plugin-organize-imports": "^4.2.0",
Expand All @@ -32,5 +33,10 @@
"typecheck": "tsc --noEmit",
"lint": "eslint"
},
"type": "module"
"type": "module",
"dependencies": {
"graphology": "^0.26.0",
"graphology-dag": "^0.4.1",
"graphology-traversal": "^0.3.1"
}
Comment on lines +36 to +41
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify Graphology ESM/CJS interop under "type": "module".

Graphology packages have had mixed ESM/CJS stories. With "type": "module", confirm imports like import Graph from 'graphology' work in Bun/Node for your test/runtime environments.

Run:


🏁 Script executed:

#!/bin/bash
# Check all import sites for graphology libs and ensure only ESM-style imports are used
rg -nC2 -e "from 'graphology" -e "from 'graphology-(dag|traversal)'" --type=ts

# If unsure about module format, consult upstream quickly:

Length of output: 546


🌐 Web query:

Do graphology@0.26.0, graphology-dag@0.4.1, and graphology-traversal@0.3.1 support ESM imports in Node when package.json has "type":"module"?

Error during web search


🏁 Script executed:

#!/bin/bash
set -e

echo "graphology@0.26.0 package.json relevant fields:"
curl -s https://unpkg.com/graphology@0.26.0/package.json | grep -E '"(main|module|exports)"' -B1 -A5

echo
echo "graphology-dag@0.4.1 package.json relevant fields:"
curl -s https://unpkg.com/graphology-dag@0.4.1/package.json | grep -E '"(main|module|exports)"' -B1 -A5

echo
echo "graphology-traversal@0.3.1 package.json relevant fields:"
curl -s https://unpkg.com/graphology-traversal@0.3.1/package.json | grep -E '"(main|module|exports)"' -B1 -A5

echo
echo "Import usage of graphology-traversal:"
rg -n "graphology-traversal" -C2 --type=ts || echo "No imports found"

Length of output: 1755


Graphology packages ESM/CJS support and required import adjustments

Based on the published package manifests:

  • graphology@0.26.0
    • Provides dual‐format builds:
    – “module”: dist/graphology.esm.js
    – “exports.import”: dist/graphology.mjs
    – “require”: dist/graphology.cjs.js
    → static import Graph from 'graphology' will load the ESM build under "type":"module".

  • graphology-dag@0.4.1 & graphology-traversal@0.3.1
    • Only declare "main": "index.js" with no "module" or "exports" entries
    → these are CJS‐only packages. Static ESM named imports (e.g. import { topologicalSort } from 'graphology-dag') may fail under Node’s ESM loader or in Bun.

Action items:

  • Confirm in your target runtimes (Node ≥14+, Bun) that all import sites still resolve as expected under "type":"module".
  • For CJS-only packages, switch to a default‐then‐destructure pattern if named imports error:
    import dag from 'graphology-dag';
    const { topologicalSort } = dag;
  • Alternatively, use dynamic import('graphology-dag') or adjust bundler settings to enable CJS–ESM interop where necessary.

Please update import statements or add compatibility shims so that the three packages load reliably in both Node and Bun under ESM.

🤖 Prompt for AI Agents
In package.json around lines 36 to 41, the review points out graphology is
dual-format ESM/CJS while graphology-dag and graphology-traversal are CJS-only,
which can break static ESM named imports under "type":"module"; update code so
imports are compatible: confirm behavior on target runtimes (Node ≥14+, Bun) and
replace any static named ESM imports from the CJS-only packages with a
default-then-destructure pattern or use dynamic import() where used, or add
bundler/CJS-interop shim; ensure any import sites that rely on named imports are
updated to first import the default and then destructure (or use await
import(...) in async contexts) so the packages load reliably under ESM.

}
111 changes: 0 additions & 111 deletions src/traverse/ast-traversal.ts

This file was deleted.

Loading