@ebowwa/dependency-graph 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -0
- package/dist/analysis.d.ts +31 -0
- package/dist/analysis.d.ts.map +1 -0
- package/dist/analysis.js +151 -0
- package/dist/analysis.js.map +1 -0
- package/dist/builder.d.ts +61 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +281 -0
- package/dist/builder.js.map +1 -0
- package/dist/formatters.d.ts +27 -0
- package/dist/formatters.d.ts.map +1 -0
- package/dist/formatters.js +145 -0
- package/dist/formatters.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +51 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +38 -0
- package/src/analysis.ts +188 -0
- package/src/builder.ts +366 -0
- package/src/formatters.ts +191 -0
- package/src/index.ts +56 -0
- package/src/types.ts +58 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ebowwa/dependency-graph - Output Formatters
|
|
3
|
+
*
|
|
4
|
+
* Format dependency graphs for various output formats
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Format the graph for output
|
|
8
|
+
*/
|
|
9
|
+
export function formatGraph(graph, format) {
|
|
10
|
+
switch (format) {
|
|
11
|
+
case "mermaid":
|
|
12
|
+
return formatAsMermaid(graph);
|
|
13
|
+
case "dot":
|
|
14
|
+
return formatAsDot(graph);
|
|
15
|
+
case "tree":
|
|
16
|
+
return formatAsTree(graph);
|
|
17
|
+
case "json":
|
|
18
|
+
default:
|
|
19
|
+
return formatAsJson(graph);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Format as JSON
|
|
24
|
+
*/
|
|
25
|
+
export function formatAsJson(graph) {
|
|
26
|
+
return JSON.stringify({
|
|
27
|
+
nodes: Array.from(graph.nodes.values()),
|
|
28
|
+
edges: graph.edges,
|
|
29
|
+
}, null, 2);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Format as Mermaid diagram
|
|
33
|
+
*/
|
|
34
|
+
export function formatAsMermaid(graph) {
|
|
35
|
+
const lines = ["graph TD"];
|
|
36
|
+
// Add nodes
|
|
37
|
+
for (const [name, node] of graph.nodes) {
|
|
38
|
+
const label = node.type === "workspace" ? `📦 ${name}` : `📚 ${name}`;
|
|
39
|
+
lines.push(` ${sanitizeId(name)}["${label}"]`);
|
|
40
|
+
}
|
|
41
|
+
// Add edges
|
|
42
|
+
for (const edge of graph.edges) {
|
|
43
|
+
const from = sanitizeId(edge.from);
|
|
44
|
+
const to = sanitizeId(edge.to);
|
|
45
|
+
const label = edge.type === "workspace"
|
|
46
|
+
? "workspace"
|
|
47
|
+
: edge.type === "import"
|
|
48
|
+
? "imports"
|
|
49
|
+
: "external";
|
|
50
|
+
lines.push(` ${from} -->|${label}| ${to}`);
|
|
51
|
+
}
|
|
52
|
+
// Add styles
|
|
53
|
+
lines.push(" classDef workspace fill:#e1f5fe");
|
|
54
|
+
lines.push(" classDef external fill:#f5f5f5");
|
|
55
|
+
lines.push(" classDef import fill:#fff3e0");
|
|
56
|
+
for (const [name, node] of graph.nodes) {
|
|
57
|
+
const id = sanitizeId(name);
|
|
58
|
+
if (node.type === "workspace") {
|
|
59
|
+
lines.push(` class ${id} workspace`);
|
|
60
|
+
}
|
|
61
|
+
else if (node.type === "external") {
|
|
62
|
+
lines.push(` class ${id} external`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return lines.join("\n");
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Format as Graphviz DOT
|
|
69
|
+
*/
|
|
70
|
+
export function formatAsDot(graph) {
|
|
71
|
+
const lines = ["digraph dependencies {"];
|
|
72
|
+
lines.push(" rankdir=LR;");
|
|
73
|
+
lines.push(" node [shape=box];");
|
|
74
|
+
// Add nodes
|
|
75
|
+
for (const [name, node] of graph.nodes) {
|
|
76
|
+
const color = node.type === "workspace" ? "lightblue" : "lightgray";
|
|
77
|
+
lines.push(` "${name}" [fillcolor=${color}, style=filled];`);
|
|
78
|
+
}
|
|
79
|
+
// Add edges
|
|
80
|
+
for (const edge of graph.edges) {
|
|
81
|
+
const style = edge.type === "workspace" ? "solid" : "dashed";
|
|
82
|
+
lines.push(` "${edge.from}" -> "${edge.to}" [style=${style}, label="${edge.type}"];`);
|
|
83
|
+
}
|
|
84
|
+
lines.push("}");
|
|
85
|
+
return lines.join("\n");
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Format as ASCII tree
|
|
89
|
+
*/
|
|
90
|
+
export function formatAsTree(graph) {
|
|
91
|
+
const lines = [];
|
|
92
|
+
const seen = new Set();
|
|
93
|
+
// Find root nodes (no incoming edges from other workspace packages)
|
|
94
|
+
const workspaceNodes = Array.from(graph.nodes.values()).filter((n) => n.type === "workspace");
|
|
95
|
+
const incomingEdges = new Map();
|
|
96
|
+
for (const node of workspaceNodes) {
|
|
97
|
+
incomingEdges.set(node.name, 0);
|
|
98
|
+
}
|
|
99
|
+
for (const edge of graph.edges) {
|
|
100
|
+
if (graph.nodes.get(edge.to)?.type === "workspace") {
|
|
101
|
+
incomingEdges.set(edge.to, (incomingEdges.get(edge.to) || 0) + 1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Start from roots
|
|
105
|
+
for (const [name, node] of graph.nodes) {
|
|
106
|
+
if (node.type === "workspace" && (incomingEdges.get(name) || 0) === 0) {
|
|
107
|
+
lines.push(`📦 ${name}`);
|
|
108
|
+
printTree(graph, name, "", seen, lines);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Add any disconnected nodes
|
|
112
|
+
for (const [name, node] of graph.nodes) {
|
|
113
|
+
if (!seen.has(name) && node.type === "workspace") {
|
|
114
|
+
lines.push(`📦 ${name} (disconnected)`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return lines.join("\n");
|
|
118
|
+
}
|
|
119
|
+
function printTree(graph, nodeName, prefix, seen, lines) {
|
|
120
|
+
seen.add(nodeName);
|
|
121
|
+
// Find outgoing edges to other workspace packages
|
|
122
|
+
const outgoing = graph.edges.filter((e) => e.from === nodeName && graph.nodes.get(e.to)?.type === "workspace");
|
|
123
|
+
for (let i = 0; i < outgoing.length; i++) {
|
|
124
|
+
const edge = outgoing[i];
|
|
125
|
+
const isLast = i === outgoing.length - 1;
|
|
126
|
+
const connector = isLast ? "└──" : "├──";
|
|
127
|
+
const childPrefix = prefix + (isLast ? " " : "│ ");
|
|
128
|
+
lines.push(`${prefix}${connector} 📦 ${edge.to} [${edge.type}]`);
|
|
129
|
+
if (!seen.has(edge.to)) {
|
|
130
|
+
printTree(graph, edge.to, childPrefix, seen, lines);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Show external dependencies count
|
|
134
|
+
const externals = graph.edges.filter((e) => e.from === nodeName && graph.nodes.get(e.to)?.type === "external");
|
|
135
|
+
if (externals.length > 0) {
|
|
136
|
+
lines.push(`${prefix}└── 📚 ${externals.length} external dependencies`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Sanitize ID for Mermaid/DOT formats
|
|
141
|
+
*/
|
|
142
|
+
function sanitizeId(name) {
|
|
143
|
+
return name.replace(/[^a-zA-Z0-9]/g, "_");
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=formatters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatters.js","sourceRoot":"","sources":["../src/formatters.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ebowwa/dependency-graph
|
|
3
|
+
*
|
|
4
|
+
* Core dependency graph analysis and visualization for monorepos
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { DependencyGraphBuilder, formatGraph, analyzeImpact, findCircularDependencies } from '@ebowwa/dependency-graph';
|
|
9
|
+
*
|
|
10
|
+
* const builder = new DependencyGraphBuilder('/path/to/monorepo');
|
|
11
|
+
* const graph = await builder.build({ analyzeImports: true });
|
|
12
|
+
*
|
|
13
|
+
* // Format as Mermaid diagram
|
|
14
|
+
* const mermaid = formatGraph(graph, 'mermaid');
|
|
15
|
+
*
|
|
16
|
+
* // Find circular dependencies
|
|
17
|
+
* const cycles = findCircularDependencies(graph);
|
|
18
|
+
*
|
|
19
|
+
* // Analyze impact of changing a package
|
|
20
|
+
* const impact = analyzeImpact(graph, '@ebowwa/terminal');
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export type { DependencyNode, DependencyEdge, DependencyGraph, ImportInfo, BuildOptions, ImpactResult, OutputFormat, PackageInfo, } from "./types.js";
|
|
24
|
+
export { DependencyGraphBuilder } from "./builder.js";
|
|
25
|
+
export { findCircularDependencies, analyzeImpact, findUnusedPackages, getPackageInfo, getWorkspacePackages, getExternalDependencies, } from "./analysis.js";
|
|
26
|
+
export { formatGraph, formatAsJson, formatAsMermaid, formatAsDot, formatAsTree, } from "./formatters.js";
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,YAAY,EACV,cAAc,EACd,cAAc,EACd,eAAe,EACf,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,GACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAGtD,OAAO,EACL,wBAAwB,EACxB,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,eAAe,EACf,WAAW,EACX,YAAY,GACb,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ebowwa/dependency-graph
|
|
3
|
+
*
|
|
4
|
+
* Core dependency graph analysis and visualization for monorepos
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { DependencyGraphBuilder, formatGraph, analyzeImpact, findCircularDependencies } from '@ebowwa/dependency-graph';
|
|
9
|
+
*
|
|
10
|
+
* const builder = new DependencyGraphBuilder('/path/to/monorepo');
|
|
11
|
+
* const graph = await builder.build({ analyzeImports: true });
|
|
12
|
+
*
|
|
13
|
+
* // Format as Mermaid diagram
|
|
14
|
+
* const mermaid = formatGraph(graph, 'mermaid');
|
|
15
|
+
*
|
|
16
|
+
* // Find circular dependencies
|
|
17
|
+
* const cycles = findCircularDependencies(graph);
|
|
18
|
+
*
|
|
19
|
+
* // Analyze impact of changing a package
|
|
20
|
+
* const impact = analyzeImpact(graph, '@ebowwa/terminal');
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
// Builder
|
|
24
|
+
export { DependencyGraphBuilder } from "./builder.js";
|
|
25
|
+
// Analysis
|
|
26
|
+
export { findCircularDependencies, analyzeImpact, findUnusedPackages, getPackageInfo, getWorkspacePackages, getExternalDependencies, } from "./analysis.js";
|
|
27
|
+
// Formatters
|
|
28
|
+
export { formatGraph, formatAsJson, formatAsMermaid, formatAsDot, formatAsTree, } from "./formatters.js";
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAcH,UAAU;AACV,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEtD,WAAW;AACX,OAAO,EACL,wBAAwB,EACxB,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,eAAe,CAAC;AAEvB,aAAa;AACb,OAAO,EACL,WAAW,EACX,YAAY,EACZ,eAAe,EACf,WAAW,EACX,YAAY,GACb,MAAM,iBAAiB,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ebowwa/dependency-graph - Core Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for dependency graph analysis
|
|
5
|
+
*/
|
|
6
|
+
export interface DependencyNode {
|
|
7
|
+
name: string;
|
|
8
|
+
path: string;
|
|
9
|
+
type: "package" | "workspace" | "external";
|
|
10
|
+
version?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface DependencyEdge {
|
|
13
|
+
from: string;
|
|
14
|
+
to: string;
|
|
15
|
+
type: "workspace" | "external" | "import";
|
|
16
|
+
importPath?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface DependencyGraph {
|
|
19
|
+
nodes: Map<string, DependencyNode>;
|
|
20
|
+
edges: DependencyEdge[];
|
|
21
|
+
reverseEdges: Map<string, Set<string>>;
|
|
22
|
+
}
|
|
23
|
+
export interface ImportInfo {
|
|
24
|
+
from: string;
|
|
25
|
+
imports: string[];
|
|
26
|
+
file: string;
|
|
27
|
+
}
|
|
28
|
+
export interface BuildOptions {
|
|
29
|
+
includeDevDependencies?: boolean;
|
|
30
|
+
analyzeImports?: boolean;
|
|
31
|
+
excludePatterns?: string[];
|
|
32
|
+
}
|
|
33
|
+
export interface ImpactResult {
|
|
34
|
+
direct: string[];
|
|
35
|
+
transitive: string[];
|
|
36
|
+
all: string[];
|
|
37
|
+
}
|
|
38
|
+
export type OutputFormat = "json" | "mermaid" | "dot" | "tree";
|
|
39
|
+
export interface PackageInfo {
|
|
40
|
+
name: string;
|
|
41
|
+
type: "package" | "workspace" | "external";
|
|
42
|
+
path: string;
|
|
43
|
+
version?: string;
|
|
44
|
+
dependencies: Array<{
|
|
45
|
+
name: string;
|
|
46
|
+
type: "workspace" | "external" | "import";
|
|
47
|
+
version?: string;
|
|
48
|
+
}>;
|
|
49
|
+
dependents: string[];
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAC;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACnC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,GAAG,EAAE,MAAM,EAAE,CAAC;CACf;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC;AAE/D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,KAAK,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAC;QAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ebowwa/dependency-graph",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Core dependency graph analysis and visualization for monorepos",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"dev": "tsc --watch"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"src"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"dependency",
|
|
24
|
+
"graph",
|
|
25
|
+
"monorepo",
|
|
26
|
+
"visualization",
|
|
27
|
+
"impact-analysis"
|
|
28
|
+
],
|
|
29
|
+
"author": "ebowwa",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^22.10.2",
|
|
33
|
+
"typescript": "^5.7.2"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=20.0.0"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/analysis.ts
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ebowwa/dependency-graph - Analysis Functions
|
|
3
|
+
*
|
|
4
|
+
* Circular dependency detection, impact analysis, unused code detection
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { DependencyGraph, ImpactResult, PackageInfo } from "./types.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Find circular dependencies using DFS
|
|
11
|
+
*/
|
|
12
|
+
export function findCircularDependencies(
|
|
13
|
+
graph: DependencyGraph,
|
|
14
|
+
maxDepth: number = 10
|
|
15
|
+
): string[][] {
|
|
16
|
+
const cycles: string[][] = [];
|
|
17
|
+
const visited = new Set<string>();
|
|
18
|
+
const recursionStack = new Set<string>();
|
|
19
|
+
|
|
20
|
+
function dfs(node: string, path: string[]): void {
|
|
21
|
+
if (path.length > maxDepth) return;
|
|
22
|
+
|
|
23
|
+
visited.add(node);
|
|
24
|
+
recursionStack.add(node);
|
|
25
|
+
path.push(node);
|
|
26
|
+
|
|
27
|
+
// Check all outgoing edges
|
|
28
|
+
for (const edge of graph.edges) {
|
|
29
|
+
if (edge.from === node) {
|
|
30
|
+
if (recursionStack.has(edge.to)) {
|
|
31
|
+
// Found a cycle
|
|
32
|
+
const cycleStart = path.indexOf(edge.to);
|
|
33
|
+
if (cycleStart >= 0) {
|
|
34
|
+
cycles.push([...path.slice(cycleStart), edge.to]);
|
|
35
|
+
}
|
|
36
|
+
} else if (!visited.has(edge.to)) {
|
|
37
|
+
dfs(edge.to, [...path]);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
recursionStack.delete(node);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Start DFS from each workspace node
|
|
46
|
+
for (const [name, node] of graph.nodes) {
|
|
47
|
+
if (node.type === "workspace" && !visited.has(name)) {
|
|
48
|
+
dfs(name, []);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return cycles;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Analyze the impact of changing a package
|
|
57
|
+
*/
|
|
58
|
+
export function analyzeImpact(
|
|
59
|
+
graph: DependencyGraph,
|
|
60
|
+
packageName: string,
|
|
61
|
+
includeTransitive: boolean = true
|
|
62
|
+
): ImpactResult {
|
|
63
|
+
const direct: string[] = [];
|
|
64
|
+
const transitive: string[] = [];
|
|
65
|
+
const visited = new Set<string>();
|
|
66
|
+
|
|
67
|
+
// Get direct dependents
|
|
68
|
+
const directDependents = graph.reverseEdges.get(packageName);
|
|
69
|
+
if (directDependents) {
|
|
70
|
+
for (const dep of directDependents) {
|
|
71
|
+
direct.push(dep);
|
|
72
|
+
visited.add(dep);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Get transitive dependents
|
|
77
|
+
if (includeTransitive) {
|
|
78
|
+
for (const dep of direct) {
|
|
79
|
+
collectTransitive(graph, dep, visited, transitive);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
direct,
|
|
85
|
+
transitive,
|
|
86
|
+
all: Array.from(visited),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function collectTransitive(
|
|
91
|
+
graph: DependencyGraph,
|
|
92
|
+
packageName: string,
|
|
93
|
+
visited: Set<string>,
|
|
94
|
+
result: string[]
|
|
95
|
+
): void {
|
|
96
|
+
const dependents = graph.reverseEdges.get(packageName);
|
|
97
|
+
if (!dependents) return;
|
|
98
|
+
|
|
99
|
+
for (const dep of dependents) {
|
|
100
|
+
if (!visited.has(dep)) {
|
|
101
|
+
visited.add(dep);
|
|
102
|
+
result.push(dep);
|
|
103
|
+
collectTransitive(graph, dep, visited, result);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Find potentially unused packages (no dependents)
|
|
110
|
+
*/
|
|
111
|
+
export function findUnusedPackages(
|
|
112
|
+
graph: DependencyGraph,
|
|
113
|
+
includeExternal: boolean = false
|
|
114
|
+
): string[] {
|
|
115
|
+
const unused: string[] = [];
|
|
116
|
+
|
|
117
|
+
for (const [name, node] of graph.nodes) {
|
|
118
|
+
if (node.type === "workspace" || includeExternal) {
|
|
119
|
+
const dependents = graph.reverseEdges.get(name);
|
|
120
|
+
if (!dependents || dependents.size === 0) {
|
|
121
|
+
unused.push(name);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return unused;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get detailed information about a specific package
|
|
131
|
+
*/
|
|
132
|
+
export function getPackageInfo(
|
|
133
|
+
graph: DependencyGraph,
|
|
134
|
+
packageName: string
|
|
135
|
+
): PackageInfo | null {
|
|
136
|
+
const node = graph.nodes.get(packageName);
|
|
137
|
+
if (!node) return null;
|
|
138
|
+
|
|
139
|
+
// Get dependencies
|
|
140
|
+
const dependencies = graph.edges
|
|
141
|
+
.filter((e) => e.from === packageName)
|
|
142
|
+
.map((edge) => {
|
|
143
|
+
const depNode = graph.nodes.get(edge.to);
|
|
144
|
+
return {
|
|
145
|
+
name: edge.to,
|
|
146
|
+
type: edge.type,
|
|
147
|
+
version: depNode?.version,
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Get dependents
|
|
152
|
+
const dependents = Array.from(graph.reverseEdges.get(packageName) || []);
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
name: packageName,
|
|
156
|
+
type: node.type,
|
|
157
|
+
path: node.path || "N/A",
|
|
158
|
+
version: node.version,
|
|
159
|
+
dependencies,
|
|
160
|
+
dependents,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get all workspace packages
|
|
166
|
+
*/
|
|
167
|
+
export function getWorkspacePackages(graph: DependencyGraph): string[] {
|
|
168
|
+
const packages: string[] = [];
|
|
169
|
+
for (const [name, node] of graph.nodes) {
|
|
170
|
+
if (node.type === "workspace") {
|
|
171
|
+
packages.push(name);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return packages;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get all external dependencies
|
|
179
|
+
*/
|
|
180
|
+
export function getExternalDependencies(graph: DependencyGraph): string[] {
|
|
181
|
+
const deps: string[] = [];
|
|
182
|
+
for (const [name, node] of graph.nodes) {
|
|
183
|
+
if (node.type === "external") {
|
|
184
|
+
deps.push(name);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return deps;
|
|
188
|
+
}
|