Skip to content

Conversation

@DaxServer
Copy link
Owner

This commit introduces several improvements to the dependency traversal
logic, specifically handling interface inheritance scenarios:

  1. Correctly extracts dependencies from interfaces that inherit from other
    interfaces, ensuring the correct order of dependencies.
  2. Handles multiple interface inheritance, where an interface extends
    multiple other interfaces.
  3. Supports nested interface inheritance, where an interface extends an
    interface that extends another interface.

These changes ensure that the dependency traversal algorithm can
accurately identify and order the dependencies for complex interface
inheritance structures, which is crucial for generating correct code.

This commit introduces several improvements to the dependency traversal
logic, specifically handling interface inheritance scenarios:

1. Correctly extracts dependencies from interfaces that inherit from other
   interfaces, ensuring the correct order of dependencies.
2. Handles multiple interface inheritance, where an interface extends
   multiple other interfaces.
3. Supports nested interface inheritance, where an interface extends an
   interface that extends another interface.

These changes ensure that the dependency traversal algorithm can
accurately identify and order the dependencies for complex interface
inheritance structures, which is crucial for generating correct code.
@coderabbitai
Copy link

coderabbitai bot commented Aug 28, 2025

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added API to generate an interactive HTML dependency graph with configurable visualization options (shapes, colors, layout, legend, zoom/hover).
  • Breaking Changes
    • Code generation function now accepts a single options object (signature updated).
  • Improvements
    • Expanded dependency extraction to fully support type aliases, interfaces (including inheritance), enums, and functions; improved ordering and cycle handling; prevented duplicate graph edges.
  • Documentation
    • Expanded architecture docs with parsers and visualization details.
  • Tests
    • Added extensive suites for dependency extraction, ordering, inheritance, and circular references.
  • Chores
    • Added graph visualization dependencies and package metadata.

Walkthrough

Adds interactive graph visualization and visualization API, expands dependency extraction to aliases/interfaces/enums/functions (including interface inheritance), refactors generateCode signature, makes NodeGraph edge addition idempotent, removes unused-node pruning, updates docs, adds Graphology/ForceAtlas2 deps, and introduces traversal tests.

Changes

