@girardelli/architect-core 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/src/core/analyzer.d.ts +42 -0
- package/dist/src/core/analyzer.js +431 -0
- package/dist/src/core/analyzer.js.map +1 -0
- package/dist/src/core/analyzers/forecast.d.ts +84 -0
- package/dist/src/core/analyzers/forecast.js +338 -0
- package/dist/src/core/analyzers/forecast.js.map +1 -0
- package/dist/src/core/analyzers/index.d.ts +9 -0
- package/dist/src/core/analyzers/index.js +7 -0
- package/dist/src/core/analyzers/index.js.map +1 -0
- package/dist/src/core/analyzers/temporal-scorer.d.ts +71 -0
- package/dist/src/core/analyzers/temporal-scorer.js +141 -0
- package/dist/src/core/analyzers/temporal-scorer.js.map +1 -0
- package/dist/src/core/anti-patterns.d.ts +28 -0
- package/dist/src/core/anti-patterns.js +264 -0
- package/dist/src/core/anti-patterns.js.map +1 -0
- package/dist/src/core/ast/ast-parser.interface.d.ts +20 -0
- package/dist/src/core/ast/ast-parser.interface.js +2 -0
- package/dist/src/core/ast/ast-parser.interface.js.map +1 -0
- package/dist/src/core/ast/path-resolver.d.ts +13 -0
- package/dist/src/core/ast/path-resolver.js +54 -0
- package/dist/src/core/ast/path-resolver.js.map +1 -0
- package/dist/src/core/ast/tree-sitter-parser.d.ts +10 -0
- package/dist/src/core/ast/tree-sitter-parser.js +142 -0
- package/dist/src/core/ast/tree-sitter-parser.js.map +1 -0
- package/dist/src/core/config.d.ts +11 -0
- package/dist/src/core/config.js +112 -0
- package/dist/src/core/config.js.map +1 -0
- package/dist/src/core/diagram.d.ts +9 -0
- package/dist/src/core/diagram.js +101 -0
- package/dist/src/core/diagram.js.map +1 -0
- package/dist/src/core/i18n.d.ts +14 -0
- package/dist/src/core/i18n.js +54 -0
- package/dist/src/core/i18n.js.map +1 -0
- package/dist/src/core/locales/en.d.ts +2 -0
- package/dist/src/core/locales/en.js +337 -0
- package/dist/src/core/locales/en.js.map +1 -0
- package/dist/src/core/locales/pt-BR.d.ts +172 -0
- package/dist/src/core/locales/pt-BR.js +337 -0
- package/dist/src/core/locales/pt-BR.js.map +1 -0
- package/dist/src/core/locales/types.d.ts +86 -0
- package/dist/src/core/locales/types.js +2 -0
- package/dist/src/core/locales/types.js.map +1 -0
- package/dist/src/core/plugin-loader.d.ts +11 -0
- package/dist/src/core/plugin-loader.js +67 -0
- package/dist/src/core/plugin-loader.js.map +1 -0
- package/dist/src/core/project-summarizer.d.ts +16 -0
- package/dist/src/core/project-summarizer.js +37 -0
- package/dist/src/core/project-summarizer.js.map +1 -0
- package/dist/src/core/refactor-engine.d.ts +18 -0
- package/dist/src/core/refactor-engine.js +87 -0
- package/dist/src/core/refactor-engine.js.map +1 -0
- package/dist/src/core/rules/barrel-optimizer.d.ts +13 -0
- package/dist/src/core/rules/barrel-optimizer.js +76 -0
- package/dist/src/core/rules/barrel-optimizer.js.map +1 -0
- package/dist/src/core/rules/dead-code-detector.d.ts +21 -0
- package/dist/src/core/rules/dead-code-detector.js +116 -0
- package/dist/src/core/rules/dead-code-detector.js.map +1 -0
- package/dist/src/core/rules/hub-splitter.d.ts +13 -0
- package/dist/src/core/rules/hub-splitter.js +117 -0
- package/dist/src/core/rules/hub-splitter.js.map +1 -0
- package/dist/src/core/rules/import-organizer.d.ts +13 -0
- package/dist/src/core/rules/import-organizer.js +84 -0
- package/dist/src/core/rules/import-organizer.js.map +1 -0
- package/dist/src/core/rules/module-grouper.d.ts +13 -0
- package/dist/src/core/rules/module-grouper.js +116 -0
- package/dist/src/core/rules/module-grouper.js.map +1 -0
- package/dist/src/core/rules-engine.d.ts +7 -0
- package/dist/src/core/rules-engine.js +89 -0
- package/dist/src/core/rules-engine.js.map +1 -0
- package/dist/src/core/scorer.d.ts +15 -0
- package/dist/src/core/scorer.js +165 -0
- package/dist/src/core/scorer.js.map +1 -0
- package/dist/src/core/summarizer/keyword-extractor.d.ts +6 -0
- package/dist/src/core/summarizer/keyword-extractor.js +38 -0
- package/dist/src/core/summarizer/keyword-extractor.js.map +1 -0
- package/dist/src/core/summarizer/module-inferrer.d.ts +11 -0
- package/dist/src/core/summarizer/module-inferrer.js +171 -0
- package/dist/src/core/summarizer/module-inferrer.js.map +1 -0
- package/dist/src/core/summarizer/package-reader.d.ts +3 -0
- package/dist/src/core/summarizer/package-reader.js +33 -0
- package/dist/src/core/summarizer/package-reader.js.map +1 -0
- package/dist/src/core/summarizer/purpose-inferrer.d.ts +8 -0
- package/dist/src/core/summarizer/purpose-inferrer.js +179 -0
- package/dist/src/core/summarizer/purpose-inferrer.js.map +1 -0
- package/dist/src/core/summarizer/readme-reader.d.ts +3 -0
- package/dist/src/core/summarizer/readme-reader.js +24 -0
- package/dist/src/core/summarizer/readme-reader.js.map +1 -0
- package/dist/src/core/types/architect-rules.d.ts +27 -0
- package/dist/src/core/types/architect-rules.js +2 -0
- package/dist/src/core/types/architect-rules.js.map +1 -0
- package/dist/src/core/types/core.d.ts +87 -0
- package/dist/src/core/types/core.js +2 -0
- package/dist/src/core/types/core.js.map +1 -0
- package/dist/src/core/types/infrastructure.d.ts +38 -0
- package/dist/src/core/types/infrastructure.js +2 -0
- package/dist/src/core/types/infrastructure.js.map +1 -0
- package/dist/src/core/types/plugin.d.ts +12 -0
- package/dist/src/core/types/plugin.js +2 -0
- package/dist/src/core/types/plugin.js.map +1 -0
- package/dist/src/core/types/rules.d.ts +53 -0
- package/dist/src/core/types/rules.js +2 -0
- package/dist/src/core/types/rules.js.map +1 -0
- package/dist/src/core/types/summarizer.d.ts +12 -0
- package/dist/src/core/types/summarizer.js +2 -0
- package/dist/src/core/types/summarizer.js.map +1 -0
- package/dist/src/infrastructure/git-cache.d.ts +6 -0
- package/dist/src/infrastructure/git-cache.js +41 -0
- package/dist/src/infrastructure/git-cache.js.map +1 -0
- package/dist/src/infrastructure/git-history.d.ts +112 -0
- package/dist/src/infrastructure/git-history.js +340 -0
- package/dist/src/infrastructure/git-history.js.map +1 -0
- package/dist/src/infrastructure/logger.d.ts +20 -0
- package/dist/src/infrastructure/logger.js +57 -0
- package/dist/src/infrastructure/logger.js.map +1 -0
- package/dist/src/infrastructure/scanner.d.ts +31 -0
- package/dist/src/infrastructure/scanner.js +334 -0
- package/dist/src/infrastructure/scanner.js.map +1 -0
- package/dist/tests/analyzers-integration.test.d.ts +7 -0
- package/dist/tests/analyzers-integration.test.js +140 -0
- package/dist/tests/analyzers-integration.test.js.map +1 -0
- package/dist/tests/anti-patterns.test.d.ts +1 -0
- package/dist/tests/anti-patterns.test.js +81 -0
- package/dist/tests/anti-patterns.test.js.map +1 -0
- package/dist/tests/ast-parser.test.d.ts +1 -0
- package/dist/tests/ast-parser.test.js +94 -0
- package/dist/tests/ast-parser.test.js.map +1 -0
- package/dist/tests/fixtures/monorepo/packages/app/src/index.d.ts +1 -0
- package/dist/tests/fixtures/monorepo/packages/app/src/index.js +9 -0
- package/dist/tests/fixtures/monorepo/packages/app/src/index.js.map +1 -0
- package/dist/tests/fixtures/monorepo/packages/core/src/index.d.ts +2 -0
- package/dist/tests/fixtures/monorepo/packages/core/src/index.js +11 -0
- package/dist/tests/fixtures/monorepo/packages/core/src/index.js.map +1 -0
- package/dist/tests/forecast.test.d.ts +7 -0
- package/dist/tests/forecast.test.js +380 -0
- package/dist/tests/forecast.test.js.map +1 -0
- package/dist/tests/git-history.test.d.ts +7 -0
- package/dist/tests/git-history.test.js +193 -0
- package/dist/tests/git-history.test.js.map +1 -0
- package/dist/tests/i18n.test.d.ts +1 -0
- package/dist/tests/i18n.test.js +39 -0
- package/dist/tests/i18n.test.js.map +1 -0
- package/dist/tests/monorepo-scan.test.d.ts +11 -0
- package/dist/tests/monorepo-scan.test.js +143 -0
- package/dist/tests/monorepo-scan.test.js.map +1 -0
- package/dist/tests/plugin-loader.test.d.ts +1 -0
- package/dist/tests/plugin-loader.test.js +31 -0
- package/dist/tests/plugin-loader.test.js.map +1 -0
- package/dist/tests/rules-engine.test.d.ts +1 -0
- package/dist/tests/rules-engine.test.js +112 -0
- package/dist/tests/rules-engine.test.js.map +1 -0
- package/dist/tests/scanner.test.d.ts +1 -0
- package/dist/tests/scanner.test.js +44 -0
- package/dist/tests/scanner.test.js.map +1 -0
- package/dist/tests/scorer.test.d.ts +1 -0
- package/dist/tests/scorer.test.js +610 -0
- package/dist/tests/scorer.test.js.map +1 -0
- package/dist/tests/temporal-scorer.test.d.ts +7 -0
- package/dist/tests/temporal-scorer.test.js +239 -0
- package/dist/tests/temporal-scorer.test.js.map +1 -0
- package/package.json +29 -0
- package/src/core/analyzer.ts +499 -0
- package/src/core/analyzers/forecast.ts +497 -0
- package/src/core/analyzers/index.ts +33 -0
- package/src/core/analyzers/temporal-scorer.ts +227 -0
- package/src/core/anti-patterns.ts +324 -0
- package/src/core/ast/ast-parser.interface.ts +21 -0
- package/src/core/ast/path-resolver.ts +61 -0
- package/src/core/ast/tree-sitter-parser.ts +158 -0
- package/src/core/config.ts +125 -0
- package/src/core/diagram.ts +129 -0
- package/src/core/i18n.ts +64 -0
- package/src/core/locales/en.ts +340 -0
- package/src/core/locales/pt-BR.ts +341 -0
- package/src/core/locales/types.ts +95 -0
- package/src/core/plugin-loader.ts +80 -0
- package/src/core/project-summarizer.ts +42 -0
- package/src/core/refactor-engine.ts +112 -0
- package/src/core/rules/barrel-optimizer.ts +99 -0
- package/src/core/rules/dead-code-detector.ts +134 -0
- package/src/core/rules/hub-splitter.ts +135 -0
- package/src/core/rules/import-organizer.ts +100 -0
- package/src/core/rules/module-grouper.ts +133 -0
- package/src/core/rules-engine.ts +100 -0
- package/src/core/scorer.ts +181 -0
- package/src/core/summarizer/keyword-extractor.ts +53 -0
- package/src/core/summarizer/module-inferrer.ts +194 -0
- package/src/core/summarizer/package-reader.ts +34 -0
- package/src/core/summarizer/purpose-inferrer.ts +197 -0
- package/src/core/summarizer/readme-reader.ts +24 -0
- package/src/core/types/architect-rules.ts +29 -0
- package/src/core/types/core.ts +94 -0
- package/src/core/types/infrastructure.ts +41 -0
- package/src/core/types/plugin.ts +19 -0
- package/src/core/types/rules.ts +51 -0
- package/src/core/types/summarizer.ts +8 -0
- package/src/infrastructure/git-cache.ts +52 -0
- package/src/infrastructure/git-history.ts +496 -0
- package/src/infrastructure/logger.ts +68 -0
- package/src/infrastructure/scanner.ts +349 -0
- package/tests/analyzers-integration.test.ts +174 -0
- package/tests/anti-patterns.test.ts +95 -0
- package/tests/ast-parser.test.ts +102 -0
- package/tests/fixtures/monorepo/package.json +6 -0
- package/tests/fixtures/monorepo/packages/app/package.json +12 -0
- package/tests/fixtures/monorepo/packages/app/src/index.ts +6 -0
- package/tests/fixtures/monorepo/packages/core/package.json +7 -0
- package/tests/fixtures/monorepo/packages/core/src/index.ts +7 -0
- package/tests/forecast.test.ts +504 -0
- package/tests/git-history.test.ts +254 -0
- package/tests/i18n.test.ts +47 -0
- package/tests/monorepo-scan.test.ts +170 -0
- package/tests/plugin-loader.test.ts +40 -0
- package/tests/rules-engine.test.ts +131 -0
- package/tests/scanner.test.ts +54 -0
- package/tests/scorer.test.ts +675 -0
- package/tests/temporal-scorer.test.ts +306 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { basename, dirname } from 'path';
|
|
2
|
+
/**
|
|
3
|
+
* Import Organizer Rule (Tier 1)
|
|
4
|
+
* Detects files that import from too many different modules (cross-boundary).
|
|
5
|
+
* Suggests dependency injection or facade patterns.
|
|
6
|
+
*/
|
|
7
|
+
export class ImportOrganizerRule {
|
|
8
|
+
name = 'import-organizer';
|
|
9
|
+
tier = 1;
|
|
10
|
+
analyze(report, _projectPath) {
|
|
11
|
+
const steps = [];
|
|
12
|
+
// Find files that import from many different directories
|
|
13
|
+
const crossBoundary = {};
|
|
14
|
+
for (const edge of report.dependencyGraph.edges) {
|
|
15
|
+
const fromDir = dirname(edge.from);
|
|
16
|
+
const toDir = dirname(edge.to);
|
|
17
|
+
if (!crossBoundary[edge.from]) {
|
|
18
|
+
crossBoundary[edge.from] = { targets: new Set(), dirs: new Set() };
|
|
19
|
+
}
|
|
20
|
+
crossBoundary[edge.from].targets.add(edge.to);
|
|
21
|
+
if (fromDir !== toDir) {
|
|
22
|
+
crossBoundary[edge.from].dirs.add(toDir);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Outliers reais espalhados consumindo +5 diretórios. Testa bypass em unit-tests (.test.) naturais de altíssima injeção (mock).
|
|
26
|
+
const violators = Object.entries(crossBoundary)
|
|
27
|
+
.filter(([fileName, data]) => data.dirs.size >= 5 && !fileName.includes('.test.'))
|
|
28
|
+
.sort((a, b) => b[1].dirs.size - a[1].dirs.size);
|
|
29
|
+
for (const [file, data] of violators) {
|
|
30
|
+
const operations = [];
|
|
31
|
+
const fileName = basename(file);
|
|
32
|
+
const fileDir = dirname(file);
|
|
33
|
+
// Suggest creating a facade/service layer
|
|
34
|
+
const ext = fileName.split('.').pop() || 'py';
|
|
35
|
+
const nameBase = fileName.replace(/\.[^.]+$/, '');
|
|
36
|
+
const facadePath = `${fileDir}/${nameBase}_deps.${ext}`;
|
|
37
|
+
operations.push({
|
|
38
|
+
type: 'CREATE',
|
|
39
|
+
path: facadePath,
|
|
40
|
+
description: `Create dependency facade \`${basename(facadePath)}\` — centralizes ${data.dirs.size} cross-module imports`,
|
|
41
|
+
content: this.generateFacadeContent(ext, Array.from(data.targets), Array.from(data.dirs)),
|
|
42
|
+
});
|
|
43
|
+
operations.push({
|
|
44
|
+
type: 'MODIFY',
|
|
45
|
+
path: file,
|
|
46
|
+
description: `Refactor \`${fileName}\` to import from local facade instead of ${data.dirs.size} different modules`,
|
|
47
|
+
});
|
|
48
|
+
steps.push({
|
|
49
|
+
id: 0,
|
|
50
|
+
tier: 1,
|
|
51
|
+
rule: this.name,
|
|
52
|
+
priority: data.dirs.size >= 5 ? 'HIGH' : 'MEDIUM',
|
|
53
|
+
title: `Reduce cross-boundary imports: ${fileName}`,
|
|
54
|
+
description: `\`${file}\` imports from ${data.dirs.size} different modules: ` +
|
|
55
|
+
`${Array.from(data.dirs).map((d) => `\`${d}\``).join(', ')}. ` +
|
|
56
|
+
`Consider using a facade or dependency injection.`,
|
|
57
|
+
rationale: `Files with imports scattered across many modules have high afferent coupling. ` +
|
|
58
|
+
`A facade centralizes these dependencies, making the file easier to test (mock one facade) ` +
|
|
59
|
+
`and reducing the impact of changes in dependent modules.`,
|
|
60
|
+
operations,
|
|
61
|
+
scoreImpact: [
|
|
62
|
+
{ metric: 'cohesion', before: report.score.breakdown.cohesion, after: Math.min(95, report.score.breakdown.cohesion + 5) },
|
|
63
|
+
{ metric: 'coupling', before: report.score.breakdown.coupling, after: Math.min(95, report.score.breakdown.coupling + 5) },
|
|
64
|
+
],
|
|
65
|
+
aiPrompt: `Analyze the file \`${file}\` which currently imports from ${data.dirs.size} cross-boundary distinct directories/modules (${Array.from(data.dirs).join(', ')}).\nPlease refactor this to use the Dependency Injection (DI) or Facade pattern:\n1. Extract these imports into a dedicated file \`${facadePath}\`.\n2. Re-export or bundle them appropriately.\n3. Update \`${file}\` to import strictly from the new \`${facadePath}\` rather than directly reaching into multiple disjointed modules, lowering its afferent coupling.`,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return steps;
|
|
69
|
+
}
|
|
70
|
+
generateFacadeContent(ext, targets, dirs) {
|
|
71
|
+
if (ext === 'py') {
|
|
72
|
+
const imports = targets
|
|
73
|
+
.map((t) => `# from ${t.replace(/\//g, '.')} import ...`)
|
|
74
|
+
.join('\n');
|
|
75
|
+
return `"""Dependency facade — centralizes cross-module imports."""\n\n${imports}\n\n# Re-export what ${dirs.length} modules need\n`;
|
|
76
|
+
}
|
|
77
|
+
// JS/TS
|
|
78
|
+
const imports = targets
|
|
79
|
+
.map((t) => `// export { ... } from '${t}';`)
|
|
80
|
+
.join('\n');
|
|
81
|
+
return `/**\n * Dependency facade — centralizes cross-module imports.\n */\n\n${imports}\n`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=import-organizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-organizer.js","sourceRoot":"","sources":["../../../../src/core/rules/import-organizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAIzC;;;;GAIG;AACH,MAAM,OAAO,mBAAmB;IAC9B,IAAI,GAAG,kBAAkB,CAAC;IAC1B,IAAI,GAAG,CAAU,CAAC;IAElB,OAAO,CAAC,MAAsB,EAAE,YAAoB;QAClD,MAAM,KAAK,GAAmB,EAAE,CAAC;QAEjC,yDAAyD;QACzD,MAAM,aAAa,GAAgE,EAAE,CAAC;QAEtF,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;YAChD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAE/B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;YACrE,CAAC;YACD,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAE9C,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;gBACtB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC3C,CAAC;QACH,CAAC;QAED,gIAAgI;QAChI,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC;aAC5C,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;aACjF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEnD,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;YACrC,MAAM,UAAU,GAAoB,EAAE,CAAC;YACvC,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;YAChC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;YAE9B,0CAA0C;YAC1C,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC;YAC9C,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YAClD,MAAM,UAAU,GAAG,GAAG,OAAO,IAAI,QAAQ,SAAS,GAAG,EAAE,CAAC;YAExD,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,UAAU;gBAChB,WAAW,EAAE,8BAA8B,QAAQ,CAAC,UAAU,CAAC,oBAAoB,IAAI,CAAC,IAAI,CAAC,IAAI,uBAAuB;gBACxH,OAAO,EAAE,IAAI,CAAC,qBAAqB,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aAC1F,CAAC,CAAC;YAEH,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,IAAI;gBACV,WAAW,EAAE,cAAc,QAAQ,6CAA6C,IAAI,CAAC,IAAI,CAAC,IAAI,oBAAoB;aACnH,CAAC,CAAC;YAEH,KAAK,CAAC,IAAI,CAAC;gBACT,EAAE,EAAE,CAAC;gBACL,IAAI,EAAE,CAAC;gBACP,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;gBACjD,KAAK,EAAE,kCAAkC,QAAQ,EAAE;gBACnD,WAAW,EAAE,KAAK,IAAI,mBAAmB,IAAI,CAAC,IAAI,CAAC,IAAI,sBAAsB;oBAC3E,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;oBAC9D,kDAAkD;gBACpD,SAAS,EAAE,gFAAgF;oBACzF,4FAA4F;oBAC5F,0DAA0D;gBAC5D,UAAU;gBACV,WAAW,EAAE;oBACX,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,GAAG,CAAC,CAAC,EAAE;oBACzH,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,GAAG,CAAC,CAAC,EAAE;iBAC1H;gBACD,QAAQ,EAAE,sBAAsB,IAAI,mCAAmC,IAAI,CAAC,IAAI,CAAC,IAAI,iDAAiD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,sIAAsI,UAAU,gEAAgE,IAAI,wCAAwC,UAAU,oGAAoG;aACjhB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,qBAAqB,CAAC,GAAW,EAAE,OAAiB,EAAE,IAAc;QAC1E,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,OAAO;iBACpB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,aAAa,CAAC;iBACxD,IAAI,CAAC,IAAI,CAAC,CAAC;YACd,OAAO,kEAAkE,OAAO,wBAAwB,IAAI,CAAC,MAAM,iBAAiB,CAAC;QACvI,CAAC;QAED,QAAQ;QACR,MAAM,OAAO,GAAG,OAAO;aACpB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,2BAA2B,CAAC,IAAI,CAAC;aAC5C,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,OAAO,yEAAyE,OAAO,IAAI,CAAC;IAC9F,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AnalysisReport } from '../types/core.js';
|
|
2
|
+
import { RefactorRule, RefactorStep } from '../types/rules.js';
|
|
3
|
+
/**
|
|
4
|
+
* Module Grouper Rule (Tier 1)
|
|
5
|
+
* Analyzes which files are frequently imported together and suggests
|
|
6
|
+
* grouping them into cohesive modules/packages.
|
|
7
|
+
*/
|
|
8
|
+
export declare class ModuleGrouperRule implements RefactorRule {
|
|
9
|
+
name: string;
|
|
10
|
+
tier: 1;
|
|
11
|
+
analyze(report: AnalysisReport, _projectPath: string): RefactorStep[];
|
|
12
|
+
private suggestModuleName;
|
|
13
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { basename, dirname, join } from 'path';
|
|
2
|
+
/**
|
|
3
|
+
* Module Grouper Rule (Tier 1)
|
|
4
|
+
* Analyzes which files are frequently imported together and suggests
|
|
5
|
+
* grouping them into cohesive modules/packages.
|
|
6
|
+
*/
|
|
7
|
+
export class ModuleGrouperRule {
|
|
8
|
+
name = 'module-grouper';
|
|
9
|
+
tier = 1;
|
|
10
|
+
analyze(report, _projectPath) {
|
|
11
|
+
const steps = [];
|
|
12
|
+
// Build co-import matrix: which files are imported together?
|
|
13
|
+
const coImportCount = {};
|
|
14
|
+
// For each source file, see what it imports
|
|
15
|
+
const importsBySource = {};
|
|
16
|
+
for (const edge of report.dependencyGraph.edges) {
|
|
17
|
+
if (!importsBySource[edge.from])
|
|
18
|
+
importsBySource[edge.from] = [];
|
|
19
|
+
importsBySource[edge.from].push(edge.to);
|
|
20
|
+
}
|
|
21
|
+
// Count co-imports
|
|
22
|
+
for (const [_source, targets] of Object.entries(importsBySource)) {
|
|
23
|
+
for (let i = 0; i < targets.length; i++) {
|
|
24
|
+
for (let j = i + 1; j < targets.length; j++) {
|
|
25
|
+
const a = targets[i];
|
|
26
|
+
const b = targets[j];
|
|
27
|
+
if (!coImportCount[a])
|
|
28
|
+
coImportCount[a] = {};
|
|
29
|
+
if (!coImportCount[b])
|
|
30
|
+
coImportCount[b] = {};
|
|
31
|
+
coImportCount[a][b] = (coImportCount[a][b] || 0) + 1;
|
|
32
|
+
coImportCount[b][a] = (coImportCount[b][a] || 0) + 1;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Find clusters: files that are always imported together
|
|
37
|
+
const clusters = [];
|
|
38
|
+
const visited = new Set();
|
|
39
|
+
for (const [fileA, partners] of Object.entries(coImportCount)) {
|
|
40
|
+
if (visited.has(fileA))
|
|
41
|
+
continue;
|
|
42
|
+
const strongPartners = Object.entries(partners)
|
|
43
|
+
.filter(([_, count]) => count >= 3)
|
|
44
|
+
.sort((a, b) => b[1] - a[1]);
|
|
45
|
+
if (strongPartners.length >= 2) {
|
|
46
|
+
const cluster = [fileA, ...strongPartners.map(([f]) => f)];
|
|
47
|
+
const inSameDir = cluster.every((f) => dirname(f) === dirname(cluster[0]));
|
|
48
|
+
// Only suggest if NOT already in the same directory
|
|
49
|
+
if (!inSameDir) {
|
|
50
|
+
// Arquitetura Limpa: Impede arrastar Módulos Transversais (DTOs/Types) para se tornarem fechados em Pastas de Domínios
|
|
51
|
+
const hasTypes = cluster.some(f => {
|
|
52
|
+
const lowerF = f.toLowerCase();
|
|
53
|
+
return lowerF.includes('types') || lowerF.includes('interface') || lowerF.includes('/types/');
|
|
54
|
+
});
|
|
55
|
+
if (hasTypes)
|
|
56
|
+
continue;
|
|
57
|
+
const score = strongPartners.reduce((sum, [_, c]) => sum + c, 0);
|
|
58
|
+
clusters.push({ files: cluster, coImportScore: score });
|
|
59
|
+
cluster.forEach((f) => visited.add(f));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Generate steps for each cluster
|
|
64
|
+
for (const cluster of clusters.slice(0, 3)) {
|
|
65
|
+
const operations = [];
|
|
66
|
+
const clusterName = this.suggestModuleName(cluster.files);
|
|
67
|
+
const targetDir = `${dirname(cluster.files[0])}/${clusterName}`;
|
|
68
|
+
// Create new module directory
|
|
69
|
+
operations.push({
|
|
70
|
+
type: 'CREATE',
|
|
71
|
+
path: `${targetDir}/__init__.py`,
|
|
72
|
+
description: `Create new module \`${clusterName}/\` to group ${cluster.files.length} co-dependent files`,
|
|
73
|
+
content: `"""Module ${clusterName} — grouped by co-import pattern."""\n`,
|
|
74
|
+
});
|
|
75
|
+
// Move files
|
|
76
|
+
for (const file of cluster.files) {
|
|
77
|
+
const newPath = join(targetDir, basename(file));
|
|
78
|
+
operations.push({
|
|
79
|
+
type: 'MOVE',
|
|
80
|
+
path: file,
|
|
81
|
+
newPath,
|
|
82
|
+
description: `Move \`${basename(file)}\` → \`${clusterName}/${basename(file)}\``,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
steps.push({
|
|
86
|
+
id: 0,
|
|
87
|
+
tier: 1,
|
|
88
|
+
rule: this.name,
|
|
89
|
+
priority: 'MEDIUM',
|
|
90
|
+
title: `Group co-dependent files into \`${clusterName}/\``,
|
|
91
|
+
description: `Files ${cluster.files.map((f) => `\`${basename(f)}\``).join(', ')} ` +
|
|
92
|
+
`are frequently imported together (co-import score: ${cluster.coImportScore}). ` +
|
|
93
|
+
`Grouping them improves cohesion.`,
|
|
94
|
+
rationale: `Files that are frequently imported together belong in the same module. ` +
|
|
95
|
+
`This improves discoverability and reduces the cognitive load of understanding ` +
|
|
96
|
+
`which files work together.`,
|
|
97
|
+
operations,
|
|
98
|
+
scoreImpact: [
|
|
99
|
+
{ metric: 'cohesion', before: report.score.breakdown.cohesion, after: Math.min(95, report.score.breakdown.cohesion + 10) },
|
|
100
|
+
{ metric: 'modularity', before: report.score.breakdown.modularity, after: Math.min(95, report.score.breakdown.modularity + 5) },
|
|
101
|
+
],
|
|
102
|
+
aiPrompt: `Analyze the files: ${cluster.files.map(f => `\`${f}\``).join(', ')}.\nThese files are frequently imported together but are currently scattered across different directories.\nPlease refactor to improve cohesion:\n1. Create a new directory named \`${targetDir}\`.\n2. Move these files solidly into this new directory.\n3. Add an index/barrel file if appropriate.\n4. Securely scan the entire project to fix all broken imports resulting from this move.`,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return steps;
|
|
106
|
+
}
|
|
107
|
+
suggestModuleName(files) {
|
|
108
|
+
// Try to infer a common theme from filenames
|
|
109
|
+
const names = files.map((f) => basename(f).replace(/\.[^.]+$/, '').toLowerCase());
|
|
110
|
+
const commonParts = names[0].split(/[_-]/).filter((part) => names.every((n) => n.includes(part)));
|
|
111
|
+
if (commonParts.length > 0)
|
|
112
|
+
return commonParts[0];
|
|
113
|
+
return 'shared';
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=module-grouper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module-grouper.js","sourceRoot":"","sources":["../../../../src/core/rules/module-grouper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { AnalysisReport } from './types/core.js';
|
|
2
|
+
import { ArchitectRules, ValidationResult } from './types/architect-rules.js';
|
|
3
|
+
export declare class RulesEngine {
|
|
4
|
+
validate(report: AnalysisReport, rules: ArchitectRules): ValidationResult;
|
|
5
|
+
private checkQualityGates;
|
|
6
|
+
private checkBoundaries;
|
|
7
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export class RulesEngine {
|
|
2
|
+
validate(report, rules) {
|
|
3
|
+
const violations = [];
|
|
4
|
+
this.checkQualityGates(report, rules, violations);
|
|
5
|
+
this.checkBoundaries(report, rules, violations);
|
|
6
|
+
return {
|
|
7
|
+
success: violations.every(v => v.level !== 'error'),
|
|
8
|
+
violations,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
checkQualityGates(report, rules, violations) {
|
|
12
|
+
if (!rules.quality_gates)
|
|
13
|
+
return;
|
|
14
|
+
const { min_overall_score, max_critical_anti_patterns, max_high_anti_patterns } = rules.quality_gates;
|
|
15
|
+
// 1. Minimum Overall Score (Error)
|
|
16
|
+
if (min_overall_score !== undefined && report.score.overall < min_overall_score) {
|
|
17
|
+
violations.push({
|
|
18
|
+
level: 'error',
|
|
19
|
+
rule: 'quality_gates.min_overall_score',
|
|
20
|
+
message: `Overall score (${report.score.overall}) is below the minimum required (${min_overall_score}).`,
|
|
21
|
+
actual: report.score.overall,
|
|
22
|
+
expected: min_overall_score,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
// 2. Max Critical Anti-Patterns (Error)
|
|
26
|
+
if (max_critical_anti_patterns !== undefined) {
|
|
27
|
+
const criticalCount = report.antiPatterns.filter(p => p.severity === 'CRITICAL').length;
|
|
28
|
+
if (criticalCount > max_critical_anti_patterns) {
|
|
29
|
+
violations.push({
|
|
30
|
+
level: 'error',
|
|
31
|
+
rule: 'quality_gates.max_critical_anti_patterns',
|
|
32
|
+
message: `Too many CRITICAL anti-patterns detected (${criticalCount}). Maximum allowed is ${max_critical_anti_patterns}.`,
|
|
33
|
+
actual: criticalCount,
|
|
34
|
+
expected: max_critical_anti_patterns,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// 3. Max High Anti-Patterns (Warning)
|
|
39
|
+
if (max_high_anti_patterns !== undefined) {
|
|
40
|
+
const highCount = report.antiPatterns.filter(p => p.severity === 'HIGH').length;
|
|
41
|
+
if (highCount > max_high_anti_patterns) {
|
|
42
|
+
violations.push({
|
|
43
|
+
level: 'warning',
|
|
44
|
+
rule: 'quality_gates.max_high_anti_patterns',
|
|
45
|
+
message: `High number of HIGH severity anti-patterns (${highCount}). Maximum recommended is ${max_high_anti_patterns}.`,
|
|
46
|
+
actual: highCount,
|
|
47
|
+
expected: max_high_anti_patterns,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
checkBoundaries(report, rules, violations) {
|
|
53
|
+
if (!rules.boundaries)
|
|
54
|
+
return;
|
|
55
|
+
const { allow_circular_dependencies, banned_imports } = rules.boundaries;
|
|
56
|
+
// 1. Allow Circular Dependencies
|
|
57
|
+
if (allow_circular_dependencies === false) {
|
|
58
|
+
const hasCycles = report.antiPatterns.filter(p => p.name === 'Circular Dependency');
|
|
59
|
+
if (hasCycles.length > 0) {
|
|
60
|
+
violations.push({
|
|
61
|
+
level: 'error',
|
|
62
|
+
rule: 'boundaries.allow_circular_dependencies',
|
|
63
|
+
message: `Circular dependencies are strictly forbidden, but ${hasCycles.length} were found.`,
|
|
64
|
+
actual: hasCycles.length,
|
|
65
|
+
expected: 0,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// 2. Banned Imports (Checks the AST Dependency Graph edges)
|
|
70
|
+
if (banned_imports && banned_imports.length > 0) {
|
|
71
|
+
for (const edge of report.dependencyGraph.edges) {
|
|
72
|
+
// Look through 'to' targets
|
|
73
|
+
for (const banned of banned_imports) {
|
|
74
|
+
if (edge.to.includes(banned)) {
|
|
75
|
+
// Found a banned import!
|
|
76
|
+
violations.push({
|
|
77
|
+
level: 'error',
|
|
78
|
+
rule: 'boundaries.banned_imports',
|
|
79
|
+
message: `Banned import detected: File '${edge.from}' imports '${banned}'.`,
|
|
80
|
+
actual: edge.to,
|
|
81
|
+
expected: `Not to include ${banned}`,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=rules-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules-engine.js","sourceRoot":"","sources":["../../../src/core/rules-engine.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,WAAW;IACf,QAAQ,CAAC,MAAsB,EAAE,KAAqB;QAC3D,MAAM,UAAU,GAAoB,EAAE,CAAC;QAEvC,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QAClD,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QAEhD,OAAO;YACL,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC;YACnD,UAAU;SACX,CAAC;IACJ,CAAC;IAEO,iBAAiB,CAAC,MAAsB,EAAE,KAAqB,EAAE,UAA2B;QAClG,IAAI,CAAC,KAAK,CAAC,aAAa;YAAE,OAAO;QAEjC,MAAM,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,GAAG,KAAK,CAAC,aAAa,CAAC;QAEtG,mCAAmC;QACnC,IAAI,iBAAiB,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,iBAAiB,EAAE,CAAC;YAChF,UAAU,CAAC,IAAI,CAAC;gBACd,KAAK,EAAE,OAAO;gBACd,IAAI,EAAE,iCAAiC;gBACvC,OAAO,EAAE,kBAAkB,MAAM,CAAC,KAAK,CAAC,OAAO,oCAAoC,iBAAiB,IAAI;gBACxG,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO;gBAC5B,QAAQ,EAAE,iBAAiB;aAC5B,CAAC,CAAC;QACL,CAAC;QAED,wCAAwC;QACxC,IAAI,0BAA0B,KAAK,SAAS,EAAE,CAAC;YAC7C,MAAM,aAAa,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM,CAAC;YACxF,IAAI,aAAa,GAAG,0BAA0B,EAAE,CAAC;gBAC/C,UAAU,CAAC,IAAI,CAAC;oBACd,KAAK,EAAE,OAAO;oBACd,IAAI,EAAE,0CAA0C;oBAChD,OAAO,EAAE,6CAA6C,aAAa,yBAAyB,0BAA0B,GAAG;oBACzH,MAAM,EAAE,aAAa;oBACrB,QAAQ,EAAE,0BAA0B;iBACrC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,sCAAsC;QACtC,IAAI,sBAAsB,KAAK,SAAS,EAAE,CAAC;YACzC,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;YAChF,IAAI,SAAS,GAAG,sBAAsB,EAAE,CAAC;gBACvC,UAAU,CAAC,IAAI,CAAC;oBACd,KAAK,EAAE,SAAS;oBAChB,IAAI,EAAE,sCAAsC;oBAC5C,OAAO,EAAE,+CAA+C,SAAS,6BAA6B,sBAAsB,GAAG;oBACvH,MAAM,EAAE,SAAS;oBACjB,QAAQ,EAAE,sBAAsB;iBACjC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAEO,eAAe,CAAC,MAAsB,EAAE,KAAqB,EAAE,UAA2B;QAChG,IAAI,CAAC,KAAK,CAAC,UAAU;YAAE,OAAO;QAE9B,MAAM,EAAE,2BAA2B,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC,UAAU,CAAC;QAEzE,iCAAiC;QACjC,IAAI,2BAA2B,KAAK,KAAK,EAAE,CAAC;YAC1C,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,qBAAqB,CAAC,CAAC;YACpF,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzB,UAAU,CAAC,IAAI,CAAC;oBACd,KAAK,EAAE,OAAO;oBACd,IAAI,EAAE,wCAAwC;oBAC9C,OAAO,EAAE,qDAAqD,SAAS,CAAC,MAAM,cAAc;oBAC5F,MAAM,EAAE,SAAS,CAAC,MAAM;oBACxB,QAAQ,EAAE,CAAC;iBACZ,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,4DAA4D;QAC5D,IAAI,cAAc,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;gBAChD,4BAA4B;gBAC5B,KAAK,MAAM,MAAM,IAAI,cAAc,EAAE,CAAC;oBACpC,IAAI,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;wBAC7B,yBAAyB;wBACzB,UAAU,CAAC,IAAI,CAAC;4BACd,KAAK,EAAE,OAAO;4BACd,IAAI,EAAE,2BAA2B;4BACjC,OAAO,EAAE,iCAAiC,IAAI,CAAC,IAAI,cAAc,MAAM,IAAI;4BAC3E,MAAM,EAAE,IAAI,CAAC,EAAE;4BACf,QAAQ,EAAE,kBAAkB,MAAM,EAAE;yBACrC,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ArchitectureScore, DependencyEdge, AntiPattern } from './types/core.js';
|
|
2
|
+
export declare class ArchitectureScorer {
|
|
3
|
+
/**
|
|
4
|
+
* Barrel/index files that naturally have many connections and should be
|
|
5
|
+
* excluded from coupling max-edge penalty calculations.
|
|
6
|
+
*/
|
|
7
|
+
private static readonly BARREL_FILES;
|
|
8
|
+
score(edges: DependencyEdge[], antiPatterns: AntiPattern[], totalFiles: number): ArchitectureScore;
|
|
9
|
+
private calculateModularity;
|
|
10
|
+
private calculateCoupling;
|
|
11
|
+
private findNodeWithMaxEdges;
|
|
12
|
+
private calculateCohesion;
|
|
13
|
+
private isInternalDependency;
|
|
14
|
+
private calculateLayering;
|
|
15
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { basename } from 'path';
|
|
2
|
+
export class ArchitectureScorer {
|
|
3
|
+
/**
|
|
4
|
+
* Barrel/index files that naturally have many connections and should be
|
|
5
|
+
* excluded from coupling max-edge penalty calculations.
|
|
6
|
+
*/
|
|
7
|
+
static BARREL_FILES = new Set([
|
|
8
|
+
'__init__.py', 'index.ts', 'index.js', 'index.tsx', 'index.jsx',
|
|
9
|
+
'mod.rs', '__init__.pyi', 'types.ts', 'types.js', 'logger.ts', 'logger.js'
|
|
10
|
+
]);
|
|
11
|
+
score(edges, antiPatterns, totalFiles) {
|
|
12
|
+
const modularity = this.calculateModularity(edges, totalFiles);
|
|
13
|
+
const coupling = this.calculateCoupling(edges, totalFiles);
|
|
14
|
+
const cohesion = this.calculateCohesion(edges);
|
|
15
|
+
const layering = this.calculateLayering(antiPatterns, totalFiles);
|
|
16
|
+
const components = [
|
|
17
|
+
{
|
|
18
|
+
name: 'Modularity',
|
|
19
|
+
score: Math.round(modularity),
|
|
20
|
+
maxScore: 100,
|
|
21
|
+
weight: 0.4,
|
|
22
|
+
explanation: 'Measures appropriate module boundaries and single responsibility principle adherence',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'Coupling',
|
|
26
|
+
score: Math.round(coupling),
|
|
27
|
+
maxScore: 100,
|
|
28
|
+
weight: 0.25,
|
|
29
|
+
explanation: 'Evaluates interdependencies between modules; lower coupling is better',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'Cohesion',
|
|
33
|
+
score: Math.round(cohesion),
|
|
34
|
+
maxScore: 100,
|
|
35
|
+
weight: 0.2,
|
|
36
|
+
explanation: 'Assesses how closely related functionality is grouped together',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'Layering',
|
|
40
|
+
score: Math.round(layering),
|
|
41
|
+
maxScore: 100,
|
|
42
|
+
weight: 0.15,
|
|
43
|
+
explanation: 'Checks adherence to architectural layer separation',
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
const overall = Math.round(components[0].score * components[0].weight +
|
|
47
|
+
components[1].score * components[1].weight +
|
|
48
|
+
components[2].score * components[2].weight +
|
|
49
|
+
components[3].score * components[3].weight);
|
|
50
|
+
return {
|
|
51
|
+
overall: Math.min(100, Math.max(0, overall)),
|
|
52
|
+
components,
|
|
53
|
+
breakdown: {
|
|
54
|
+
modularity: Math.round(modularity),
|
|
55
|
+
coupling: Math.round(coupling),
|
|
56
|
+
cohesion: Math.round(cohesion),
|
|
57
|
+
layering: Math.round(layering),
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
calculateModularity(edges, totalFiles) {
|
|
62
|
+
if (totalFiles === 0)
|
|
63
|
+
return 50;
|
|
64
|
+
const avgEdgesPerFile = edges.length / totalFiles;
|
|
65
|
+
if (avgEdgesPerFile < 2)
|
|
66
|
+
return 100;
|
|
67
|
+
if (avgEdgesPerFile < 4)
|
|
68
|
+
return 85;
|
|
69
|
+
if (avgEdgesPerFile < 6)
|
|
70
|
+
return 70;
|
|
71
|
+
if (avgEdgesPerFile < 10)
|
|
72
|
+
return 50;
|
|
73
|
+
return 30;
|
|
74
|
+
}
|
|
75
|
+
calculateCoupling(edges, totalFiles) {
|
|
76
|
+
if (totalFiles === 0 || totalFiles === 1)
|
|
77
|
+
return 50;
|
|
78
|
+
const nonBarrelEdges = edges.filter((e) => {
|
|
79
|
+
const fromFile = basename(e.from);
|
|
80
|
+
const toFile = basename(e.to);
|
|
81
|
+
return !ArchitectureScorer.BARREL_FILES.has(fromFile) &&
|
|
82
|
+
!ArchitectureScorer.BARREL_FILES.has(toFile);
|
|
83
|
+
});
|
|
84
|
+
const nodeWithMaxEdges = this.findNodeWithMaxEdges(nonBarrelEdges);
|
|
85
|
+
const maxEdgeCount = nodeWithMaxEdges ? nodeWithMaxEdges.count : 0;
|
|
86
|
+
const effectiveFiles = Math.max(totalFiles - 1, 1);
|
|
87
|
+
const couplingRatio = maxEdgeCount / effectiveFiles;
|
|
88
|
+
if (couplingRatio < 0.15)
|
|
89
|
+
return 100;
|
|
90
|
+
if (couplingRatio < 0.25)
|
|
91
|
+
return 85;
|
|
92
|
+
if (couplingRatio < 0.35)
|
|
93
|
+
return 75;
|
|
94
|
+
if (couplingRatio < 0.5)
|
|
95
|
+
return 65;
|
|
96
|
+
if (couplingRatio < 0.7)
|
|
97
|
+
return 50;
|
|
98
|
+
if (couplingRatio < 0.85)
|
|
99
|
+
return 35;
|
|
100
|
+
return 20;
|
|
101
|
+
}
|
|
102
|
+
findNodeWithMaxEdges(edges) {
|
|
103
|
+
const nodeEdgeCount = {};
|
|
104
|
+
for (const edge of edges) {
|
|
105
|
+
nodeEdgeCount[edge.from] = (nodeEdgeCount[edge.from] || 0) + 1;
|
|
106
|
+
nodeEdgeCount[edge.to] = (nodeEdgeCount[edge.to] || 0) + 1;
|
|
107
|
+
}
|
|
108
|
+
let maxNode = null;
|
|
109
|
+
let maxCount = 0;
|
|
110
|
+
for (const [node, count] of Object.entries(nodeEdgeCount)) {
|
|
111
|
+
if (count > maxCount) {
|
|
112
|
+
maxCount = count;
|
|
113
|
+
maxNode = node;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return maxNode ? { node: maxNode, count: maxCount } : null;
|
|
117
|
+
}
|
|
118
|
+
calculateCohesion(edges) {
|
|
119
|
+
if (edges.length === 0)
|
|
120
|
+
return 50;
|
|
121
|
+
const internalEdges = edges.filter((e) => this.isInternalDependency(e.from, e.to)).length;
|
|
122
|
+
const cohesionRatio = internalEdges / edges.length;
|
|
123
|
+
if (cohesionRatio > 0.8)
|
|
124
|
+
return 100;
|
|
125
|
+
if (cohesionRatio > 0.6)
|
|
126
|
+
return 85;
|
|
127
|
+
if (cohesionRatio > 0.45)
|
|
128
|
+
return 75;
|
|
129
|
+
if (cohesionRatio > 0.3)
|
|
130
|
+
return 65;
|
|
131
|
+
if (cohesionRatio > 0.15)
|
|
132
|
+
return 50;
|
|
133
|
+
return 30;
|
|
134
|
+
}
|
|
135
|
+
isInternalDependency(from, to) {
|
|
136
|
+
const fromParts = from.split('/');
|
|
137
|
+
const toParts = to.split('/');
|
|
138
|
+
if (fromParts.length <= 1 && toParts.length <= 1)
|
|
139
|
+
return true;
|
|
140
|
+
const fromTopLevel = fromParts.length > 1 ? fromParts[0] : '';
|
|
141
|
+
const toTopLevel = toParts.length > 1 ? toParts[0] : '';
|
|
142
|
+
return fromTopLevel === toTopLevel;
|
|
143
|
+
}
|
|
144
|
+
calculateLayering(antiPatterns, totalFiles) {
|
|
145
|
+
const layeringViolations = antiPatterns.filter((p) => p.name === 'Leaky Abstraction' ||
|
|
146
|
+
p.name === 'Shotgun Surgery' ||
|
|
147
|
+
p.name === 'Circular Dependency').length;
|
|
148
|
+
if (layeringViolations === 0)
|
|
149
|
+
return 100;
|
|
150
|
+
const fileCount = Math.max(totalFiles || 50, 10);
|
|
151
|
+
const violationRatio = layeringViolations / fileCount;
|
|
152
|
+
if (violationRatio < 0.05)
|
|
153
|
+
return 95;
|
|
154
|
+
if (violationRatio < 0.15)
|
|
155
|
+
return 85;
|
|
156
|
+
if (violationRatio < 0.25)
|
|
157
|
+
return 70;
|
|
158
|
+
if (violationRatio < 0.40)
|
|
159
|
+
return 50;
|
|
160
|
+
if (violationRatio < 0.60)
|
|
161
|
+
return 35;
|
|
162
|
+
return 20;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=scorer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scorer.js","sourceRoot":"","sources":["../../../src/core/scorer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAEhC,MAAM,OAAO,kBAAkB;IAC7B;;;OAGG;IACK,MAAM,CAAU,YAAY,GAAG,IAAI,GAAG,CAAC;QAC7C,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW;QAC/D,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW;KAC3E,CAAC,CAAC;IAEH,KAAK,CACH,KAAuB,EACvB,YAA2B,EAC3B,UAAkB;QAElB,MAAM,UAAU,GAAG,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAC3D,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;QAElE,MAAM,UAAU,GAAG;YACjB;gBACE,IAAI,EAAE,YAAY;gBAClB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;gBAC7B,QAAQ,EAAE,GAAG;gBACb,MAAM,EAAE,GAAG;gBACX,WAAW,EAAE,sFAAsF;aACpG;YACD;gBACE,IAAI,EAAE,UAAU;gBAChB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;gBAC3B,QAAQ,EAAE,GAAG;gBACb,MAAM,EAAE,IAAI;gBACZ,WAAW,EAAE,uEAAuE;aACrF;YACD;gBACE,IAAI,EAAE,UAAU;gBAChB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;gBAC3B,QAAQ,EAAE,GAAG;gBACb,MAAM,EAAE,GAAG;gBACX,WAAW,EAAE,gEAAgE;aAC9E;YACD;gBACE,IAAI,EAAE,UAAU;gBAChB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;gBAC3B,QAAQ,EAAE,GAAG;gBACb,MAAM,EAAE,IAAI;gBACZ,WAAW,EAAE,oDAAoD;aAClE;SACF,CAAC;QAEF,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CACxB,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM;YAC1C,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM;YAC1C,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM;YAC1C,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAC3C,CAAC;QAEF,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YAC5C,UAAU;YACV,SAAS,EAAE;gBACT,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;gBAClC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;gBAC9B,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;gBAC9B,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;aAC/B;SACF,CAAC;IACJ,CAAC;IAEO,mBAAmB,CAAC,KAAuB,EAAE,UAAkB;QACrE,IAAI,UAAU,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAEhC,MAAM,eAAe,GAAG,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC;QAElD,IAAI,eAAe,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC;QACpC,IAAI,eAAe,GAAG,CAAC;YAAE,OAAO,EAAE,CAAC;QACnC,IAAI,eAAe,GAAG,CAAC;YAAE,OAAO,EAAE,CAAC;QACnC,IAAI,eAAe,GAAG,EAAE;YAAE,OAAO,EAAE,CAAC;QACpC,OAAO,EAAE,CAAC;IACZ,CAAC;IAEO,iBAAiB,CAAC,KAAuB,EAAE,UAAkB;QACnE,IAAI,UAAU,KAAK,CAAC,IAAI,UAAU,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAEpD,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YACxC,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC9B,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAC9C,CAAC,kBAAkB,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;QAEH,MAAM,gBAAgB,GAAG,IAAI,CAAC,oBAAoB,CAAC,cAAc,CAAC,CAAC;QACnE,MAAM,YAAY,GAAG,gBAAgB,CAAC,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAEnE,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACnD,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,CAAC;QAEpD,IAAI,aAAa,GAAG,IAAI;YAAE,OAAO,GAAG,CAAC;QACrC,IAAI,aAAa,GAAG,IAAI;YAAE,OAAO,EAAE,CAAC;QACpC,IAAI,aAAa,GAAG,IAAI;YAAE,OAAO,EAAE,CAAC;QACpC,IAAI,aAAa,GAAG,GAAG;YAAE,OAAO,EAAE,CAAC;QACnC,IAAI,aAAa,GAAG,GAAG;YAAE,OAAO,EAAE,CAAC;QACnC,IAAI,aAAa,GAAG,IAAI;YAAE,OAAO,EAAE,CAAC;QACpC,OAAO,EAAE,CAAC;IACZ,CAAC;IAEO,oBAAoB,CAAC,KAAuB;QAClD,MAAM,aAAa,GAA2B,EAAE,CAAC;QAEjD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;YAC/D,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAC7D,CAAC;QAED,IAAI,OAAO,GAAkB,IAAI,CAAC;QAClC,IAAI,QAAQ,GAAG,CAAC,CAAC;QAEjB,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;YAC1D,IAAI,KAAK,GAAG,QAAQ,EAAE,CAAC;gBACrB,QAAQ,GAAG,KAAK,CAAC;gBACjB,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7D,CAAC;IAEO,iBAAiB,CAAC,KAAuB;QAC/C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAElC,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CACvC,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CACxC,CAAC,MAAM,CAAC;QAET,MAAM,aAAa,GAAG,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC;QAEnD,IAAI,aAAa,GAAG,GAAG;YAAE,OAAO,GAAG,CAAC;QACpC,IAAI,aAAa,GAAG,GAAG;YAAE,OAAO,EAAE,CAAC;QACnC,IAAI,aAAa,GAAG,IAAI;YAAE,OAAO,EAAE,CAAC;QACpC,IAAI,aAAa,GAAG,GAAG;YAAE,OAAO,EAAE,CAAC;QACnC,IAAI,aAAa,GAAG,IAAI;YAAE,OAAO,EAAE,CAAC;QACpC,OAAO,EAAE,CAAC;IACZ,CAAC;IAEO,oBAAoB,CAAC,IAAY,EAAE,EAAU;QACnD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAE9B,IAAI,SAAS,CAAC,MAAM,IAAI,CAAC,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAE9D,MAAM,YAAY,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAExD,OAAO,YAAY,KAAK,UAAU,CAAC;IACrC,CAAC;IAEO,iBAAiB,CAAC,YAA2B,EAAE,UAAmB;QACxE,MAAM,kBAAkB,GAAG,YAAY,CAAC,MAAM,CAC5C,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,IAAI,KAAK,mBAAmB;YAC9B,CAAC,CAAC,IAAI,KAAK,iBAAiB;YAC5B,CAAC,CAAC,IAAI,KAAK,qBAAqB,CACnC,CAAC,MAAM,CAAC;QAET,IAAI,kBAAkB,KAAK,CAAC;YAAE,OAAO,GAAG,CAAC;QAEzC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QACjD,MAAM,cAAc,GAAG,kBAAkB,GAAG,SAAS,CAAC;QAEtD,IAAI,cAAc,GAAG,IAAI;YAAE,OAAO,EAAE,CAAC;QACrC,IAAI,cAAc,GAAG,IAAI;YAAE,OAAO,EAAE,CAAC;QACrC,IAAI,cAAc,GAAG,IAAI;YAAE,OAAO,EAAE,CAAC;QACrC,IAAI,cAAc,GAAG,IAAI;YAAE,OAAO,EAAE,CAAC;QACrC,IAAI,cAAc,GAAG,IAAI;YAAE,OAAO,EAAE,CAAC;QACrC,OAAO,EAAE,CAAC;IACZ,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { AnalysisReport } from '../types/core.js';
|
|
2
|
+
import { ProjectSummary } from '../types/summarizer.js';
|
|
3
|
+
export declare class KeywordExtractor {
|
|
4
|
+
static readonly KEYWORD_BLACKLIST: Set<string>;
|
|
5
|
+
extractKeywords(packageInfo: Record<string, unknown>, _readme: string, modules: ProjectSummary['modules'], report: AnalysisReport): string[];
|
|
6
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// import { FileNode, WorkspaceInfo } from '../types/infrastructure.js';
|
|
2
|
+
export class KeywordExtractor {
|
|
3
|
+
static KEYWORD_BLACKLIST = new Set([
|
|
4
|
+
'node_modules', 'dist', 'build', '.git', '.next', 'coverage',
|
|
5
|
+
'__tests__', '__mocks__', 'src', 'lib', 'index', 'main',
|
|
6
|
+
'out', 'tmp', '.cache', 'vendor', '.vscode', '.idea',
|
|
7
|
+
]);
|
|
8
|
+
extractKeywords(packageInfo, _readme, modules, report) {
|
|
9
|
+
const keywords = new Set();
|
|
10
|
+
// From package.json keywords
|
|
11
|
+
if (Array.isArray(packageInfo.keywords)) {
|
|
12
|
+
for (const kw of packageInfo.keywords) {
|
|
13
|
+
if (typeof kw === 'string')
|
|
14
|
+
keywords.add(kw.toLowerCase());
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// From module names (only clean names)
|
|
18
|
+
for (const mod of modules) {
|
|
19
|
+
const name = mod.name.toLowerCase();
|
|
20
|
+
if (!KeywordExtractor.KEYWORD_BLACKLIST.has(name)) {
|
|
21
|
+
keywords.add(name);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// From frameworks detected
|
|
25
|
+
for (const fw of report.projectInfo.frameworks) {
|
|
26
|
+
keywords.add(fw.toLowerCase());
|
|
27
|
+
}
|
|
28
|
+
// From languages
|
|
29
|
+
for (const lang of report.projectInfo.primaryLanguages) {
|
|
30
|
+
keywords.add(lang.toLowerCase());
|
|
31
|
+
}
|
|
32
|
+
// Filter out blacklisted and generic entries
|
|
33
|
+
return [...keywords]
|
|
34
|
+
.filter(kw => !KeywordExtractor.KEYWORD_BLACKLIST.has(kw) && kw.length > 1)
|
|
35
|
+
.slice(0, 20);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=keyword-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyword-extractor.js","sourceRoot":"","sources":["../../../../src/core/summarizer/keyword-extractor.ts"],"names":[],"mappings":"AAIA,wEAAwE;AAExE,MAAM,OAAO,gBAAgB;IACpB,MAAM,CAAU,iBAAiB,GAAG,IAAI,GAAG,CAAC;QACjD,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU;QAC5D,WAAW,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM;QACvD,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO;KACrD,CAAC,CAAC;IAEE,eAAe,CAClB,WAAoC,EACpC,OAAe,EACf,OAAkC,EAClC,MAAsB;QAEtB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;QAEnC,6BAA6B;QAC7B,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;YACxC,KAAK,MAAM,EAAE,IAAI,WAAW,CAAC,QAAQ,EAAE,CAAC;gBACtC,IAAI,OAAO,EAAE,KAAK,QAAQ;oBAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC;QAED,uCAAuC;QACvC,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACpC,IAAI,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;QAED,2BAA2B;QAC3B,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC;YAC/C,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QACjC,CAAC;QAED,iBAAiB;QACjB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,WAAW,CAAC,gBAAgB,EAAE,CAAC;YACvD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QACnC,CAAC;QAED,6CAA6C;QAC7C,OAAO,CAAC,GAAG,QAAQ,CAAC;aACjB,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;aAC1E,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAClB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AnalysisReport } from '../types/core.js';
|
|
2
|
+
import { ProjectSummary } from '../types/summarizer.js';
|
|
3
|
+
import { WorkspaceInfo } from '../types/infrastructure.js';
|
|
4
|
+
export declare class ModuleInferrer {
|
|
5
|
+
inferModules(report: AnalysisReport, _projectPath: string): ProjectSummary['modules'];
|
|
6
|
+
inferModulesFromWorkspaces(workspaces: WorkspaceInfo[], report: AnalysisReport): ProjectSummary['modules'];
|
|
7
|
+
getWorkspaceDescription(ws: WorkspaceInfo): string;
|
|
8
|
+
countFilesInDir(dirPath: string): number;
|
|
9
|
+
inferModulesFromStructure(report: AnalysisReport): ProjectSummary['modules'];
|
|
10
|
+
describeModule(name: string, hints: Set<string>): string;
|
|
11
|
+
}
|