mirror of
https://github.com/samiyev/puaros.git
synced 2025-12-27 23:06:54 +05:00
feat(ipuaro): display transitive counts in High Impact Files table
- Change table header to include Direct and Transitive columns - Sort by transitive count first, then by impact score - Update tests for new table format
This commit is contained in:
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [0.30.1] - 2025-12-05 - Display Transitive Counts in Context
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **High Impact Files table now includes transitive counts**
|
||||||
|
- Table header changed from `| File | Impact | Dependents |` to `| File | Impact | Direct | Transitive |`
|
||||||
|
- Shows both direct dependent count and transitive dependent count
|
||||||
|
- Sorting changed: now sorts by transitive count first, then by impact score
|
||||||
|
- Example: `| utils/validation | 67% | 12 | 24 |`
|
||||||
|
|
||||||
|
### Technical Details
|
||||||
|
|
||||||
|
- Total tests: 1839 passed
|
||||||
|
- 0 ESLint errors, 3 warnings (pre-existing complexity)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [0.30.0] - 2025-12-05 - Transitive Dependencies Count
|
## [0.30.0] - 2025-12-05 - Transitive Dependencies Count
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -580,12 +580,13 @@ export function formatCircularDeps(cycles: string[][]): string | null {
|
|||||||
/**
|
/**
|
||||||
* Format high impact files table for display in context.
|
* Format high impact files table for display in context.
|
||||||
* Shows files with highest impact scores (most dependents).
|
* Shows files with highest impact scores (most dependents).
|
||||||
|
* Includes both direct and transitive dependent counts.
|
||||||
*
|
*
|
||||||
* Format:
|
* Format:
|
||||||
* ## High Impact Files
|
* ## High Impact Files
|
||||||
* | File | Impact | Dependents |
|
* | File | Impact | Direct | Transitive |
|
||||||
* |------|--------|------------|
|
* |------|--------|--------|------------|
|
||||||
* | src/utils/validation.ts | 67% | 12 files |
|
* | src/utils/validation.ts | 67% | 12 | 24 |
|
||||||
*
|
*
|
||||||
* @param metas - Map of file paths to their metadata
|
* @param metas - Map of file paths to their metadata
|
||||||
* @param limit - Maximum number of files to show (default: 10)
|
* @param limit - Maximum number of files to show (default: 10)
|
||||||
@@ -601,7 +602,12 @@ export function formatHighImpactFiles(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Collect files with impact score >= minImpact
|
// Collect files with impact score >= minImpact
|
||||||
const impactFiles: { path: string; impact: number; dependents: number }[] = []
|
const impactFiles: {
|
||||||
|
path: string
|
||||||
|
impact: number
|
||||||
|
dependents: number
|
||||||
|
transitive: number
|
||||||
|
}[] = []
|
||||||
|
|
||||||
for (const [path, meta] of metas) {
|
for (const [path, meta] of metas) {
|
||||||
if (meta.impactScore >= minImpact) {
|
if (meta.impactScore >= minImpact) {
|
||||||
@@ -609,6 +615,7 @@ export function formatHighImpactFiles(
|
|||||||
path,
|
path,
|
||||||
impact: meta.impactScore,
|
impact: meta.impactScore,
|
||||||
dependents: meta.dependents.length,
|
dependents: meta.dependents.length,
|
||||||
|
transitive: meta.transitiveDepCount,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -617,8 +624,11 @@ export function formatHighImpactFiles(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by impact score descending, then by path
|
// Sort by transitive count descending, then by impact, then by path
|
||||||
impactFiles.sort((a, b) => {
|
impactFiles.sort((a, b) => {
|
||||||
|
if (a.transitive !== b.transitive) {
|
||||||
|
return b.transitive - a.transitive
|
||||||
|
}
|
||||||
if (a.impact !== b.impact) {
|
if (a.impact !== b.impact) {
|
||||||
return b.impact - a.impact
|
return b.impact - a.impact
|
||||||
}
|
}
|
||||||
@@ -631,15 +641,16 @@ export function formatHighImpactFiles(
|
|||||||
const lines: string[] = [
|
const lines: string[] = [
|
||||||
"## High Impact Files",
|
"## High Impact Files",
|
||||||
"",
|
"",
|
||||||
"| File | Impact | Dependents |",
|
"| File | Impact | Direct | Transitive |",
|
||||||
"|------|--------|------------|",
|
"|------|--------|--------|------------|",
|
||||||
]
|
]
|
||||||
|
|
||||||
for (const file of topFiles) {
|
for (const file of topFiles) {
|
||||||
const shortPath = shortenPath(file.path)
|
const shortPath = shortenPath(file.path)
|
||||||
const impact = `${String(file.impact)}%`
|
const impact = `${String(file.impact)}%`
|
||||||
const dependents = file.dependents === 1 ? "1 file" : `${String(file.dependents)} files`
|
const direct = String(file.dependents)
|
||||||
lines.push(`| ${shortPath} | ${impact} | ${dependents} |`)
|
const transitive = String(file.transitive)
|
||||||
|
lines.push(`| ${shortPath} | ${impact} | ${direct} | ${transitive} |`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines.join("\n")
|
return lines.join("\n")
|
||||||
|
|||||||
@@ -2418,6 +2418,8 @@ describe("prompts", () => {
|
|||||||
isEntryPoint: false,
|
isEntryPoint: false,
|
||||||
fileType: "source",
|
fileType: "source",
|
||||||
impactScore: 2,
|
impactScore: 2,
|
||||||
|
transitiveDepCount: 0,
|
||||||
|
transitiveDepByCount: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
@@ -2427,7 +2429,7 @@ describe("prompts", () => {
|
|||||||
expect(result).toBeNull()
|
expect(result).toBeNull()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should format file with high impact score", () => {
|
it("should format file with high impact score and transitive counts", () => {
|
||||||
const metas = new Map<string, FileMeta>([
|
const metas = new Map<string, FileMeta>([
|
||||||
[
|
[
|
||||||
"src/utils/validation.ts",
|
"src/utils/validation.ts",
|
||||||
@@ -2452,6 +2454,8 @@ describe("prompts", () => {
|
|||||||
isEntryPoint: false,
|
isEntryPoint: false,
|
||||||
fileType: "source",
|
fileType: "source",
|
||||||
impactScore: 67,
|
impactScore: 67,
|
||||||
|
transitiveDepCount: 24,
|
||||||
|
transitiveDepByCount: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
@@ -2459,11 +2463,11 @@ describe("prompts", () => {
|
|||||||
const result = formatHighImpactFiles(metas)
|
const result = formatHighImpactFiles(metas)
|
||||||
|
|
||||||
expect(result).toContain("## High Impact Files")
|
expect(result).toContain("## High Impact Files")
|
||||||
expect(result).toContain("| File | Impact | Dependents |")
|
expect(result).toContain("| File | Impact | Direct | Transitive |")
|
||||||
expect(result).toContain("| utils/validation | 67% | 12 files |")
|
expect(result).toContain("| utils/validation | 67% | 12 | 24 |")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should sort by impact score descending", () => {
|
it("should sort by transitive count descending, then by impact", () => {
|
||||||
const metas = new Map<string, FileMeta>([
|
const metas = new Map<string, FileMeta>([
|
||||||
[
|
[
|
||||||
"src/low.ts",
|
"src/low.ts",
|
||||||
@@ -2475,6 +2479,8 @@ describe("prompts", () => {
|
|||||||
isEntryPoint: false,
|
isEntryPoint: false,
|
||||||
fileType: "source",
|
fileType: "source",
|
||||||
impactScore: 10,
|
impactScore: 10,
|
||||||
|
transitiveDepCount: 5,
|
||||||
|
transitiveDepByCount: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -2487,6 +2493,8 @@ describe("prompts", () => {
|
|||||||
isEntryPoint: false,
|
isEntryPoint: false,
|
||||||
fileType: "source",
|
fileType: "source",
|
||||||
impactScore: 50,
|
impactScore: 50,
|
||||||
|
transitiveDepCount: 15,
|
||||||
|
transitiveDepByCount: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
@@ -2511,6 +2519,8 @@ describe("prompts", () => {
|
|||||||
isEntryPoint: false,
|
isEntryPoint: false,
|
||||||
fileType: "source",
|
fileType: "source",
|
||||||
impactScore: 10 + i,
|
impactScore: 10 + i,
|
||||||
|
transitiveDepCount: i,
|
||||||
|
transitiveDepByCount: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2535,6 +2545,8 @@ describe("prompts", () => {
|
|||||||
isEntryPoint: false,
|
isEntryPoint: false,
|
||||||
fileType: "source",
|
fileType: "source",
|
||||||
impactScore: 30,
|
impactScore: 30,
|
||||||
|
transitiveDepCount: 5,
|
||||||
|
transitiveDepByCount: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -2547,6 +2559,8 @@ describe("prompts", () => {
|
|||||||
isEntryPoint: false,
|
isEntryPoint: false,
|
||||||
fileType: "source",
|
fileType: "source",
|
||||||
impactScore: 5,
|
impactScore: 5,
|
||||||
|
transitiveDepCount: 1,
|
||||||
|
transitiveDepByCount: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
@@ -2558,28 +2572,6 @@ describe("prompts", () => {
|
|||||||
expect(result).not.toContain("low")
|
expect(result).not.toContain("low")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should show singular 'file' for 1 dependent", () => {
|
|
||||||
const metas = new Map<string, FileMeta>([
|
|
||||||
[
|
|
||||||
"src/single.ts",
|
|
||||||
{
|
|
||||||
complexity: { loc: 10, nesting: 1, cyclomaticComplexity: 1, score: 10 },
|
|
||||||
dependencies: [],
|
|
||||||
dependents: ["a.ts"],
|
|
||||||
isHub: false,
|
|
||||||
isEntryPoint: false,
|
|
||||||
fileType: "source",
|
|
||||||
impactScore: 10,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
])
|
|
||||||
|
|
||||||
const result = formatHighImpactFiles(metas)
|
|
||||||
|
|
||||||
expect(result).toContain("1 file")
|
|
||||||
expect(result).not.toContain("1 files")
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should shorten src/ prefix", () => {
|
it("should shorten src/ prefix", () => {
|
||||||
const metas = new Map<string, FileMeta>([
|
const metas = new Map<string, FileMeta>([
|
||||||
[
|
[
|
||||||
@@ -2592,6 +2584,8 @@ describe("prompts", () => {
|
|||||||
isEntryPoint: false,
|
isEntryPoint: false,
|
||||||
fileType: "source",
|
fileType: "source",
|
||||||
impactScore: 20,
|
impactScore: 20,
|
||||||
|
transitiveDepCount: 5,
|
||||||
|
transitiveDepByCount: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
@@ -2614,6 +2608,8 @@ describe("prompts", () => {
|
|||||||
isEntryPoint: false,
|
isEntryPoint: false,
|
||||||
fileType: "source",
|
fileType: "source",
|
||||||
impactScore: 20,
|
impactScore: 20,
|
||||||
|
transitiveDepCount: 3,
|
||||||
|
transitiveDepByCount: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
@@ -2660,6 +2656,8 @@ describe("prompts", () => {
|
|||||||
isEntryPoint: true,
|
isEntryPoint: true,
|
||||||
fileType: "source",
|
fileType: "source",
|
||||||
impactScore: 20,
|
impactScore: 20,
|
||||||
|
transitiveDepCount: 5,
|
||||||
|
transitiveDepByCount: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
@@ -2681,6 +2679,8 @@ describe("prompts", () => {
|
|||||||
isEntryPoint: true,
|
isEntryPoint: true,
|
||||||
fileType: "source",
|
fileType: "source",
|
||||||
impactScore: 20,
|
impactScore: 20,
|
||||||
|
transitiveDepCount: 5,
|
||||||
|
transitiveDepByCount: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
|
|||||||
Reference in New Issue
Block a user