Cohort / File(s) Summary
Docs
ARCHITECTURE.md
Expanded architecture: detailed dependency extraction for Type Aliases, Interfaces, Enums, Functions; Base/Specialized Parsers; and new Sigma.js-based Graph Visualization section.
Package & deps
package.json
Consolidated top-level metadata; added graph-related dependencies (graphology, graphology-dag, graphology-layout-forceatlas2, graphology-traversal); removed duplicate fields.
Public API
src/index.ts
Added CodeGenerationOptions (extends InputOptions) and generateVisualization(options): Promise<string>; refactored generateCode to accept a single options object and reuse traversal for visualization.
Traversal core
src/traverse/dependency-traversal.ts
Unified extraction across TypeAlias/Interface/Enum/Function; support for interface extends and TypeQuery; added visualizeGraph(options) and getNodeGraph(); updated getNodesToPrint to use graph-derived ordering with cycle fallback.
Node graph
src/traverse/node-graph.ts
Made addDependency guard against missing nodes and duplicate edges (idempotent); removed removeUnusedImportedNodes() method.
Visualization utils
src/utils/graph-visualizer.ts
New GraphVisualizer and VisualizationOptions; converts NodeGraph to Sigma.js data, runs ForceAtlas2 layout, generates interactive HTML (writes via Bun).
Tests
tests/traverse/*
Added/updated test suites for dependency extraction, ordering, interface inheritance, circular cases, and a renamed integration label. Files: simple-dependency.test.ts, dependency-ordering.test.ts, dependency-extraction.test.ts, circular-dependency.test.ts, interface-inheritance-dependency.test.ts, dependency-traversal.integration.test.ts.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User
  participant API as index.generateVisualization
  participant TS as SourceFileBuilder
  participant DT as DependencyTraversal
  participant GV as GraphVisualizer

  User->>API: generateVisualization(options)
  API->>TS: build SourceFile from options
  TS-->>API: sourceFile
  API->>DT: startTraversal(sourceFile)
  DT-->>API: traversed node graph
  API->>DT: visualizeGraph(visualizationOptions)
  DT->>GV: generateVisualization(nodeGraph, options)
  GV-->>API: outputPath (HTML)
  API-->>User: Promise<string> (HTML path)
Loading
sequenceDiagram
  autonumber
  participant DT as DependencyTraversal
  participant AST as TS AST Nodes
  participant NG as NodeGraph

  DT->>AST: visit node
  alt TypeAlias
    DT->>AST: analyze TypeNode for references
  else Interface
    DT->>AST: collect properties + extends clauses
  else Enum / Function
    DT->>AST: collect referenced types
  end
  DT->>NG: ensure node exists
  loop for each referenced type
    DT->>NG: addDependency(ref -> current) [guarded, idempotent]
  end
  DT-->>DT: compute ordering (topological or insertion-order fallback)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch interface-inheritance

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/traverse/dependency-traversal.ts (1)

271-288: Support typeof references in types (TypeQuery).

Dependencies via typeof (e.g., typeof createUser) aren’t captured. Add TypeQuery handling.

       if (Node.isTypeReference(node)) {
         const typeRefNode = node as TypeReferenceNode
         const typeName = typeRefNode.getTypeName().getText()
         ...
       }
+      // Handle `typeof Foo` used in type positions
+      if (Node.isTypeQueryNode && Node.isTypeQueryNode(node)) {
+        const baseName = node.getExprName().getText().split('.').pop()!
+        for (const qualifiedName of this.nodeGraph.nodes()) {
+          const nodeData = this.nodeGraph.getNode(qualifiedName)
+          if (nodeData.originalName === baseName) {
+            references.push(qualifiedName)
+            break
+          }
+        }
+      }
🧹 Nitpick comments (13)
ARCHITECTURE.md (2)

73-78: Minor grammar fixes in “Dependency Extraction” bullets.

Tighten phrasing and articles.

-3. **Dependency Extraction**: Analyzes type references to build dependency graph for all supported TypeScript constructs:
+3. **Dependency Extraction**: Analyzes type references to build a dependency graph for all supported TypeScript constructs:
-   - **Type Aliases**: Extracts dependencies from type alias declarations
+   - **Type Aliases**: Extracts dependencies from type-alias declarations
-   - **Interfaces**: Analyzes interface property types and heritage clauses
+   - **Interfaces**: Analyzes interface property types and heritage clauses
-   - **Enums**: Processes enum member value dependencies
+   - **Enums**: Processes enum-member value dependencies
-   - **Functions**: Extracts dependencies from parameter types, return types, and type parameters
+   - **Functions**: Extracts dependencies from parameter types, return types, and type parameters

80-101: Document CDN vs. package usage and expected environment.

The section implies loading Sigma programs via CDN. Clarify:

  • Whether local packages are required or the HTML exclusively uses CDN modules.
  • That visualization is browser-only (WebGL) and not needed for core codegen, to avoid confusion for headless/CI environments.

Add a short “Runtime assumptions” note.

tests/traverse/simple-dependency.test.ts (1)

5-39: Strengthen assertions to ensure exact ordering and membership.

Add an equality check to prevent accidental extras/omissions.

-    // Check correct ordering
+    // Check correct ordering and exact membership
     expect(userIdIndex).toBeLessThan(userIndex)
     expect(userIndex).toBeLessThan(usersIndex)
     expect(userIdIndex).toBeLessThan(usersIndex)
+    expect(result.map((n) => n.originalName)).toEqual(['UserId', 'User', 'Users'])
src/traverse/dependency-traversal.ts (1)

275-286: Name matching is O(N) per reference and ambiguous across modules.

Scanning all nodes and matching by originalName is slow and can mis-link when multiple modules share a name. Build an index or resolve by symbol.

I can propose a minimal Map<string, string[]> index built once in extractDependencies. Would you like a patch?

Also applies to: 301-307

tests/traverse/dependency-ordering.test.ts (2)

95-96: Avoid shadowing the shared Project instance.

Redundant const project = new Project() hides the beforeEach project and can lead to inconsistent setup.

-    const project = new Project()
+    // use the project from beforeEach
-    const project = new Project()
+    // use the project from beforeEach

Also applies to: 119-120


150-153: Assert the full chain ordering for clarity.

Also check Entity < Entities to fully cover the intended ordering.

     expect(entityIdIndex).toBeLessThan(entitiesIndex)
+    expect(entityIndex).toBeLessThan(entitiesIndex)
tests/traverse/circular-dependency.test.ts (1)

32-34: Add assertion for base interface in generic extends.

Item extends EntityInfo should also order EntityInfo before Item; this guards the generic-extends path.

Add:

const entityInfoIndex = result.findIndex((n) => n.originalName === 'EntityInfo')
const itemIndex = result.findIndex((n) => n.originalName === 'Item')
expect(entityInfoIndex).toBeGreaterThanOrEqual(0)
expect(entityInfoIndex).toBeLessThan(itemIndex)

This will fail until extends resolution handles generics (see src fix).

Also applies to: 48-63

tests/traverse/interface-inheritance-dependency.test.ts (3)

36-39: Use the return value of startTraversal directly.

No need to call getNodesToPrint() again.

-    traverser.startTraversal(sourceFile)
-    const dependencies = traverser.getNodesToPrint()
+    const dependencies = traverser.startTraversal(sourceFile)
-    traverser.startTraversal(sourceFile)
-    const dependencies = traverser.getNodesToPrint()
+    const dependencies = traverser.startTraversal(sourceFile)
-    traverser.startTraversal(sourceFile)
-    const dependencies = traverser.getNodesToPrint()
+    const dependencies = traverser.startTraversal(sourceFile)

Also applies to: 71-73, 100-102


7-10: Deduplicate helper across tests.

getNodeName appears in multiple suites; consider moving to tests/utils to avoid repetition.


61-88: Add a generics case to multiple inheritance.

A small extension (e.g., interface C extends A, B) would ensure generic params don’t affect ordering.

I can add a compact test if you want.

tests/traverse/dependency-extraction.test.ts (1)

7-10: Deduplicate helper across tests.

Move getNodeName to tests/utils to remove duplication.

src/index.ts (1)

54-57: Remove or use the unused visualize flag in CodeGenerationOptions.
visualize?: boolean isn't read anywhere. Either drop it to avoid API noise or make generateCode optionally trigger visualization.

 export interface CodeGenerationOptions extends InputOptions {
-  visualize?: boolean
   visualizationOptions?: VisualizationOptions
 }
src/utils/graph-visualizer.ts (1)

229-231: Consider pinning CDN integrity and versions.
Add SRI and exact versions to improve supply-chain safety and reproducibility.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6f520f1 and b614495.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • ARCHITECTURE.md (1 hunks)
  • package.json (1 hunks)
  • src/index.ts (2 hunks)
  • src/traverse/dependency-traversal.ts (4 hunks)
  • src/traverse/node-graph.ts (1 hunks)
  • src/utils/graph-visualizer.ts (1 hunks)
  • tests/traverse/circular-dependency.test.ts (1 hunks)
  • tests/traverse/dependency-extraction.test.ts (1 hunks)
  • tests/traverse/dependency-ordering.test.ts (2 hunks)
  • tests/traverse/dependency-traversal.integration.test.ts (1 hunks)
  • tests/traverse/interface-inheritance-dependency.test.ts (1 hunks)
  • tests/traverse/simple-dependency.test.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
tests/traverse/circular-dependency.test.ts (1)
src/traverse/dependency-traversal.ts (1)
  • DependencyTraversal (26-319)
tests/traverse/simple-dependency.test.ts (1)
src/traverse/dependency-traversal.ts (1)
  • DependencyTraversal (26-319)
tests/traverse/interface-inheritance-dependency.test.ts (3)
src/traverse/types.ts (1)
  • TraversedNode (5-12)
src/traverse/dependency-traversal.ts (1)
  • DependencyTraversal (26-319)
tests/utils.ts (1)
  • createSourceFile (12-14)
tests/traverse/dependency-ordering.test.ts (1)
src/traverse/dependency-traversal.ts (1)
  • DependencyTraversal (26-319)
src/traverse/dependency-traversal.ts (3)
src/traverse/types.ts (1)
  • TraversedNode (5-12)
src/utils/graph-visualizer.ts (2)
  • VisualizationOptions (6-9)
  • GraphVisualizer (32-424)
src/traverse/node-graph.ts (1)
  • NodeGraph (8-39)
src/utils/graph-visualizer.ts (1)
src/traverse/types.ts (1)
  • TraversedNode (5-12)
tests/traverse/dependency-extraction.test.ts (3)
src/traverse/types.ts (1)
  • TraversedNode (5-12)
src/traverse/dependency-traversal.ts (1)
  • DependencyTraversal (26-319)
tests/utils.ts (1)
  • createSourceFile (12-14)
src/index.ts (3)
src/input-handler.ts (2)
  • InputOptions (5-10)
  • createSourceFileFromInput (64-101)
src/utils/graph-visualizer.ts (2)
  • VisualizationOptions (6-9)
  • generateVisualization (36-54)
src/traverse/dependency-traversal.ts (1)
  • DependencyTraversal (26-319)
🪛 LanguageTool
ARCHITECTURE.md

[grammar] ~73-~73: There might be a mistake here.
Context: ...traction**: Analyzes type references to build dependency graph for all supported Type...

(QB_NEW_EN)


[grammar] ~73-~73: There might be a mistake here.
Context: ...for all supported TypeScript constructs: - Type Aliases: Extracts dependencies fr...

(QB_NEW_EN)

🔇 Additional comments (8)
tests/traverse/dependency-traversal.integration.test.ts (1)

11-11: Rename aligns terminology across suite.

The updated suite name improves clarity and doesn’t affect behavior. LGTM.

src/traverse/node-graph.ts (1)

29-38: Idempotent edge addition is a solid improvement.

Guarding missing nodes and duplicate edges will stabilize traversal builds. Nice.

src/traverse/dependency-traversal.ts (1)

38-41: Confirm intent: direct imports marked as isMainCode=true.

collectFromImports(..., true) labels directly imported declarations as main code; only transitive imports become non-main. If visualize/printing relies on isMainCode to prioritize main-file declarations, this may skew results.

Do you want direct imports to be considered main code? If not, pass false here.

tests/traverse/dependency-ordering.test.ts (1)

41-92: LGTM: ordering expectations align with traversal intent.

These cases validate simple and multi-usage chains well.

src/index.ts (3)

8-8: LGTM on type-only import.
No issues with importing VisualizationOptions for API typing.


59-69: OK to reuse traversal; ensure package export is wired.
generateVisualization correctly reuses DependencyTraversal. Verify it’s exported in package exports/types so consumers can import it.


71-74: LGTM on simplified parameter handling.
Switching from destructuring to passing the InputOptions object directly is non-breaking and clearer.

src/utils/graph-visualizer.ts (1)

229-231: Use the correct UMD globals for Graphology and Sigma
• Graphology v0.26.0 exports a single global graphology object (not Graph), with DirectedGraph as a top-level property. Replace any use of window.graphology.Graph or new Graph.DirectedGraph() with:

const { DirectedGraph } = window.graphology;
const graph = new DirectedGraph();

• Sigma v3.0.2 exposes only window.Sigma (no lowercase sigma). Use:

const Sigma = window.Sigma; // add “|| window.sigma” only if you must support older bundles

package.json Outdated
Comment on lines 38 to 42
"@sigma/node-border": "^3.0.0",
"@sigma/node-square": "^3.0.0",
"graphology": "^0.26.0",
"graphology-dag": "^0.4.1",
"graphology-layout-forceatlas2": "^0.10.1",
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

Validate need for new runtime deps; consider dev/optional or CDN-only.

If the visualization HTML loads Sigma node programs via unpkg at runtime (as ARCHITECTURE.md suggests), keeping @sigma/node-border, @sigma/node-square, and graphology-layout-forceatlas2 as hard runtime dependencies may be unnecessary and increases install footprint for consumers who won't use visualization.

  • If they’re not imported in Node code, remove them or move to devDependencies.
  • If only used to type-check/bundle examples, prefer devDeps; otherwise document why they must be runtime deps.

Run to confirm actual imports:

Optionally move to devDeps:

   "dependencies": {
-    "@sigma/node-border": "^3.0.0",
-    "@sigma/node-square": "^3.0.0",
     "graphology": "^0.26.0",
     "graphology-dag": "^0.4.1",
-    "graphology-layout-forceatlas2": "^0.10.1",
     "graphology-traversal": "^0.3.1"
   },
+  "devDependencies": {
+    "@sigma/node-border": "^3.0.0",
+    "@sigma/node-square": "^3.0.0",
+    "graphology-layout-forceatlas2": "^0.10.1",
+    ...
+  }

🏁 Script executed:

#!/bin/bash
rg -n "@sigma/node-(border|square)|graphology-layout-forceatlas2" -g '!**/node_modules/**' -C2

Length of output: 3581


Move @sigma/node-border and @sigma/node-square to devDependencies
They’re not imported anywhere in the codebase and—as ARCHITECTURE.md describes—are loaded via unpkg at runtime; retain graphology-layout-forceatlas2 in dependencies because it’s imported in src/utils/graph-visualizer.ts.

🤖 Prompt for AI Agents
In package.json around lines 38 to 42, two packages (@sigma/node-border and
@sigma/node-square) are listed in dependencies but are not imported and are
loaded at runtime via unpkg per ARCHITECTURE.md; move "@sigma/node-border" and
"@sigma/node-square" from "dependencies" into "devDependencies" and leave
"graphology-layout-forceatlas2" (and other graphology packages) in
"dependencies" since graphology-layout-forceatlas2 is imported in
src/utils/graph-visualizer.ts; after updating package.json, run your package
manager (npm install or yarn) to update the lockfile.

Comment on lines 36 to 54
static async generateVisualization(
nodeGraph: DirectedGraph<TraversedNode>,
options: VisualizationOptions = {},
): Promise<string> {
const { outputPath = './dependency-graph.html', title = 'TypeScript Dependency Graph' } =
options

const { nodes, edges } = this.convertGraphToSigmaData(nodeGraph)

// Always apply ForceAtlas2 layout
this.applyForceAtlas2Layout(nodes)

const htmlContent = this.generateHTMLContent(nodes, edges, title)

const file = Bun.file(outputPath)
await file.write(htmlContent)

return outputPath
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Do not depend on Bun runtime; use Node’s fs/promises.
Bun.file(...).write(...) breaks in Node environments. Use fs/promises and ensure the output directory exists.

+import { promises as fs } from 'node:fs'
+import { dirname } from 'node:path'
@@
-    const htmlContent = this.generateHTMLContent(nodes, edges, title)
-
-    const file = Bun.file(outputPath)
-    await file.write(htmlContent)
+    const htmlContent = this.generateHTMLContent(nodes, edges, title)
+    await fs.mkdir(dirname(outputPath), { recursive: true })
+    await fs.writeFile(outputPath, htmlContent, 'utf8')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static async generateVisualization(
nodeGraph: DirectedGraph<TraversedNode>,
options: VisualizationOptions = {},
): Promise<string> {
const { outputPath = './dependency-graph.html', title = 'TypeScript Dependency Graph' } =
options
const { nodes, edges } = this.convertGraphToSigmaData(nodeGraph)
// Always apply ForceAtlas2 layout
this.applyForceAtlas2Layout(nodes)
const htmlContent = this.generateHTMLContent(nodes, edges, title)
const file = Bun.file(outputPath)
await file.write(htmlContent)
return outputPath
}
// At the top of src/utils/graph-visualizer.ts
import { promises as fs } from 'node:fs'
import { dirname } from 'node:path'
export class GraphVisualizer {
static async generateVisualization(
nodeGraph: DirectedGraph<TraversedNode>,
options: VisualizationOptions = {},
): Promise<string> {
const { outputPath = './dependency-graph.html', title = 'TypeScript Dependency Graph' } =
options
const { nodes, edges } = this.convertGraphToSigmaData(nodeGraph)
// Always apply ForceAtlas2 layout
this.applyForceAtlas2Layout(nodes)
- const htmlContent = this.generateHTMLContent(nodes, edges, title)
-
- const file = Bun.file(outputPath)
const htmlContent = this.generateHTMLContent(nodes, edges, title)
// Ensure the output directory exists
await fs.mkdir(dirname(outputPath), { recursive: true })
// Write the HTML to the specified file
await fs.writeFile(outputPath, htmlContent, 'utf8')
return outputPath
}
}
🤖 Prompt for AI Agents
In src/utils/graph-visualizer.ts around lines 36 to 54, the code uses
Bun.file(...).write(...) which is Bun-specific and will fail in Node; replace
this with Node's fs/promises: import writeFile and mkdir from 'fs/promises' and
path from 'path', compute the output directory with path.dirname(outputPath),
call mkdir(dir, { recursive: true }) to ensure it exists, then await
writeFile(outputPath, htmlContent, 'utf8'); finally return outputPath.

Comment on lines 202 to 211
private static calculateImportNestingLevel(nodeData: TraversedNode): number {
if (nodeData.isMainCode) {
return 4 // Highest intensity for main code
}

if (nodeData.isImported) {
// Simple heuristic: count path depth
const filePath = nodeData.node.getSourceFile().getFilePath()
const pathDepth = filePath.split('/').length
return Math.max(1, Math.min(3, pathDepth - 3))
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix path splitting for Windows compatibility.
filePath.split('/') fails on Windows paths. Normalize and split by platform separator.

+import path from 'node:path'
@@
-      const filePath = nodeData.node.getSourceFile().getFilePath()
-      const pathDepth = filePath.split('/').length
+      const filePath = nodeData.node.getSourceFile().getFilePath()
+      const pathDepth = path.normalize(filePath).split(path.sep).length
       return Math.max(1, Math.min(3, pathDepth - 3))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private static calculateImportNestingLevel(nodeData: TraversedNode): number {
if (nodeData.isMainCode) {
return 4 // Highest intensity for main code
}
if (nodeData.isImported) {
// Simple heuristic: count path depth
const filePath = nodeData.node.getSourceFile().getFilePath()
const pathDepth = filePath.split('/').length
return Math.max(1, Math.min(3, pathDepth - 3))
// At the top of src/utils/graph-visualizer.ts, alongside your other imports
import path from 'node:path'
private static calculateImportNestingLevel(nodeData: TraversedNode): number {
if (nodeData.isMainCode) {
return 4 // Highest intensity for main code
}
if (nodeData.isImported) {
// Simple heuristic: count path depth
const filePath = nodeData.node.getSourceFile().getFilePath()
const pathDepth = path.normalize(filePath).split(path.sep).length
return Math.max(1, Math.min(3, pathDepth - 3))
}
// …rest of method…
}
🤖 Prompt for AI Agents
In src/utils/graph-visualizer.ts around lines 202 to 211, the code uses
filePath.split('/') which breaks on Windows; change to normalize the path and
split by the platform separator (import path from 'path'), e.g. const parts =
path.normalize(filePath).split(path.sep).filter(Boolean) and use parts.length
for pathDepth so the depth calculation works cross-platform.

Comment on lines +224 to +231
): string {
return `<!DOCTYPE html>
<html>
<head>
<title>${title}</title>
<script src="https://cdn.jsdelivr.net/npm/graphology@0.26.0/dist/graphology.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sigma@3.0.2/dist/sigma.min.js"></script>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Escape title to avoid HTML injection.
Users can pass arbitrary title. Escape it before templating.

-  ): string {
-    return `<!DOCTYPE html>
+  ): string {
+    const safeTitle = String(title)
+      .replace(/&/g, '&amp;')
+      .replace(/</g, '&lt;')
+      .replace(/>/g, '&gt;')
+      .replace(/"/g, '&quot;')
+      .replace(/'/g, '&#39;')
+    return `<!DOCTYPE html>
 <html>
 <head>
-    <title>${title}</title>
+    <title>${safeTitle}</title>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
): string {
return `<!DOCTYPE html>
<html>
<head>
<title>${title}</title>
<script src="https://cdn.jsdelivr.net/npm/graphology@0.26.0/dist/graphology.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sigma@3.0.2/dist/sigma.min.js"></script>
): string {
const safeTitle = String(title)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
return `<!DOCTYPE html>
<html>
<head>
<title>${safeTitle}</title>
<script src="https://cdn.jsdelivr.net/npm/graphology@0.26.0/dist/graphology.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sigma@3.0.2/dist/sigma.min.js"></script>
🤖 Prompt for AI Agents
In src/utils/graph-visualizer.ts around lines 224 to 231, the template injects
the raw title into the HTML which allows HTML injection; create or reuse a small
escapeHtml function that replaces &, <, >, ", and ' (and optionally /) with
their corresponding HTML entities and call it on title before interpolation
(e.g. const safeTitle = escapeHtml(title)); then use safeTitle in the template
string so the generated HTML cannot be exploited by injected markup or scripts.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/traverse/dependency-traversal.ts (2)

37-41: Don’t mark imported nodes as main code.

collectFromImports(importDeclarations, true) flags first-level imports as isMainCode, skewing visualization and sizing. Pass false.

-    const importDeclarations = mainSourceFile.getImportDeclarations()
-    this.collectFromImports(importDeclarations, true)
+    const importDeclarations = mainSourceFile.getImportDeclarations()
+    this.collectFromImports(importDeclarations, false)

268-279: Normalize TypeReference names (handles namespaces).

getTypeName().getText() can be ns.Base; compare against originalName by stripping qualification.

-      if (Node.isTypeReference(node)) {
-        const typeName = node.getTypeName().getText()
+      if (Node.isTypeReference(node)) {
+        const typeName = node.getTypeName().getText().split('.').pop()!
src/index.ts (1)

70-90: Update call sites and docs for generateCode(options) signature

  • tests/utils.ts (around line 30): replace
    const code = generateCode(source, filePath)
    with
    const code = generateCode({
      sourceCode: source,
      callerFile: filePath
    })
  • ARCHITECTURE.md (examples at lines 256–258 and 265–267): wrap the former positional args in an options object.

Migration note:

In vX.Y.Z we changed generateCode from

generateCode(sourceCode: string, filePath?: string)

to

generateCode(options: {
  sourceCode: string;
  callerFile?: string;
  filePath?: string;
})

Replace all calls accordingly and update any code samples in docs to use the new options shape.

♻️ Duplicate comments (4)
src/traverse/dependency-traversal.ts (1)

297-315: Fix interface extends resolution for generics/namespaces.

Using typeNode.getText() yields Base<T> or ns.Base, failing to match originalName. Extract base identifier from the expression.

-          for (const typeNode of heritageClause.getTypeNodes()) {
-            const typeName = typeNode.getText()
+          for (const typeNode of heritageClause.getTypeNodes()) {
+            const typeName = typeNode.getExpression().getText().split('.').pop()!
src/utils/graph-visualizer.ts (3)

47-50: Good: FA2 layout now considers edges.

Using connectivity improves positioning quality.


45-57: Remove Bun dependency and duplicate write; use fs/promises only.

Bun.file(...).write(...) breaks in Node and you also write twice. Switch to async fs and create the directory before writing.

-import { mkdirSync, writeFileSync } from 'node:fs'
-import { dirname } from 'node:path'
+import { promises as fs } from 'node:fs'
+import { dirname } from 'node:path'
@@
-    const htmlContent = this.generateHTMLContent(nodes, edges, title)
-
-    const file = Bun.file(outputPath)
-    await file.write(htmlContent)
-
-    mkdirSync(dirname(outputPath), { recursive: true })
-    writeFileSync(outputPath, htmlContent, 'utf8')
+    const htmlContent = this.generateHTMLContent(nodes, edges, title)
+    await fs.mkdir(dirname(outputPath), { recursive: true })
+    await fs.writeFile(outputPath, htmlContent, 'utf8')

232-237: Escape HTML title to prevent injection.

User-provided title is injected raw.

-  ): string {
-    return `<!DOCTYPE html>
+  ): string {
+    const safeTitle = String(title)
+      .replace(/&/g, '&amp;')
+      .replace(/</g, '&lt;')
+      .replace(/>/g, '&gt;')
+      .replace(/"/g, '&quot;')
+      .replace(/'/g, '&#39;')
+    return `<!DOCTYPE html>
@@
-    <title>${title}</title>
+    <title>${safeTitle}</title>
🧹 Nitpick comments (3)
tests/traverse/interface-inheritance-dependency.test.ts (1)

88-114: Add tests for generics and qualified names in extends.

Current traversal uses string text for extends; a regression could drop deps like C extends A<string> or C extends ns.A. Add tests to lock this down.

Example tests to append:

test('should handle generic base in extends', () => {
  const sourceFile = createSourceFile(project, `
    interface A<T> { a: T }
    interface C extends A<string> { c: boolean }
  `)
  const deps = traverser.startTraversal(sourceFile).map(getNodeName)
  expect(deps).toEqual(expect.arrayContaining(['A', 'C']))
  expect(deps.indexOf('A')).toBeLessThan(deps.indexOf('C'))
})

test('should handle qualified name in extends', () => {
  const sourceFile = createSourceFile(project, `
    namespace ns { export interface A { a: string } }
    interface C extends ns.A { c: boolean }
  `)
  const deps = traverser.startTraversal(sourceFile).map(getNodeName)
  expect(deps).toEqual(expect.arrayContaining(['A', 'C']))
  expect(deps.indexOf('A')).toBeLessThan(deps.indexOf('C'))
})
src/traverse/dependency-traversal.ts (2)

260-324: De-duplicate collected references to reduce redundant edge checks.

Prevent repeated pushes when the same type is encountered multiple times.

-  private extractTypeReferences(node: Node): string[] {
-    const references: string[] = []
+  private extractTypeReferences(node: Node): string[] {
+    const references = new Set<string>()
@@
-            references.push(qualifiedName)
+            references.add(qualifiedName)
@@
-              references.push(qualifiedName)
+              references.add(qualifiedName)
@@
-                references.push(qualifiedName)
+                references.add(qualifiedName)
@@
-    return references
+    return Array.from(references)

239-244: Minor: prefer isAcyclic for clarity.

Functionally equivalent to !hasCycle, but reads clearer.

-import { hasCycle, topologicalSort } from 'graphology-dag'
+import { isAcyclic, topologicalSort } from 'graphology-dag'
@@
-    const nodes = hasCycle(this.nodeGraph)
+    const nodes = isAcyclic(this.nodeGraph)
-      ? Array.from(this.nodeGraph.nodes())
-      : topologicalSort(this.nodeGraph)
+      ? topologicalSort(this.nodeGraph)
+      : Array.from(this.nodeGraph.nodes())
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b614495 and 0e7c211.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • package.json (1 hunks)
  • src/index.ts (2 hunks)
  • src/traverse/dependency-traversal.ts (5 hunks)
  • src/traverse/node-graph.ts (1 hunks)
  • src/utils/graph-visualizer.ts (1 hunks)
  • tests/traverse/dependency-extraction.test.ts (1 hunks)
  • tests/traverse/dependency-ordering.test.ts (2 hunks)
  • tests/traverse/interface-inheritance-dependency.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • tests/traverse/dependency-extraction.test.ts
  • package.json
  • src/traverse/node-graph.ts
  • tests/traverse/dependency-ordering.test.ts
🧰 Additional context used
🧬 Code graph analysis (4)
src/traverse/dependency-traversal.ts (3)
src/traverse/types.ts (1)
  • TraversedNode (5-12)
src/utils/graph-visualizer.ts (2)
  • VisualizationOptions (8-11)
  • GraphVisualizer (34-432)
src/traverse/node-graph.ts (1)
  • NodeGraph (8-40)
tests/traverse/interface-inheritance-dependency.test.ts (3)
src/traverse/types.ts (1)
  • TraversedNode (5-12)
src/traverse/dependency-traversal.ts (1)
  • DependencyTraversal (25-325)
tests/utils.ts (1)
  • createSourceFile (12-14)
src/utils/graph-visualizer.ts (1)
src/traverse/types.ts (1)
  • TraversedNode (5-12)
src/index.ts (1)
src/input-handler.ts (2)
  • InputOptions (5-10)
  • createSourceFileFromInput (64-101)
🔇 Additional comments (2)
tests/traverse/interface-inheritance-dependency.test.ts (1)

20-58: Good coverage for base, multiple, and nested interface inheritance.

Assertions validate membership and ordering correctly.

src/index.ts (1)

58-69: API addition looks good.

generateVisualization cleanly reuses traversal and exposes options.

Comment on lines +281 to +295
if (Node.isTypeQuery(node)) {
const exprName = node.getExprName()

if (Node.isIdentifier(exprName) || Node.isQualifiedName(exprName)) {
const typeName = exprName.getText()

for (const qualifiedName of this.nodeGraph.nodes()) {
const nodeData = this.nodeGraph.getNode(qualifiedName)
if (nodeData.originalName === typeName) {
references.push(qualifiedName)
break
}
}
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Normalize typeof references.

exprName.getText() may be ns.Symbol; strip qualification to match originalName.

-        if (Node.isIdentifier(exprName) || Node.isQualifiedName(exprName)) {
-          const typeName = exprName.getText()
+        if (Node.isIdentifier(exprName) || Node.isQualifiedName(exprName)) {
+          const typeName = exprName.getText().split('.').pop()!
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (Node.isTypeQuery(node)) {
const exprName = node.getExprName()
if (Node.isIdentifier(exprName) || Node.isQualifiedName(exprName)) {
const typeName = exprName.getText()
for (const qualifiedName of this.nodeGraph.nodes()) {
const nodeData = this.nodeGraph.getNode(qualifiedName)
if (nodeData.originalName === typeName) {
references.push(qualifiedName)
break
}
}
}
}
if (Node.isTypeQuery(node)) {
const exprName = node.getExprName()
if (Node.isIdentifier(exprName) || Node.isQualifiedName(exprName)) {
// Strip namespace qualification so it matches nodeData.originalName
const typeName = exprName.getText().split('.').pop()!
for (const qualifiedName of this.nodeGraph.nodes()) {
const nodeData = this.nodeGraph.getNode(qualifiedName)
if (nodeData.originalName === typeName) {
references.push(qualifiedName)
break
}
}
}
}
🤖 Prompt for AI Agents
In src/traverse/dependency-traversal.ts around lines 281-295, the
typeof/typeQuery handling compares exprName.getText() directly to
nodeData.originalName which fails for qualified names like "ns.Symbol";
normalize by extracting the unqualified identifier before comparing (e.g. take
the last segment after the final "." or the right-most part of a QualifiedName)
so that "ns.Symbol" becomes "Symbol", then use that normalized typeName to match
nodeData.originalName and push the qualifiedName when equal.

Comment on lines +316 to +325
<script>
// Wait for all libraries to load
window.addEventListener('load', function() {
// Use global variables from UMD builds
const Graph = window.graphology.Graph;
const Sigma = window.Sigma;
// Create a new graph instance
const graph = new Graph.DirectedGraph();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix Graphology UMD usage (DirectedGraph constructor).

new Graph.DirectedGraph() is incorrect; DirectedGraph is a separate export on window.graphology.

-            const Graph = window.graphology.Graph;
-            const Sigma = window.Sigma;
-
-        // Create a new graph instance
-        const graph = new Graph.DirectedGraph();
+            const { DirectedGraph } = window.graphology;
+            const Sigma = window.Sigma;
+            // Create a new graph instance
+            const graph = new DirectedGraph();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<script>
// Wait for all libraries to load
window.addEventListener('load', function() {
// Use global variables from UMD builds
const Graph = window.graphology.Graph;
const Sigma = window.Sigma;
// Create a new graph instance
const graph = new Graph.DirectedGraph();
<script>
// Wait for all libraries to load
window.addEventListener('load', function() {
// Use global variables from UMD builds
const { DirectedGraph } = window.graphology;
const Sigma = window.Sigma;
// Create a new graph instance
const graph = new DirectedGraph();
🤖 Prompt for AI Agents
In src/utils/graph-visualizer.ts around lines 316 to 325, the code uses new
Graph.DirectedGraph() which is incorrect for the Graphology UMD build because
DirectedGraph is a top-level export; replace the current usage by grabbing
DirectedGraph from window.graphology (e.g., const DirectedGraph =
window.graphology.DirectedGraph) and instantiate the graph with new
DirectedGraph(), keeping Sigma and any other Graphology exports referenced
separately.

@DaxServer DaxServer merged commit 6075948 into main Aug 29, 2025
7 checks passed
@DaxServer DaxServer deleted the interface-inheritance branch August 29, 2025 12:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants