tech-debt-score 0.1.5 → 0.1.7

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.
Files changed (48) hide show
  1. package/dist/adapters/input/FileSystemReader.d.ts.map +1 -1
  2. package/dist/adapters/input/FileSystemReader.js +4 -5
  3. package/dist/adapters/input/FileSystemReader.js.map +1 -1
  4. package/dist/adapters/output/TerminalReporter.d.ts.map +1 -1
  5. package/dist/adapters/output/TerminalReporter.js +8 -0
  6. package/dist/adapters/output/TerminalReporter.js.map +1 -1
  7. package/dist/application/config/AnalysisConfig.d.ts.map +1 -1
  8. package/dist/application/config/AnalysisConfig.js +10 -2
  9. package/dist/application/config/AnalysisConfig.js.map +1 -1
  10. package/dist/application/services/AnalysisService.d.ts.map +1 -1
  11. package/dist/application/services/AnalysisService.js +29 -11
  12. package/dist/application/services/AnalysisService.js.map +1 -1
  13. package/dist/cli/commands/analyze.d.ts.map +1 -1
  14. package/dist/cli/commands/analyze.js +8 -7
  15. package/dist/cli/commands/analyze.js.map +1 -1
  16. package/package.json +8 -2
  17. package/DEVELOPMENT.md +0 -147
  18. package/SETUP_COMPLETE.md +0 -188
  19. package/TECHNICAL_DESIGN.md +0 -563
  20. package/src/adapters/input/FileSystemReader.ts +0 -47
  21. package/src/adapters/input/TypeScriptParser.ts +0 -367
  22. package/src/adapters/output/JsonExporter.ts +0 -48
  23. package/src/adapters/output/TerminalReporter.ts +0 -94
  24. package/src/application/config/AnalysisConfig.ts +0 -58
  25. package/src/application/ports/IFileReader.ts +0 -36
  26. package/src/application/ports/IParser.ts +0 -40
  27. package/src/application/ports/IReporter.ts +0 -26
  28. package/src/application/services/AnalysisService.ts +0 -218
  29. package/src/application/services/DependencyAnalyzer.ts +0 -158
  30. package/src/application/services/DuplicationDetector.ts +0 -207
  31. package/src/cli/commands/analyze.ts +0 -77
  32. package/src/cli/index.ts +0 -81
  33. package/src/domain/entities/Finding.ts +0 -79
  34. package/src/domain/entities/Metric.ts +0 -70
  35. package/src/domain/entities/Rule.ts +0 -49
  36. package/src/domain/entities/Score.ts +0 -94
  37. package/src/domain/index.ts +0 -15
  38. package/src/domain/rules/CircularDependencyRule.ts +0 -65
  39. package/src/domain/rules/ComplexityRule.ts +0 -88
  40. package/src/domain/rules/DuplicationRule.ts +0 -70
  41. package/src/domain/rules/SizeRule.ts +0 -98
  42. package/src/domain/rules/TypeSafetyRule.ts +0 -63
  43. package/src/index.ts +0 -0
  44. package/src/shared/types.ts +0 -18
  45. package/tests/application/index.test.ts +0 -12
  46. package/tests/domain/index.test.ts +0 -14
  47. package/tests/e2e/index.test.ts +0 -13
  48. package/tsconfig.json +0 -31
