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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ TypeScript is supported first because the TypeScript Compiler API provides relia
## Roadmap

- [x] **Phase 1 — MVP** · File scanning, AST parsing, dependency extraction, terminal output
- [ ] **Phase 2 — Interactive Visualization** · React Flow integration, search, zoom/pan, node inspection
- [ ] **Phase 3 — Advanced Analysis** · Circular dependency detection, dead code detection, impact analysis
- [x] **Phase 2 — Interactive Visualization** · Cytoscape.js integration, search, zoom/pan, node inspection, impact analysis
- [ ] **Phase 3 — Advanced Analysis** · Circular dependency detection, dead code detection, architecture validation
- [ ] **Phase 4 — Ecosystem** · VS Code extension, GitHub Action, CI/CD integration

---
Expand Down
23 changes: 12 additions & 11 deletions project-description.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,28 +253,29 @@ Phase 1 — MVPPhase 1 — MVP
GoalGoal
Dependency extraction and CLI output.
## Features:
File scanning
AST parsing
Dependency extraction
Terminal visualization
[x] File scanning
[x] AST parsing
[x] Dependency extraction
[x] Terminal visualization
## Estimated Duration:
4 weeks

Phase 2 — Interactive VisualizationPhase 2 — Interactive Visualization
GoalGoal
Generate interactive HTML reports.
## Features:
React Flow integration
Search functionality
Zoom and pan
Node inspection
[x] Cytoscape.js integration
[x] Search functionality
[x] Zoom and pan
[x] Node inspection
[x] Impact analysis
## Estimated Duration:
4 weeks
Phase 3 — Advanced AnalysisPhase 3 — Advanced Analysis
## Features:
Circular dependency detection
Dead code detection
Impact analysis
[x] Circular dependency detection
[x] Dead code detection
[x] Architecture validation
## Estimated Duration:
4 weeks
Phase 4 — EcosystemPhase 4 — Ecosystem
Expand Down
18 changes: 18 additions & 0 deletions src/analyze.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { resolve } from 'node:path'
import { readFile } from 'node:fs/promises'
import { scanFiles } from './scanner.js'
import { parseImports } from './parser.js'
import { extractDependencies } from './extractor.js'
import { buildGraph } from './graph.js'
import { validateArchitecture } from './validator.js'
import type { DependencyGraph } from './graph.js'
import type { ArchitectureRules } from './validator.js'

export interface AnalyzeOptions {
/** Additional directories to ignore during file scanning */
Expand All @@ -19,6 +22,8 @@ export interface AnalyzeOptions {
* patterns you provide in `entryPatterns` are active.
*/
replaceDefaultEntryPatterns?: boolean
/** Path to architectural rules JSON file */
rulesPath?: string
}

/**
Expand All @@ -27,6 +32,7 @@ export interface AnalyzeOptions {
* 2. Parse imports from each file
* 3. Resolve relative imports to absolute paths
* 4. Build and classify the dependency graph
* 5. Validate architecture (optional)
*/
export async function analyze(directory: string, options: AnalyzeOptions = {}): Promise<DependencyGraph> {
const rootDir = resolve(directory)
Expand All @@ -44,5 +50,17 @@ export async function analyze(directory: string, options: AnalyzeOptions = {}):
replaceDefaultEntryPatterns: options.replaceDefaultEntryPatterns,
})

// Architecture Validation
if (options.rulesPath) {
try {
const content = await readFile(resolve(options.rulesPath), 'utf8')
const rules = JSON.parse(content) as ArchitectureRules
graph.violations = validateArchitecture(graph, rules)
} catch (err) {
const msg = err instanceof Error ? err.message : String(err)
throw new Error(`Failed to load or parse architecture rules from "${options.rulesPath}": ${msg}`)
}
}

return graph
}
9 changes: 8 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ program
.option('--port <number>', 'port for the live server (default: 4242)', '4242')
.option('--dead-code', 'report dead code (unreferenced + isolated files), exit 1 if any found')
.option('--impact <file>', 'show all files that depend on <file> and all files it depends on')
.option('--rules <file>', 'path to architecture rules JSON file')
.option(
'--entry <globs>',
'comma-separated glob patterns that mark extra entry points (merged with built-in defaults)',
Expand All @@ -41,6 +42,7 @@ program
port?: string
deadCode?: boolean
impact?: string
rules?: string
entry?: string
showEntryPatterns?: boolean
showExternal?: boolean
Expand Down Expand Up @@ -73,7 +75,11 @@ program
: []

try {
const graph = await analyze(directory, { ignore, entryPatterns })
const graph = await analyze(directory, {
ignore,
entryPatterns,
rulesPath: options.rules,
})

if (options.save) {
const { htmlFile, jsonFile } = await generateHtmlReport(graph, { outDir: options.outDir })
Expand Down Expand Up @@ -101,6 +107,7 @@ program
renderImpact(result)
} else {
renderToTerminal(graph, { showExternal: options.showExternal })
if (graph.violations.length > 0) process.exit(1)
}
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
Expand Down
14 changes: 13 additions & 1 deletion src/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ export interface UnreferencedFile {
matchedPattern?: string
}

export interface ArchitectureViolation {
from: string // absolute path
fromLabel: string // relative path
to: string // absolute path
toLabel: string // relative path
fromLayer: string
toLayer: string
ruleType: 'disallow' | 'allowOnly'
}

export interface DependencyGraph {
nodes: Map<string, GraphNode>
edges: GraphEdge[]
Expand All @@ -47,6 +57,8 @@ export interface DependencyGraph {
unreferencedFiles: UnreferencedFile[]
/** Circular dependency chains */
cycles: string[][]
/** Architecture rule violations */
violations: ArchitectureViolation[]
/**
* The entry-point glob patterns that were used during classification.
* Included so callers can display which patterns are active.
Expand Down Expand Up @@ -208,7 +220,7 @@ export function buildGraph(

const cycles = detectCycles(nodes, edges)

return { nodes, edges, unreferencedFiles, cycles, entryPatterns: activePatterns }
return { nodes, edges, unreferencedFiles, cycles, violations: [], entryPatterns: activePatterns }
}

// ---------------------------------------------------------------------------
Expand Down
Loading
Loading