@aiready/context-analyzer 0.22.7 → 0.22.8
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/.turbo/turbo-build.log +14 -14
- package/.turbo/turbo-format-check.log +7 -0
- package/.turbo/turbo-lint.log +5 -4
- package/.turbo/turbo-test.log +24 -24
- package/.turbo/turbo-type-check.log +5 -0
- package/dist/chunk-BTDF2ZA4.mjs +91 -0
- package/dist/chunk-JSM7Q5CY.mjs +143 -0
- package/dist/chunk-ZA7HRDAH.mjs +1304 -0
- package/dist/cli-action-HREY7TK5.mjs +95 -0
- package/dist/cli.js +8 -2
- package/dist/cli.mjs +1 -1
- package/dist/index.js +8 -2
- package/dist/index.mjs +5 -5
- package/dist/orchestrator-62YVSQFV.mjs +10 -0
- package/dist/summary-RSPRRY6S.mjs +7 -0
- package/package.json +5 -3
- package/src/metrics.ts +10 -4
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
> @aiready/context-analyzer@0.22.
|
|
3
|
+
> @aiready/context-analyzer@0.22.8 build /Users/pengcao/projects/aiready/packages/context-analyzer
|
|
4
4
|
> tsup src/index.ts src/cli.ts --format cjs,esm --dts
|
|
5
5
|
|
|
6
6
|
[34mCLI[39m Building entry: src/cli.ts, src/index.ts
|
|
@@ -9,23 +9,23 @@
|
|
|
9
9
|
[34mCLI[39m Target: es2020
|
|
10
10
|
[34mCJS[39m Build start
|
|
11
11
|
[34mESM[39m Build start
|
|
12
|
-
[32mCJS[39m [1mdist/cli.js [22m[32m69.59 KB[39m
|
|
13
|
-
[32mCJS[39m [1mdist/index.js [22m[32m83.38 KB[39m
|
|
14
|
-
[32mCJS[39m ⚡️ Build success in 567ms
|
|
15
12
|
[32mESM[39m [1mdist/index.mjs [22m[32m12.69 KB[39m
|
|
16
|
-
[32mESM[39m [1mdist/cli-action-VCXBZGZP.mjs [22m[32m2.86 KB[39m
|
|
17
|
-
[32mESM[39m [1mdist/summary-7PZVW72O.mjs [22m[32m119.00 B[39m
|
|
18
|
-
[32mESM[39m [1mdist/chunk-J3SZQZNU.mjs [22m[32m8.11 KB[39m
|
|
19
|
-
[32mESM[39m [1mdist/python-context-BWDC4E5Z.mjs [22m[32m4.48 KB[39m
|
|
20
|
-
[32mESM[39m [1mdist/chunk-M2EGQ36M.mjs [22m[32m41.00 KB[39m
|
|
21
13
|
[32mESM[39m [1mdist/cli.mjs [22m[32m1.81 KB[39m
|
|
22
|
-
[32mESM[39m [1mdist/
|
|
14
|
+
[32mESM[39m [1mdist/python-context-BWDC4E5Z.mjs [22m[32m4.48 KB[39m
|
|
15
|
+
[32mESM[39m [1mdist/orchestrator-62YVSQFV.mjs [22m[32m190.00 B[39m
|
|
16
|
+
[32mESM[39m [1mdist/cli-action-HREY7TK5.mjs [22m[32m2.86 KB[39m
|
|
17
|
+
[32mESM[39m [1mdist/chunk-BTDF2ZA4.mjs [22m[32m3.35 KB[39m
|
|
18
|
+
[32mESM[39m [1mdist/chunk-J3SZQZNU.mjs [22m[32m8.11 KB[39m
|
|
23
19
|
[32mESM[39m [1mdist/chunk-64U3PNO3.mjs [22m[32m2.65 KB[39m
|
|
24
|
-
[32mESM[39m [1mdist/
|
|
25
|
-
[32mESM[39m [1mdist/
|
|
26
|
-
[32mESM[39m
|
|
20
|
+
[32mESM[39m [1mdist/summary-RSPRRY6S.mjs [22m[32m119.00 B[39m
|
|
21
|
+
[32mESM[39m [1mdist/chunk-JSM7Q5CY.mjs [22m[32m4.98 KB[39m
|
|
22
|
+
[32mESM[39m [1mdist/chunk-ZA7HRDAH.mjs [22m[32m41.00 KB[39m
|
|
23
|
+
[32mESM[39m ⚡️ Build success in 180ms
|
|
24
|
+
[32mCJS[39m [1mdist/index.js [22m[32m83.44 KB[39m
|
|
25
|
+
[32mCJS[39m [1mdist/cli.js [22m[32m69.65 KB[39m
|
|
26
|
+
[32mCJS[39m ⚡️ Build success in 180ms
|
|
27
27
|
DTS Build start
|
|
28
|
-
DTS ⚡️ Build success in
|
|
28
|
+
DTS ⚡️ Build success in 2332ms
|
|
29
29
|
DTS dist/cli.d.ts 20.00 B
|
|
30
30
|
DTS dist/index.d.ts 25.64 KB
|
|
31
31
|
DTS dist/cli.d.mts 20.00 B
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
> @aiready/context-analyzer@0.22.8 format-check /Users/pengcao/projects/aiready/packages/context-analyzer
|
|
4
|
+
> prettier --check . --ignore-path ../../.prettierignore
|
|
5
|
+
|
|
6
|
+
Checking formatting...
|
|
7
|
+
.aireadyignore[2K[1GCONTRIBUTING.md[2K[1GLICENSE[2K[1Gpackage.json[2K[1GREADME.md[2K[1Gsrc/__tests__/analyzer.test.ts[2K[1Gsrc/__tests__/auto-detection.test.ts[2K[1Gsrc/__tests__/boilerplate-barrel.test.ts[2K[1Gsrc/__tests__/cluster-detector.test.ts[2K[1Gsrc/__tests__/consolidation.test.ts[2K[1Gsrc/__tests__/contract.test.ts[2K[1Gsrc/__tests__/defaults.test.ts[2K[1Gsrc/__tests__/domain-inference.test.ts[2K[1Gsrc/__tests__/enhanced-cohesion.test.ts[2K[1Gsrc/__tests__/file-classification.test.ts[2K[1Gsrc/__tests__/fragmentation-advanced.test.ts[2K[1Gsrc/__tests__/fragmentation-coupling.test.ts[2K[1Gsrc/__tests__/fragmentation-log.test.ts[2K[1Gsrc/__tests__/issue-analyzer.test.ts[2K[1Gsrc/__tests__/orchestrator.test.ts[2K[1Gsrc/__tests__/provider.test.ts[2K[1Gsrc/__tests__/python-context.test.ts[2K[1Gsrc/__tests__/remediation.test.ts[2K[1Gsrc/__tests__/report/console-report.test.ts[2K[1Gsrc/__tests__/report/html-report.test.ts[2K[1Gsrc/__tests__/scoring.test.ts[2K[1Gsrc/__tests__/structural-cohesion.test.ts[2K[1Gsrc/analyzers/python-context.ts[2K[1Gsrc/ast-utils.ts[2K[1Gsrc/classifier.ts[2K[1Gsrc/classify/classification-patterns.ts[2K[1Gsrc/classify/file-classifiers.ts[2K[1Gsrc/cli-action.ts[2K[1Gsrc/cli-definition.ts[2K[1Gsrc/cli.ts[2K[1Gsrc/cluster-detector.ts[2K[1Gsrc/defaults.ts[2K[1Gsrc/graph-builder.ts[2K[1Gsrc/index.ts[2K[1Gsrc/issue-analyzer.ts[2K[1Gsrc/metrics.ts[2K[1Gsrc/orchestrator.ts[2K[1Gsrc/provider.ts[2K[1Gsrc/remediation.ts[2K[1Gsrc/report/console-report.ts[2K[1Gsrc/report/html-report.ts[2K[1Gsrc/report/interactive-setup.ts[2K[1Gsrc/scoring.ts[2K[1Gsrc/semantic/co-usage.ts[2K[1Gsrc/semantic/consolidation.ts[2K[1Gsrc/semantic/domain-inference.ts[2K[1Gsrc/semantic/type-graph.ts[2K[1Gsrc/summary.ts[2K[1Gsrc/types.ts[2K[1Gsrc/utils/dependency-graph-utils.ts[2K[1Gsrc/utils/string-utils.ts[2K[1Gtsconfig.json[2K[1Gtsconfig.tsbuildinfo[2K[1GAll matched files use Prettier code style!
|
package/.turbo/turbo-lint.log
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
>
|
|
4
|
-
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
> @aiready/context-analyzer@0.22.8 lint /Users/pengcao/projects/aiready/packages/context-analyzer
|
|
4
|
+
> eslint src
|
|
5
|
+
|
package/.turbo/turbo-test.log
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
> @aiready/context-analyzer@0.22.
|
|
3
|
+
> @aiready/context-analyzer@0.22.7 test /Users/pengcao/projects/aiready/packages/context-analyzer
|
|
4
4
|
> vitest run --exclude "**/dist/**"
|
|
5
5
|
|
|
6
6
|
[?25l
|
|
7
7
|
[1m[46m RUN [49m[22m [36mv4.0.18 [39m[90m/Users/pengcao/projects/aiready/packages/context-analyzer[39m
|
|
8
8
|
|
|
9
|
-
[32m✓[39m src/__tests__/
|
|
10
|
-
[32m✓[39m src/__tests__/
|
|
11
|
-
[32m✓[39m src/__tests__/report/
|
|
12
|
-
[32m✓[39m src/__tests__/domain-inference.test.ts [2m([22m[2m26 tests[22m[2m)[22m[32m
|
|
9
|
+
[32m✓[39m src/__tests__/consolidation.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 3[2mms[22m[39m
|
|
10
|
+
[32m✓[39m src/__tests__/report/html-report.test.ts [2m([22m[2m11 tests[22m[2m)[22m[32m 32[2mms[22m[39m
|
|
11
|
+
[32m✓[39m src/__tests__/report/console-report.test.ts [2m([22m[2m8 tests[22m[2m)[22m[32m 124[2mms[22m[39m
|
|
12
|
+
[32m✓[39m src/__tests__/domain-inference.test.ts [2m([22m[2m26 tests[22m[2m)[22m[32m 6[2mms[22m[39m
|
|
13
13
|
[90mstderr[2m | src/__tests__/python-context.test.ts[2m > [22m[2mpython-context[2m > [22m[2manalyzePythonContext[2m > [22m[2mshould filter for Python files only
|
|
14
14
|
[22m[39mFailed to analyze src/file2.py: TypeError: Cannot read properties of undefined (reading 'split')
|
|
15
15
|
at Module.analyzePythonContext [90m(/Users/pengcao/projects/aiready/packages/context-analyzer/[39msrc/analyzers/python-context.ts:91:32[90m)[39m
|
|
@@ -24,28 +24,28 @@
|
|
|
24
24
|
at [90m/Users/pengcao/projects/aiready/packages/context-analyzer/[39msrc/__tests__/python-context.test.ts:84:23
|
|
25
25
|
at file:///Users/pengcao/projects/aiready/node_modules/[4m.pnpm[24m/@vitest+runner@4.0.18/node_modules/[4m@vitest/runner[24m/dist/index.js:915:20
|
|
26
26
|
|
|
27
|
-
[32m✓[39m src/__tests__/python-context.test.ts [2m([22m[2m4 tests[22m[2m)[22m[32m
|
|
28
|
-
[32m✓[39m src/__tests__/
|
|
29
|
-
[32m✓[39m src/__tests__/
|
|
30
|
-
[32m✓[39m src/__tests__/
|
|
31
|
-
[32m✓[39m src/__tests__/
|
|
32
|
-
[32m✓[39m src/__tests__/
|
|
33
|
-
[32m✓[39m src/__tests__/
|
|
34
|
-
[32m✓[39m src/__tests__/
|
|
35
|
-
[32m✓[39m src/__tests__/
|
|
36
|
-
[32m✓[39m src/__tests__/
|
|
37
|
-
[32m✓[39m src/__tests__/
|
|
38
|
-
[32m✓[39m src/__tests__/
|
|
39
|
-
[32m✓[39m src/__tests__/
|
|
40
|
-
[32m✓[39m src/__tests__/scoring.test.ts [2m([22m[2m15 tests[22m[2m)[22m[32m
|
|
41
|
-
[32m✓[39m src/__tests__/fragmentation-advanced.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m 2[2mms[22m[39m
|
|
27
|
+
[32m✓[39m src/__tests__/python-context.test.ts [2m([22m[2m4 tests[22m[2m)[22m[32m 14[2mms[22m[39m
|
|
28
|
+
[32m✓[39m src/__tests__/fragmentation-log.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m 1[2mms[22m[39m
|
|
29
|
+
[32m✓[39m src/__tests__/fragmentation-coupling.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 42[2mms[22m[39m
|
|
30
|
+
[32m✓[39m src/__tests__/analyzer.test.ts [2m([22m[2m14 tests[22m[2m)[22m[32m 46[2mms[22m[39m
|
|
31
|
+
[32m✓[39m src/__tests__/issue-analyzer.test.ts [2m([22m[2m17 tests[22m[2m)[22m[32m 14[2mms[22m[39m
|
|
32
|
+
[32m✓[39m src/__tests__/auto-detection.test.ts [2m([22m[2m8 tests[22m[2m)[22m[32m 177[2mms[22m[39m
|
|
33
|
+
[32m✓[39m src/__tests__/orchestrator.test.ts [2m([22m[2m8 tests[22m[2m)[22m[32m 226[2mms[22m[39m
|
|
34
|
+
[32m✓[39m src/__tests__/provider.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 10[2mms[22m[39m
|
|
35
|
+
[32m✓[39m src/__tests__/defaults.test.ts [2m([22m[2m7 tests[22m[2m)[22m[32m 3[2mms[22m[39m
|
|
36
|
+
[32m✓[39m src/__tests__/cluster-detector.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m 35[2mms[22m[39m
|
|
37
|
+
[32m✓[39m src/__tests__/file-classification.test.ts [2m([22m[2m63 tests[22m[2m)[22m[32m 12[2mms[22m[39m
|
|
38
|
+
[32m✓[39m src/__tests__/remediation.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 2[2mms[22m[39m
|
|
39
|
+
[32m✓[39m src/__tests__/contract.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m 19[2mms[22m[39m
|
|
40
|
+
[32m✓[39m src/__tests__/scoring.test.ts [2m([22m[2m15 tests[22m[2m)[22m[32m 3[2mms[22m[39m
|
|
42
41
|
[32m✓[39m src/__tests__/enhanced-cohesion.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 2[2mms[22m[39m
|
|
43
|
-
[32m✓[39m src/__tests__/
|
|
44
|
-
[32m✓[39m src/__tests__/
|
|
42
|
+
[32m✓[39m src/__tests__/structural-cohesion.test.ts [2m([22m[2m4 tests[22m[2m)[22m[32m 39[2mms[22m[39m
|
|
43
|
+
[32m✓[39m src/__tests__/fragmentation-advanced.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m 1[2mms[22m[39m
|
|
44
|
+
[32m✓[39m src/__tests__/boilerplate-barrel.test.ts [2m([22m[2m7 tests[22m[2m)[22m[32m 3[2mms[22m[39m
|
|
45
45
|
|
|
46
46
|
[2m Test Files [22m [1m[32m22 passed[39m[22m[90m (22)[39m
|
|
47
47
|
[2m Tests [22m [1m[32m224 passed[39m[22m[90m (224)[39m
|
|
48
|
-
[2m Start at [22m
|
|
49
|
-
[2m Duration [22m
|
|
48
|
+
[2m Start at [22m 00:08:32
|
|
49
|
+
[2m Duration [22m 5.11s[2m (transform 8.77s, setup 0ms, import 35.15s, tests 815ms, environment 9ms)[22m
|
|
50
50
|
|
|
51
51
|
[?25h
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import {
|
|
2
|
+
calculatePathEntropy
|
|
3
|
+
} from "./chunk-JSM7Q5CY.mjs";
|
|
4
|
+
|
|
5
|
+
// src/summary.ts
|
|
6
|
+
import { GLOBAL_SCAN_OPTIONS } from "@aiready/core";
|
|
7
|
+
function generateSummary(results, options = {}) {
|
|
8
|
+
const config = options ? Object.fromEntries(
|
|
9
|
+
Object.entries(options).filter(
|
|
10
|
+
([key]) => !GLOBAL_SCAN_OPTIONS.includes(key) || key === "rootDir"
|
|
11
|
+
)
|
|
12
|
+
) : {};
|
|
13
|
+
const totalFiles = results.length;
|
|
14
|
+
const totalTokens = results.reduce((sum, r) => sum + r.tokenCost, 0);
|
|
15
|
+
const avgContextBudget = totalFiles > 0 ? results.reduce((sum, r) => sum + r.contextBudget, 0) / totalFiles : 0;
|
|
16
|
+
const deepFiles = results.filter((r) => r.importDepth > 5).map((r) => ({ file: r.file, depth: r.importDepth }));
|
|
17
|
+
const maxImportDepth = Math.max(0, ...results.map((r) => r.importDepth));
|
|
18
|
+
const moduleMap = /* @__PURE__ */ new Map();
|
|
19
|
+
results.forEach((r) => {
|
|
20
|
+
const parts = r.file.split("/");
|
|
21
|
+
let domain = "root";
|
|
22
|
+
if (parts.length > 2) {
|
|
23
|
+
domain = parts.slice(0, 2).join("/");
|
|
24
|
+
}
|
|
25
|
+
if (!moduleMap.has(domain)) moduleMap.set(domain, []);
|
|
26
|
+
moduleMap.get(domain).push(r);
|
|
27
|
+
});
|
|
28
|
+
const fragmentedModules = [];
|
|
29
|
+
moduleMap.forEach((files, domain) => {
|
|
30
|
+
const clusterTokens = files.reduce((sum, f) => sum + f.tokenCost, 0);
|
|
31
|
+
const filePaths = files.map((f) => f.file);
|
|
32
|
+
const avgEntropy = calculatePathEntropy(filePaths);
|
|
33
|
+
const fragmentationScore = Math.min(1, avgEntropy * (files.length / 10));
|
|
34
|
+
if (fragmentationScore > 0.4) {
|
|
35
|
+
fragmentedModules.push({
|
|
36
|
+
domain,
|
|
37
|
+
files: filePaths,
|
|
38
|
+
fragmentationScore,
|
|
39
|
+
totalTokens: clusterTokens,
|
|
40
|
+
avgCohesion: files.reduce((sum, f) => sum + f.cohesionScore, 0) / files.length,
|
|
41
|
+
suggestedStructure: {
|
|
42
|
+
targetFiles: Math.ceil(files.length / 2),
|
|
43
|
+
consolidationPlan: [
|
|
44
|
+
`Consolidate ${files.length} files in ${domain} into fewer modules`
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
fragmentedModules.sort((a, b) => b.fragmentationScore - a.fragmentationScore);
|
|
51
|
+
const avgFragmentation = fragmentedModules.length > 0 ? fragmentedModules.reduce((sum, m) => sum + m.fragmentationScore, 0) / fragmentedModules.length : 0;
|
|
52
|
+
const avgCohesion = results.reduce((sum, r) => sum + r.cohesionScore, 0) / (totalFiles || 1);
|
|
53
|
+
const lowCohesionFiles = results.filter((r) => r.cohesionScore < 0.4).map((r) => ({ file: r.file, score: r.cohesionScore }));
|
|
54
|
+
const criticalIssues = results.filter(
|
|
55
|
+
(r) => r.severity === "critical"
|
|
56
|
+
).length;
|
|
57
|
+
const majorIssues = results.filter((r) => r.severity === "major").length;
|
|
58
|
+
const minorIssues = results.filter((r) => r.severity === "minor").length;
|
|
59
|
+
const totalPotentialSavings = results.reduce(
|
|
60
|
+
(sum, r) => sum + (r.potentialSavings || 0),
|
|
61
|
+
0
|
|
62
|
+
);
|
|
63
|
+
const topExpensiveFiles = [...results].sort((a, b) => b.contextBudget - a.contextBudget).slice(0, 10).map((r) => ({
|
|
64
|
+
file: r.file,
|
|
65
|
+
contextBudget: r.contextBudget,
|
|
66
|
+
severity: r.severity
|
|
67
|
+
}));
|
|
68
|
+
return {
|
|
69
|
+
totalFiles,
|
|
70
|
+
totalTokens,
|
|
71
|
+
avgContextBudget,
|
|
72
|
+
maxContextBudget: Math.max(0, ...results.map((r) => r.contextBudget)),
|
|
73
|
+
avgImportDepth: results.reduce((sum, r) => sum + r.importDepth, 0) / (totalFiles || 1),
|
|
74
|
+
maxImportDepth,
|
|
75
|
+
deepFiles,
|
|
76
|
+
avgFragmentation,
|
|
77
|
+
fragmentedModules,
|
|
78
|
+
avgCohesion,
|
|
79
|
+
lowCohesionFiles,
|
|
80
|
+
criticalIssues,
|
|
81
|
+
majorIssues,
|
|
82
|
+
minorIssues,
|
|
83
|
+
totalPotentialSavings,
|
|
84
|
+
topExpensiveFiles,
|
|
85
|
+
config
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export {
|
|
90
|
+
generateSummary
|
|
91
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// src/metrics.ts
|
|
2
|
+
import { calculateImportSimilarity, isTestFile } from "@aiready/core";
|
|
3
|
+
function calculateEnhancedCohesion(exports, filePath, options) {
|
|
4
|
+
if (exports.length <= 1) return 1;
|
|
5
|
+
if (filePath && isTestFile(filePath)) return 1;
|
|
6
|
+
const domains = exports.map((e) => e.inferredDomain || "unknown");
|
|
7
|
+
const domainCounts = /* @__PURE__ */ new Map();
|
|
8
|
+
for (const domain of domains)
|
|
9
|
+
domainCounts.set(domain, (domainCounts.get(domain) || 0) + 1);
|
|
10
|
+
if (domainCounts.size === 1 && domains[0] !== "unknown") {
|
|
11
|
+
if (!options?.weights) return 1;
|
|
12
|
+
}
|
|
13
|
+
const probs = Array.from(domainCounts.values()).map(
|
|
14
|
+
(count) => count / exports.length
|
|
15
|
+
);
|
|
16
|
+
let domainEntropy = 0;
|
|
17
|
+
for (const prob of probs) {
|
|
18
|
+
if (prob > 0) domainEntropy -= prob * Math.log2(prob);
|
|
19
|
+
}
|
|
20
|
+
const maxEntropy = Math.log2(Math.max(2, domainCounts.size));
|
|
21
|
+
const domainScore = 1 - domainEntropy / maxEntropy;
|
|
22
|
+
let importScoreTotal = 0;
|
|
23
|
+
let pairsWithData = 0;
|
|
24
|
+
let anyImportData = false;
|
|
25
|
+
for (let i = 0; i < exports.length; i++) {
|
|
26
|
+
for (let j = i + 1; j < exports.length; j++) {
|
|
27
|
+
const exp1Imports = exports[i].imports;
|
|
28
|
+
const exp2Imports = exports[j].imports;
|
|
29
|
+
if (exp1Imports || exp2Imports) {
|
|
30
|
+
anyImportData = true;
|
|
31
|
+
const sim = calculateImportSimilarity(
|
|
32
|
+
{
|
|
33
|
+
...exports[i],
|
|
34
|
+
imports: exp1Imports || []
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
...exports[j],
|
|
38
|
+
imports: exp2Imports || []
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
importScoreTotal += sim;
|
|
42
|
+
pairsWithData++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const avgImportScore = pairsWithData > 0 ? importScoreTotal / pairsWithData : 0;
|
|
47
|
+
let score = anyImportData ? domainScore * 0.4 + avgImportScore * 0.6 : domainScore;
|
|
48
|
+
if (anyImportData && score === 0 && domainScore === 0) {
|
|
49
|
+
score = 0.1;
|
|
50
|
+
}
|
|
51
|
+
let structuralScore = 0;
|
|
52
|
+
for (const exp of exports) {
|
|
53
|
+
if (exp.dependencies && exp.dependencies.length > 0) {
|
|
54
|
+
structuralScore += 1;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (structuralScore > 0) {
|
|
58
|
+
score = Math.min(1, score + 0.1);
|
|
59
|
+
}
|
|
60
|
+
if (!options?.weights && !anyImportData && domainCounts.size === 1) return 1;
|
|
61
|
+
return score;
|
|
62
|
+
}
|
|
63
|
+
function calculateStructuralCohesionFromCoUsage(file, coUsageMatrix) {
|
|
64
|
+
if (!coUsageMatrix) return 1;
|
|
65
|
+
const coUsages = coUsageMatrix.get(file);
|
|
66
|
+
if (!coUsages || coUsages.size === 0) return 1;
|
|
67
|
+
let total = 0;
|
|
68
|
+
for (const count of coUsages.values()) total += count;
|
|
69
|
+
if (total === 0) return 1;
|
|
70
|
+
const probs = [];
|
|
71
|
+
for (const count of coUsages.values()) {
|
|
72
|
+
if (count > 0) probs.push(count / total);
|
|
73
|
+
}
|
|
74
|
+
if (probs.length <= 1) return 1;
|
|
75
|
+
let entropy = 0;
|
|
76
|
+
for (const prob of probs) {
|
|
77
|
+
entropy -= prob * Math.log2(prob);
|
|
78
|
+
}
|
|
79
|
+
const maxEntropy = Math.log2(probs.length);
|
|
80
|
+
return maxEntropy > 0 ? 1 - entropy / maxEntropy : 1;
|
|
81
|
+
}
|
|
82
|
+
function calculateFragmentation(files, domain, options) {
|
|
83
|
+
if (files.length <= 1) return 0;
|
|
84
|
+
const directories = new Set(
|
|
85
|
+
files.map((file) => file.split("/").slice(0, -1).join("/"))
|
|
86
|
+
);
|
|
87
|
+
const uniqueDirs = directories.size;
|
|
88
|
+
let score = options?.useLogScale ? uniqueDirs <= 1 ? 0 : Math.log(uniqueDirs) / Math.log(options.logBase || Math.E) / (Math.log(files.length) / Math.log(options.logBase || Math.E)) : (uniqueDirs - 1) / (files.length - 1);
|
|
89
|
+
if (options?.sharedImportRatio && options.sharedImportRatio > 0.5) {
|
|
90
|
+
const discount = (options.sharedImportRatio - 0.5) * 0.4;
|
|
91
|
+
score = score * (1 - discount);
|
|
92
|
+
}
|
|
93
|
+
return score;
|
|
94
|
+
}
|
|
95
|
+
function calculatePathEntropy(files) {
|
|
96
|
+
if (!files || files.length === 0) return 0;
|
|
97
|
+
const dirCounts = /* @__PURE__ */ new Map();
|
|
98
|
+
for (const file of files) {
|
|
99
|
+
const dir = file.split("/").slice(0, -1).join("/") || ".";
|
|
100
|
+
dirCounts.set(dir, (dirCounts.get(dir) || 0) + 1);
|
|
101
|
+
}
|
|
102
|
+
const counts = Array.from(dirCounts.values());
|
|
103
|
+
if (counts.length <= 1) return 0;
|
|
104
|
+
const total = counts.reduce((sum, value) => sum + value, 0);
|
|
105
|
+
let entropy = 0;
|
|
106
|
+
for (const count of counts) {
|
|
107
|
+
const prob = count / total;
|
|
108
|
+
entropy -= prob * Math.log2(prob);
|
|
109
|
+
}
|
|
110
|
+
const maxEntropy = Math.log2(counts.length);
|
|
111
|
+
return maxEntropy > 0 ? entropy / maxEntropy : 0;
|
|
112
|
+
}
|
|
113
|
+
function calculateDirectoryDistance(files) {
|
|
114
|
+
if (!files || files.length <= 1) return 0;
|
|
115
|
+
const pathSegments = (pathStr) => pathStr.split("/").filter(Boolean);
|
|
116
|
+
const commonAncestorDepth = (pathA, pathB) => {
|
|
117
|
+
const minLen = Math.min(pathA.length, pathB.length);
|
|
118
|
+
let i = 0;
|
|
119
|
+
while (i < minLen && pathA[i] === pathB[i]) i++;
|
|
120
|
+
return i;
|
|
121
|
+
};
|
|
122
|
+
let totalNormalized = 0;
|
|
123
|
+
let comparisons = 0;
|
|
124
|
+
for (let i = 0; i < files.length; i++) {
|
|
125
|
+
for (let j = i + 1; j < files.length; j++) {
|
|
126
|
+
const segA = pathSegments(files[i]);
|
|
127
|
+
const segB = pathSegments(files[j]);
|
|
128
|
+
const shared = commonAncestorDepth(segA, segB);
|
|
129
|
+
const maxDepth = Math.max(segA.length, segB.length);
|
|
130
|
+
totalNormalized += 1 - (maxDepth > 0 ? shared / maxDepth : 0);
|
|
131
|
+
comparisons++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return comparisons > 0 ? totalNormalized / comparisons : 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export {
|
|
138
|
+
calculateEnhancedCohesion,
|
|
139
|
+
calculateStructuralCohesionFromCoUsage,
|
|
140
|
+
calculateFragmentation,
|
|
141
|
+
calculatePathEntropy,
|
|
142
|
+
calculateDirectoryDistance
|
|
143
|
+
};
|