@@ -1,218 +0,0 @@
1
- /**
2
- * Application Service: Analysis Service
3
- * Orchestrates the entire code analysis workflow
4
- */
5
-
6
- import type { IFileReader } from '../ports/IFileReader.js';
7
- import type { IParser } from '../ports/IParser.js';
8
- import type { IReporter, AnalysisReport } from '../ports/IReporter.js';
9
- import type { AnalysisConfig } from '../config/AnalysisConfig.js';
10
- import type { Rule } from '../../domain/entities/Rule.js';
11
- import type { Metric } from '../../domain/entities/Metric.js';
12
- import type { Finding } from '../../domain/entities/Finding.js';
13
- import type { CategoryScore, Score } from '../../domain/entities/Score.js';
14
- import { ScoreCalculator } from '../../domain/entities/Score.js';
15
-
16
- export class AnalysisService {
17
- constructor(
18
- private readonly fileReader: IFileReader,
19
- private readonly parser: IParser,
20
- private readonly rules: Rule[],
21
- private readonly reporter: IReporter
22
- ) {}
23
-
24
- /**
25
- * Run the complete analysis workflow
26
- */
27
- async analyze(config: AnalysisConfig): Promise<AnalysisReport> {
28
- const startTime = Date.now();
29
-
30
- // 1. Scan for files
31
- console.log('📁 Scanning files...');
32
- const filePaths = await this.fileReader.scan(
33
- config.rootPath,
34
- config.patterns,
35
- config.ignore
36
- );
37
-
38
- if (filePaths.length === 0) {
39
- console.log(' ❌ No files found matching patterns.');
40
- console.log(` Root: ${config.rootPath}`);
41
- console.log(` Patterns: ${config.patterns.join(', ')}`);
42
- console.log(' Check your directory structure and ensure files exist.');
43
- } else {
44
- console.log(` Found ${filePaths.length} files`);
45
- }
46
-
47
- // 2. Read and parse files
48
- console.log('🔍 Parsing files...');
49
- const allMetrics: Metric[] = [];
50
- const fileContents = new Map<string, string>();
51
-
52
- let supportedFilesCount = 0;
53
- for (const filePath of filePaths) {
54
- try {
55
- const fileResult = await this.fileReader.read(filePath);
56
- fileContents.set(filePath, fileResult.content);
57
-
58
- if (this.parser.supports(filePath)) {
59
- supportedFilesCount++;
60
- const parseResult = await this.parser.parse(filePath, fileResult.content);
61
- if (parseResult.success) {
62
- allMetrics.push(...parseResult.metrics);
63
- } else {
64
- console.warn(` ⚠️ Failed to parse ${filePath}: ${parseResult.error}`);
65
- }
66
- }
67
- } catch (err) {
68
- console.warn(` ⚠️ Error reading ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
69
- }
70
- }
71
-
72
- if (filePaths.length > 0 && supportedFilesCount === 0) {
73
- console.log(' ⚠️ None of the found files are supported by the parser (.ts, .js, etc)');
74
- }
75
-
76
- console.log(` Extracted ${allMetrics.length} metrics from ${supportedFilesCount} files`);
77
-
78
- // 3. Analyze dependencies and duplication
79
- console.log('🔗 Analyzing dependencies and duplication...');
80
- const additionalMetrics = await this.analyzeAdvancedMetrics(fileContents);
81
- allMetrics.push(...additionalMetrics);
82
- console.log(` Added ${additionalMetrics.length} advanced metrics`);
83
-
84
- // 4. Apply rules and collect findings
85
- console.log('📊 Applying rules...');
86
- const allFindings: Finding[] = [];
87
- const rawCategoryScores: CategoryScore[] = [];
88
-
89
- for (const rule of this.rules) {
90
- const findings = rule.evaluate(allMetrics);
91
- allFindings.push(...findings);
92
-
93
- const score = rule.calculateScore(findings);
94
- rawCategoryScores.push({
95
- name: rule.name,
96
- score,
97
- weight: this.getCategoryWeight(rule.id, config),
98
- description: rule.description,
99
- });
100
- }
101
- console.log(` Generated ${allFindings.length} findings`);
102
-
103
- // Normalize weights to sum to 1.0 (in case not all categories are active)
104
- const categoryScores = this.normalizeWeights(rawCategoryScores);
105
-
106
- // 5. Calculate overall score
107
- console.log('🎯 Calculating score...');
108
- const overallScore = ScoreCalculator.calculateOverall(categoryScores);
109
-
110
- const finalScore: Score = {
111
- overall: overallScore,
112
- categories: categoryScores,
113
- timestamp: new Date(),
114
- metadata: {
115
- filesAnalyzed: filePaths.length,
116
- totalMetrics: allMetrics.length,
117
- totalFindings: allFindings.length,
118
- },
119
- };
120
-
121
- // 6. Prepare report
122
- const duration = Date.now() - startTime;
123
- const report: AnalysisReport = {
124
- score: finalScore,
125
- findings: allFindings,
126
- metadata: {
127
- filesAnalyzed: filePaths.length,
128
- duration,
129
- timestamp: new Date(),
130
- },
131
- };
132
-
133
- // 7. Generate output
134
- console.log('📋 Generating report...');
135
- await this.reporter.generate(report);
136
-
137
- return report;
138
- }
139
-
140
- /**
141
- * Analyze dependencies and code duplication
142
- */
143
- private async analyzeAdvancedMetrics(fileContents: Map<string, string>): Promise<Metric[]> {
144
- const { DependencyAnalyzer } = await import('./DependencyAnalyzer.js');
145
- const { DuplicationDetector } = await import('./DuplicationDetector.js');
146
-
147
- const depAnalyzer = new DependencyAnalyzer();
148
- const dupDetector = new DuplicationDetector();
149
- const metrics: Metric[] = [];
150
-
151
- // Analyze all files
152
- for (const [filePath, content] of fileContents.entries()) {
153
- if (filePath.endsWith('.ts') || filePath.endsWith('.tsx') ||
154
- filePath.endsWith('.js') || filePath.endsWith('.jsx')) {
155
- depAnalyzer.analyzeDependencies(filePath, content);
156
- dupDetector.analyzeFile(filePath, content);
157
- }
158
- }
159
-
160
- // Detect circular dependencies
161
- const circularDeps = depAnalyzer.detectCircularDependencies();
162
- metrics.push(...circularDeps);
163
-
164
- // Detect code duplication
165
- const duplicates = dupDetector.detectDuplicates();
166
- metrics.push(...duplicates);
167
-
168
- return metrics;
169
- }
170
-
171
- private getCategoryWeight(ruleId: string, config: AnalysisConfig): number {
172
- const weights = config.weights ?? {
173
- complexity: 0.30,
174
- size: 0.25,
175
- typeSafety: 0.20,
176
- codeQuality: 0.15,
177
- structure: 0.10,
178
- };
179
-
180
- switch (ruleId) {
181
- case 'complexity':
182
- return weights.complexity;
183
- case 'size':
184
- return weights.size;
185
- case 'type-safety':
186
- return weights.typeSafety;
187
- case 'duplication':
188
- return weights.codeQuality;
189
- case 'circular-dependency':
190
- return weights.structure;
191
- default:
192
- return 0.05; // Default weight for unknown categories
193
- }
194
- }
195
-
196
- /**
197
- * Normalize weights so they sum to 1.0
198
- * This handles cases where not all categories are active
199
- */
200
- private normalizeWeights(categories: CategoryScore[]): CategoryScore[] {
201
- const totalWeight = categories.reduce((sum, cat) => sum + cat.weight, 0);
202
-
203
- if (totalWeight === 0) {
204
- // If all weights are 0, distribute evenly
205
- const evenWeight = 1.0 / categories.length;
206
- return categories.map(cat => ({
207
- ...cat,
208
- weight: evenWeight,
209
- }));
210
- }
211
-
212
- // Normalize proportionally
213
- return categories.map(cat => ({
214
- ...cat,
215
- weight: cat.weight / totalWeight,
216
- }));
217
- }
218
- }
@@ -1,158 +0,0 @@
1
- /**
2
- * Application Service: Dependency Analyzer
3
- * Analyzes import/require statements to detect circular dependencies
4
- */
5
-
6
- import * as ts from 'typescript';
7
- import { resolve, dirname, join } from 'node:path';
8
- import type { Metric } from '../../domain/entities/Metric.js';
9
- import { MetricBuilder } from '../../domain/entities/Metric.js';
10
-
11
- interface DependencyNode {
12
- filePath: string;
13
- imports: string[];
14
- }
15
-
16
- export class DependencyAnalyzer {
17
- private dependencyGraph: Map<string, DependencyNode> = new Map();
18
-
19
- /**
20
- * Analyze files for import statements and build dependency graph
21
- */
22
- analyzeDependencies(filePath: string, content: string): void {
23
- const sourceFile = ts.createSourceFile(
24
- filePath,
25
- content,
26
- ts.ScriptTarget.Latest,
27
- true
28
- );
29
-
30
- const imports: string[] = [];
31
-
32
- // Extract import statements
33
- const visit = (node: ts.Node): void => {
34
- if (ts.isImportDeclaration(node)) {
35
- const moduleSpecifier = node.moduleSpecifier;
36
- if (ts.isStringLiteral(moduleSpecifier)) {
37
- const importPath = this.resolveImportPath(filePath, moduleSpecifier.text);
38
- if (importPath) {
39
- imports.push(importPath);
40
- }
41
- }
42
- }
43
- ts.forEachChild(node, visit);
44
- };
45
-
46
- visit(sourceFile);
47
-
48
- this.dependencyGraph.set(filePath, {
49
- filePath,
50
- imports,
51
- });
52
- }
53
-
54
- /**
55
- * Detect circular dependencies using DFS
56
- */
57
- detectCircularDependencies(): Metric[] {
58
- const metrics: Metric[] = [];
59
- const visited = new Set<string>();
60
- const recursionStack = new Set<string>();
61
- const cycles = new Set<string>(); // Track unique cycles
62
-
63
- const dfs = (filePath: string, path: string[]): void => {
64
- if (cycles.has(filePath)) return; // Already found this cycle
65
-
66
- visited.add(filePath);
67
- recursionStack.add(filePath);
68
-
69
- const node = this.dependencyGraph.get(filePath);
70
- if (!node) {
71
- recursionStack.delete(filePath);
72
- return;
73
- }
74
-
75
- for (const importPath of node.imports) {
76
- if (!visited.has(importPath)) {
77
- dfs(importPath, [...path, filePath]);
78
- } else if (recursionStack.has(importPath)) {
79
- // Found a cycle!
80
- const cycleStart = path.indexOf(importPath);
81
- if (cycleStart !== -1) {
82
- const cycle = [...path.slice(cycleStart), importPath];
83
- const cycleKey = [...cycle].sort().join('->');
84
-
85
- if (!cycles.has(cycleKey)) {
86
- cycles.add(cycleKey);
87
-
88
- // Create metric for this cycle
89
- metrics.push(
90
- new MetricBuilder()
91
- .withName('circular-dependency')
92
- .withValue(cycle.length)
93
- .withFilePath(filePath)
94
- .withContext(cycle.map(p => this.getFileName(p)).join(' → '))
95
- .build()
96
- );
97
- }
98
- }
99
- }
100
- }
101
-
102
- recursionStack.delete(filePath);
103
- };
104
-
105
- // Check all nodes for cycles
106
- for (const filePath of this.dependencyGraph.keys()) {
107
- if (!visited.has(filePath)) {
108
- dfs(filePath, []);
109
- }
110
- }
111
-
112
- return metrics;
113
- }
114
-
115
- /**
116
- * Resolve relative import to absolute path
117
- */
118
- private resolveImportPath(fromFile: string, importSpecifier: string): string | null {
119
- // Skip node_modules and external packages
120
- if (!importSpecifier.startsWith('.')) {
121
- return null;
122
- }
123
-
124
- try {
125
- const dir = dirname(fromFile);
126
- let resolvedPath = resolve(dir, importSpecifier);
127
-
128
- // Try adding extensions if file doesn't exist
129
- const extensions = ['.ts', '.tsx', '.js', '.jsx', '/index.ts', '/index.js'];
130
- for (const ext of extensions) {
131
- const pathWithExt = resolvedPath + ext;
132
- // We can't check if file exists in this context, so just normalize
133
- if (ext.startsWith('/')) {
134
- return resolvedPath + ext;
135
- }
136
- }
137
-
138
- return resolvedPath + '.ts'; // Default to .ts
139
- } catch {
140
- return null;
141
- }
142
- }
143
-
144
- /**
145
- * Get file name from path for display
146
- */
147
- private getFileName(filePath: string): string {
148
- const parts = filePath.split('/');
149
- return parts[parts.length - 1] ?? filePath;
150
- }
151
-
152
- /**
153
- * Clear the dependency graph (useful for new analysis)
154
- */
155
- clear(): void {
156
- this.dependencyGraph.clear();
157
- }
158
- }
@@ -1,207 +0,0 @@
1
- /**
2
- * Application Service: Code Duplication Detector
3
- * Detects duplicate code blocks using token-based similarity
4
- */
5
-
6
- import * as ts from 'typescript';
7
- import type { Metric } from '../../domain/entities/Metric.js';
8
- import { MetricBuilder } from '../../domain/entities/Metric.js';
9
-
10
- interface CodeBlock {
11
- filePath: string;
12
- tokens: string;
13
- startLine: number;
14
- endLine: number;
15
- functionName?: string | undefined;
16
- }
17
-
18
- export class DuplicationDetector {
19
- private codeBlocks: CodeBlock[] = [];
20
- private readonly MIN_BLOCK_SIZE = 50; // Minimum token count
21
-
22
- /**
23
- * Analyze file for code blocks
24
- */
25
- analyzeFile(filePath: string, content: string): void {
26
- const sourceFile = ts.createSourceFile(
27
- filePath,
28
- content,
29
- ts.ScriptTarget.Latest,
30
- true
31
- );
32
-
33
- // Extract function bodies as code blocks
34
- const visit = (node: ts.Node): void => {
35
- if (this.isFunctionNode(node)) {
36
- const block = this.extractCodeBlock(sourceFile, filePath, node);
37
- if (block && block.tokens.length >= this.MIN_BLOCK_SIZE) {
38
- this.codeBlocks.push(block);
39
- }
40
- }
41
- ts.forEachChild(node, visit);
42
- };
43
-
44
- visit(sourceFile);
45
- }
46
-
47
- /**
48
- * Detect duplicates across all analyzed files
49
- */
50
- detectDuplicates(): Metric[] {
51
- const metrics: Metric[] = [];
52
- const seen = new Set<number>();
53
-
54
- for (let i = 0; i < this.codeBlocks.length; i++) {
55
- if (seen.has(i)) continue;
56
-
57
- const block1 = this.codeBlocks[i];
58
- if (!block1) continue;
59
-
60
- let duplicateCount = 0;
61
-
62
- for (let j = i + 1; j < this.codeBlocks.length; j++) {
63
- if (seen.has(j)) continue;
64
-
65
- const block2 = this.codeBlocks[j];
66
- if (!block2) continue;
67
-
68
- const similarity = this.calculateSimilarity(block1.tokens, block2.tokens);
69
-
70
- if (similarity >= 0.85) { // 85% similarity threshold
71
- duplicateCount++;
72
- seen.add(j);
73
- }
74
- }
75
-
76
- if (duplicateCount > 0) {
77
- metrics.push(
78
- new MetricBuilder()
79
- .withName('code-duplication')
80
- .withValue(duplicateCount)
81
- .withFilePath(block1.filePath)
82
- .withContext(block1.functionName || 'code block')
83
- .withLocation({
84
- startLine: block1.startLine,
85
- endLine: block1.endLine,
86
- startColumn: 0,
87
- endColumn: 0,
88
- })
89
- .build()
90
- );
91
- }
92
- }
93
-
94
- return metrics;
95
- }
96
-
97
- /**
98
- * Extract code block from function node
99
- */
100
- private extractCodeBlock(
101
- sourceFile: ts.SourceFile,
102
- filePath: string,
103
- node: ts.Node
104
- ): CodeBlock | null {
105
- const start = sourceFile.getLineAndCharacterOfPosition(node.getStart());
106
- const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
107
-
108
- // Tokenize the code (simple whitespace normalization)
109
- const text = node.getText(sourceFile);
110
- const tokens = this.tokenize(text);
111
-
112
- return {
113
- filePath,
114
- tokens,
115
- startLine: start.line + 1,
116
- endLine: end.line + 1,
117
- functionName: this.getFunctionName(node),
118
- };
119
- }
120
-
121
- /**
122
- * Simple tokenization (normalize whitespace)
123
- */
124
- private tokenize(code: string): string {
125
- return code
126
- .replace(/\s+/g, ' ') // Normalize whitespace
127
- .replace(/\/\*[\s\S]*?\*\//g, '') // Remove block comments
128
- .replace(/\/\/.*/g, '') // Remove line comments
129
- .trim();
130
- }
131
-
132
- /**
133
- * Calculate similarity between two token strings
134
- */
135
- private calculateSimilarity(tokens1: string, tokens2: string): number {
136
- if (tokens1 === tokens2) return 1.0;
137
-
138
- // Use Levenshtein distance for similarity
139
- const len1 = tokens1.length;
140
- const len2 = tokens2.length;
141
- const maxLen = Math.max(len1, len2);
142
-
143
- if (maxLen === 0) return 1.0;
144
-
145
- const distance = this.levenshteinDistance(tokens1, tokens2);
146
- return 1 - distance / maxLen;
147
- }
148
-
149
- /**
150
- * Calculate Levenshtein distance
151
- */
152
- private levenshteinDistance(str1: string, str2: string): number {
153
- const len1 = str1.length;
154
- const len2 = str2.length;
155
- const matrix: number[][] = [];
156
-
157
- for (let i = 0; i <= len1; i++) {
158
- matrix[i] = [i];
159
- }
160
-
161
- for (let j = 0; j <= len2; j++) {
162
- matrix[0]![j] = j;
163
- }
164
-
165
- for (let i = 1; i <= len1; i++) {
166
- for (let j = 1; j <= len2; j++) {
167
- const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
168
- matrix[i]![j] = Math.min(
169
- matrix[i - 1]![j]! + 1, // deletion
170
- matrix[i]![j - 1]! + 1, // insertion
171
- matrix[i - 1]![j - 1]! + cost // substitution
172
- );
173
- }
174
- }
175
-
176
- return matrix[len1]![len2]!;
177
- }
178
-
179
- /**
180
- * Check if node is a function
181
- */
182
- private isFunctionNode(node: ts.Node): boolean {
183
- return ts.isFunctionDeclaration(node) ||
184
- ts.isMethodDeclaration(node) ||
185
- ts.isArrowFunction(node) ||
186
- ts.isFunctionExpression(node);
187
- }
188
-
189
- /**
190
- * Get function name for context
191
- */
192
- private getFunctionName(node: ts.Node): string | undefined {
193
- if (ts.isFunctionDeclaration(node) && node.name) {
194
- return node.name.getText();
195
- } else if (ts.isMethodDeclaration(node) && node.name) {
196
- return node.name.getText();
197
- }
198
- return undefined;
199
- }
200
-
201
- /**
202
- * Clear stored blocks
203
- */
204
- clear(): void {
205
- this.codeBlocks = [];
206
- }
207
- }
@@ -1,77 +0,0 @@
1
- import { resolve } from 'node:path';
2
- import { existsSync } from 'node:fs';
3
- import { AnalysisService } from '../../application/services/AnalysisService.js';
4
- import { FileSystemReader } from '../../adapters/input/FileSystemReader.js';
5
- import { TypeScriptParser } from '../../adapters/input/TypeScriptParser.js';
6
- import { TerminalReporter } from '../../adapters/output/TerminalReporter.js';
7
- import { JsonExporter } from '../../adapters/output/JsonExporter.js';
8
- import type { IReporter, AnalysisReport } from '../../application/ports/IReporter.js';
9
-
10
- import { ComplexityRule } from '../../domain/rules/ComplexityRule.js';
11
- import { SizeRule } from '../../domain/rules/SizeRule.js';
12
- import { TypeSafetyRule } from '../../domain/rules/TypeSafetyRule.js';
13
- import { DuplicationRule } from '../../domain/rules/DuplicationRule.js';
14
- import { CircularDependencyRule } from '../../domain/rules/CircularDependencyRule.js';
15
- import { DEFAULT_CONFIG } from '../../application/config/AnalysisConfig.js';
16
- import type { AnalysisConfig } from '../../application/config/AnalysisConfig.js';
17
-
18
- // Helper to broadcast to multiple reporters
19
- class CompositeReporter implements IReporter {
20
- constructor(private reporters: IReporter[]) {}
21
-
22
- async generate(report: AnalysisReport): Promise<void> {
23
- await Promise.all(this.reporters.map(r => r.generate(report)));
24
- }
25
- }
26
-
27
- export async function analyzeCommand(rootPath: string, jsonOutputPath?: string): Promise<void> {
28
- console.log('🚀 Starting technical debt analysis...\n');
29
-
30
- // Wiring: Create all adapters and services (dependency injection)
31
- const fileReader = new FileSystemReader();
32
- const parser = new TypeScriptParser();
33
-
34
- // Use both terminal and JSON reporters
35
- const reporter = new CompositeReporter([
36
- new TerminalReporter(),
37
- new JsonExporter(jsonOutputPath)
38
- ]);
39
-
40
- const rules = [
41
- // ... rules
42
-
43
- new ComplexityRule(),
44
- new SizeRule(),
45
- new TypeSafetyRule(),
46
- new DuplicationRule(),
47
- new CircularDependencyRule(),
48
- ];
49
-
50
- const analysisService = new AnalysisService(
51
- fileReader,
52
- parser,
53
- rules,
54
- reporter
55
- );
56
-
57
- // Build configuration
58
- // Use broad patterns by default and rely on ignore list for exclusions.
59
- // This makes the tool structure-agnostic (works for src/, lib/, or root files).
60
- const defaultPatterns = ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'];
61
-
62
- const config: AnalysisConfig = {
63
- rootPath,
64
- ...DEFAULT_CONFIG,
65
- patterns: defaultPatterns,
66
- };
67
-
68
- // Log scan start for transparency
69
- if (config.patterns.length > 0) {
70
- console.log(`📂 Scanning for patterns: ${config.patterns.join(', ')}`);
71
- }
72
-
73
- // Execute analysis
74
- await analysisService.analyze(config);
75
-
76
- console.log('✅ Analysis complete!\n');
77
- }