@depxray/core 1.3.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/dist/buildGraph.d.ts +11 -0
- package/dist/buildGraph.d.ts.map +1 -0
- package/dist/buildGraph.js +111 -0
- package/dist/buildGraph.js.map +1 -0
- package/dist/buildStructureGraph.d.ts +3 -0
- package/dist/buildStructureGraph.d.ts.map +1 -0
- package/dist/buildStructureGraph.js +45 -0
- package/dist/buildStructureGraph.js.map +1 -0
- package/dist/configLoader.d.ts +18 -0
- package/dist/configLoader.d.ts.map +1 -0
- package/dist/configLoader.js +188 -0
- package/dist/configLoader.js.map +1 -0
- package/dist/detectCircularDeps.d.ts +21 -0
- package/dist/detectCircularDeps.d.ts.map +1 -0
- package/dist/detectCircularDeps.js +159 -0
- package/dist/detectCircularDeps.js.map +1 -0
- package/dist/detectOrphanFiles.d.ts +4 -0
- package/dist/detectOrphanFiles.d.ts.map +1 -0
- package/dist/detectOrphanFiles.js +58 -0
- package/dist/detectOrphanFiles.js.map +1 -0
- package/dist/exportGraph.d.ts +16 -0
- package/dist/exportGraph.d.ts.map +1 -0
- package/dist/exportGraph.js +50 -0
- package/dist/exportGraph.js.map +1 -0
- package/dist/fileDiscovery.d.ts +21 -0
- package/dist/fileDiscovery.d.ts.map +1 -0
- package/dist/fileDiscovery.js +119 -0
- package/dist/fileDiscovery.js.map +1 -0
- package/dist/filterTreeByDepth.d.ts +3 -0
- package/dist/filterTreeByDepth.d.ts.map +1 -0
- package/dist/filterTreeByDepth.js +22 -0
- package/dist/filterTreeByDepth.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/parseImports.d.ts +30 -0
- package/dist/parseImports.d.ts.map +1 -0
- package/dist/parseImports.js +196 -0
- package/dist/parseImports.js.map +1 -0
- package/dist/resolveImports.d.ts +25 -0
- package/dist/resolveImports.d.ts.map +1 -0
- package/dist/resolveImports.js +189 -0
- package/dist/resolveImports.js.map +1 -0
- package/dist/scanFileTree.d.ts +3 -0
- package/dist/scanFileTree.d.ts.map +1 -0
- package/dist/scanFileTree.js +118 -0
- package/dist/scanFileTree.js.map +1 -0
- package/dist/scanProject.d.ts +39 -0
- package/dist/scanProject.d.ts.map +1 -0
- package/dist/scanProject.js +224 -0
- package/dist/scanProject.js.map +1 -0
- package/dist/types.d.ts +316 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +45 -0
- package/dist/types.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ResolvedImport, DependencyGraph, ScanMetadata } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build a dependency graph from the resolved import data.
|
|
4
|
+
*
|
|
5
|
+
* @param fileImportsMap - Map from absolute file path → array of resolved imports
|
|
6
|
+
* @param rootDir - Project root (for computing relative paths)
|
|
7
|
+
* @param metadata - Scan metadata to include in the graph
|
|
8
|
+
* @returns A complete DependencyGraph (without circular dependency analysis)
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildGraph(fileImportsMap: Map<string, ResolvedImport[]>, rootDir: string, metadata: ScanMetadata): DependencyGraph;
|
|
11
|
+
//# sourceMappingURL=buildGraph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildGraph.d.ts","sourceRoot":"","sources":["../src/buildGraph.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAGV,cAAc,EACd,eAAe,EACf,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,EAC7C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,YAAY,GACrB,eAAe,CAoEjB"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// buildGraph — Construct the dependency graph from resolved imports
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Takes a map of files → resolved imports and builds the graph data structure
|
|
6
|
+
// (nodes + edges). Calculates in/out degrees for each node.
|
|
7
|
+
// ============================================================================
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.buildGraph = buildGraph;
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
/**
|
|
45
|
+
* Build a dependency graph from the resolved import data.
|
|
46
|
+
*
|
|
47
|
+
* @param fileImportsMap - Map from absolute file path → array of resolved imports
|
|
48
|
+
* @param rootDir - Project root (for computing relative paths)
|
|
49
|
+
* @param metadata - Scan metadata to include in the graph
|
|
50
|
+
* @returns A complete DependencyGraph (without circular dependency analysis)
|
|
51
|
+
*/
|
|
52
|
+
function buildGraph(fileImportsMap, rootDir, metadata) {
|
|
53
|
+
// ── Build edges ──────────────────────────────────────────────────────
|
|
54
|
+
const edges = [];
|
|
55
|
+
const inDegreeMap = new Map();
|
|
56
|
+
const outDegreeMap = new Map();
|
|
57
|
+
// Initialize degree counts for all known files
|
|
58
|
+
for (const filePath of fileImportsMap.keys()) {
|
|
59
|
+
inDegreeMap.set(filePath, 0);
|
|
60
|
+
outDegreeMap.set(filePath, 0);
|
|
61
|
+
}
|
|
62
|
+
for (const [sourceFile, resolvedImports] of fileImportsMap.entries()) {
|
|
63
|
+
for (const resolved of resolvedImports) {
|
|
64
|
+
// Skip unresolved imports (external packages, broken paths)
|
|
65
|
+
if (!resolved.resolvedPath) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const targetFile = resolved.resolvedPath;
|
|
69
|
+
// Ensure target is in the degree maps (it might be an internal file
|
|
70
|
+
// that wasn't in our initial file list but is imported)
|
|
71
|
+
if (!inDegreeMap.has(targetFile)) {
|
|
72
|
+
inDegreeMap.set(targetFile, 0);
|
|
73
|
+
}
|
|
74
|
+
edges.push({
|
|
75
|
+
source: sourceFile,
|
|
76
|
+
target: targetFile,
|
|
77
|
+
importSpecifier: resolved.raw.source,
|
|
78
|
+
importedNames: resolved.raw.specifiers,
|
|
79
|
+
isTypeOnly: resolved.raw.isTypeOnly,
|
|
80
|
+
isDynamic: resolved.raw.isDynamic,
|
|
81
|
+
});
|
|
82
|
+
// Update degree counts
|
|
83
|
+
outDegreeMap.set(sourceFile, (outDegreeMap.get(sourceFile) || 0) + 1);
|
|
84
|
+
inDegreeMap.set(targetFile, (inDegreeMap.get(targetFile) || 0) + 1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// ── Build nodes ──────────────────────────────────────────────────────
|
|
88
|
+
// Collect all unique file paths (from both source files and import targets)
|
|
89
|
+
const allFiles = new Set([
|
|
90
|
+
...fileImportsMap.keys(),
|
|
91
|
+
...edges.map((e) => e.target),
|
|
92
|
+
]);
|
|
93
|
+
const nodes = Array.from(allFiles).map((filePath) => ({
|
|
94
|
+
id: filePath,
|
|
95
|
+
relativePath: path.relative(rootDir, filePath),
|
|
96
|
+
extension: path.extname(filePath),
|
|
97
|
+
inDegree: inDegreeMap.get(filePath) || 0,
|
|
98
|
+
outDegree: outDegreeMap.get(filePath) || 0,
|
|
99
|
+
isCircular: false, // Will be set by detectCircularDeps
|
|
100
|
+
}));
|
|
101
|
+
// Sort nodes by relative path for consistent output
|
|
102
|
+
nodes.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
103
|
+
return {
|
|
104
|
+
rootDir,
|
|
105
|
+
nodes,
|
|
106
|
+
edges,
|
|
107
|
+
circularDependencies: [], // Populated later by detectCircularDeps
|
|
108
|
+
metadata,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=buildGraph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildGraph.js","sourceRoot":"","sources":["../src/buildGraph.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildStructureGraph.d.ts","sourceRoot":"","sources":["../src/buildStructureGraph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EAGf,MAAM,YAAY,CAAC;AAUpB,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,YAAY,EACtB,QAAQ,GAAE,MAAiB,GAC1B,cAAc,CAyChB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildStructureGraph = buildStructureGraph;
|
|
4
|
+
const filterTreeByDepth_js_1 = require("./filterTreeByDepth.js");
|
|
5
|
+
function countDescendants(node) {
|
|
6
|
+
return node.children.reduce((count, child) => count + 1 + countDescendants(child), 0);
|
|
7
|
+
}
|
|
8
|
+
function buildStructureGraph(rootNode, maxDepth = Infinity) {
|
|
9
|
+
const nodes = [];
|
|
10
|
+
const edges = [];
|
|
11
|
+
const visibleTree = (0, filterTreeByDepth_js_1.filterTreeByDepth)(rootNode, maxDepth);
|
|
12
|
+
function visit(node, parentId) {
|
|
13
|
+
nodes.push({
|
|
14
|
+
id: node.id,
|
|
15
|
+
label: node.name,
|
|
16
|
+
relativePath: node.relativePath,
|
|
17
|
+
absolutePath: node.absolutePath,
|
|
18
|
+
kind: node.kind,
|
|
19
|
+
extension: node.extension,
|
|
20
|
+
depth: node.depth,
|
|
21
|
+
collapsed: false,
|
|
22
|
+
hidden: false,
|
|
23
|
+
childCount: node.children.length,
|
|
24
|
+
descendantCount: countDescendants(node),
|
|
25
|
+
...(node.sizeBytes !== undefined ? { sizeBytes: node.sizeBytes } : {}),
|
|
26
|
+
});
|
|
27
|
+
if (parentId) {
|
|
28
|
+
edges.push({
|
|
29
|
+
id: `${parentId}->${node.id}`,
|
|
30
|
+
source: parentId,
|
|
31
|
+
target: node.id,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
for (const child of node.children) {
|
|
35
|
+
visit(child, node.id);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
visit(visibleTree, null);
|
|
39
|
+
return {
|
|
40
|
+
rootDir: rootNode.absolutePath,
|
|
41
|
+
nodes,
|
|
42
|
+
edges,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=buildStructureGraph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildStructureGraph.js","sourceRoot":"","sources":["../src/buildStructureGraph.ts"],"names":[],"mappings":";;AAeA,kDA4CC;AArDD,iEAA2D;AAE3D,SAAS,gBAAgB,CAAC,IAAkB;IAC1C,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CACzB,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,GAAG,gBAAgB,CAAC,KAAK,CAAC,EACrD,CAAC,CACF,CAAC;AACJ,CAAC;AAED,SAAgB,mBAAmB,CACjC,QAAsB,EACtB,WAAmB,QAAQ;IAE3B,MAAM,KAAK,GAAyB,EAAE,CAAC;IACvC,MAAM,KAAK,GAAyB,EAAE,CAAC;IACvC,MAAM,WAAW,GAAG,IAAA,wCAAiB,EAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAE1D,SAAS,KAAK,CAAC,IAAkB,EAAE,QAAuB;QACxD,KAAK,CAAC,IAAI,CAAC;YACT,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,KAAK,EAAE,IAAI,CAAC,IAAI;YAChB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,SAAS,EAAE,KAAK;YAChB,MAAM,EAAE,KAAK;YACb,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM;YAChC,eAAe,EAAE,gBAAgB,CAAC,IAAI,CAAC;YACvC,GAAG,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACvE,CAAC,CAAC;QAEH,IAAI,QAAQ,EAAE,CAAC;YACb,KAAK,CAAC,IAAI,CAAC;gBACT,EAAE,EAAE,GAAG,QAAQ,KAAK,IAAI,CAAC,EAAE,EAAE;gBAC7B,MAAM,EAAE,QAAQ;gBAChB,MAAM,EAAE,IAAI,CAAC,EAAE;aAChB,CAAC,CAAC;QACL,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IAEzB,OAAO;QACL,OAAO,EAAE,QAAQ,CAAC,YAAY;QAC9B,KAAK;QACL,KAAK;KACN,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AliasMapping } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Load path alias mappings from tsconfig.json or jsconfig.json.
|
|
4
|
+
*
|
|
5
|
+
* Tries tsconfig.json first, then falls back to jsconfig.json.
|
|
6
|
+
* Follows the `extends` chain to find inherited path mappings.
|
|
7
|
+
*
|
|
8
|
+
* @param rootDir - The project root directory
|
|
9
|
+
* @returns Array of alias mappings, empty if none found
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const aliases = loadAliases('/path/to/project');
|
|
14
|
+
* // [{ prefix: '@/', paths: ['/path/to/project/src/'] }]
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadAliases(rootDir: string): AliasMapping[];
|
|
18
|
+
//# sourceMappingURL=configLoader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configLoader.d.ts","sourceRoot":"","sources":["../src/configLoader.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAgG/C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CA2C3D"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// configLoader — Load path aliases from tsconfig.json / jsconfig.json
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Parses the `compilerOptions.paths` and `compilerOptions.baseUrl` fields
|
|
6
|
+
// from tsconfig.json (or jsconfig.json) to build alias mappings.
|
|
7
|
+
//
|
|
8
|
+
// Example tsconfig.json:
|
|
9
|
+
// {
|
|
10
|
+
// "compilerOptions": {
|
|
11
|
+
// "baseUrl": ".",
|
|
12
|
+
// "paths": {
|
|
13
|
+
// "@/*": ["./src/*"],
|
|
14
|
+
// "@components/*": ["./src/components/*"]
|
|
15
|
+
// }
|
|
16
|
+
// }
|
|
17
|
+
// }
|
|
18
|
+
//
|
|
19
|
+
// This produces aliases:
|
|
20
|
+
// [
|
|
21
|
+
// { prefix: '@/', paths: ['/abs/path/to/src/'] },
|
|
22
|
+
// { prefix: '@components/', paths: ['/abs/path/to/src/components/'] }
|
|
23
|
+
// ]
|
|
24
|
+
// ============================================================================
|
|
25
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
26
|
+
if (k2 === undefined) k2 = k;
|
|
27
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
28
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
29
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
30
|
+
}
|
|
31
|
+
Object.defineProperty(o, k2, desc);
|
|
32
|
+
}) : (function(o, m, k, k2) {
|
|
33
|
+
if (k2 === undefined) k2 = k;
|
|
34
|
+
o[k2] = m[k];
|
|
35
|
+
}));
|
|
36
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
37
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
38
|
+
}) : function(o, v) {
|
|
39
|
+
o["default"] = v;
|
|
40
|
+
});
|
|
41
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
42
|
+
var ownKeys = function(o) {
|
|
43
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
44
|
+
var ar = [];
|
|
45
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
46
|
+
return ar;
|
|
47
|
+
};
|
|
48
|
+
return ownKeys(o);
|
|
49
|
+
};
|
|
50
|
+
return function (mod) {
|
|
51
|
+
if (mod && mod.__esModule) return mod;
|
|
52
|
+
var result = {};
|
|
53
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
54
|
+
__setModuleDefault(result, mod);
|
|
55
|
+
return result;
|
|
56
|
+
};
|
|
57
|
+
})();
|
|
58
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
59
|
+
exports.loadAliases = loadAliases;
|
|
60
|
+
const path = __importStar(require("path"));
|
|
61
|
+
const fs = __importStar(require("fs"));
|
|
62
|
+
/**
|
|
63
|
+
* Strip JSON comments (single-line // and multi-line) and trailing commas
|
|
64
|
+
* so that JSON.parse can handle tsconfig.json files, which allow comments.
|
|
65
|
+
*/
|
|
66
|
+
function stripJsonComments(jsonString) {
|
|
67
|
+
// Remove single-line comments
|
|
68
|
+
let result = jsonString.replace(/\/\/.*$/gm, '');
|
|
69
|
+
// Remove multi-line comments
|
|
70
|
+
result = result.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
71
|
+
// Remove trailing commas before } or ]
|
|
72
|
+
result = result.replace(/,\s*([\]}])/g, '$1');
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Read and parse a JSON config file, handling comments and trailing commas
|
|
77
|
+
* (which are allowed in tsconfig.json but not standard JSON).
|
|
78
|
+
*/
|
|
79
|
+
function readJsonConfig(configPath) {
|
|
80
|
+
try {
|
|
81
|
+
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
82
|
+
const cleaned = stripJsonComments(raw);
|
|
83
|
+
return JSON.parse(cleaned);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Resolve the `extends` chain in tsconfig.json to merge configurations.
|
|
91
|
+
*
|
|
92
|
+
* Many projects use `"extends": "@tsconfig/react-native"` or similar.
|
|
93
|
+
* We follow the chain to find `paths` definitions in parent configs.
|
|
94
|
+
*/
|
|
95
|
+
function resolveExtendsChain(configPath, visited = new Set()) {
|
|
96
|
+
const absPath = path.resolve(configPath);
|
|
97
|
+
// Prevent infinite loops
|
|
98
|
+
if (visited.has(absPath)) {
|
|
99
|
+
return {};
|
|
100
|
+
}
|
|
101
|
+
visited.add(absPath);
|
|
102
|
+
const config = readJsonConfig(absPath);
|
|
103
|
+
if (!config) {
|
|
104
|
+
return {};
|
|
105
|
+
}
|
|
106
|
+
// If this config extends another, merge them
|
|
107
|
+
if (typeof config.extends === 'string') {
|
|
108
|
+
let parentPath = config.extends;
|
|
109
|
+
// Resolve the parent config path
|
|
110
|
+
if (parentPath.startsWith('.')) {
|
|
111
|
+
parentPath = path.resolve(path.dirname(absPath), parentPath);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
// It's a package reference like "@tsconfig/react"
|
|
115
|
+
// Try to resolve it from node_modules
|
|
116
|
+
try {
|
|
117
|
+
parentPath = require.resolve(parentPath, {
|
|
118
|
+
paths: [path.dirname(absPath)],
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// Can't resolve the parent — skip it
|
|
123
|
+
return config;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Ensure .json extension
|
|
127
|
+
if (!parentPath.endsWith('.json')) {
|
|
128
|
+
parentPath += '.json';
|
|
129
|
+
}
|
|
130
|
+
const parentConfig = resolveExtendsChain(parentPath, visited);
|
|
131
|
+
// Merge: child overrides parent
|
|
132
|
+
return {
|
|
133
|
+
...parentConfig,
|
|
134
|
+
...config,
|
|
135
|
+
compilerOptions: {
|
|
136
|
+
...(parentConfig.compilerOptions || {}),
|
|
137
|
+
...(config.compilerOptions || {}),
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return config;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Load path alias mappings from tsconfig.json or jsconfig.json.
|
|
145
|
+
*
|
|
146
|
+
* Tries tsconfig.json first, then falls back to jsconfig.json.
|
|
147
|
+
* Follows the `extends` chain to find inherited path mappings.
|
|
148
|
+
*
|
|
149
|
+
* @param rootDir - The project root directory
|
|
150
|
+
* @returns Array of alias mappings, empty if none found
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* const aliases = loadAliases('/path/to/project');
|
|
155
|
+
* // [{ prefix: '@/', paths: ['/path/to/project/src/'] }]
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
function loadAliases(rootDir) {
|
|
159
|
+
// Try tsconfig.json first, then jsconfig.json
|
|
160
|
+
const configNames = ['tsconfig.json', 'jsconfig.json'];
|
|
161
|
+
for (const configName of configNames) {
|
|
162
|
+
const configPath = path.join(rootDir, configName);
|
|
163
|
+
if (!fs.existsSync(configPath)) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
const config = resolveExtendsChain(configPath);
|
|
167
|
+
const compilerOptions = config.compilerOptions;
|
|
168
|
+
if (!compilerOptions) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const baseUrl = compilerOptions.baseUrl || '.';
|
|
172
|
+
const paths = compilerOptions.paths || {};
|
|
173
|
+
// No path mappings defined
|
|
174
|
+
if (Object.keys(paths).length === 0) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const absoluteBaseUrl = path.resolve(rootDir, baseUrl);
|
|
178
|
+
const aliases = Object.entries(paths).map(([pattern, targets]) => ({
|
|
179
|
+
// Remove the wildcard: '@/*' → '@/'
|
|
180
|
+
prefix: pattern.replace(/\*$/, ''),
|
|
181
|
+
// Resolve each target to an absolute path, remove wildcard
|
|
182
|
+
paths: targets.map((t) => path.resolve(absoluteBaseUrl, t.replace(/\*$/, ''))),
|
|
183
|
+
}));
|
|
184
|
+
return aliases;
|
|
185
|
+
}
|
|
186
|
+
return [];
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=configLoader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configLoader.js","sourceRoot":"","sources":["../src/configLoader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DependencyGraph } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Detect all circular dependency chains in a dependency graph.
|
|
4
|
+
*
|
|
5
|
+
* Modifies the graph in-place:
|
|
6
|
+
* - Sets `isCircular = true` on nodes that participate in cycles
|
|
7
|
+
* - Populates `circularDependencies` with all detected chains
|
|
8
|
+
*
|
|
9
|
+
* @param graph - The dependency graph to analyze (modified in-place)
|
|
10
|
+
* @returns The same graph reference, with circular dependency data populated
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const graph = buildGraph(fileImportsMap, rootDir, metadata);
|
|
15
|
+
* detectCircularDeps(graph);
|
|
16
|
+
* console.log(graph.circularDependencies);
|
|
17
|
+
* // [{ chain: ['A.tsx', 'B.tsx', 'A.tsx'], description: 'A.tsx → B.tsx → A.tsx' }]
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function detectCircularDeps(graph: DependencyGraph): DependencyGraph;
|
|
21
|
+
//# sourceMappingURL=detectCircularDeps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detectCircularDeps.d.ts","sourceRoot":"","sources":["../src/detectCircularDeps.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,YAAY,CAAC;AAMjE;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,eAAe,CAqG1E"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// detectCircularDeps — Find circular dependency chains in the graph
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Uses DFS (Depth-First Search) with node coloring to detect cycles.
|
|
6
|
+
//
|
|
7
|
+
// Algorithm:
|
|
8
|
+
// WHITE (0) = unvisited
|
|
9
|
+
// GRAY (1) = currently in the DFS stack (being explored)
|
|
10
|
+
// BLACK (2) = fully explored (all descendants visited)
|
|
11
|
+
//
|
|
12
|
+
// A cycle is found when we encounter a GRAY node during DFS traversal,
|
|
13
|
+
// meaning we've reached a node that's still being explored — a back edge.
|
|
14
|
+
// ============================================================================
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
exports.detectCircularDeps = detectCircularDeps;
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
const WHITE = 0;
|
|
52
|
+
const GRAY = 1;
|
|
53
|
+
const BLACK = 2;
|
|
54
|
+
/**
|
|
55
|
+
* Detect all circular dependency chains in a dependency graph.
|
|
56
|
+
*
|
|
57
|
+
* Modifies the graph in-place:
|
|
58
|
+
* - Sets `isCircular = true` on nodes that participate in cycles
|
|
59
|
+
* - Populates `circularDependencies` with all detected chains
|
|
60
|
+
*
|
|
61
|
+
* @param graph - The dependency graph to analyze (modified in-place)
|
|
62
|
+
* @returns The same graph reference, with circular dependency data populated
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const graph = buildGraph(fileImportsMap, rootDir, metadata);
|
|
67
|
+
* detectCircularDeps(graph);
|
|
68
|
+
* console.log(graph.circularDependencies);
|
|
69
|
+
* // [{ chain: ['A.tsx', 'B.tsx', 'A.tsx'], description: 'A.tsx → B.tsx → A.tsx' }]
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
function detectCircularDeps(graph) {
|
|
73
|
+
const { nodes, edges, rootDir } = graph;
|
|
74
|
+
// Build adjacency list for fast traversal
|
|
75
|
+
const adjacency = new Map();
|
|
76
|
+
for (const node of nodes) {
|
|
77
|
+
adjacency.set(node.id, []);
|
|
78
|
+
}
|
|
79
|
+
for (const edge of edges) {
|
|
80
|
+
const targets = adjacency.get(edge.source);
|
|
81
|
+
if (targets) {
|
|
82
|
+
targets.push(edge.target);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Track node state for DFS
|
|
86
|
+
const color = new Map();
|
|
87
|
+
for (const node of nodes) {
|
|
88
|
+
color.set(node.id, WHITE);
|
|
89
|
+
}
|
|
90
|
+
// Track the current DFS path for cycle extraction
|
|
91
|
+
const currentPath = [];
|
|
92
|
+
const cycles = [];
|
|
93
|
+
const circularNodeIds = new Set();
|
|
94
|
+
/**
|
|
95
|
+
* DFS visit function. Returns true if a cycle was found downstream.
|
|
96
|
+
*/
|
|
97
|
+
function dfs(nodeId) {
|
|
98
|
+
color.set(nodeId, GRAY);
|
|
99
|
+
currentPath.push(nodeId);
|
|
100
|
+
const neighbors = adjacency.get(nodeId) || [];
|
|
101
|
+
for (const neighbor of neighbors) {
|
|
102
|
+
const neighborColor = color.get(neighbor);
|
|
103
|
+
if (neighborColor === GRAY) {
|
|
104
|
+
// Found a cycle! Extract the chain from the current path.
|
|
105
|
+
const cycleStart = currentPath.indexOf(neighbor);
|
|
106
|
+
if (cycleStart !== -1) {
|
|
107
|
+
const chain = [
|
|
108
|
+
...currentPath.slice(cycleStart),
|
|
109
|
+
neighbor, // Complete the cycle
|
|
110
|
+
];
|
|
111
|
+
// Convert to relative paths for readability
|
|
112
|
+
const relativeChain = chain.map((p) => path.relative(rootDir, p));
|
|
113
|
+
const description = relativeChain.join(' → ');
|
|
114
|
+
// Avoid duplicate cycle reports (same cycle can be found from
|
|
115
|
+
// different starting nodes)
|
|
116
|
+
const key = [...relativeChain]
|
|
117
|
+
.slice(0, -1)
|
|
118
|
+
.sort()
|
|
119
|
+
.join('|');
|
|
120
|
+
const isDuplicate = cycles.some((c) => {
|
|
121
|
+
const existingKey = [...c.chain]
|
|
122
|
+
.slice(0, -1)
|
|
123
|
+
.sort()
|
|
124
|
+
.join('|');
|
|
125
|
+
return existingKey === key;
|
|
126
|
+
});
|
|
127
|
+
if (!isDuplicate) {
|
|
128
|
+
cycles.push({ chain: relativeChain, description });
|
|
129
|
+
// Mark all nodes in this cycle as circular
|
|
130
|
+
for (const nodeInCycle of chain) {
|
|
131
|
+
circularNodeIds.add(nodeInCycle);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else if (neighborColor === WHITE) {
|
|
137
|
+
dfs(neighbor);
|
|
138
|
+
}
|
|
139
|
+
// BLACK nodes are already fully explored — skip them
|
|
140
|
+
}
|
|
141
|
+
currentPath.pop();
|
|
142
|
+
color.set(nodeId, BLACK);
|
|
143
|
+
}
|
|
144
|
+
// Run DFS from every unvisited node
|
|
145
|
+
for (const node of nodes) {
|
|
146
|
+
if (color.get(node.id) === WHITE) {
|
|
147
|
+
dfs(node.id);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Update the graph with results
|
|
151
|
+
graph.circularDependencies = cycles;
|
|
152
|
+
graph.metadata.circularCount = cycles.length;
|
|
153
|
+
// Mark circular nodes
|
|
154
|
+
for (const node of graph.nodes) {
|
|
155
|
+
node.isCircular = circularNodeIds.has(node.id);
|
|
156
|
+
}
|
|
157
|
+
return graph;
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=detectCircularDeps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detectCircularDeps.js","sourceRoot":"","sources":["../src/detectCircularDeps.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DependencyGraph, OrphanDetectionOptions } from './types.js';
|
|
2
|
+
export declare function matchesAnyPattern(relativePath: string, patterns: string[]): boolean;
|
|
3
|
+
export declare function detectOrphanFiles(graph: DependencyGraph, options?: OrphanDetectionOptions): string[];
|
|
4
|
+
//# sourceMappingURL=detectOrphanFiles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detectOrphanFiles.d.ts","sourceRoot":"","sources":["../src/detectOrphanFiles.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,eAAe,EACf,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAiDpB,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAGT;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,eAAe,EACtB,OAAO,GAAE,sBAA2B,GACnC,MAAM,EAAE,CAQV"}
|