Skip to content

Commit af78ef2

Browse files
Merge pull request #45 from wolfmanfx/main
refactor: remove ts-morph dependency and simplify unused members metr…
2 parents 3597515 + 9fe319f commit af78ef2

11 files changed

Lines changed: 383 additions & 637 deletions

File tree

.detective/config.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"scopes": [
3-
"apps/frontend",
43
"apps/backend/src/infrastructure",
54
"apps/backend/src/mcp",
65
"apps/backend/src/model",
76
"apps/backend/src/options",
87
"apps/backend/src/services",
9-
"apps/backend/src/utils"
8+
"apps/backend/src/utils",
9+
"apps/frontend"
1010
],
1111
"groups": ["apps/backend/src", "apps/backend", "apps"],
1212
"entries": [],

.detective/hash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0a7d0219e81ae606e07a3ab3fedd5499f9db7f49, v1.3.0
1+
1b2f59522186c242168b2282b7815a75b48d7a6b, v1.3.0

apps/backend/src/services/trend-analysis/x-ray/class-metrics-analyzer.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export class ClassMetricsAnalyzer extends BaseMetricsAnalyzer {
5353
const allSections = [
5454
...(reasonsMeta.ui.sections ?? []),
5555
...(duplicateMeta.ui.sections ?? []),
56+
...(unusedMeta.ui.sections ?? []),
5657
];
5758

5859
return {
@@ -96,7 +97,7 @@ export class ClassMetricsAnalyzer extends BaseMetricsAnalyzer {
9697
complexity,
9798
dependencyAnalysis,
9899
reasonsMetric,
99-
unusedMembers,
100+
unusedMembersResult,
100101
duplicateAnalysis,
101102
] = await Promise.all([
102103
this.complexityMetric.analyzeAsync({ ...this.context, scopeNode: node }),
@@ -143,7 +144,8 @@ export class ClassMetricsAnalyzer extends BaseMetricsAnalyzer {
143144
internalFiles: reasonsMetric.internalFiles,
144145

145146
// Code quality metrics
146-
unusedMembers: unusedMembers,
147+
unusedMembers: unusedMembersResult.unusedMembers,
148+
unusedMemberNames: unusedMembersResult.unusedMemberNames,
147149
duplicateBlocks,
148150
duplicateDetails,
149151
};

apps/backend/src/services/trend-analysis/x-ray/metrics/unused-members.metric.spec.ts

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,15 @@ describe('UnusedMembersMetric', () => {
3333
}
3434
`;
3535
const { sourceFile, classNode, program, checker } = buildContext(code);
36-
const count = metric.analyze({
36+
const result = metric.analyze({
3737
sourceFile,
3838
program,
3939
checker,
4040
sourceCode: code,
4141
scopeNode: classNode,
4242
});
43-
expect(count).toBe(5);
43+
expect(result.unusedMembers).toBe(5);
44+
expect(result.unusedMemberNames?.length).toBe(5);
4445
});
4546

4647
it('detects unused members with usage patterns', () => {
@@ -61,14 +62,17 @@ describe('UnusedMembersMetric', () => {
6162
}
6263
`;
6364
const { sourceFile, classNode, program, checker } = buildContext(code);
64-
const count = metric.analyze({
65+
const result = metric.analyze({
6566
sourceFile,
6667
program,
6768
checker,
6869
sourceCode: code,
6970
scopeNode: classNode,
7071
});
71-
expect(count).toBe(3);
72+
expect(result.unusedMembers).toBe(3);
73+
expect(result.unusedMemberNames).toEqual(
74+
expect.arrayContaining(['unused1', 'unused2', '#unusedSuper'])
75+
);
7276
});
7377

7478
it('detects unused constructor parameter properties', () => {
@@ -78,14 +82,15 @@ describe('UnusedMembersMetric', () => {
7882
}
7983
`;
8084
const { sourceFile, classNode, program, checker } = buildContext(code);
81-
const count = metric.analyze({
85+
const result = metric.analyze({
8286
sourceFile,
8387
program,
8488
checker,
8589
sourceCode: code,
8690
scopeNode: classNode,
8791
});
88-
expect(count).toBe(1);
92+
expect(result.unusedMembers).toBe(1);
93+
expect(result.unusedMemberNames).toEqual(['unused']);
8994
});
9095

9196
it('finds no unused members when all are used or public', () => {
@@ -110,13 +115,43 @@ describe('UnusedMembersMetric', () => {
110115
}
111116
`;
112117
const { sourceFile, classNode, program, checker } = buildContext(code);
113-
const count = metric.analyze({
118+
const result = metric.analyze({
119+
sourceFile,
120+
program,
121+
checker,
122+
sourceCode: code,
123+
scopeNode: classNode,
124+
});
125+
expect(result.unusedMembers).toBe(0);
126+
expect(result.unusedMemberNames).toEqual([]);
127+
});
128+
129+
it('does not flag private static members used via ClassName access', () => {
130+
const code = `
131+
class Example {
132+
private static readonly CONST = new Set(['a']);
133+
private static helper() { return 'ok'; }
134+
private value = 'v';
135+
136+
method() {
137+
if (Example.CONST.has('a')) {
138+
(Example as any).helper();
139+
}
140+
// also ensure element access is handled
141+
const c = (Example as any)['CONST'];
142+
this.value = 'x';
143+
}
144+
}
145+
`;
146+
const { sourceFile, classNode, program, checker } = buildContext(code);
147+
const result = metric.analyze({
114148
sourceFile,
115149
program,
116150
checker,
117151
sourceCode: code,
118152
scopeNode: classNode,
119153
});
120-
expect(count).toBe(0);
154+
expect(result.unusedMembers).toBe(0);
155+
expect(result.unusedMemberNames).toEqual([]);
121156
});
122157
});

0 commit comments

Comments
 (0)