@nahisaho/musubix-core 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/bin/musubix.js +18 -0
- package/dist/__tests__/index.test.d.ts +2 -0
- package/dist/__tests__/index.test.d.ts.map +1 -0
- package/dist/__tests__/index.test.js +27 -0
- package/dist/__tests__/index.test.js.map +1 -0
- package/dist/auth/auth-manager.d.ts +320 -0
- package/dist/auth/auth-manager.d.ts.map +1 -0
- package/dist/auth/auth-manager.js +580 -0
- package/dist/auth/auth-manager.js.map +1 -0
- package/dist/cli/base.d.ts +58 -0
- package/dist/cli/base.d.ts.map +1 -0
- package/dist/cli/base.js +93 -0
- package/dist/cli/base.js.map +1 -0
- package/dist/cli/commands/help.d.ts +17 -0
- package/dist/cli/commands/help.d.ts.map +1 -0
- package/dist/cli/commands/help.js +228 -0
- package/dist/cli/commands/help.js.map +1 -0
- package/dist/cli/commands/index.d.ts +14 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +25 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/init.d.ts +38 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +258 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +9 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/codegen/coding-standards.d.ts +250 -0
- package/dist/codegen/coding-standards.d.ts.map +1 -0
- package/dist/codegen/coding-standards.js +976 -0
- package/dist/codegen/coding-standards.js.map +1 -0
- package/dist/codegen/coverage-reporter.d.ts +264 -0
- package/dist/codegen/coverage-reporter.d.ts.map +1 -0
- package/dist/codegen/coverage-reporter.js +697 -0
- package/dist/codegen/coverage-reporter.js.map +1 -0
- package/dist/codegen/dependency-analyzer.d.ts +271 -0
- package/dist/codegen/dependency-analyzer.d.ts.map +1 -0
- package/dist/codegen/dependency-analyzer.js +661 -0
- package/dist/codegen/dependency-analyzer.js.map +1 -0
- package/dist/codegen/generator.d.ts +275 -0
- package/dist/codegen/generator.d.ts.map +1 -0
- package/dist/codegen/generator.js +781 -0
- package/dist/codegen/generator.js.map +1 -0
- package/dist/codegen/index.d.ts +18 -0
- package/dist/codegen/index.d.ts.map +1 -0
- package/dist/codegen/index.js +27 -0
- package/dist/codegen/index.js.map +1 -0
- package/dist/codegen/integration-test-generator.d.ts +312 -0
- package/dist/codegen/integration-test-generator.d.ts.map +1 -0
- package/dist/codegen/integration-test-generator.js +765 -0
- package/dist/codegen/integration-test-generator.js.map +1 -0
- package/dist/codegen/pattern-conformance.d.ts +309 -0
- package/dist/codegen/pattern-conformance.d.ts.map +1 -0
- package/dist/codegen/pattern-conformance.js +590 -0
- package/dist/codegen/pattern-conformance.js.map +1 -0
- package/dist/codegen/quality-metrics.d.ts +235 -0
- package/dist/codegen/quality-metrics.d.ts.map +1 -0
- package/dist/codegen/quality-metrics.js +439 -0
- package/dist/codegen/quality-metrics.js.map +1 -0
- package/dist/codegen/security-scanner.d.ts +179 -0
- package/dist/codegen/security-scanner.d.ts.map +1 -0
- package/dist/codegen/security-scanner.js +495 -0
- package/dist/codegen/security-scanner.js.map +1 -0
- package/dist/codegen/static-analyzer.d.ts +188 -0
- package/dist/codegen/static-analyzer.d.ts.map +1 -0
- package/dist/codegen/static-analyzer.js +490 -0
- package/dist/codegen/static-analyzer.js.map +1 -0
- package/dist/codegen/unit-test-generator.d.ts +289 -0
- package/dist/codegen/unit-test-generator.d.ts.map +1 -0
- package/dist/codegen/unit-test-generator.js +634 -0
- package/dist/codegen/unit-test-generator.js.map +1 -0
- package/dist/design/adr-generator.d.ts +227 -0
- package/dist/design/adr-generator.d.ts.map +1 -0
- package/dist/design/adr-generator.js +423 -0
- package/dist/design/adr-generator.js.map +1 -0
- package/dist/design/c4-generator.d.ts +267 -0
- package/dist/design/c4-generator.d.ts.map +1 -0
- package/dist/design/c4-generator.js +453 -0
- package/dist/design/c4-generator.js.map +1 -0
- package/dist/design/framework-optimizer.d.ts +190 -0
- package/dist/design/framework-optimizer.d.ts.map +1 -0
- package/dist/design/framework-optimizer.js +589 -0
- package/dist/design/framework-optimizer.js.map +1 -0
- package/dist/design/index.d.ts +12 -0
- package/dist/design/index.d.ts.map +1 -0
- package/dist/design/index.js +13 -0
- package/dist/design/index.js.map +1 -0
- package/dist/design/pattern-detector.d.ts +270 -0
- package/dist/design/pattern-detector.d.ts.map +1 -0
- package/dist/design/pattern-detector.js +621 -0
- package/dist/design/pattern-detector.js.map +1 -0
- package/dist/design/solid-validator.d.ts +188 -0
- package/dist/design/solid-validator.d.ts.map +1 -0
- package/dist/design/solid-validator.js +579 -0
- package/dist/design/solid-validator.js.map +1 -0
- package/dist/error/data-persistence.d.ts +311 -0
- package/dist/error/data-persistence.d.ts.map +1 -0
- package/dist/error/data-persistence.js +586 -0
- package/dist/error/data-persistence.js.map +1 -0
- package/dist/error/graceful-degradation.d.ts +309 -0
- package/dist/error/graceful-degradation.d.ts.map +1 -0
- package/dist/error/graceful-degradation.js +510 -0
- package/dist/error/graceful-degradation.js.map +1 -0
- package/dist/error/index.d.ts +11 -0
- package/dist/error/index.d.ts.map +1 -0
- package/dist/error/index.js +19 -0
- package/dist/error/index.js.map +1 -0
- package/dist/explanation/explanation-generator.d.ts +228 -0
- package/dist/explanation/explanation-generator.d.ts.map +1 -0
- package/dist/explanation/explanation-generator.js +662 -0
- package/dist/explanation/explanation-generator.js.map +1 -0
- package/dist/explanation/index.d.ts +11 -0
- package/dist/explanation/index.d.ts.map +1 -0
- package/dist/explanation/index.js +19 -0
- package/dist/explanation/index.js.map +1 -0
- package/dist/explanation/reasoning-chain.d.ts +314 -0
- package/dist/explanation/reasoning-chain.d.ts.map +1 -0
- package/dist/explanation/reasoning-chain.js +414 -0
- package/dist/explanation/reasoning-chain.js.map +1 -0
- package/dist/explanation/visual-explanation.d.ts +315 -0
- package/dist/explanation/visual-explanation.d.ts.map +1 -0
- package/dist/explanation/visual-explanation.js +667 -0
- package/dist/explanation/visual-explanation.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/requirements/decomposer.d.ts +235 -0
- package/dist/requirements/decomposer.d.ts.map +1 -0
- package/dist/requirements/decomposer.js +587 -0
- package/dist/requirements/decomposer.js.map +1 -0
- package/dist/requirements/related-finder.d.ts +261 -0
- package/dist/requirements/related-finder.d.ts.map +1 -0
- package/dist/requirements/related-finder.js +629 -0
- package/dist/requirements/related-finder.js.map +1 -0
- package/dist/traceability/impact.d.ts +196 -0
- package/dist/traceability/impact.d.ts.map +1 -0
- package/dist/traceability/impact.js +438 -0
- package/dist/traceability/impact.js.map +1 -0
- package/dist/traceability/index.d.ts +9 -0
- package/dist/traceability/index.d.ts.map +1 -0
- package/dist/traceability/index.js +10 -0
- package/dist/traceability/index.js.map +1 -0
- package/dist/traceability/manager.d.ts +266 -0
- package/dist/traceability/manager.d.ts.map +1 -0
- package/dist/traceability/manager.js +412 -0
- package/dist/traceability/manager.js.map +1 -0
- package/dist/types/common.d.ts +294 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/common.js +15 -0
- package/dist/types/common.js.map +1 -0
- package/dist/types/ears.d.ts +158 -0
- package/dist/types/ears.d.ts.map +1 -0
- package/dist/types/ears.js +33 -0
- package/dist/types/ears.js.map +1 -0
- package/dist/types/errors.d.ts +176 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +55 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +10 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/data-protector.d.ts +122 -0
- package/dist/utils/data-protector.d.ts.map +1 -0
- package/dist/utils/data-protector.js +275 -0
- package/dist/utils/data-protector.js.map +1 -0
- package/dist/utils/error-handler.d.ts +101 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +324 -0
- package/dist/utils/error-handler.js.map +1 -0
- package/dist/utils/i18n-manager.d.ts +259 -0
- package/dist/utils/i18n-manager.d.ts.map +1 -0
- package/dist/utils/i18n-manager.js +554 -0
- package/dist/utils/i18n-manager.js.map +1 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +10 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +120 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +237 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/performance-profiler.d.ts +251 -0
- package/dist/utils/performance-profiler.d.ts.map +1 -0
- package/dist/utils/performance-profiler.js +458 -0
- package/dist/utils/performance-profiler.js.map +1 -0
- package/dist/utils/scalability-optimizer.d.ts +294 -0
- package/dist/utils/scalability-optimizer.d.ts.map +1 -0
- package/dist/utils/scalability-optimizer.js +606 -0
- package/dist/utils/scalability-optimizer.js.map +1 -0
- package/dist/utils/structured-logger.d.ts +294 -0
- package/dist/utils/structured-logger.d.ts.map +1 -0
- package/dist/utils/structured-logger.js +630 -0
- package/dist/utils/structured-logger.js.map +1 -0
- package/dist/utils/version-compatibility.d.ts +217 -0
- package/dist/utils/version-compatibility.d.ts.map +1 -0
- package/dist/utils/version-compatibility.js +443 -0
- package/dist/utils/version-compatibility.js.map +1 -0
- package/dist/validators/ears-validator.d.ts +182 -0
- package/dist/validators/ears-validator.d.ts.map +1 -0
- package/dist/validators/ears-validator.js +357 -0
- package/dist/validators/ears-validator.js.map +1 -0
- package/dist/validators/index.d.ts +8 -0
- package/dist/validators/index.d.ts.map +1 -0
- package/dist/validators/index.js +9 -0
- package/dist/validators/index.js.map +1 -0
- package/dist/version.d.ts +8 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +8 -0
- package/dist/version.js.map +1 -0
- package/package.json +100 -0
|
@@ -0,0 +1,661 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Analyzes code dependencies and generates dependency graphs
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
* @module codegen/dependency-analyzer
|
|
8
|
+
*
|
|
9
|
+
* @see REQ-COD-004 - Dependency Analysis
|
|
10
|
+
* @see Article VI - Implementation Standards
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Default configuration
|
|
14
|
+
*/
|
|
15
|
+
export const DEFAULT_ANALYZER_CONFIG = {
|
|
16
|
+
includeDevDependencies: false,
|
|
17
|
+
includeTypeOnly: true,
|
|
18
|
+
maxDependenciesPerModule: 20,
|
|
19
|
+
maxCouplingFactor: 0.5,
|
|
20
|
+
detectCycles: true,
|
|
21
|
+
detectUnused: true,
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Built-in Node.js modules
|
|
25
|
+
*/
|
|
26
|
+
const BUILTIN_MODULES = new Set([
|
|
27
|
+
'assert', 'buffer', 'child_process', 'cluster', 'console', 'constants',
|
|
28
|
+
'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'https',
|
|
29
|
+
'module', 'net', 'os', 'path', 'perf_hooks', 'process', 'punycode',
|
|
30
|
+
'querystring', 'readline', 'repl', 'stream', 'string_decoder', 'sys',
|
|
31
|
+
'timers', 'tls', 'tty', 'url', 'util', 'v8', 'vm', 'wasi', 'worker_threads', 'zlib',
|
|
32
|
+
'node:assert', 'node:buffer', 'node:child_process', 'node:cluster',
|
|
33
|
+
'node:console', 'node:constants', 'node:crypto', 'node:dgram', 'node:dns',
|
|
34
|
+
'node:domain', 'node:events', 'node:fs', 'node:http', 'node:https',
|
|
35
|
+
'node:module', 'node:net', 'node:os', 'node:path', 'node:perf_hooks',
|
|
36
|
+
'node:process', 'node:punycode', 'node:querystring', 'node:readline',
|
|
37
|
+
'node:repl', 'node:stream', 'node:string_decoder', 'node:sys',
|
|
38
|
+
'node:timers', 'node:tls', 'node:tty', 'node:url', 'node:util', 'node:v8',
|
|
39
|
+
'node:vm', 'node:wasi', 'node:worker_threads', 'node:zlib',
|
|
40
|
+
]);
|
|
41
|
+
/**
|
|
42
|
+
* Dependency Analyzer
|
|
43
|
+
*/
|
|
44
|
+
export class DependencyAnalyzer {
|
|
45
|
+
config;
|
|
46
|
+
constructor(config) {
|
|
47
|
+
this.config = { ...DEFAULT_ANALYZER_CONFIG, ...config };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Analyze dependencies in code
|
|
51
|
+
*/
|
|
52
|
+
analyze(code, filePath) {
|
|
53
|
+
const dependencies = [];
|
|
54
|
+
// Parse import statements
|
|
55
|
+
dependencies.push(...this.parseImports(code, filePath));
|
|
56
|
+
// Parse require statements
|
|
57
|
+
dependencies.push(...this.parseRequires(code, filePath));
|
|
58
|
+
// Parse class relationships
|
|
59
|
+
dependencies.push(...this.parseClassRelationships(code, filePath));
|
|
60
|
+
// Parse type references
|
|
61
|
+
if (this.config.includeTypeOnly) {
|
|
62
|
+
dependencies.push(...this.parseTypeReferences(code, filePath));
|
|
63
|
+
}
|
|
64
|
+
return dependencies;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Analyze multiple files and build graph
|
|
68
|
+
*/
|
|
69
|
+
analyzeProject(files) {
|
|
70
|
+
const graph = this.buildGraph(files);
|
|
71
|
+
const metrics = this.calculateMetrics(graph);
|
|
72
|
+
const issues = this.detectIssues(graph, metrics);
|
|
73
|
+
const recommendations = this.generateRecommendations(graph, metrics, issues);
|
|
74
|
+
return {
|
|
75
|
+
graph,
|
|
76
|
+
metrics,
|
|
77
|
+
issues,
|
|
78
|
+
recommendations,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Parse ES6 import statements
|
|
83
|
+
*/
|
|
84
|
+
parseImports(code, source) {
|
|
85
|
+
const dependencies = [];
|
|
86
|
+
// Standard imports: import { x } from 'module'
|
|
87
|
+
const importRegex = /import\s+(?:(type)\s+)?(?:(\*\s+as\s+\w+)|(\w+)(?:\s*,\s*\{([^}]*)\})?|\{([^}]*)\})\s+from\s+['"]([^'"]+)['"]/g;
|
|
88
|
+
let match;
|
|
89
|
+
while ((match = importRegex.exec(code)) !== null) {
|
|
90
|
+
const isTypeOnly = match[1] === 'type';
|
|
91
|
+
const isNamespace = !!match[2];
|
|
92
|
+
const defaultImport = match[3];
|
|
93
|
+
const namedWithDefault = match[4];
|
|
94
|
+
const namedImports = match[5];
|
|
95
|
+
const target = match[6];
|
|
96
|
+
const line = code.substring(0, match.index).split('\n').length;
|
|
97
|
+
const specifiers = [];
|
|
98
|
+
if (defaultImport)
|
|
99
|
+
specifiers.push(defaultImport);
|
|
100
|
+
if (namedWithDefault) {
|
|
101
|
+
specifiers.push(...namedWithDefault.split(',').map((s) => s.trim()));
|
|
102
|
+
}
|
|
103
|
+
if (namedImports) {
|
|
104
|
+
specifiers.push(...namedImports.split(',').map((s) => s.trim().split(' as ')[0]));
|
|
105
|
+
}
|
|
106
|
+
dependencies.push({
|
|
107
|
+
source,
|
|
108
|
+
target,
|
|
109
|
+
type: 'import',
|
|
110
|
+
strength: this.calculateStrength(target),
|
|
111
|
+
moduleType: this.getModuleType(target),
|
|
112
|
+
specifiers: specifiers.length > 0 ? specifiers : undefined,
|
|
113
|
+
isDefault: !!defaultImport,
|
|
114
|
+
isNamespace,
|
|
115
|
+
isTypeOnly,
|
|
116
|
+
location: { line, column: 0 },
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
// Side-effect imports: import 'module'
|
|
120
|
+
const sideEffectRegex = /import\s+['"]([^'"]+)['"]/g;
|
|
121
|
+
while ((match = sideEffectRegex.exec(code)) !== null) {
|
|
122
|
+
const target = match[1];
|
|
123
|
+
const line = code.substring(0, match.index).split('\n').length;
|
|
124
|
+
// Skip if already captured
|
|
125
|
+
if (dependencies.some((d) => d.target === target))
|
|
126
|
+
continue;
|
|
127
|
+
dependencies.push({
|
|
128
|
+
source,
|
|
129
|
+
target,
|
|
130
|
+
type: 'import',
|
|
131
|
+
strength: 'weak',
|
|
132
|
+
moduleType: this.getModuleType(target),
|
|
133
|
+
location: { line, column: 0 },
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
return dependencies;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Parse CommonJS require statements
|
|
140
|
+
*/
|
|
141
|
+
parseRequires(code, source) {
|
|
142
|
+
const dependencies = [];
|
|
143
|
+
const requireRegex = /(?:const|let|var)\s+(?:(\w+)|{([^}]+)})\s*=\s*require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
144
|
+
let match;
|
|
145
|
+
while ((match = requireRegex.exec(code)) !== null) {
|
|
146
|
+
const defaultRequire = match[1];
|
|
147
|
+
const destructured = match[2];
|
|
148
|
+
const target = match[3];
|
|
149
|
+
const line = code.substring(0, match.index).split('\n').length;
|
|
150
|
+
const specifiers = [];
|
|
151
|
+
if (defaultRequire)
|
|
152
|
+
specifiers.push(defaultRequire);
|
|
153
|
+
if (destructured) {
|
|
154
|
+
specifiers.push(...destructured.split(',').map((s) => s.trim().split(':')[0]));
|
|
155
|
+
}
|
|
156
|
+
dependencies.push({
|
|
157
|
+
source,
|
|
158
|
+
target,
|
|
159
|
+
type: 'require',
|
|
160
|
+
strength: this.calculateStrength(target),
|
|
161
|
+
moduleType: this.getModuleType(target),
|
|
162
|
+
specifiers: specifiers.length > 0 ? specifiers : undefined,
|
|
163
|
+
isDefault: !!defaultRequire,
|
|
164
|
+
location: { line, column: 0 },
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
// Dynamic require
|
|
168
|
+
const dynamicRegex = /require\s*\(\s*(['"`])([^'"]+)\1\s*\)/g;
|
|
169
|
+
while ((match = dynamicRegex.exec(code)) !== null) {
|
|
170
|
+
const target = match[2];
|
|
171
|
+
if (dependencies.some((d) => d.target === target))
|
|
172
|
+
continue;
|
|
173
|
+
const line = code.substring(0, match.index).split('\n').length;
|
|
174
|
+
dependencies.push({
|
|
175
|
+
source,
|
|
176
|
+
target,
|
|
177
|
+
type: 'dynamic',
|
|
178
|
+
strength: 'weak',
|
|
179
|
+
moduleType: this.getModuleType(target),
|
|
180
|
+
location: { line, column: 0 },
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
return dependencies;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Parse class relationships (extends, implements)
|
|
187
|
+
*/
|
|
188
|
+
parseClassRelationships(code, source) {
|
|
189
|
+
const dependencies = [];
|
|
190
|
+
const classRegex = /class\s+\w+(?:\s+extends\s+(\w+))?(?:\s+implements\s+([^{]+))?\s*\{/g;
|
|
191
|
+
let match;
|
|
192
|
+
while ((match = classRegex.exec(code)) !== null) {
|
|
193
|
+
const extendsClass = match[1];
|
|
194
|
+
const implementsList = match[2];
|
|
195
|
+
const line = code.substring(0, match.index).split('\n').length;
|
|
196
|
+
if (extendsClass) {
|
|
197
|
+
dependencies.push({
|
|
198
|
+
source,
|
|
199
|
+
target: extendsClass,
|
|
200
|
+
type: 'extends',
|
|
201
|
+
strength: 'strong',
|
|
202
|
+
moduleType: 'internal',
|
|
203
|
+
location: { line, column: 0 },
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
if (implementsList) {
|
|
207
|
+
for (const impl of implementsList.split(',')) {
|
|
208
|
+
dependencies.push({
|
|
209
|
+
source,
|
|
210
|
+
target: impl.trim(),
|
|
211
|
+
type: 'implements',
|
|
212
|
+
strength: 'strong',
|
|
213
|
+
moduleType: 'internal',
|
|
214
|
+
location: { line, column: 0 },
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return dependencies;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Parse type references
|
|
223
|
+
*/
|
|
224
|
+
parseTypeReferences(code, source) {
|
|
225
|
+
const dependencies = [];
|
|
226
|
+
// Type annotations: : TypeName
|
|
227
|
+
const typeRegex = /:\s*(\w+)(?:<[^>]+>)?(?:\[\])?/g;
|
|
228
|
+
let match;
|
|
229
|
+
const foundTypes = new Set();
|
|
230
|
+
while ((match = typeRegex.exec(code)) !== null) {
|
|
231
|
+
const typeName = match[1];
|
|
232
|
+
// Skip primitives and common types
|
|
233
|
+
if (this.isPrimitive(typeName))
|
|
234
|
+
continue;
|
|
235
|
+
if (foundTypes.has(typeName))
|
|
236
|
+
continue;
|
|
237
|
+
foundTypes.add(typeName);
|
|
238
|
+
const line = code.substring(0, match.index).split('\n').length;
|
|
239
|
+
dependencies.push({
|
|
240
|
+
source,
|
|
241
|
+
target: typeName,
|
|
242
|
+
type: 'type-reference',
|
|
243
|
+
strength: 'weak',
|
|
244
|
+
moduleType: 'internal',
|
|
245
|
+
isTypeOnly: true,
|
|
246
|
+
location: { line, column: 0 },
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
return dependencies;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Build dependency graph
|
|
253
|
+
*/
|
|
254
|
+
buildGraph(files) {
|
|
255
|
+
const modules = new Map();
|
|
256
|
+
const allDependencies = [];
|
|
257
|
+
// First pass: analyze all files
|
|
258
|
+
for (const file of files) {
|
|
259
|
+
const deps = this.analyze(file.content, file.path);
|
|
260
|
+
const exports = this.parseExports(file.content);
|
|
261
|
+
modules.set(file.path, {
|
|
262
|
+
path: file.path,
|
|
263
|
+
name: this.getModuleName(file.path),
|
|
264
|
+
type: 'internal',
|
|
265
|
+
exports,
|
|
266
|
+
dependencies: deps,
|
|
267
|
+
dependents: [],
|
|
268
|
+
});
|
|
269
|
+
allDependencies.push(...deps);
|
|
270
|
+
}
|
|
271
|
+
// Second pass: build dependents
|
|
272
|
+
for (const [path, module] of modules) {
|
|
273
|
+
for (const dep of module.dependencies) {
|
|
274
|
+
const resolvedTarget = this.resolveModule(dep.target, path);
|
|
275
|
+
const targetModule = modules.get(resolvedTarget);
|
|
276
|
+
if (targetModule) {
|
|
277
|
+
targetModule.dependents.push(path);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// Find roots and leaves
|
|
282
|
+
const roots = [...modules.entries()]
|
|
283
|
+
.filter(([_, m]) => m.dependents.length === 0)
|
|
284
|
+
.map(([p]) => p);
|
|
285
|
+
const leaves = [...modules.entries()]
|
|
286
|
+
.filter(([_, m]) => m.dependencies.filter((d) => d.moduleType === 'internal').length === 0)
|
|
287
|
+
.map(([p]) => p);
|
|
288
|
+
// Detect cycles
|
|
289
|
+
const cycles = this.config.detectCycles ? this.detectCycles(modules) : [];
|
|
290
|
+
return {
|
|
291
|
+
modules,
|
|
292
|
+
dependencies: allDependencies,
|
|
293
|
+
roots,
|
|
294
|
+
leaves,
|
|
295
|
+
cycles,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Parse exports
|
|
300
|
+
*/
|
|
301
|
+
parseExports(code) {
|
|
302
|
+
const exports = [];
|
|
303
|
+
// Named exports
|
|
304
|
+
const namedExportRegex = /export\s+(?:(async\s+)?function|class|const|let|var|interface|type|enum)\s+(\w+)/g;
|
|
305
|
+
let match;
|
|
306
|
+
while ((match = namedExportRegex.exec(code)) !== null) {
|
|
307
|
+
const name = match[2];
|
|
308
|
+
const line = code.substring(0, match.index).split('\n').length;
|
|
309
|
+
const isFunction = match[0].includes('function');
|
|
310
|
+
const isClass = match[0].includes('class');
|
|
311
|
+
const isInterface = match[0].includes('interface');
|
|
312
|
+
const isType = match[0].includes('type');
|
|
313
|
+
const isEnum = match[0].includes('enum');
|
|
314
|
+
exports.push({
|
|
315
|
+
name,
|
|
316
|
+
isDefault: false,
|
|
317
|
+
type: isFunction ? 'function' :
|
|
318
|
+
isClass ? 'class' :
|
|
319
|
+
isInterface ? 'interface' :
|
|
320
|
+
isType ? 'type' :
|
|
321
|
+
isEnum ? 'enum' : 'variable',
|
|
322
|
+
line,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
// Default export
|
|
326
|
+
const defaultExportRegex = /export\s+default\s+(?:(async\s+)?function|class)?\s*(\w+)?/g;
|
|
327
|
+
while ((match = defaultExportRegex.exec(code)) !== null) {
|
|
328
|
+
const name = match[2] || 'default';
|
|
329
|
+
const line = code.substring(0, match.index).split('\n').length;
|
|
330
|
+
exports.push({
|
|
331
|
+
name,
|
|
332
|
+
isDefault: true,
|
|
333
|
+
type: match[0].includes('class') ? 'class' :
|
|
334
|
+
match[0].includes('function') ? 'function' : 'variable',
|
|
335
|
+
line,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
return exports;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Calculate metrics
|
|
342
|
+
*/
|
|
343
|
+
calculateMetrics(graph) {
|
|
344
|
+
const totalModules = graph.modules.size;
|
|
345
|
+
const internalDeps = graph.dependencies.filter((d) => d.moduleType === 'internal');
|
|
346
|
+
const externalDeps = graph.dependencies.filter((d) => d.moduleType === 'external');
|
|
347
|
+
let maxDeps = 0;
|
|
348
|
+
let maxDepsModule;
|
|
349
|
+
for (const [path, module] of graph.modules) {
|
|
350
|
+
const depCount = module.dependencies.length;
|
|
351
|
+
if (depCount > maxDeps) {
|
|
352
|
+
maxDeps = depCount;
|
|
353
|
+
maxDepsModule = path;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
const avgDeps = totalModules > 0
|
|
357
|
+
? graph.dependencies.length / totalModules
|
|
358
|
+
: 0;
|
|
359
|
+
// Coupling factor: actual dependencies / maximum possible dependencies
|
|
360
|
+
const maxPossibleDeps = totalModules * (totalModules - 1);
|
|
361
|
+
const couplingFactor = maxPossibleDeps > 0
|
|
362
|
+
? internalDeps.length / maxPossibleDeps
|
|
363
|
+
: 0;
|
|
364
|
+
// Graph complexity: edges - nodes + 2 * connected components
|
|
365
|
+
const edges = internalDeps.length;
|
|
366
|
+
const nodes = totalModules;
|
|
367
|
+
const connectedComponents = this.countConnectedComponents(graph);
|
|
368
|
+
const graphComplexity = Math.max(0, edges - nodes + 2 * connectedComponents);
|
|
369
|
+
return {
|
|
370
|
+
totalModules,
|
|
371
|
+
totalDependencies: graph.dependencies.length,
|
|
372
|
+
internalDependencies: internalDeps.length,
|
|
373
|
+
externalDependencies: externalDeps.length,
|
|
374
|
+
avgDependenciesPerModule: Math.round(avgDeps * 100) / 100,
|
|
375
|
+
maxDependencies: maxDeps,
|
|
376
|
+
maxDependenciesModule: maxDepsModule,
|
|
377
|
+
couplingFactor: Math.round(couplingFactor * 1000) / 1000,
|
|
378
|
+
graphComplexity,
|
|
379
|
+
cycleCount: graph.cycles.length,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Count connected components
|
|
384
|
+
*/
|
|
385
|
+
countConnectedComponents(graph) {
|
|
386
|
+
const visited = new Set();
|
|
387
|
+
let components = 0;
|
|
388
|
+
for (const path of graph.modules.keys()) {
|
|
389
|
+
if (!visited.has(path)) {
|
|
390
|
+
this.dfs(path, graph, visited);
|
|
391
|
+
components++;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return components;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* DFS for connected components
|
|
398
|
+
*/
|
|
399
|
+
dfs(start, graph, visited) {
|
|
400
|
+
const stack = [start];
|
|
401
|
+
while (stack.length > 0) {
|
|
402
|
+
const current = stack.pop();
|
|
403
|
+
if (visited.has(current))
|
|
404
|
+
continue;
|
|
405
|
+
visited.add(current);
|
|
406
|
+
const module = graph.modules.get(current);
|
|
407
|
+
if (module) {
|
|
408
|
+
for (const dep of module.dependencies) {
|
|
409
|
+
const resolved = this.resolveModule(dep.target, current);
|
|
410
|
+
if (graph.modules.has(resolved) && !visited.has(resolved)) {
|
|
411
|
+
stack.push(resolved);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
for (const dependent of module.dependents) {
|
|
415
|
+
if (!visited.has(dependent)) {
|
|
416
|
+
stack.push(dependent);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Detect cycles using DFS
|
|
424
|
+
*/
|
|
425
|
+
detectCycles(modules) {
|
|
426
|
+
const cycles = [];
|
|
427
|
+
const visited = new Set();
|
|
428
|
+
const recursionStack = new Set();
|
|
429
|
+
const path = [];
|
|
430
|
+
const dfs = (node) => {
|
|
431
|
+
if (recursionStack.has(node)) {
|
|
432
|
+
// Found cycle
|
|
433
|
+
const cycleStart = path.indexOf(node);
|
|
434
|
+
const cycle = path.slice(cycleStart);
|
|
435
|
+
cycle.push(node);
|
|
436
|
+
cycles.push(cycle);
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
if (visited.has(node))
|
|
440
|
+
return;
|
|
441
|
+
visited.add(node);
|
|
442
|
+
recursionStack.add(node);
|
|
443
|
+
path.push(node);
|
|
444
|
+
const module = modules.get(node);
|
|
445
|
+
if (module) {
|
|
446
|
+
for (const dep of module.dependencies) {
|
|
447
|
+
if (dep.moduleType === 'internal') {
|
|
448
|
+
const resolved = this.resolveModule(dep.target, node);
|
|
449
|
+
if (modules.has(resolved)) {
|
|
450
|
+
dfs(resolved);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
path.pop();
|
|
456
|
+
recursionStack.delete(node);
|
|
457
|
+
};
|
|
458
|
+
for (const path of modules.keys()) {
|
|
459
|
+
visited.clear();
|
|
460
|
+
recursionStack.clear();
|
|
461
|
+
dfs(path);
|
|
462
|
+
}
|
|
463
|
+
return cycles;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Detect issues
|
|
467
|
+
*/
|
|
468
|
+
detectIssues(graph, metrics) {
|
|
469
|
+
const issues = [];
|
|
470
|
+
// Circular dependencies
|
|
471
|
+
for (const cycle of graph.cycles) {
|
|
472
|
+
issues.push({
|
|
473
|
+
id: `cycle-${cycle.join('-')}`,
|
|
474
|
+
severity: 'error',
|
|
475
|
+
type: 'cycle',
|
|
476
|
+
message: `Circular dependency detected: ${cycle.join(' → ')}`,
|
|
477
|
+
modules: cycle,
|
|
478
|
+
suggestion: 'Consider extracting shared code into a separate module',
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
// High coupling modules
|
|
482
|
+
for (const [path, module] of graph.modules) {
|
|
483
|
+
if (module.dependencies.length > this.config.maxDependenciesPerModule) {
|
|
484
|
+
issues.push({
|
|
485
|
+
id: `high-coupling-${path}`,
|
|
486
|
+
severity: 'warning',
|
|
487
|
+
type: 'coupling',
|
|
488
|
+
message: `Module ${path} has too many dependencies (${module.dependencies.length} > ${this.config.maxDependenciesPerModule})`,
|
|
489
|
+
modules: [path],
|
|
490
|
+
suggestion: 'Consider splitting this module or using dependency injection',
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
// High overall coupling
|
|
495
|
+
if (metrics.couplingFactor > this.config.maxCouplingFactor) {
|
|
496
|
+
issues.push({
|
|
497
|
+
id: 'high-overall-coupling',
|
|
498
|
+
severity: 'warning',
|
|
499
|
+
type: 'coupling',
|
|
500
|
+
message: `Overall coupling factor is too high (${metrics.couplingFactor} > ${this.config.maxCouplingFactor})`,
|
|
501
|
+
modules: [],
|
|
502
|
+
suggestion: 'Consider refactoring to reduce inter-module dependencies',
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
// Unused exports (if detectUnused is enabled)
|
|
506
|
+
if (this.config.detectUnused) {
|
|
507
|
+
for (const [path, module] of graph.modules) {
|
|
508
|
+
for (const exp of module.exports) {
|
|
509
|
+
const isUsed = [...graph.modules.values()].some((m) => m.dependencies.some((d) => d.specifiers?.includes(exp.name)));
|
|
510
|
+
if (!isUsed && !exp.isDefault && module.dependents.length === 0) {
|
|
511
|
+
issues.push({
|
|
512
|
+
id: `unused-export-${path}-${exp.name}`,
|
|
513
|
+
severity: 'info',
|
|
514
|
+
type: 'unused',
|
|
515
|
+
message: `Export '${exp.name}' in ${path} appears to be unused`,
|
|
516
|
+
modules: [path],
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return issues;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Generate recommendations
|
|
526
|
+
*/
|
|
527
|
+
generateRecommendations(graph, metrics, issues) {
|
|
528
|
+
const recommendations = [];
|
|
529
|
+
// Based on issues
|
|
530
|
+
if (issues.some((i) => i.type === 'cycle')) {
|
|
531
|
+
recommendations.push('Break circular dependencies by extracting shared interfaces');
|
|
532
|
+
}
|
|
533
|
+
if (issues.some((i) => i.type === 'coupling')) {
|
|
534
|
+
recommendations.push('Use dependency injection to reduce coupling');
|
|
535
|
+
recommendations.push('Consider applying the SOLID principles');
|
|
536
|
+
}
|
|
537
|
+
// Based on metrics
|
|
538
|
+
if (metrics.avgDependenciesPerModule > 10) {
|
|
539
|
+
recommendations.push('Average dependencies per module is high - consider splitting modules');
|
|
540
|
+
}
|
|
541
|
+
if (graph.roots.length === 0 && graph.modules.size > 0) {
|
|
542
|
+
recommendations.push('No root modules found - all modules have dependents');
|
|
543
|
+
}
|
|
544
|
+
if (metrics.graphComplexity > metrics.totalModules) {
|
|
545
|
+
recommendations.push('Graph complexity is high - simplify module relationships');
|
|
546
|
+
}
|
|
547
|
+
return recommendations;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Get module type
|
|
551
|
+
*/
|
|
552
|
+
getModuleType(target) {
|
|
553
|
+
if (BUILTIN_MODULES.has(target))
|
|
554
|
+
return 'builtin';
|
|
555
|
+
if (target.startsWith('.') || target.startsWith('/'))
|
|
556
|
+
return 'relative';
|
|
557
|
+
if (target.startsWith('@') || /^[a-z]/i.test(target))
|
|
558
|
+
return 'external';
|
|
559
|
+
return 'internal';
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Calculate dependency strength
|
|
563
|
+
*/
|
|
564
|
+
calculateStrength(target) {
|
|
565
|
+
const type = this.getModuleType(target);
|
|
566
|
+
if (type === 'external')
|
|
567
|
+
return 'strong';
|
|
568
|
+
if (type === 'relative')
|
|
569
|
+
return 'medium';
|
|
570
|
+
return 'weak';
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Check if type is primitive
|
|
574
|
+
*/
|
|
575
|
+
isPrimitive(type) {
|
|
576
|
+
return ['string', 'number', 'boolean', 'void', 'null', 'undefined',
|
|
577
|
+
'any', 'unknown', 'never', 'object', 'symbol', 'bigint',
|
|
578
|
+
'Array', 'Object', 'Function', 'Promise', 'Map', 'Set',
|
|
579
|
+
'Record', 'Partial', 'Required', 'Readonly', 'Pick', 'Omit'].includes(type);
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Get module name from path
|
|
583
|
+
*/
|
|
584
|
+
getModuleName(path) {
|
|
585
|
+
const parts = path.split('/');
|
|
586
|
+
const filename = parts[parts.length - 1];
|
|
587
|
+
return filename.replace(/\.(ts|js|tsx|jsx)$/, '');
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Resolve module path
|
|
591
|
+
*/
|
|
592
|
+
resolveModule(target, source) {
|
|
593
|
+
// Handle aliases
|
|
594
|
+
if (this.config.aliases) {
|
|
595
|
+
for (const [alias, path] of Object.entries(this.config.aliases)) {
|
|
596
|
+
if (target.startsWith(alias)) {
|
|
597
|
+
return target.replace(alias, path);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
// Handle relative paths
|
|
602
|
+
if (target.startsWith('.')) {
|
|
603
|
+
const sourceParts = source.split('/');
|
|
604
|
+
sourceParts.pop();
|
|
605
|
+
const targetParts = target.split('/');
|
|
606
|
+
for (const part of targetParts) {
|
|
607
|
+
if (part === '..') {
|
|
608
|
+
sourceParts.pop();
|
|
609
|
+
}
|
|
610
|
+
else if (part !== '.') {
|
|
611
|
+
sourceParts.push(part);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return sourceParts.join('/');
|
|
615
|
+
}
|
|
616
|
+
return target;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Generate dependency report
|
|
620
|
+
*/
|
|
621
|
+
generateReport(result) {
|
|
622
|
+
const lines = [];
|
|
623
|
+
lines.push('# Dependency Analysis Report');
|
|
624
|
+
lines.push('');
|
|
625
|
+
lines.push('## Metrics');
|
|
626
|
+
lines.push(`- Total Modules: ${result.metrics.totalModules}`);
|
|
627
|
+
lines.push(`- Total Dependencies: ${result.metrics.totalDependencies}`);
|
|
628
|
+
lines.push(`- Internal Dependencies: ${result.metrics.internalDependencies}`);
|
|
629
|
+
lines.push(`- External Dependencies: ${result.metrics.externalDependencies}`);
|
|
630
|
+
lines.push(`- Avg Dependencies/Module: ${result.metrics.avgDependenciesPerModule}`);
|
|
631
|
+
lines.push(`- Coupling Factor: ${result.metrics.couplingFactor}`);
|
|
632
|
+
lines.push(`- Cycles: ${result.metrics.cycleCount}`);
|
|
633
|
+
lines.push('');
|
|
634
|
+
if (result.issues.length > 0) {
|
|
635
|
+
lines.push('## Issues');
|
|
636
|
+
for (const issue of result.issues) {
|
|
637
|
+
const icon = issue.severity === 'error' ? '❌' :
|
|
638
|
+
issue.severity === 'warning' ? '⚠️' : 'ℹ️';
|
|
639
|
+
lines.push(`${icon} [${issue.type}] ${issue.message}`);
|
|
640
|
+
if (issue.suggestion) {
|
|
641
|
+
lines.push(` → ${issue.suggestion}`);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
lines.push('');
|
|
645
|
+
}
|
|
646
|
+
if (result.recommendations.length > 0) {
|
|
647
|
+
lines.push('## Recommendations');
|
|
648
|
+
for (const rec of result.recommendations) {
|
|
649
|
+
lines.push(`- ${rec}`);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return lines.join('\n');
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Create dependency analyzer instance
|
|
657
|
+
*/
|
|
658
|
+
export function createDependencyAnalyzer(config) {
|
|
659
|
+
return new DependencyAnalyzer(config);
|
|
660
|
+
}
|
|
661
|
+
//# sourceMappingURL=dependency-analyzer.js.map
|