@girardelli/architect 5.0.0 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{cli.d.ts → src/adapters/cli.d.ts} +1 -2
- package/dist/{cli.js → src/adapters/cli.js} +191 -213
- package/dist/src/adapters/cli.js.map +1 -0
- package/dist/src/adapters/github-action.d.ts +9 -0
- package/dist/src/adapters/github-action.js +94 -0
- package/dist/src/adapters/github-action.js.map +1 -0
- package/dist/src/adapters/html-reporter/scripts.d.ts +5 -0
- package/dist/src/adapters/html-reporter/scripts.js +400 -0
- package/dist/src/adapters/html-reporter/scripts.js.map +1 -0
- package/dist/src/adapters/html-reporter/sections/agents.d.ts +2 -0
- package/dist/src/adapters/html-reporter/sections/agents.js +260 -0
- package/dist/src/adapters/html-reporter/sections/agents.js.map +1 -0
- package/dist/src/adapters/html-reporter/sections/anti-patterns.d.ts +13 -0
- package/dist/src/adapters/html-reporter/sections/anti-patterns.js +64 -0
- package/dist/src/adapters/html-reporter/sections/anti-patterns.js.map +1 -0
- package/dist/src/adapters/html-reporter/sections/header.d.ts +3 -0
- package/dist/src/adapters/html-reporter/sections/header.js +30 -0
- package/dist/src/adapters/html-reporter/sections/header.js.map +1 -0
- package/dist/src/adapters/html-reporter/sections/layers.d.ts +9 -0
- package/dist/src/adapters/html-reporter/sections/layers.js +143 -0
- package/dist/src/adapters/html-reporter/sections/layers.js.map +1 -0
- package/dist/src/adapters/html-reporter/sections/overview.d.ts +2 -0
- package/dist/src/adapters/html-reporter/sections/overview.js +58 -0
- package/dist/src/adapters/html-reporter/sections/overview.js.map +1 -0
- package/dist/src/adapters/html-reporter/sections/refactoring-plan.d.ts +3 -0
- package/dist/src/adapters/html-reporter/sections/refactoring-plan.js +151 -0
- package/dist/src/adapters/html-reporter/sections/refactoring-plan.js.map +1 -0
- package/dist/src/adapters/html-reporter/sections/score.d.ts +7 -0
- package/dist/src/adapters/html-reporter/sections/score.js +70 -0
- package/dist/src/adapters/html-reporter/sections/score.js.map +1 -0
- package/dist/src/adapters/html-reporter/sections/suggestions.d.ts +7 -0
- package/dist/src/adapters/html-reporter/sections/suggestions.js +34 -0
- package/dist/src/adapters/html-reporter/sections/suggestions.js.map +1 -0
- package/dist/src/adapters/html-reporter/styles.d.ts +1 -0
- package/dist/src/adapters/html-reporter/styles.js +526 -0
- package/dist/src/adapters/html-reporter/styles.js.map +1 -0
- package/dist/src/adapters/html-reporter/utils_adapters.d.ts +20 -0
- package/dist/src/adapters/html-reporter/utils_adapters.js +32 -0
- package/dist/src/adapters/html-reporter/utils_adapters.js.map +1 -0
- package/dist/src/adapters/html-reporter/utils_sections.d.ts +7 -0
- package/dist/src/adapters/html-reporter/utils_sections.js +58 -0
- package/dist/src/adapters/html-reporter/utils_sections.js.map +1 -0
- package/dist/src/adapters/html-reporter.d.ts +10 -0
- package/dist/src/adapters/html-reporter.js +97 -0
- package/dist/src/adapters/html-reporter.js.map +1 -0
- package/dist/src/adapters/progress-logger.d.ts +55 -0
- package/dist/src/adapters/progress-logger.js +200 -0
- package/dist/src/adapters/progress-logger.js.map +1 -0
- package/dist/{refactor-reporter.d.ts → src/adapters/refactor-reporter.d.ts} +1 -2
- package/dist/{refactor-reporter.js → src/adapters/refactor-reporter.js} +1 -1
- package/dist/src/adapters/refactor-reporter.js.map +1 -0
- package/dist/{reporter.d.ts → src/adapters/reporter.d.ts} +1 -2
- package/dist/src/adapters/reporter.js.map +1 -0
- package/dist/src/core/GenesisTerminal.d.ts +8 -0
- package/dist/src/core/GenesisTerminal.js +105 -0
- package/dist/src/core/GenesisTerminal.js.map +1 -0
- package/dist/{index.d.ts → src/core/architect.d.ts} +4 -18
- package/dist/{index.js → src/core/architect.js} +22 -21
- package/dist/src/core/architect.js.map +1 -0
- package/dist/tests/architect-adapter-enrichment.test.d.ts +1 -0
- package/dist/tests/architect-adapter-enrichment.test.js +11 -0
- package/dist/tests/architect-adapter-enrichment.test.js.map +1 -0
- package/dist/tests/github-action.test.d.ts +1 -0
- package/dist/tests/github-action.test.js +92 -0
- package/dist/tests/github-action.test.js.map +1 -0
- package/package.json +15 -65
- package/src/adapters/cli.ts +492 -0
- package/src/adapters/github-action.ts +109 -0
- package/src/adapters/html-reporter/scripts.ts +402 -0
- package/src/adapters/html-reporter/sections/agents.ts +267 -0
- package/src/adapters/html-reporter/sections/anti-patterns.ts +81 -0
- package/src/adapters/html-reporter/sections/header.ts +35 -0
- package/src/adapters/html-reporter/sections/layers.ts +165 -0
- package/src/adapters/html-reporter/sections/overview.ts +64 -0
- package/src/adapters/html-reporter/sections/refactoring-plan.ts +166 -0
- package/src/adapters/html-reporter/sections/score.ts +80 -0
- package/src/adapters/html-reporter/sections/suggestions.ts +39 -0
- package/src/adapters/html-reporter/styles.ts +525 -0
- package/src/adapters/html-reporter/utils_adapters.ts +39 -0
- package/src/adapters/html-reporter/utils_sections.ts +55 -0
- package/src/adapters/html-reporter.ts +102 -0
- package/src/adapters/progress-logger.ts +236 -0
- package/src/{refactor-reporter.ts → adapters/refactor-reporter.ts} +2 -2
- package/src/{reporter.ts → adapters/reporter.ts} +1 -1
- package/src/core/GenesisTerminal.ts +127 -0
- package/src/{index.ts → core/architect.ts} +27 -45
- package/tests/github-action.test.ts +109 -0
- package/tsconfig.json +12 -19
- package/CONTRIBUTING.md +0 -140
- package/LICENSE +0 -21
- package/PROJECT_STRUCTURE.txt +0 -168
- package/README.md +0 -257
- package/architect-run.sh +0 -431
- package/assets/banner-v3.html +0 -561
- package/dist/agent-generator/context-enricher.d.ts +0 -58
- package/dist/agent-generator/context-enricher.d.ts.map +0 -1
- package/dist/agent-generator/context-enricher.js +0 -613
- package/dist/agent-generator/context-enricher.js.map +0 -1
- package/dist/agent-generator/domain-inferrer.d.ts +0 -52
- package/dist/agent-generator/domain-inferrer.d.ts.map +0 -1
- package/dist/agent-generator/domain-inferrer.js +0 -585
- package/dist/agent-generator/domain-inferrer.js.map +0 -1
- package/dist/agent-generator/framework-detector.d.ts +0 -40
- package/dist/agent-generator/framework-detector.d.ts.map +0 -1
- package/dist/agent-generator/framework-detector.js +0 -611
- package/dist/agent-generator/framework-detector.js.map +0 -1
- package/dist/agent-generator/index.d.ts +0 -47
- package/dist/agent-generator/index.d.ts.map +0 -1
- package/dist/agent-generator/index.js +0 -545
- package/dist/agent-generator/index.js.map +0 -1
- package/dist/agent-generator/stack-detector.d.ts +0 -14
- package/dist/agent-generator/stack-detector.d.ts.map +0 -1
- package/dist/agent-generator/stack-detector.js +0 -124
- package/dist/agent-generator/stack-detector.js.map +0 -1
- package/dist/agent-generator/templates/core/agents.d.ts +0 -17
- package/dist/agent-generator/templates/core/agents.d.ts.map +0 -1
- package/dist/agent-generator/templates/core/agents.js +0 -1256
- package/dist/agent-generator/templates/core/agents.js.map +0 -1
- package/dist/agent-generator/templates/core/architecture-rules.d.ts +0 -7
- package/dist/agent-generator/templates/core/architecture-rules.d.ts.map +0 -1
- package/dist/agent-generator/templates/core/architecture-rules.js +0 -274
- package/dist/agent-generator/templates/core/architecture-rules.js.map +0 -1
- package/dist/agent-generator/templates/core/general-rules.d.ts +0 -8
- package/dist/agent-generator/templates/core/general-rules.d.ts.map +0 -1
- package/dist/agent-generator/templates/core/general-rules.js +0 -301
- package/dist/agent-generator/templates/core/general-rules.js.map +0 -1
- package/dist/agent-generator/templates/core/hooks-generator.d.ts +0 -21
- package/dist/agent-generator/templates/core/hooks-generator.d.ts.map +0 -1
- package/dist/agent-generator/templates/core/hooks-generator.js +0 -233
- package/dist/agent-generator/templates/core/hooks-generator.js.map +0 -1
- package/dist/agent-generator/templates/core/index-md.d.ts +0 -7
- package/dist/agent-generator/templates/core/index-md.d.ts.map +0 -1
- package/dist/agent-generator/templates/core/index-md.js +0 -246
- package/dist/agent-generator/templates/core/index-md.js.map +0 -1
- package/dist/agent-generator/templates/core/orchestrator.d.ts +0 -8
- package/dist/agent-generator/templates/core/orchestrator.d.ts.map +0 -1
- package/dist/agent-generator/templates/core/orchestrator.js +0 -422
- package/dist/agent-generator/templates/core/orchestrator.js.map +0 -1
- package/dist/agent-generator/templates/core/preflight.d.ts +0 -8
- package/dist/agent-generator/templates/core/preflight.d.ts.map +0 -1
- package/dist/agent-generator/templates/core/preflight.js +0 -213
- package/dist/agent-generator/templates/core/preflight.js.map +0 -1
- package/dist/agent-generator/templates/core/quality-gates.d.ts +0 -11
- package/dist/agent-generator/templates/core/quality-gates.d.ts.map +0 -1
- package/dist/agent-generator/templates/core/quality-gates.js +0 -254
- package/dist/agent-generator/templates/core/quality-gates.js.map +0 -1
- package/dist/agent-generator/templates/core/security-rules.d.ts +0 -7
- package/dist/agent-generator/templates/core/security-rules.d.ts.map +0 -1
- package/dist/agent-generator/templates/core/security-rules.js +0 -528
- package/dist/agent-generator/templates/core/security-rules.js.map +0 -1
- package/dist/agent-generator/templates/core/skills-generator.d.ts +0 -19
- package/dist/agent-generator/templates/core/skills-generator.d.ts.map +0 -1
- package/dist/agent-generator/templates/core/skills-generator.js +0 -546
- package/dist/agent-generator/templates/core/skills-generator.js.map +0 -1
- package/dist/agent-generator/templates/core/workflow-fix-bug.d.ts +0 -7
- package/dist/agent-generator/templates/core/workflow-fix-bug.d.ts.map +0 -1
- package/dist/agent-generator/templates/core/workflow-fix-bug.js +0 -237
- package/dist/agent-generator/templates/core/workflow-fix-bug.js.map +0 -1
- package/dist/agent-generator/templates/core/workflow-new-feature.d.ts +0 -8
- package/dist/agent-generator/templates/core/workflow-new-feature.d.ts.map +0 -1
- package/dist/agent-generator/templates/core/workflow-new-feature.js +0 -321
- package/dist/agent-generator/templates/core/workflow-new-feature.js.map +0 -1
- package/dist/agent-generator/templates/core/workflow-review.d.ts +0 -7
- package/dist/agent-generator/templates/core/workflow-review.d.ts.map +0 -1
- package/dist/agent-generator/templates/core/workflow-review.js +0 -104
- package/dist/agent-generator/templates/core/workflow-review.js.map +0 -1
- package/dist/agent-generator/templates/domain/index.d.ts +0 -22
- package/dist/agent-generator/templates/domain/index.d.ts.map +0 -1
- package/dist/agent-generator/templates/domain/index.js +0 -1176
- package/dist/agent-generator/templates/domain/index.js.map +0 -1
- package/dist/agent-generator/templates/stack/index.d.ts +0 -8
- package/dist/agent-generator/templates/stack/index.d.ts.map +0 -1
- package/dist/agent-generator/templates/stack/index.js +0 -695
- package/dist/agent-generator/templates/stack/index.js.map +0 -1
- package/dist/agent-generator/templates/template-helpers.d.ts +0 -75
- package/dist/agent-generator/templates/template-helpers.d.ts.map +0 -1
- package/dist/agent-generator/templates/template-helpers.js +0 -726
- package/dist/agent-generator/templates/template-helpers.js.map +0 -1
- package/dist/agent-generator/types.d.ts +0 -196
- package/dist/agent-generator/types.d.ts.map +0 -1
- package/dist/agent-generator/types.js +0 -27
- package/dist/agent-generator/types.js.map +0 -1
- package/dist/analyzer.d.ts +0 -38
- package/dist/analyzer.d.ts.map +0 -1
- package/dist/analyzer.js +0 -383
- package/dist/analyzer.js.map +0 -1
- package/dist/analyzers/forecast.d.ts +0 -85
- package/dist/analyzers/forecast.d.ts.map +0 -1
- package/dist/analyzers/forecast.js +0 -337
- package/dist/analyzers/forecast.js.map +0 -1
- package/dist/analyzers/git-cache.d.ts +0 -7
- package/dist/analyzers/git-cache.d.ts.map +0 -1
- package/dist/analyzers/git-cache.js +0 -41
- package/dist/analyzers/git-cache.js.map +0 -1
- package/dist/analyzers/git-history.d.ts +0 -113
- package/dist/analyzers/git-history.d.ts.map +0 -1
- package/dist/analyzers/git-history.js +0 -333
- package/dist/analyzers/git-history.js.map +0 -1
- package/dist/analyzers/index.d.ts +0 -10
- package/dist/analyzers/index.d.ts.map +0 -1
- package/dist/analyzers/index.js +0 -7
- package/dist/analyzers/index.js.map +0 -1
- package/dist/analyzers/temporal-scorer.d.ts +0 -72
- package/dist/analyzers/temporal-scorer.d.ts.map +0 -1
- package/dist/analyzers/temporal-scorer.js +0 -140
- package/dist/analyzers/temporal-scorer.js.map +0 -1
- package/dist/anti-patterns.d.ts +0 -24
- package/dist/anti-patterns.d.ts.map +0 -1
- package/dist/anti-patterns.js +0 -230
- package/dist/anti-patterns.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/config.d.ts +0 -12
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -110
- package/dist/config.js.map +0 -1
- package/dist/diagram.d.ts +0 -9
- package/dist/diagram.d.ts.map +0 -1
- package/dist/diagram.js +0 -116
- package/dist/diagram.js.map +0 -1
- package/dist/html-reporter.d.ts +0 -47
- package/dist/html-reporter.d.ts.map +0 -1
- package/dist/html-reporter.js +0 -1747
- package/dist/html-reporter.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/project-summarizer.d.ts +0 -38
- package/dist/project-summarizer.d.ts.map +0 -1
- package/dist/project-summarizer.js +0 -463
- package/dist/project-summarizer.js.map +0 -1
- package/dist/refactor-engine.d.ts +0 -18
- package/dist/refactor-engine.d.ts.map +0 -1
- package/dist/refactor-engine.js +0 -86
- package/dist/refactor-engine.js.map +0 -1
- package/dist/refactor-reporter.d.ts.map +0 -1
- package/dist/refactor-reporter.js.map +0 -1
- package/dist/reporter.d.ts.map +0 -1
- package/dist/reporter.js.map +0 -1
- package/dist/rules/barrel-optimizer.d.ts +0 -13
- package/dist/rules/barrel-optimizer.d.ts.map +0 -1
- package/dist/rules/barrel-optimizer.js +0 -77
- package/dist/rules/barrel-optimizer.js.map +0 -1
- package/dist/rules/dead-code-detector.d.ts +0 -21
- package/dist/rules/dead-code-detector.d.ts.map +0 -1
- package/dist/rules/dead-code-detector.js +0 -117
- package/dist/rules/dead-code-detector.js.map +0 -1
- package/dist/rules/hub-splitter.d.ts +0 -13
- package/dist/rules/hub-splitter.d.ts.map +0 -1
- package/dist/rules/hub-splitter.js +0 -110
- package/dist/rules/hub-splitter.js.map +0 -1
- package/dist/rules/import-organizer.d.ts +0 -13
- package/dist/rules/import-organizer.d.ts.map +0 -1
- package/dist/rules/import-organizer.js +0 -85
- package/dist/rules/import-organizer.js.map +0 -1
- package/dist/rules/module-grouper.d.ts +0 -13
- package/dist/rules/module-grouper.d.ts.map +0 -1
- package/dist/rules/module-grouper.js +0 -110
- package/dist/rules/module-grouper.js.map +0 -1
- package/dist/scanner.d.ts +0 -31
- package/dist/scanner.d.ts.map +0 -1
- package/dist/scanner.js +0 -328
- package/dist/scanner.js.map +0 -1
- package/dist/scorer.d.ts +0 -27
- package/dist/scorer.d.ts.map +0 -1
- package/dist/scorer.js +0 -229
- package/dist/scorer.js.map +0 -1
- package/dist/types.d.ts +0 -186
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/examples/sample-report.md +0 -207
- package/jest.config.js +0 -18
- package/src/agent-generator/context-enricher.ts +0 -672
- package/src/agent-generator/domain-inferrer.ts +0 -635
- package/src/agent-generator/framework-detector.ts +0 -669
- package/src/agent-generator/index.ts +0 -634
- package/src/agent-generator/stack-detector.ts +0 -115
- package/src/agent-generator/templates/core/agents.ts +0 -1296
- package/src/agent-generator/templates/core/architecture-rules.ts +0 -287
- package/src/agent-generator/templates/core/general-rules.ts +0 -306
- package/src/agent-generator/templates/core/hooks-generator.ts +0 -242
- package/src/agent-generator/templates/core/index-md.ts +0 -260
- package/src/agent-generator/templates/core/orchestrator.ts +0 -459
- package/src/agent-generator/templates/core/preflight.ts +0 -215
- package/src/agent-generator/templates/core/quality-gates.ts +0 -256
- package/src/agent-generator/templates/core/security-rules.ts +0 -543
- package/src/agent-generator/templates/core/skills-generator.ts +0 -585
- package/src/agent-generator/templates/core/workflow-fix-bug.ts +0 -239
- package/src/agent-generator/templates/core/workflow-new-feature.ts +0 -323
- package/src/agent-generator/templates/core/workflow-review.ts +0 -106
- package/src/agent-generator/templates/domain/index.ts +0 -1201
- package/src/agent-generator/templates/stack/index.ts +0 -705
- package/src/agent-generator/templates/template-helpers.ts +0 -776
- package/src/agent-generator/types.ts +0 -232
- package/src/analyzer.ts +0 -447
- package/src/analyzers/forecast.ts +0 -496
- package/src/analyzers/git-cache.ts +0 -52
- package/src/analyzers/git-history.ts +0 -488
- package/src/analyzers/index.ts +0 -33
- package/src/analyzers/temporal-scorer.ts +0 -227
- package/src/anti-patterns.ts +0 -287
- package/src/cli.ts +0 -517
- package/src/config.ts +0 -123
- package/src/diagram.ts +0 -144
- package/src/html-reporter.ts +0 -1830
- package/src/project-summarizer.ts +0 -521
- package/src/refactor-engine.ts +0 -117
- package/src/rules/barrel-optimizer.ts +0 -97
- package/src/rules/dead-code-detector.ts +0 -132
- package/src/rules/hub-splitter.ts +0 -123
- package/src/rules/import-organizer.ts +0 -98
- package/src/rules/module-grouper.ts +0 -124
- package/src/scanner.ts +0 -344
- package/src/scorer.ts +0 -254
- package/src/types.ts +0 -193
- package/tests/agent-generator.test.ts +0 -427
- package/tests/analyzers-integration.test.ts +0 -174
- package/tests/anti-patterns.test.ts +0 -94
- package/tests/context-enricher.test.ts +0 -971
- package/tests/fixtures/monorepo/package.json +0 -6
- package/tests/fixtures/monorepo/packages/app/package.json +0 -12
- package/tests/fixtures/monorepo/packages/app/src/index.ts +0 -6
- package/tests/fixtures/monorepo/packages/core/package.json +0 -7
- package/tests/fixtures/monorepo/packages/core/src/index.ts +0 -7
- package/tests/forecast.test.ts +0 -509
- package/tests/framework-detector.test.ts +0 -1172
- package/tests/git-history.test.ts +0 -254
- package/tests/monorepo-scan.test.ts +0 -170
- package/tests/scanner.test.ts +0 -54
- package/tests/scorer.test.ts +0 -674
- package/tests/stack-detector.test.ts +0 -241
- package/tests/template-generation.test.ts +0 -706
- package/tests/template-helpers.test.ts +0 -1152
- package/tests/temporal-scorer.test.ts +0 -307
- /package/dist/{reporter.js → src/adapters/reporter.js} +0 -0
package/src/scanner.ts
DELETED
|
@@ -1,344 +0,0 @@
|
|
|
1
|
-
import { globSync } from 'glob';
|
|
2
|
-
import { readFileSync, lstatSync, existsSync } from 'fs';
|
|
3
|
-
import { join, relative, extname, resolve } from 'path';
|
|
4
|
-
import { FileNode, ProjectInfo, ArchitectConfig, WorkspaceInfo } from './types.js';
|
|
5
|
-
|
|
6
|
-
export class ProjectScanner {
|
|
7
|
-
private projectPath: string;
|
|
8
|
-
private config: ArchitectConfig;
|
|
9
|
-
private frameworks: Set<string> = new Set();
|
|
10
|
-
|
|
11
|
-
constructor(projectPath: string, config: ArchitectConfig) {
|
|
12
|
-
this.projectPath = projectPath;
|
|
13
|
-
this.config = config;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
scan(): ProjectInfo {
|
|
17
|
-
const files = this.scanDirectory();
|
|
18
|
-
const fileTree = this.buildFileTree(files);
|
|
19
|
-
|
|
20
|
-
// Detect workspaces from root package.json
|
|
21
|
-
const workspaces = this.detectWorkspaces();
|
|
22
|
-
|
|
23
|
-
// Detect frameworks ONLY from root + workspace package.json files (never from node_modules)
|
|
24
|
-
const workspacePkgJsonPaths = [
|
|
25
|
-
join(this.projectPath, 'package.json'),
|
|
26
|
-
...workspaces.map(ws => join(ws.path, 'package.json')),
|
|
27
|
-
].filter(p => existsSync(p));
|
|
28
|
-
|
|
29
|
-
const frameworks = this.detectFrameworks(workspacePkgJsonPaths);
|
|
30
|
-
const languages = this.detectLanguages(files);
|
|
31
|
-
const totalLines = this.countTotalLines(files);
|
|
32
|
-
const projectName = this.resolveProjectName(workspacePkgJsonPaths);
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
path: this.projectPath,
|
|
36
|
-
name: projectName,
|
|
37
|
-
frameworks: Array.from(frameworks),
|
|
38
|
-
totalFiles: files.length,
|
|
39
|
-
totalLines,
|
|
40
|
-
primaryLanguages: languages,
|
|
41
|
-
fileTree,
|
|
42
|
-
workspaces: workspaces.length > 0 ? workspaces : undefined,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Detect npm/yarn/pnpm workspaces from root package.json.
|
|
48
|
-
* Reads the "workspaces" field and resolves each workspace to its package.json.
|
|
49
|
-
*/
|
|
50
|
-
private detectWorkspaces(): WorkspaceInfo[] {
|
|
51
|
-
const rootPkgPath = join(this.projectPath, 'package.json');
|
|
52
|
-
if (!existsSync(rootPkgPath)) return [];
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
const rootPkg = JSON.parse(readFileSync(rootPkgPath, 'utf-8'));
|
|
56
|
-
let workspaceGlobs: string[] = [];
|
|
57
|
-
|
|
58
|
-
if (Array.isArray(rootPkg.workspaces)) {
|
|
59
|
-
workspaceGlobs = rootPkg.workspaces;
|
|
60
|
-
} else if (rootPkg.workspaces?.packages && Array.isArray(rootPkg.workspaces.packages)) {
|
|
61
|
-
workspaceGlobs = rootPkg.workspaces.packages;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (workspaceGlobs.length === 0) return [];
|
|
65
|
-
|
|
66
|
-
const workspaces: WorkspaceInfo[] = [];
|
|
67
|
-
|
|
68
|
-
for (const pattern of workspaceGlobs) {
|
|
69
|
-
// Resolve glob patterns like "packages/*"
|
|
70
|
-
const dirs = globSync(pattern, {
|
|
71
|
-
cwd: this.projectPath,
|
|
72
|
-
absolute: true,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
for (const dir of dirs) {
|
|
76
|
-
const pkgPath = join(dir, 'package.json');
|
|
77
|
-
if (!existsSync(pkgPath)) continue;
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
81
|
-
workspaces.push({
|
|
82
|
-
name: pkg.name || relative(this.projectPath, dir),
|
|
83
|
-
path: dir,
|
|
84
|
-
relativePath: relative(this.projectPath, dir),
|
|
85
|
-
description: pkg.description || '',
|
|
86
|
-
version: pkg.version || '0.0.0',
|
|
87
|
-
dependencies: pkg.dependencies || {},
|
|
88
|
-
devDependencies: pkg.devDependencies || {},
|
|
89
|
-
bin: pkg.bin || undefined,
|
|
90
|
-
main: pkg.main || undefined,
|
|
91
|
-
});
|
|
92
|
-
} catch {
|
|
93
|
-
// Skip unparseable package.json
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return workspaces;
|
|
99
|
-
} catch {
|
|
100
|
-
return [];
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Resolve project name from nearest package.json or directory name
|
|
106
|
-
*/
|
|
107
|
-
private resolveProjectName(packageJsonPaths: string[]): string {
|
|
108
|
-
for (const pkgPath of packageJsonPaths) {
|
|
109
|
-
try {
|
|
110
|
-
const content = readFileSync(pkgPath, 'utf-8');
|
|
111
|
-
const parsed = JSON.parse(content);
|
|
112
|
-
if (parsed.name) {
|
|
113
|
-
return parsed.name;
|
|
114
|
-
}
|
|
115
|
-
} catch {
|
|
116
|
-
// skip
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return this.projectPath.split('/').pop() || 'project';
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
private scanDirectory(): string[] {
|
|
123
|
-
const ignorePatterns = this.config.ignore || [];
|
|
124
|
-
|
|
125
|
-
const files = globSync('**/*', {
|
|
126
|
-
cwd: this.projectPath,
|
|
127
|
-
ignore: ignorePatterns,
|
|
128
|
-
absolute: true,
|
|
129
|
-
nodir: true,
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
return files.filter(
|
|
133
|
-
(f) =>
|
|
134
|
-
!lstatSync(f).isDirectory() && this.isSourceFile(f)
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
private isSourceFile(filePath: string): boolean {
|
|
139
|
-
const ext = extname(filePath).toLowerCase();
|
|
140
|
-
const sourceExtensions = [
|
|
141
|
-
'.js',
|
|
142
|
-
'.ts',
|
|
143
|
-
'.tsx',
|
|
144
|
-
'.jsx',
|
|
145
|
-
'.py',
|
|
146
|
-
'.java',
|
|
147
|
-
'.go',
|
|
148
|
-
'.rb',
|
|
149
|
-
'.php',
|
|
150
|
-
'.cs',
|
|
151
|
-
'.cpp',
|
|
152
|
-
'.c',
|
|
153
|
-
'.h',
|
|
154
|
-
'.rs',
|
|
155
|
-
'.kt',
|
|
156
|
-
'.scala',
|
|
157
|
-
'.groovy',
|
|
158
|
-
'.sql',
|
|
159
|
-
'.graphql',
|
|
160
|
-
'.json',
|
|
161
|
-
'.yaml',
|
|
162
|
-
'.yml',
|
|
163
|
-
'.xml',
|
|
164
|
-
];
|
|
165
|
-
return sourceExtensions.includes(ext);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
private buildFileTree(files: string[]): FileNode {
|
|
169
|
-
const root: FileNode = {
|
|
170
|
-
path: this.projectPath,
|
|
171
|
-
name: this.projectPath.split('/').pop() || 'root',
|
|
172
|
-
type: 'directory',
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
for (const file of files) {
|
|
176
|
-
const relativePath = relative(this.projectPath, file);
|
|
177
|
-
const parts = relativePath.split('/');
|
|
178
|
-
let current = root;
|
|
179
|
-
|
|
180
|
-
for (let i = 0; i < parts.length; i++) {
|
|
181
|
-
const part = parts[i];
|
|
182
|
-
const isFile = i === parts.length - 1;
|
|
183
|
-
|
|
184
|
-
let child = (current.children || []).find((c) => c.name === part);
|
|
185
|
-
if (!child) {
|
|
186
|
-
child = {
|
|
187
|
-
path: join(current.path, part),
|
|
188
|
-
name: part,
|
|
189
|
-
type: isFile ? 'file' : 'directory',
|
|
190
|
-
extension: isFile ? extname(part) : undefined,
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
if (isFile) {
|
|
194
|
-
child.lines = this.countLines(file);
|
|
195
|
-
child.language = this.detectLanguage(file);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (!current.children) current.children = [];
|
|
199
|
-
current.children.push(child);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
current = child;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
return root;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Detect frameworks ONLY from specified package.json files.
|
|
211
|
-
* Never reads package.json from node_modules.
|
|
212
|
-
* No string-matching fallback — only structured dependency key detection.
|
|
213
|
-
*/
|
|
214
|
-
private detectFrameworks(packageJsonPaths: string[]): Set<string> {
|
|
215
|
-
const frameworks = new Set<string>();
|
|
216
|
-
|
|
217
|
-
for (const file of packageJsonPaths) {
|
|
218
|
-
if (!file.endsWith('package.json')) continue;
|
|
219
|
-
|
|
220
|
-
// Safety: skip any path that includes node_modules
|
|
221
|
-
if (file.includes('node_modules')) continue;
|
|
222
|
-
|
|
223
|
-
try {
|
|
224
|
-
const content = readFileSync(file, 'utf-8');
|
|
225
|
-
const parsed = JSON.parse(content);
|
|
226
|
-
const allDeps = {
|
|
227
|
-
...parsed.dependencies,
|
|
228
|
-
...parsed.devDependencies,
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
// Detect from actual dependency keys — no string fallback
|
|
232
|
-
if (allDeps['@nestjs/core'] || allDeps['@nestjs/common']) frameworks.add('NestJS');
|
|
233
|
-
if (allDeps['react'] || allDeps['react-dom']) frameworks.add('React');
|
|
234
|
-
if (allDeps['@angular/core']) frameworks.add('Angular');
|
|
235
|
-
if (allDeps['vue'] || allDeps['@vue/core']) frameworks.add('Vue.js');
|
|
236
|
-
if (allDeps['express']) frameworks.add('Express.js');
|
|
237
|
-
if (allDeps['next']) frameworks.add('Next.js');
|
|
238
|
-
if (allDeps['fastify']) frameworks.add('Fastify');
|
|
239
|
-
if (allDeps['typeorm']) frameworks.add('TypeORM');
|
|
240
|
-
if (allDeps['prisma'] || allDeps['@prisma/client']) frameworks.add('Prisma');
|
|
241
|
-
if (allDeps['sequelize']) frameworks.add('Sequelize');
|
|
242
|
-
if (allDeps['mongoose']) frameworks.add('Mongoose');
|
|
243
|
-
if (allDeps['@modelcontextprotocol/sdk']) frameworks.add('MCP SDK');
|
|
244
|
-
if (allDeps['probot']) frameworks.add('Probot');
|
|
245
|
-
if (allDeps['hono']) frameworks.add('Hono');
|
|
246
|
-
} catch {
|
|
247
|
-
// Skip unparseable files — NO fallback string matching
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Check for pom.xml only at project root
|
|
252
|
-
const pomPath = join(this.projectPath, 'pom.xml');
|
|
253
|
-
if (existsSync(pomPath)) {
|
|
254
|
-
try {
|
|
255
|
-
const content = readFileSync(pomPath, 'utf-8');
|
|
256
|
-
if (content.includes('spring-boot')) frameworks.add('Spring Boot');
|
|
257
|
-
if (content.includes('spring') && !content.includes('spring-boot')) frameworks.add('Spring');
|
|
258
|
-
} catch {
|
|
259
|
-
// skip
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Check for requirements.txt only at project root
|
|
264
|
-
const reqPath = join(this.projectPath, 'requirements.txt');
|
|
265
|
-
if (existsSync(reqPath)) {
|
|
266
|
-
try {
|
|
267
|
-
const content = readFileSync(reqPath, 'utf-8');
|
|
268
|
-
if (content.includes('django')) frameworks.add('Django');
|
|
269
|
-
if (content.includes('flask')) frameworks.add('Flask');
|
|
270
|
-
if (content.includes('fastapi')) frameworks.add('FastAPI');
|
|
271
|
-
} catch {
|
|
272
|
-
// skip
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Check for Gemfile only at project root
|
|
277
|
-
const gemPath = join(this.projectPath, 'Gemfile');
|
|
278
|
-
if (existsSync(gemPath)) {
|
|
279
|
-
try {
|
|
280
|
-
const content = readFileSync(gemPath, 'utf-8');
|
|
281
|
-
if (content.includes('rails')) frameworks.add('Ruby on Rails');
|
|
282
|
-
} catch {
|
|
283
|
-
// skip
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Check for go.mod only at project root
|
|
288
|
-
if (existsSync(join(this.projectPath, 'go.mod'))) {
|
|
289
|
-
frameworks.add('Go');
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return frameworks;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
private detectLanguages(files: string[]): string[] {
|
|
296
|
-
const languages = new Set<string>();
|
|
297
|
-
|
|
298
|
-
for (const file of files) {
|
|
299
|
-
const lang = this.detectLanguage(file);
|
|
300
|
-
if (lang && lang !== 'Unknown') languages.add(lang);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
return Array.from(languages);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
private detectLanguage(filePath: string): string {
|
|
307
|
-
const ext = extname(filePath).toLowerCase();
|
|
308
|
-
|
|
309
|
-
const languageMap: Record<string, string> = {
|
|
310
|
-
'.js': 'JavaScript',
|
|
311
|
-
'.ts': 'TypeScript',
|
|
312
|
-
'.tsx': 'TypeScript',
|
|
313
|
-
'.jsx': 'JavaScript',
|
|
314
|
-
'.py': 'Python',
|
|
315
|
-
'.java': 'Java',
|
|
316
|
-
'.go': 'Go',
|
|
317
|
-
'.rb': 'Ruby',
|
|
318
|
-
'.php': 'PHP',
|
|
319
|
-
'.cs': 'C#',
|
|
320
|
-
'.cpp': 'C++',
|
|
321
|
-
'.c': 'C',
|
|
322
|
-
'.rs': 'Rust',
|
|
323
|
-
'.kt': 'Kotlin',
|
|
324
|
-
'.scala': 'Scala',
|
|
325
|
-
'.sql': 'SQL',
|
|
326
|
-
'.graphql': 'GraphQL',
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
return languageMap[ext] || 'Unknown';
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
private countLines(filePath: string): number {
|
|
333
|
-
try {
|
|
334
|
-
const content = readFileSync(filePath, 'utf-8');
|
|
335
|
-
return content.split('\n').length;
|
|
336
|
-
} catch {
|
|
337
|
-
return 0;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
private countTotalLines(files: string[]): number {
|
|
342
|
-
return files.reduce((total, file) => total + this.countLines(file), 0);
|
|
343
|
-
}
|
|
344
|
-
}
|
package/src/scorer.ts
DELETED
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
import { ArchitectureScore, DependencyEdge, AntiPattern, ScoreComponent } from './types.js';
|
|
2
|
-
import { basename } from 'path';
|
|
3
|
-
|
|
4
|
-
export class ArchitectureScorer {
|
|
5
|
-
private modularity: number = 0;
|
|
6
|
-
private coupling: number = 0;
|
|
7
|
-
private cohesion: number = 0;
|
|
8
|
-
private layering: number = 0;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Barrel/index files that naturally have many connections and should be
|
|
12
|
-
* excluded from coupling max-edge penalty calculations.
|
|
13
|
-
*/
|
|
14
|
-
private static readonly BARREL_FILES = new Set([
|
|
15
|
-
'__init__.py', 'index.ts', 'index.js', 'index.tsx', 'index.jsx',
|
|
16
|
-
'mod.rs', '__init__.pyi',
|
|
17
|
-
]);
|
|
18
|
-
|
|
19
|
-
score(
|
|
20
|
-
edges: DependencyEdge[],
|
|
21
|
-
antiPatterns: AntiPattern[],
|
|
22
|
-
totalFiles: number
|
|
23
|
-
): ArchitectureScore {
|
|
24
|
-
this.calculateModularity(edges, totalFiles);
|
|
25
|
-
this.calculateCoupling(edges, totalFiles);
|
|
26
|
-
this.calculateCohesion(edges);
|
|
27
|
-
this.calculateLayering(antiPatterns, totalFiles);
|
|
28
|
-
|
|
29
|
-
const components = [
|
|
30
|
-
{
|
|
31
|
-
name: 'Modularity',
|
|
32
|
-
score: Math.round(this.modularity),
|
|
33
|
-
maxScore: 100,
|
|
34
|
-
weight: 0.4,
|
|
35
|
-
explanation:
|
|
36
|
-
'Measures appropriate module boundaries and single responsibility principle adherence',
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
name: 'Coupling',
|
|
40
|
-
score: Math.round(this.coupling),
|
|
41
|
-
maxScore: 100,
|
|
42
|
-
weight: 0.25,
|
|
43
|
-
explanation:
|
|
44
|
-
'Evaluates interdependencies between modules; lower coupling is better',
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
name: 'Cohesion',
|
|
48
|
-
score: Math.round(this.cohesion),
|
|
49
|
-
maxScore: 100,
|
|
50
|
-
weight: 0.2,
|
|
51
|
-
explanation:
|
|
52
|
-
'Assesses how closely related functionality is grouped together',
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
name: 'Layering',
|
|
56
|
-
score: Math.round(this.layering),
|
|
57
|
-
maxScore: 100,
|
|
58
|
-
weight: 0.15,
|
|
59
|
-
explanation: 'Checks adherence to architectural layer separation',
|
|
60
|
-
},
|
|
61
|
-
];
|
|
62
|
-
|
|
63
|
-
const overall = Math.round(
|
|
64
|
-
components[0].score * components[0].weight +
|
|
65
|
-
components[1].score * components[1].weight +
|
|
66
|
-
components[2].score * components[2].weight +
|
|
67
|
-
components[3].score * components[3].weight
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
return {
|
|
71
|
-
overall: Math.min(100, Math.max(0, overall)),
|
|
72
|
-
components,
|
|
73
|
-
breakdown: {
|
|
74
|
-
modularity: Math.round(this.modularity),
|
|
75
|
-
coupling: Math.round(this.coupling),
|
|
76
|
-
cohesion: Math.round(this.cohesion),
|
|
77
|
-
layering: Math.round(this.layering),
|
|
78
|
-
},
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
private calculateModularity(edges: DependencyEdge[], totalFiles: number): void {
|
|
83
|
-
if (totalFiles === 0) {
|
|
84
|
-
this.modularity = 50;
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const avgEdgesPerFile = edges.length / totalFiles;
|
|
89
|
-
|
|
90
|
-
if (avgEdgesPerFile < 2) {
|
|
91
|
-
this.modularity = 95;
|
|
92
|
-
} else if (avgEdgesPerFile < 4) {
|
|
93
|
-
this.modularity = 85;
|
|
94
|
-
} else if (avgEdgesPerFile < 6) {
|
|
95
|
-
this.modularity = 70;
|
|
96
|
-
} else if (avgEdgesPerFile < 10) {
|
|
97
|
-
this.modularity = 50;
|
|
98
|
-
} else {
|
|
99
|
-
this.modularity = 30;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
private calculateCoupling(edges: DependencyEdge[], totalFiles: number): void {
|
|
104
|
-
if (totalFiles === 0 || totalFiles === 1) {
|
|
105
|
-
this.coupling = 50;
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Exclude barrel/index files from max-edge calculation —
|
|
110
|
-
// they naturally have many connections by design.
|
|
111
|
-
const nonBarrelEdges = edges.filter((e) => {
|
|
112
|
-
const fromFile = basename(e.from);
|
|
113
|
-
const toFile = basename(e.to);
|
|
114
|
-
return !ArchitectureScorer.BARREL_FILES.has(fromFile) &&
|
|
115
|
-
!ArchitectureScorer.BARREL_FILES.has(toFile);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
const nodeWithMaxEdges = this.findNodeWithMaxEdges(nonBarrelEdges);
|
|
119
|
-
const maxEdgeCount = nodeWithMaxEdges ? nodeWithMaxEdges.count : 0;
|
|
120
|
-
|
|
121
|
-
// Use non-barrel file count for ratio calculation
|
|
122
|
-
const effectiveFiles = Math.max(totalFiles - 1, 1);
|
|
123
|
-
const couplingRatio = maxEdgeCount / effectiveFiles;
|
|
124
|
-
|
|
125
|
-
// More granular thresholds
|
|
126
|
-
if (couplingRatio < 0.15) {
|
|
127
|
-
this.coupling = 95;
|
|
128
|
-
} else if (couplingRatio < 0.25) {
|
|
129
|
-
this.coupling = 85;
|
|
130
|
-
} else if (couplingRatio < 0.35) {
|
|
131
|
-
this.coupling = 75;
|
|
132
|
-
} else if (couplingRatio < 0.5) {
|
|
133
|
-
this.coupling = 65;
|
|
134
|
-
} else if (couplingRatio < 0.7) {
|
|
135
|
-
this.coupling = 50;
|
|
136
|
-
} else if (couplingRatio < 0.85) {
|
|
137
|
-
this.coupling = 35;
|
|
138
|
-
} else {
|
|
139
|
-
this.coupling = 20;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
private findNodeWithMaxEdges(
|
|
144
|
-
edges: DependencyEdge[]
|
|
145
|
-
): { node: string; count: number } | null {
|
|
146
|
-
const nodeEdgeCount: Record<string, number> = {};
|
|
147
|
-
|
|
148
|
-
for (const edge of edges) {
|
|
149
|
-
nodeEdgeCount[edge.from] = (nodeEdgeCount[edge.from] || 0) + 1;
|
|
150
|
-
nodeEdgeCount[edge.to] = (nodeEdgeCount[edge.to] || 0) + 1;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
let maxNode: string | null = null;
|
|
154
|
-
let maxCount = 0;
|
|
155
|
-
|
|
156
|
-
for (const [node, count] of Object.entries(nodeEdgeCount)) {
|
|
157
|
-
if (count > maxCount) {
|
|
158
|
-
maxCount = count;
|
|
159
|
-
maxNode = node;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return maxNode ? { node: maxNode, count: maxCount } : null;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
private calculateCohesion(edges: DependencyEdge[]): void {
|
|
167
|
-
if (edges.length === 0) {
|
|
168
|
-
this.cohesion = 50;
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const internalEdges = edges.filter((e) =>
|
|
173
|
-
this.isInternalDependency(e.from, e.to)
|
|
174
|
-
).length;
|
|
175
|
-
|
|
176
|
-
const cohesionRatio = internalEdges / edges.length;
|
|
177
|
-
|
|
178
|
-
// More granular thresholds
|
|
179
|
-
if (cohesionRatio > 0.8) {
|
|
180
|
-
this.cohesion = 95;
|
|
181
|
-
} else if (cohesionRatio > 0.6) {
|
|
182
|
-
this.cohesion = 85;
|
|
183
|
-
} else if (cohesionRatio > 0.45) {
|
|
184
|
-
this.cohesion = 75;
|
|
185
|
-
} else if (cohesionRatio > 0.3) {
|
|
186
|
-
this.cohesion = 65;
|
|
187
|
-
} else if (cohesionRatio > 0.15) {
|
|
188
|
-
this.cohesion = 50;
|
|
189
|
-
} else {
|
|
190
|
-
this.cohesion = 30;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Determines if a dependency is "internal" (cohesive).
|
|
196
|
-
* Two files are considered cohesive if they share the same top-level
|
|
197
|
-
* package/directory (e.g., deepguard/cli.py → deepguard/analyzer.py).
|
|
198
|
-
* This is crucial for Python flat packages where all files live in
|
|
199
|
-
* one directory but ARE cohesive.
|
|
200
|
-
*/
|
|
201
|
-
private isInternalDependency(from: string, to: string): boolean {
|
|
202
|
-
const fromParts = from.split('/');
|
|
203
|
-
const toParts = to.split('/');
|
|
204
|
-
|
|
205
|
-
// If both are in root (no directory), they're cohesive
|
|
206
|
-
if (fromParts.length <= 1 && toParts.length <= 1) return true;
|
|
207
|
-
|
|
208
|
-
// Compare top-level directory (package name)
|
|
209
|
-
// e.g., "deepguard/cli.py" and "deepguard/analyzer.py" → same package
|
|
210
|
-
const fromTopLevel = fromParts.length > 1 ? fromParts[0] : '';
|
|
211
|
-
const toTopLevel = toParts.length > 1 ? toParts[0] : '';
|
|
212
|
-
|
|
213
|
-
return fromTopLevel === toTopLevel;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
private calculateLayering(antiPatterns: AntiPattern[], totalFiles?: number): void {
|
|
217
|
-
const layeringViolations = antiPatterns.filter(
|
|
218
|
-
(p) =>
|
|
219
|
-
p.name === 'Leaky Abstraction' ||
|
|
220
|
-
p.name === 'Shotgun Surgery' ||
|
|
221
|
-
p.name === 'Circular Dependency'
|
|
222
|
-
).length;
|
|
223
|
-
|
|
224
|
-
if (layeringViolations === 0) {
|
|
225
|
-
this.layering = 95;
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Use ratio-based scoring: violations per 100 files
|
|
230
|
-
// This makes scoring fair regardless of project size
|
|
231
|
-
const fileCount = Math.max(totalFiles || 50, 10);
|
|
232
|
-
const violationRatio = layeringViolations / fileCount;
|
|
233
|
-
|
|
234
|
-
if (violationRatio < 0.02) {
|
|
235
|
-
// < 2% — e.g. 1 violation in 50 files
|
|
236
|
-
this.layering = 90;
|
|
237
|
-
} else if (violationRatio < 0.05) {
|
|
238
|
-
// < 5% — e.g. 2-3 violations in 50 files
|
|
239
|
-
this.layering = 80;
|
|
240
|
-
} else if (violationRatio < 0.1) {
|
|
241
|
-
// < 10% — e.g. 5 violations in 50 files
|
|
242
|
-
this.layering = 65;
|
|
243
|
-
} else if (violationRatio < 0.2) {
|
|
244
|
-
// < 20% — significant issues
|
|
245
|
-
this.layering = 50;
|
|
246
|
-
} else if (violationRatio < 0.35) {
|
|
247
|
-
this.layering = 35;
|
|
248
|
-
} else {
|
|
249
|
-
// > 35% — severe layering problems
|
|
250
|
-
this.layering = 20;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|