tdepend 0.2.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 +167 -0
- package/dist/analysis/reporter.d.ts +22 -0
- package/dist/analysis/reporter.js +116 -0
- package/dist/analysis/reporter.js.map +1 -0
- package/dist/api/analyze.d.ts +75 -0
- package/dist/api/analyze.js +121 -0
- package/dist/api/analyze.js.map +1 -0
- package/dist/api/export.d.ts +50 -0
- package/dist/api/export.js +53 -0
- package/dist/api/export.js.map +1 -0
- package/dist/api/serialization.d.ts +28 -0
- package/dist/api/serialization.js +71 -0
- package/dist/api/serialization.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +134 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/loadConfig.d.ts +10 -0
- package/dist/config/loadConfig.js +56 -0
- package/dist/config/loadConfig.js.map +1 -0
- package/dist/config/schema.d.ts +35 -0
- package/dist/config/schema.js +58 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/graph/cycleDetector.d.ts +2 -0
- package/dist/graph/cycleDetector.js +56 -0
- package/dist/graph/cycleDetector.js.map +1 -0
- package/dist/graph/dependencyGraph.d.ts +10 -0
- package/dist/graph/dependencyGraph.js +41 -0
- package/dist/graph/dependencyGraph.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/metrics/abstractness.d.ts +2 -0
- package/dist/metrics/abstractness.js +10 -0
- package/dist/metrics/abstractness.js.map +1 -0
- package/dist/metrics/distance.d.ts +1 -0
- package/dist/metrics/distance.js +7 -0
- package/dist/metrics/distance.js.map +1 -0
- package/dist/metrics/index.d.ts +4 -0
- package/dist/metrics/index.js +30 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/instability.d.ts +1 -0
- package/dist/metrics/instability.js +8 -0
- package/dist/metrics/instability.js.map +1 -0
- package/dist/parser/fileScanner.d.ts +8 -0
- package/dist/parser/fileScanner.js +22 -0
- package/dist/parser/fileScanner.js.map +1 -0
- package/dist/parser/tsParser.d.ts +18 -0
- package/dist/parser/tsParser.js +158 -0
- package/dist/parser/tsParser.js.map +1 -0
- package/dist/types/api.d.ts +74 -0
- package/dist/types/api.js +3 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/graph.d.ts +14 -0
- package/dist/types/graph.js +3 -0
- package/dist/types/graph.js.map +1 -0
- package/dist/types/parser.d.ts +26 -0
- package/dist/types/parser.js +3 -0
- package/dist/types/parser.js.map +1 -0
- package/dist/utils/deepMerge.d.ts +1 -0
- package/dist/utils/deepMerge.js +23 -0
- package/dist/utils/deepMerge.js.map +1 -0
- package/dist/utils/log.d.ts +2 -0
- package/dist/utils/log.js +9 -0
- package/dist/utils/log.js.map +1 -0
- package/dist/utils/pathUtils.d.ts +12 -0
- package/dist/utils/pathUtils.js +27 -0
- package/dist/utils/pathUtils.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.serializeGraph = serializeGraph;
|
|
4
|
+
exports.normalizeCycle = normalizeCycle;
|
|
5
|
+
exports.toSerializable = toSerializable;
|
|
6
|
+
const crypto_1 = require("crypto");
|
|
7
|
+
/**
|
|
8
|
+
* Converts a DependencyGraph with Sets to a serializable format with Arrays.
|
|
9
|
+
* @param graph - The dependency graph to serialize
|
|
10
|
+
* @returns Serializable graph with array-based relationships
|
|
11
|
+
*/
|
|
12
|
+
function serializeGraph(graph) {
|
|
13
|
+
const nodes = graph.getAllNodes().map((node) => ({
|
|
14
|
+
filePath: node.filePath,
|
|
15
|
+
dependencies: Array.from(node.dependencies).sort(),
|
|
16
|
+
dependents: Array.from(node.dependents).sort(),
|
|
17
|
+
}));
|
|
18
|
+
return { nodes };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Normalizes a cycle for consistent identification across runs.
|
|
22
|
+
* Creates a unique ID based on the cycle's contents and rotates the cycle
|
|
23
|
+
* to start with the lexicographically smallest file path.
|
|
24
|
+
*
|
|
25
|
+
* @param cycle - Array of file paths forming a cycle
|
|
26
|
+
* @returns Normalized cycle with unique ID
|
|
27
|
+
*/
|
|
28
|
+
function normalizeCycle(cycle) {
|
|
29
|
+
if (cycle.length === 0) {
|
|
30
|
+
return {
|
|
31
|
+
id: '',
|
|
32
|
+
nodes: [],
|
|
33
|
+
length: 0,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
// Create stable ID by hashing sorted paths
|
|
37
|
+
const sorted = [...cycle].sort();
|
|
38
|
+
const id = (0, crypto_1.createHash)('sha256').update(sorted.join('|')).digest('hex').slice(0, 16);
|
|
39
|
+
// Rotate cycle to start with lexicographically smallest element
|
|
40
|
+
const minPath = sorted[0];
|
|
41
|
+
const minIndex = cycle.indexOf(minPath);
|
|
42
|
+
const normalized = [...cycle.slice(minIndex), ...cycle.slice(0, minIndex)];
|
|
43
|
+
return {
|
|
44
|
+
id,
|
|
45
|
+
nodes: normalized,
|
|
46
|
+
length: cycle.length,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Converts an AnalysisResult with rich objects to a fully serializable format.
|
|
51
|
+
* This includes:
|
|
52
|
+
* - Converting Sets to Arrays in the dependency graph
|
|
53
|
+
* - Normalizing cycles with unique IDs
|
|
54
|
+
* - Adding version and timestamp metadata
|
|
55
|
+
*
|
|
56
|
+
* @param result - The analysis result to serialize
|
|
57
|
+
* @returns Fully serializable result ready for JSON.stringify
|
|
58
|
+
*/
|
|
59
|
+
function toSerializable(result) {
|
|
60
|
+
return {
|
|
61
|
+
version: '1.0.0',
|
|
62
|
+
timestamp: new Date().toISOString(),
|
|
63
|
+
config: result.config,
|
|
64
|
+
modules: result.modules,
|
|
65
|
+
graph: serializeGraph(result.graph),
|
|
66
|
+
cycles: result.cycles.map(normalizeCycle),
|
|
67
|
+
metrics: result.metrics,
|
|
68
|
+
report: result.report,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=serialization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialization.js","sourceRoot":"","sources":["../../src/api/serialization.ts"],"names":[],"mappings":";;AAeA,wCAQC;AAUD,wCAuBC;AAYD,wCAWC;AA/ED,mCAAoC;AAUpC;;;;GAIG;AACH,SAAgB,cAAc,CAAC,KAAsB;IACnD,MAAM,KAAK,GAAiC,KAAK,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC7E,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,YAAY,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,EAAE;QAClD,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,EAAE;KAC/C,CAAC,CAAC,CAAC;IAEJ,OAAO,EAAE,KAAK,EAAE,CAAC;AACnB,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,cAAc,CAAC,KAAe;IAC5C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO;YACL,EAAE,EAAE,EAAE;YACN,KAAK,EAAE,EAAE;YACT,MAAM,EAAE,CAAC;SACV,CAAC;IACJ,CAAC;IAED,2CAA2C;IAC3C,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IACjC,MAAM,EAAE,GAAG,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEpF,gEAAgE;IAChE,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC1B,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;IAE3E,OAAO;QACL,EAAE;QACF,KAAK,EAAE,UAAU;QACjB,MAAM,EAAE,KAAK,CAAC,MAAM;KACrB,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,cAAc,CAAC,MAAsB;IACnD,OAAO;QACL,OAAO,EAAE,OAAO;QAChB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC;QACnC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC;QACzC,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const loadConfig_1 = require("../config/loadConfig");
|
|
6
|
+
const reporter_1 = require("../analysis/reporter");
|
|
7
|
+
const dependencyGraph_1 = require("../graph/dependencyGraph");
|
|
8
|
+
const cycleDetector_1 = require("../graph/cycleDetector");
|
|
9
|
+
const metrics_1 = require("../metrics");
|
|
10
|
+
const fileScanner_1 = require("../parser/fileScanner");
|
|
11
|
+
const tsParser_1 = require("../parser/tsParser");
|
|
12
|
+
const log_1 = require("../utils/log");
|
|
13
|
+
const export_1 = require("../api/export");
|
|
14
|
+
async function runAnalysis(config) {
|
|
15
|
+
(0, log_1.log)(config, '✓ Configuration loaded successfully');
|
|
16
|
+
(0, log_1.log)(config, ` Root directory: ${config.rootDir}`);
|
|
17
|
+
(0, log_1.log)(config, ` Enabled metrics: ${config.metrics.enabled.join(', ')}`);
|
|
18
|
+
if (config.analysis.target) {
|
|
19
|
+
(0, log_1.log)(config, ` Analysis scope: ${config.analysis.target} = ${config.analysis.value}`);
|
|
20
|
+
}
|
|
21
|
+
(0, log_1.log)(config, '\n📁 Scanning files...');
|
|
22
|
+
const files = await (0, fileScanner_1.scanFiles)(config);
|
|
23
|
+
(0, log_1.log)(config, `✓ Found ${files.length} TypeScript files`);
|
|
24
|
+
if (files.length === 0) {
|
|
25
|
+
(0, log_1.log)(config, '\n⚠ No files found matching the configured patterns.');
|
|
26
|
+
process.exitCode = 0;
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
(0, log_1.log)(config, '\n📝 Parsing modules...');
|
|
30
|
+
const modules = (0, tsParser_1.parseProject)(files);
|
|
31
|
+
(0, log_1.log)(config, `✓ Parsed ${modules.length} modules`);
|
|
32
|
+
(0, log_1.log)(config, '\n🔗 Building dependency graph...');
|
|
33
|
+
const graph = new dependencyGraph_1.DependencyGraph(modules);
|
|
34
|
+
(0, log_1.log)(config, '✓ Dependency graph built');
|
|
35
|
+
(0, log_1.log)(config, '\n🔄 Detecting cycles...');
|
|
36
|
+
const cycles = (0, cycleDetector_1.detectCycles)(graph);
|
|
37
|
+
(0, log_1.log)(config, `✓ Found ${cycles.length} cycle(s)`);
|
|
38
|
+
if (checkCyclesAndFail(cycles, config.ci.failOnCycle)) {
|
|
39
|
+
if (config.ci.outputFormat !== 'json') {
|
|
40
|
+
process.exitCode = 1;
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
(0, log_1.log)(config, '\n📐 Computing metrics...');
|
|
45
|
+
const metrics = (0, metrics_1.computeAllMetrics)(graph, modules, cycles);
|
|
46
|
+
(0, log_1.log)(config, '✓ Metrics computed');
|
|
47
|
+
const report = (0, reporter_1.generateReport)(modules, metrics, cycles, config);
|
|
48
|
+
// Create AnalysisResult for potential export
|
|
49
|
+
const result = {
|
|
50
|
+
modules,
|
|
51
|
+
graph,
|
|
52
|
+
cycles,
|
|
53
|
+
metrics,
|
|
54
|
+
report,
|
|
55
|
+
config,
|
|
56
|
+
};
|
|
57
|
+
if (config.ci.outputFormat === 'json') {
|
|
58
|
+
console.log((0, reporter_1.formatJsonOutput)(report));
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
(0, reporter_1.formatConsoleOutput)(report, config);
|
|
62
|
+
}
|
|
63
|
+
if (!report.success) {
|
|
64
|
+
if (config.ci.outputFormat !== 'json') {
|
|
65
|
+
console.error(report.violations.cycles.length > 0 && config.ci.failOnCycle
|
|
66
|
+
? '\n❌ Error: Cycles detected in dependency graph'
|
|
67
|
+
: '\n❌ Error: Modules exceed distance threshold');
|
|
68
|
+
}
|
|
69
|
+
process.exitCode = 1;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
process.exitCode = 0;
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
function checkCyclesAndFail(cycles, failOnCycle) {
|
|
77
|
+
if (cycles.length > 0 && failOnCycle) {
|
|
78
|
+
console.error('\n❌ Error: Cycles detected in dependency graph');
|
|
79
|
+
for (const cycle of cycles) {
|
|
80
|
+
const fileNames = cycle.map((p) => p.split('/').pop()).join(' → ');
|
|
81
|
+
console.error(` - ${fileNames}`);
|
|
82
|
+
}
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const program = new commander_1.Command();
|
|
88
|
+
program
|
|
89
|
+
.name('tdepend')
|
|
90
|
+
.description('TDepend: TypeScript dependency analysis tool')
|
|
91
|
+
.version('0.1.0');
|
|
92
|
+
program
|
|
93
|
+
.command('analyze')
|
|
94
|
+
.description('Analyze a TypeScript project for dependencies and metrics')
|
|
95
|
+
.option('-c, --config <path>', 'Path to config file (tdepend.config.json)')
|
|
96
|
+
.option('--class <name>', 'Analyze a specific class by name')
|
|
97
|
+
.option('--namespace <name>', 'Analyze a specific namespace by name')
|
|
98
|
+
.option('--ci', 'Run in CI mode with JSON output and strict exit codes')
|
|
99
|
+
.option('-e, --export <path>', 'Export full analysis to JSON file')
|
|
100
|
+
.option('-o, --output <path>', 'Alias for --export')
|
|
101
|
+
.argument('[target]', 'Optional module/file path to analyze')
|
|
102
|
+
.action(async (target, options) => {
|
|
103
|
+
try {
|
|
104
|
+
const config = (0, loadConfig_1.loadConfig)(options.config);
|
|
105
|
+
if (options.ci) {
|
|
106
|
+
config.ci.outputFormat = 'json';
|
|
107
|
+
}
|
|
108
|
+
if (target) {
|
|
109
|
+
config.analysis.target = 'module';
|
|
110
|
+
config.analysis.value = target;
|
|
111
|
+
}
|
|
112
|
+
if (options.class) {
|
|
113
|
+
config.analysis.target = 'class';
|
|
114
|
+
config.analysis.value = options.class;
|
|
115
|
+
}
|
|
116
|
+
if (options.namespace) {
|
|
117
|
+
config.analysis.target = 'namespace';
|
|
118
|
+
config.analysis.value = options.namespace;
|
|
119
|
+
}
|
|
120
|
+
const result = await runAnalysis(config);
|
|
121
|
+
// Handle export if requested
|
|
122
|
+
if (result && (options.export || options.output)) {
|
|
123
|
+
const exportPath = options.export || options.output;
|
|
124
|
+
await (0, export_1.exportToFile)(result, exportPath);
|
|
125
|
+
(0, log_1.log)(config, `\n✓ Analysis exported to ${exportPath}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
console.error('Error:', error instanceof Error ? error.message : String(error));
|
|
130
|
+
process.exitCode = 1;
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
program.parse();
|
|
134
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type TDependConfig } from './schema';
|
|
2
|
+
/**
|
|
3
|
+
* Load and validate TDepend configuration
|
|
4
|
+
*
|
|
5
|
+
* @param configPath - Optional path to config file. If not provided, searches for
|
|
6
|
+
* tdepend.config.json in current directory
|
|
7
|
+
* @returns Validated and merged configuration
|
|
8
|
+
* @throws Error if config file is not found, invalid JSON, or fails validation
|
|
9
|
+
*/
|
|
10
|
+
export declare function loadConfig(configPath?: string): TDependConfig;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadConfig = loadConfig;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const schema_1 = require("./schema");
|
|
7
|
+
const deepMerge_1 = require("../utils/deepMerge");
|
|
8
|
+
function resolveConfigPath(configPath) {
|
|
9
|
+
if (configPath) {
|
|
10
|
+
const resolvedPath = (0, node_path_1.resolve)(configPath);
|
|
11
|
+
if (!(0, node_fs_1.existsSync)(resolvedPath)) {
|
|
12
|
+
throw new Error(`Config file not found: ${resolvedPath}`);
|
|
13
|
+
}
|
|
14
|
+
return resolvedPath;
|
|
15
|
+
}
|
|
16
|
+
const defaultPath = (0, node_path_1.resolve)('tdepend.config.json');
|
|
17
|
+
if ((0, node_fs_1.existsSync)(defaultPath)) {
|
|
18
|
+
return defaultPath;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
function readConfigFile(filePath) {
|
|
23
|
+
try {
|
|
24
|
+
const fileContent = (0, node_fs_1.readFileSync)(filePath, 'utf-8');
|
|
25
|
+
return JSON.parse(fileContent);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (error instanceof SyntaxError) {
|
|
29
|
+
throw new Error(`Invalid JSON in config file: ${filePath}`);
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function validateConfig(config) {
|
|
35
|
+
try {
|
|
36
|
+
return schema_1.configSchema.parse(config);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
throw new Error(`Config validation failed: ${error}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Load and validate TDepend configuration
|
|
44
|
+
*
|
|
45
|
+
* @param configPath - Optional path to config file. If not provided, searches for
|
|
46
|
+
* tdepend.config.json in current directory
|
|
47
|
+
* @returns Validated and merged configuration
|
|
48
|
+
* @throws Error if config file is not found, invalid JSON, or fails validation
|
|
49
|
+
*/
|
|
50
|
+
function loadConfig(configPath) {
|
|
51
|
+
const resolvedPath = resolveConfigPath(configPath);
|
|
52
|
+
const loadedConfig = resolvedPath ? readConfigFile(resolvedPath) : {};
|
|
53
|
+
const mergedConfig = (0, deepMerge_1.deepMerge)(schema_1.defaultConfig, loadedConfig);
|
|
54
|
+
return validateConfig(mergedConfig);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=loadConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadConfig.js","sourceRoot":"","sources":["../../src/config/loadConfig.ts"],"names":[],"mappings":";;AAmDA,gCAKC;AAxDD,qCAAmD;AACnD,yCAAoC;AAEpC,qCAA2E;AAC3E,kDAA+C;AAE/C,SAAS,iBAAiB,CAAC,UAAmB;IAC5C,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,YAAY,GAAG,IAAA,mBAAO,EAAC,UAAU,CAAC,CAAC;QACzC,IAAI,CAAC,IAAA,oBAAU,EAAC,YAAY,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,0BAA0B,YAAY,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,WAAW,GAAG,IAAA,mBAAO,EAAC,qBAAqB,CAAC,CAAC;IACnD,IAAI,IAAA,oBAAU,EAAC,WAAW,CAAC,EAAE,CAAC;QAC5B,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,cAAc,CAAC,QAAgB;IACtC,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAA,sBAAY,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,gCAAgC,QAAQ,EAAE,CAAC,CAAC;QAC9D,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,MAA8B;IACpD,IAAI,CAAC;QACH,OAAO,qBAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,6BAA6B,KAAK,EAAE,CAAC,CAAC;IACxD,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,UAAU,CAAC,UAAmB;IAC5C,MAAM,YAAY,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACnD,MAAM,YAAY,GAAG,YAAY,CAAC,CAAC,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACtE,MAAM,YAAY,GAAG,IAAA,qBAAS,EAAC,sBAAa,EAAE,YAAY,CAAC,CAAC;IAC5D,OAAO,cAAc,CAAC,YAAY,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const configSchema: z.ZodObject<{
|
|
3
|
+
rootDir: z.ZodDefault<z.ZodString>;
|
|
4
|
+
include: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
5
|
+
exclude: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
6
|
+
metrics: z.ZodDefault<z.ZodObject<{
|
|
7
|
+
enabled: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
8
|
+
coupling: "coupling";
|
|
9
|
+
abstractness: "abstractness";
|
|
10
|
+
distance: "distance";
|
|
11
|
+
cycles: "cycles";
|
|
12
|
+
}>>>;
|
|
13
|
+
thresholds: z.ZodDefault<z.ZodObject<{
|
|
14
|
+
distance: z.ZodDefault<z.ZodNumber>;
|
|
15
|
+
}, z.core.$strip>>;
|
|
16
|
+
}, z.core.$strip>>;
|
|
17
|
+
analysis: z.ZodDefault<z.ZodObject<{
|
|
18
|
+
target: z.ZodDefault<z.ZodNullable<z.ZodEnum<{
|
|
19
|
+
module: "module";
|
|
20
|
+
class: "class";
|
|
21
|
+
namespace: "namespace";
|
|
22
|
+
}>>>;
|
|
23
|
+
value: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
24
|
+
}, z.core.$strip>>;
|
|
25
|
+
ci: z.ZodDefault<z.ZodObject<{
|
|
26
|
+
failOnThreshold: z.ZodDefault<z.ZodBoolean>;
|
|
27
|
+
failOnCycle: z.ZodDefault<z.ZodBoolean>;
|
|
28
|
+
outputFormat: z.ZodDefault<z.ZodEnum<{
|
|
29
|
+
console: "console";
|
|
30
|
+
json: "json";
|
|
31
|
+
}>>;
|
|
32
|
+
}, z.core.$strip>>;
|
|
33
|
+
}, z.core.$strip>;
|
|
34
|
+
export type TDependConfig = z.infer<typeof configSchema>;
|
|
35
|
+
export declare const defaultConfig: TDependConfig;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultConfig = exports.configSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
exports.configSchema = zod_1.z.object({
|
|
6
|
+
rootDir: zod_1.z.string().default('src'),
|
|
7
|
+
include: zod_1.z.array(zod_1.z.string()).default(['src/**/*.ts', 'src/**/*.tsx']),
|
|
8
|
+
exclude: zod_1.z.array(zod_1.z.string()).default(['**/*.test.ts', '**/*.spec.ts', 'dist']),
|
|
9
|
+
metrics: zod_1.z
|
|
10
|
+
.object({
|
|
11
|
+
enabled: zod_1.z
|
|
12
|
+
.array(zod_1.z.enum(['coupling', 'abstractness', 'distance', 'cycles']))
|
|
13
|
+
.default(['coupling', 'abstractness', 'distance', 'cycles']),
|
|
14
|
+
thresholds: zod_1.z
|
|
15
|
+
.object({
|
|
16
|
+
distance: zod_1.z.number().min(0).max(1).default(0.6),
|
|
17
|
+
})
|
|
18
|
+
.default({ distance: 0.6 }),
|
|
19
|
+
})
|
|
20
|
+
.default({
|
|
21
|
+
enabled: ['coupling', 'abstractness', 'distance', 'cycles'],
|
|
22
|
+
thresholds: { distance: 0.6 },
|
|
23
|
+
}),
|
|
24
|
+
analysis: zod_1.z
|
|
25
|
+
.object({
|
|
26
|
+
target: zod_1.z.enum(['module', 'class', 'namespace']).nullable().default(null),
|
|
27
|
+
value: zod_1.z.string().nullable().default(null),
|
|
28
|
+
})
|
|
29
|
+
.default({ target: null, value: null }),
|
|
30
|
+
ci: zod_1.z
|
|
31
|
+
.object({
|
|
32
|
+
failOnThreshold: zod_1.z.boolean().default(true),
|
|
33
|
+
failOnCycle: zod_1.z.boolean().default(false),
|
|
34
|
+
outputFormat: zod_1.z.enum(['console', 'json']).default('console'),
|
|
35
|
+
})
|
|
36
|
+
.default({ failOnThreshold: true, failOnCycle: false, outputFormat: 'console' }),
|
|
37
|
+
});
|
|
38
|
+
exports.defaultConfig = {
|
|
39
|
+
rootDir: 'src',
|
|
40
|
+
include: ['src/**/*.ts', 'src/**/*.tsx'],
|
|
41
|
+
exclude: ['**/*.test.ts', '**/*.spec.ts', 'dist'],
|
|
42
|
+
metrics: {
|
|
43
|
+
enabled: ['coupling', 'abstractness', 'distance', 'cycles'],
|
|
44
|
+
thresholds: {
|
|
45
|
+
distance: 0.6,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
analysis: {
|
|
49
|
+
target: null,
|
|
50
|
+
value: null,
|
|
51
|
+
},
|
|
52
|
+
ci: {
|
|
53
|
+
failOnThreshold: true,
|
|
54
|
+
failOnCycle: false,
|
|
55
|
+
outputFormat: 'console',
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AAEX,QAAA,YAAY,GAAG,OAAC,CAAC,MAAM,CAAC;IACnC,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAClC,OAAO,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC;IACrE,OAAO,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,cAAc,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC;IAC9E,OAAO,EAAE,OAAC;SACP,MAAM,CAAC;QACN,OAAO,EAAE,OAAC;aACP,KAAK,CAAC,OAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;aACjE,OAAO,CAAC,CAAC,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC9D,UAAU,EAAE,OAAC;aACV,MAAM,CAAC;YACN,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;SAChD,CAAC;aACD,OAAO,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;KAC9B,CAAC;SACD,OAAO,CAAC;QACP,OAAO,EAAE,CAAC,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,CAAC;QAC3D,UAAU,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE;KAC9B,CAAC;IACJ,QAAQ,EAAE,OAAC;SACR,MAAM,CAAC;QACN,MAAM,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;QACzE,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;KAC3C,CAAC;SACD,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACzC,EAAE,EAAE,OAAC;SACF,MAAM,CAAC;QACN,eAAe,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;QAC1C,WAAW,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;QACvC,YAAY,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;KAC7D,CAAC;SACD,OAAO,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;CACnF,CAAC,CAAC;AAIU,QAAA,aAAa,GAAkB;IAC1C,OAAO,EAAE,KAAK;IACd,OAAO,EAAE,CAAC,aAAa,EAAE,cAAc,CAAC;IACxC,OAAO,EAAE,CAAC,cAAc,EAAE,cAAc,EAAE,MAAM,CAAC;IACjD,OAAO,EAAE;QACP,OAAO,EAAE,CAAC,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,CAAC;QAC3D,UAAU,EAAE;YACV,QAAQ,EAAE,GAAG;SACd;KACF;IACD,QAAQ,EAAE;QACR,MAAM,EAAE,IAAI;QACZ,KAAK,EAAE,IAAI;KACZ;IACD,EAAE,EAAE;QACF,eAAe,EAAE,IAAI;QACrB,WAAW,EAAE,KAAK;QAClB,YAAY,EAAE,SAAS;KACxB;CACF,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectCycles = detectCycles;
|
|
4
|
+
function strongConnect(node, graph, state) {
|
|
5
|
+
state.indices.set(node, state.index);
|
|
6
|
+
state.lowLinks.set(node, state.index);
|
|
7
|
+
state.index++;
|
|
8
|
+
state.stack.push(node);
|
|
9
|
+
state.onStack.add(node);
|
|
10
|
+
const graphNode = graph.getNode(node);
|
|
11
|
+
if (graphNode) {
|
|
12
|
+
for (const dependency of graphNode.dependencies) {
|
|
13
|
+
if (!state.indices.has(dependency)) {
|
|
14
|
+
strongConnect(dependency, graph, state);
|
|
15
|
+
const currentLow = state.lowLinks.get(node);
|
|
16
|
+
const depLow = state.lowLinks.get(dependency);
|
|
17
|
+
state.lowLinks.set(node, Math.min(currentLow, depLow));
|
|
18
|
+
}
|
|
19
|
+
else if (state.onStack.has(dependency)) {
|
|
20
|
+
const currentLow = state.lowLinks.get(node);
|
|
21
|
+
const depIndex = state.indices.get(dependency);
|
|
22
|
+
state.lowLinks.set(node, Math.min(currentLow, depIndex));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (state.lowLinks.get(node) === state.indices.get(node)) {
|
|
27
|
+
const component = [];
|
|
28
|
+
let w;
|
|
29
|
+
do {
|
|
30
|
+
w = state.stack.pop();
|
|
31
|
+
state.onStack.delete(w);
|
|
32
|
+
component.push(w);
|
|
33
|
+
} while (w !== node);
|
|
34
|
+
if (component.length > 1) {
|
|
35
|
+
state.components.push(component);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function detectCycles(graph) {
|
|
40
|
+
const state = {
|
|
41
|
+
index: 0,
|
|
42
|
+
stack: [],
|
|
43
|
+
indices: new Map(),
|
|
44
|
+
lowLinks: new Map(),
|
|
45
|
+
onStack: new Set(),
|
|
46
|
+
components: [],
|
|
47
|
+
};
|
|
48
|
+
const allNodes = graph.getAllNodes();
|
|
49
|
+
for (const node of allNodes) {
|
|
50
|
+
if (!state.indices.has(node.filePath)) {
|
|
51
|
+
strongConnect(node.filePath, graph, state);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return state.components;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=cycleDetector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cycleDetector.js","sourceRoot":"","sources":["../../src/graph/cycleDetector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ParsedModule } from '../types/parser';
|
|
2
|
+
import type { DependencyNode } from '../types/graph';
|
|
3
|
+
export declare class DependencyGraph {
|
|
4
|
+
private readonly nodes;
|
|
5
|
+
constructor(modules: ParsedModule[]);
|
|
6
|
+
private addNode;
|
|
7
|
+
private addEdge;
|
|
8
|
+
getNode(filePath: string): DependencyNode | undefined;
|
|
9
|
+
getAllNodes(): DependencyNode[];
|
|
10
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DependencyGraph = void 0;
|
|
4
|
+
class DependencyGraph {
|
|
5
|
+
constructor(modules) {
|
|
6
|
+
this.nodes = new Map();
|
|
7
|
+
for (const module of modules) {
|
|
8
|
+
this.addNode(module.filePath);
|
|
9
|
+
}
|
|
10
|
+
for (const module of modules) {
|
|
11
|
+
for (const importPath of module.imports) {
|
|
12
|
+
this.addEdge(module.filePath, importPath);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
addNode(filePath) {
|
|
17
|
+
if (!this.nodes.has(filePath)) {
|
|
18
|
+
this.nodes.set(filePath, {
|
|
19
|
+
filePath,
|
|
20
|
+
dependencies: new Set(),
|
|
21
|
+
dependents: new Set(),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
addEdge(from, to) {
|
|
26
|
+
this.addNode(from);
|
|
27
|
+
this.addNode(to);
|
|
28
|
+
const fromNode = this.nodes.get(from);
|
|
29
|
+
const toNode = this.nodes.get(to);
|
|
30
|
+
fromNode.dependencies.add(to);
|
|
31
|
+
toNode.dependents.add(from);
|
|
32
|
+
}
|
|
33
|
+
getNode(filePath) {
|
|
34
|
+
return this.nodes.get(filePath);
|
|
35
|
+
}
|
|
36
|
+
getAllNodes() {
|
|
37
|
+
return Array.from(this.nodes.values());
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.DependencyGraph = DependencyGraph;
|
|
41
|
+
//# sourceMappingURL=dependencyGraph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependencyGraph.js","sourceRoot":"","sources":["../../src/graph/dependencyGraph.ts"],"names":[],"mappings":";;;AAGA,MAAa,eAAe;IAG1B,YAAY,OAAuB;QAFlB,UAAK,GAAgC,IAAI,GAAG,EAAE,CAAC;QAG9D,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAChC,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACxC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;YAC5C,CAAC;QACH,CAAC;IACH,CAAC;IAEO,OAAO,CAAC,QAAgB;QAC9B,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE;gBACvB,QAAQ;gBACR,YAAY,EAAE,IAAI,GAAG,EAAE;gBACvB,UAAU,EAAE,IAAI,GAAG,EAAE;aACtB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAEO,OAAO,CAAC,IAAY,EAAE,EAAU;QACtC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACnB,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAEjB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC;QAEnC,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9B,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO,CAAC,QAAgB;QACtB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IAED,WAAW;QACT,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,CAAC;CACF;AA3CD,0CA2CC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TDepend - TypeScript Dependency Analysis Library
|
|
3
|
+
*
|
|
4
|
+
* A JDepend-inspired dependency analysis tool for TypeScript projects.
|
|
5
|
+
* Computes architectural metrics and detects dependency issues.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
export { analyze, analyzeWithConfig } from './api/analyze';
|
|
10
|
+
export type { AnalyzeOptions } from './api/analyze';
|
|
11
|
+
export { exportToFile, exportToJson } from './api/export';
|
|
12
|
+
export type { ExportOptions } from './api/export';
|
|
13
|
+
export { toSerializable } from './api/serialization';
|
|
14
|
+
export { DependencyGraph } from './graph/dependencyGraph';
|
|
15
|
+
export { detectCycles } from './graph/cycleDetector';
|
|
16
|
+
export { computeAllMetrics } from './metrics';
|
|
17
|
+
export { parseProject, parseFile } from './parser/tsParser';
|
|
18
|
+
export { scanFiles } from './parser/fileScanner';
|
|
19
|
+
export { loadConfig } from './config/loadConfig';
|
|
20
|
+
export { generateReport, formatJsonOutput } from './analysis/reporter';
|
|
21
|
+
export type { ParsedModule, ParsedClass, ParsedNamespace } from './types/parser';
|
|
22
|
+
export type { DependencyNode, ModuleMetrics } from './types/graph';
|
|
23
|
+
export type { AnalysisReport } from './analysis/reporter';
|
|
24
|
+
export type { TDependConfig } from './config/schema';
|
|
25
|
+
export type { AnalysisResult, SerializableAnalysisResult, SerializableDependencyGraph, SerializableDependencyNode, SerializableCycle, } from './types/api';
|
|
26
|
+
export { defaultConfig } from './config/schema';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TDepend - TypeScript Dependency Analysis Library
|
|
4
|
+
*
|
|
5
|
+
* A JDepend-inspired dependency analysis tool for TypeScript projects.
|
|
6
|
+
* Computes architectural metrics and detects dependency issues.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.defaultConfig = exports.formatJsonOutput = exports.generateReport = exports.loadConfig = exports.scanFiles = exports.parseFile = exports.parseProject = exports.computeAllMetrics = exports.detectCycles = exports.DependencyGraph = exports.toSerializable = exports.exportToJson = exports.exportToFile = exports.analyzeWithConfig = exports.analyze = void 0;
|
|
12
|
+
// High-level API - Primary interface for library users
|
|
13
|
+
var analyze_1 = require("./api/analyze");
|
|
14
|
+
Object.defineProperty(exports, "analyze", { enumerable: true, get: function () { return analyze_1.analyze; } });
|
|
15
|
+
Object.defineProperty(exports, "analyzeWithConfig", { enumerable: true, get: function () { return analyze_1.analyzeWithConfig; } });
|
|
16
|
+
var export_1 = require("./api/export");
|
|
17
|
+
Object.defineProperty(exports, "exportToFile", { enumerable: true, get: function () { return export_1.exportToFile; } });
|
|
18
|
+
Object.defineProperty(exports, "exportToJson", { enumerable: true, get: function () { return export_1.exportToJson; } });
|
|
19
|
+
var serialization_1 = require("./api/serialization");
|
|
20
|
+
Object.defineProperty(exports, "toSerializable", { enumerable: true, get: function () { return serialization_1.toSerializable; } });
|
|
21
|
+
// Core building blocks - For advanced users who want fine-grained control
|
|
22
|
+
var dependencyGraph_1 = require("./graph/dependencyGraph");
|
|
23
|
+
Object.defineProperty(exports, "DependencyGraph", { enumerable: true, get: function () { return dependencyGraph_1.DependencyGraph; } });
|
|
24
|
+
var cycleDetector_1 = require("./graph/cycleDetector");
|
|
25
|
+
Object.defineProperty(exports, "detectCycles", { enumerable: true, get: function () { return cycleDetector_1.detectCycles; } });
|
|
26
|
+
var metrics_1 = require("./metrics");
|
|
27
|
+
Object.defineProperty(exports, "computeAllMetrics", { enumerable: true, get: function () { return metrics_1.computeAllMetrics; } });
|
|
28
|
+
var tsParser_1 = require("./parser/tsParser");
|
|
29
|
+
Object.defineProperty(exports, "parseProject", { enumerable: true, get: function () { return tsParser_1.parseProject; } });
|
|
30
|
+
Object.defineProperty(exports, "parseFile", { enumerable: true, get: function () { return tsParser_1.parseFile; } });
|
|
31
|
+
var fileScanner_1 = require("./parser/fileScanner");
|
|
32
|
+
Object.defineProperty(exports, "scanFiles", { enumerable: true, get: function () { return fileScanner_1.scanFiles; } });
|
|
33
|
+
var loadConfig_1 = require("./config/loadConfig");
|
|
34
|
+
Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return loadConfig_1.loadConfig; } });
|
|
35
|
+
var reporter_1 = require("./analysis/reporter");
|
|
36
|
+
Object.defineProperty(exports, "generateReport", { enumerable: true, get: function () { return reporter_1.generateReport; } });
|
|
37
|
+
Object.defineProperty(exports, "formatJsonOutput", { enumerable: true, get: function () { return reporter_1.formatJsonOutput; } });
|
|
38
|
+
// Configuration - Useful for customization
|
|
39
|
+
var schema_1 = require("./config/schema");
|
|
40
|
+
Object.defineProperty(exports, "defaultConfig", { enumerable: true, get: function () { return schema_1.defaultConfig; } });
|
|
41
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,uDAAuD;AACvD,yCAA2D;AAAlD,kGAAA,OAAO,OAAA;AAAE,4GAAA,iBAAiB,OAAA;AAGnC,uCAA0D;AAAjD,sGAAA,YAAY,OAAA;AAAE,sGAAA,YAAY,OAAA;AAGnC,qDAAqD;AAA5C,+GAAA,cAAc,OAAA;AAEvB,0EAA0E;AAC1E,2DAA0D;AAAjD,kHAAA,eAAe,OAAA;AACxB,uDAAqD;AAA5C,6GAAA,YAAY,OAAA;AACrB,qCAA8C;AAArC,4GAAA,iBAAiB,OAAA;AAC1B,8CAA4D;AAAnD,wGAAA,YAAY,OAAA;AAAE,qGAAA,SAAS,OAAA;AAChC,oDAAiD;AAAxC,wGAAA,SAAS,OAAA;AAClB,kDAAiD;AAAxC,wGAAA,UAAU,OAAA;AACnB,gDAAuE;AAA9D,0GAAA,cAAc,OAAA;AAAE,4GAAA,gBAAgB,OAAA;AAezC,2CAA2C;AAC3C,0CAAgD;AAAvC,uGAAA,aAAa,OAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeAbstractness = computeAbstractness;
|
|
4
|
+
function computeAbstractness(module) {
|
|
5
|
+
const abstractClasses = module.classes.filter((c) => c.isAbstract).length;
|
|
6
|
+
const abstractTypes = module.interfaces + abstractClasses;
|
|
7
|
+
const totalTypes = module.totalTypes;
|
|
8
|
+
return totalTypes === 0 ? 0 : abstractTypes / totalTypes;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=abstractness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abstractness.js","sourceRoot":"","sources":["../../src/metrics/abstractness.ts"],"names":[],"mappings":";;AAEA,kDAMC;AAND,SAAgB,mBAAmB,CAAC,MAAoB;IACtD,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC;IAC1E,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,GAAG,eAAe,CAAC;IAC1D,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IAErC,OAAO,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,GAAG,UAAU,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function computeDistance(abstractness: number, instability: number): number;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeDistance = computeDistance;
|
|
4
|
+
function computeDistance(abstractness, instability) {
|
|
5
|
+
return Math.abs(abstractness + instability - 1);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=distance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distance.js","sourceRoot":"","sources":["../../src/metrics/distance.ts"],"names":[],"mappings":";;AAAA,0CAEC;AAFD,SAAgB,eAAe,CAAC,YAAoB,EAAE,WAAmB;IACvE,OAAO,IAAI,CAAC,GAAG,CAAC,YAAY,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DependencyGraph } from '../graph/dependencyGraph';
|
|
2
|
+
import type { ParsedModule } from '../types/parser';
|
|
3
|
+
import type { ModuleMetrics } from '../types/graph';
|
|
4
|
+
export declare function computeAllMetrics(graph: DependencyGraph, modules: ParsedModule[], cycles: string[][]): ModuleMetrics[];
|