diff --git a/README.md b/README.md index dd6ca52..cac0c15 100644 --- a/README.md +++ b/README.md @@ -37,12 +37,12 @@ You can run it locally on any library published to npm by providing its package LIBRARY: cdk-ecr-deployment VERSION: 4.0.3 -OVERALL SCORE: 76/100 +OVERALL SCORE: 80/100 --- SUBSCORES - MAINTENANCE : 67/100 + MAINTENANCE : 75/100 QUALITY : 83/100 POPULARITY : 85/100 ``` @@ -51,27 +51,28 @@ SUBSCORES Add `--details` for a detailed breakdown: ``` -> cdk-construct-analyzer cdk-ecr-deployment --details +> cdk-construct-analyzer cdk-ecr-deployment --details LIBRARY: cdk-ecr-deployment VERSION: 4.0.3 -OVERALL SCORE: 76/100 +OVERALL SCORE: 80/100 --- SUBSCORES - MAINTENANCE : 67/100 + MAINTENANCE : 75/100 QUALITY : 83/100 POPULARITY : 85/100 --- === MAINTENANCE === SCORE WEIGHT -— Time To First Response ............................ ★★☆☆☆ 15 +— Time To First Response ............................ ★★☆☆☆ 10 — Provenance Verification ........................... ★★★★★ 10 — Release Frequency ................................. ★★★★☆ 10 — Number Of Contributors - Maintenance .............. ★★★★☆ 5 +— Number Of Feats And Fixes ......................... ★★★★★ 5 — Open Issues Ratio ................................. ★★★★★ 5 === QUALITY === SCORE WEIGHT @@ -83,7 +84,7 @@ SUBSCORES — Multi Language Support ............................ ★★★★★ 5 === POPULARITY === SCORE WEIGHT -— Weekly Downloads .................................. ★★★★★ 15 +— Weekly Downloads .................................. ★★★★★ 10 — Github Stars ...................................... ★★★★☆ 10 — Number Of Contributors - Popularity ............... ★★★★☆ 5 ``` @@ -149,6 +150,7 @@ Helps determine if the project is active and healthy, or abandoned. Signals incl * Open issues / total issues: A lower ratio of open issues indicates backlog health and follow through normalized by repository popularity. Note: 0 total issues scores worst (100% ratio) as it suggests no community engagement. * Release Frequency: Regular releases signal iteration, patching, and progress. * Number of Contributors: More contributors reduce risk of abandonment and reflect shared maintenance. +* Number of Features and Fixes: Counts occurrences of "feat" and "fix" in release notes from the past year, indicating active development. ##### Quality @@ -159,6 +161,7 @@ Signals that are visible in the repo/package that showcases quality: * Author Track Record: Measures how many packages the author has published, more published packages often indicate greater experience. * Changelog includes feats/fixes: Checks if there are feats/fixes published in the release notes. * Stable versioning (>=1.x.x, not deprecated): Indicates API maturity and stability.ation makes the project easier to adopt and use (README, API References, Usage Examples). +* Multi-language Support: Supporting more CDK languages shows extra effort and intent to reach a broader developer base ##### Popularity diff --git a/src/lib/config.ts b/src/lib/config.ts index 0e8f097..6662999 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -12,7 +12,7 @@ export const CONFIG: Config = { signals: [ { name: 'timeToFirstResponse', - defaultWeight: 15, + defaultWeight: 10, description: 'Time to first response on issues', benchmarks: (weeks: number) => categorizeLowerIsBetter([1, 4, 12, 52], weeks), }, @@ -36,6 +36,12 @@ export const CONFIG: Config = { description: 'Number of Contributors in the past year', benchmarks: (contributors: number) => categorizeHigherIsBetter([8, 2, 1, 1], contributors), }, + { + name: 'numberOfFeatsAndFixes', + defaultWeight: 5, + description: 'Number of features and bug fixes contributed to the project in the past year', + benchmarks: (contributors: number) => categorizeHigherIsBetter([28, 10, 2, 1], contributors), + }, { name: 'openIssuesRatio', defaultWeight: 5, diff --git a/src/lib/data/collect.ts b/src/lib/data/collect.ts index e8c191d..4d8376e 100644 --- a/src/lib/data/collect.ts +++ b/src/lib/data/collect.ts @@ -6,10 +6,11 @@ import { processContributorsData, analyzeDocumentationCompleteness, analyzeTestsPresence, + analyzeReleaseNotesContent, + countFeatsAndFixes, calculateTimeToFirstResponse, - calculateOpenIssuesRatio, calculateReleaseFrequency, - analyzeReleaseNotesContent, + calculateOpenIssuesRatio, analyzeJsiiLanguageSupport, } from '../utils'; @@ -101,6 +102,7 @@ function processPackageData(rawData: RawPackageData): PackageData { provenanceVerification: rawData.npm.hasProvenance, numberOfContributors_Popularity: processContributorsData(repository.commits), releaseFrequency: calculateReleaseFrequency(repository.releases), + numberOfFeatsAndFixes: countFeatsAndFixes(repository), multiLanguageSupport: jsiiLanguageCount, openIssuesRatio: calculateOpenIssuesRatio(repository.openIssuesCount, repository.totalIssuesCount), }; diff --git a/src/lib/types.ts b/src/lib/types.ts index 8507de2..41cd22a 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -60,6 +60,7 @@ export type PackageData = { readonly timeToFirstResponse?: number; readonly provenanceVerification?: boolean; readonly releaseFrequency?: number; + readonly numberOfFeatsAndFixes?: number; readonly multiLanguageSupport?: number; readonly openIssuesRatio?: number; } & Record; diff --git a/src/lib/utils/releaseNotes.ts b/src/lib/utils/releaseNotes.ts index eb01f87..dbb630c 100644 --- a/src/lib/utils/releaseNotes.ts +++ b/src/lib/utils/releaseNotes.ts @@ -1,9 +1,9 @@ -import { GitHubRepository } from '../types'; +import { GitHubRepository, ReleaseNotesData } from '../types'; /** * Analyzes GitHub releases to determine if they include features and fixes */ -export function analyzeReleaseNotesContent(repository: GitHubRepository): { hasFeats: boolean; hasFixes: boolean } { +export function analyzeReleaseNotesContent(repository: GitHubRepository): ReleaseNotesData { if (!repository.releases || repository.releases.length === 0) { return { hasFeats: false, hasFixes: false }; } @@ -40,6 +40,50 @@ export function analyzeReleaseNotesContent(repository: GitHubRepository): { hasF return { hasFeats, hasFixes }; } +/** + * Count the number of features and bug fixes from release notes in the past year + */ +export function countFeatsAndFixes(repository: GitHubRepository): number { + if (!repository.releases || repository.releases.length === 0) { + return 0; + } + + const oneYearAgo = new Date(); + oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1); + + const releasesInPastYear = repository.releases.filter(release => { + if (!release.publishedAt) { + return false; + } + + const publishedDate = new Date(release.publishedAt); + return publishedDate >= oneYearAgo; + }); + + let count = 0; + + for (const release of releasesInPastYear) { + if (release.description) { + count += countFeatsAndFixesInDescription(release.description); + } + } + + return count; +} + +/** + * Count features and fixes in a release description + * Looks for "feat" or "fix" keywords + */ +function countFeatsAndFixesInDescription(description: string): number { + const lowerDescription = description.toLowerCase(); + + const featCount = (lowerDescription.match(/feat/g) ?? []).length; + const fixCount = (lowerDescription.match(/fix/g) ?? []).length; + + return featCount + fixCount; +} + /** * Checks if release description contains feature-related content */ diff --git a/test/lib/data/collect.test.ts b/test/lib/data/collect.test.ts index 6292e0f..6801bd5 100644 --- a/test/lib/data/collect.test.ts +++ b/test/lib/data/collect.test.ts @@ -110,6 +110,7 @@ describe('collectPackageData', () => { weeklyDownloads: 10000, githubStars: 500, numberOfContributors_Popularity: 2, + numberOfFeatsAndFixes: 2, stableVersioning: { isStableMajorVersion: true, hasMinorReleases: false, diff --git a/test/lib/utils/releaseNotes.test.ts b/test/lib/utils/releaseNotes.test.ts index d58ed39..63b5d69 100644 --- a/test/lib/utils/releaseNotes.test.ts +++ b/test/lib/utils/releaseNotes.test.ts @@ -1,5 +1,5 @@ import type { GitHubRepository } from '../../../src/lib/types'; -import { analyzeReleaseNotesContent } from '../../../src/lib/utils/releaseNotes'; +import { analyzeReleaseNotesContent, countFeatsAndFixes } from '../../../src/lib/utils/releaseNotes'; describe('analyzeReleaseNotesContent', () => { test('should return false for both when no releases exist', () => { @@ -163,4 +163,59 @@ describe('analyzeReleaseNotesContent', () => { expect(result.hasFeats).toBe(true); expect(result.hasFixes).toBe(true); }); -}); \ No newline at end of file +}); + + +describe('countFeatsAndFixes', () => { + test('should return 0 when no releases are provided', () => { + const repository: GitHubRepository = { + stargazerCount: 0, + releases: [], + }; + + expect(countFeatsAndFixes(repository)).toBe(0); + }); + + test('should return 0 when releases is undefined', () => { + const repository: GitHubRepository = { + stargazerCount: 0, + }; + + expect(countFeatsAndFixes(repository)).toBe(0); + }); + + test('should count features and fixes in release descriptions', () => { + const repository: GitHubRepository = { + stargazerCount: 0, + releases: [ + { + publishedAt: new Date().toISOString(), + tagName: 'v1.0.0', + description: 'feat fix', + }, + ], + }; + + expect(countFeatsAndFixes(repository)).toBe(2); + }); + + test('should count across multiple releases', () => { + const repository: GitHubRepository = { + stargazerCount: 0, + releases: [ + { + publishedAt: new Date().toISOString(), + tagName: 'v1.0.0', + description: 'feat fix', + }, + { + publishedAt: new Date().toISOString(), + tagName: 'v1.1.0', + description: 'feat feat', + }, + ], + }; + + expect(countFeatsAndFixes(repository)).toBe(4); + }); +});