@@ -3,7 +3,8 @@ import path from 'path'
33import { Command } from 'commander'
44import {
55 GrowthPatternMetric ,
6- VersionChangeMetric
6+ VersionChangeMetric ,
7+ VulnerabilityFixBySeverityMetric
78} from './metrics-generator' ;
89import {
910 generateGrowthPatternChartData ,
@@ -32,12 +33,12 @@ export interface MetricOptions {
3233 inputFiles : string [ ] ;
3334}
3435
35- type MetricType = 'growth-pattern' | 'version-changes' ;
36+ type MetricType = 'growth-pattern' | 'version-changes' | 'vulnerability-fixes-by-severity' ;
3637
3738interface MetricConfig {
38- processor : ( data : any ) => any ;
39+ processor : ( data : any , libraryInfo ?: any ) => any ;
3940 requiredPrefixes : string [ ] ;
40- chartGenerator : ( results : any , options : MetricOptions ) => { data : any [ ] ; layout : any } [ ] ;
41+ chartGenerator ? : ( results : any , options : MetricOptions ) => { data : any [ ] ; layout : any } [ ] ;
4142}
4243
4344const metricsRegistry : Record < MetricType , MetricConfig > = {
@@ -50,6 +51,10 @@ const metricsRegistry: Record<MetricType, MetricConfig> = {
5051 processor : VersionChangeMetric ,
5152 requiredPrefixes : [ 'dependency-history' ] ,
5253 chartGenerator : generateVersionChangeChartData
54+ } ,
55+ 'vulnerability-fixes-by-severity' : {
56+ processor : VulnerabilityFixBySeverityMetric ,
57+ requiredPrefixes : [ 'commit-dependency-history' , 'library-info' ]
5358 }
5459} ;
5560
@@ -72,44 +77,59 @@ export async function runMetrics(historyFolder: string, options: MetricOptions):
7277 return ;
7378 }
7479
75- const validInputFiles = options . inputFiles . filter ( file =>
76- config . requiredPrefixes . some ( prefix => file . startsWith ( prefix ) )
80+ const inputBase = path . join ( historyFolder , options . inputDir || '' ) ;
81+ const outputBase = path . join ( historyFolder , options . results ) ;
82+ fs . mkdirSync ( outputBase , { recursive : true } ) ;
83+
84+ let libraryInfo : any = undefined ;
85+ const libraryInfoFile = options . inputFiles . find ( file => file . startsWith ( 'library-info' ) ) ;
86+
87+ if ( libraryInfoFile ) {
88+ const libCandidates = [
89+ path . join ( inputBase , libraryInfoFile ) ,
90+ path . join ( inputBase , `${ libraryInfoFile } .json` )
91+ ] ;
92+ const libPath = libCandidates . find ( p => fs . existsSync ( p ) ) ;
93+ if ( libPath ) {
94+ const libContent = fs . readFileSync ( libPath , 'utf-8' ) ;
95+ libraryInfo = JSON . parse ( libContent ) ;
96+ } else {
97+ console . warn ( `⚠️ Specified library-info file not found for: ${ libraryInfoFile } ` ) ;
98+ }
99+ }
100+
101+ const mainDataFile = options . inputFiles . find ( file =>
102+ config . requiredPrefixes . some ( prefix => file . startsWith ( prefix ) && prefix !== 'library-info' )
77103 ) ;
78104
79- if ( ! validInputFiles . length ) {
80- console . warn ( `⚠️ No input files match the required prefixes : ${ config . requiredPrefixes . join ( ', ' ) } ` ) ;
105+ if ( ! mainDataFile ) {
106+ console . warn ( `⚠️ No valid main data file found. Expected prefix : ${ config . requiredPrefixes . join ( ', ' ) } ` ) ;
81107 return ;
82108 }
83109
84- const inputBase = path . join ( historyFolder , options . inputDir || '' ) ;
85- const outputBase = path . join ( historyFolder , options . results ) ;
86- fs . mkdirSync ( outputBase , { recursive : true } ) ;
110+ const fileName = mainDataFile . endsWith ( '.json' ) ? mainDataFile : `${ mainDataFile } .json` ;
111+ const filePath = path . join ( inputBase , fileName ) ;
87112
88- for ( const relativeName of validInputFiles ) {
89- const fileName = relativeName . endsWith ( '.json' ) ? relativeName : `${ relativeName } .json` ;
90- const filePath = path . join ( inputBase , fileName ) ;
91-
92- if ( ! fs . existsSync ( filePath ) ) {
93- console . warn ( `⚠️ File not found: ${ filePath } ` ) ;
94- continue ;
95- }
113+ if ( ! fs . existsSync ( filePath ) ) {
114+ console . warn ( `⚠️ File not found: ${ filePath } ` ) ;
115+ return ;
116+ }
96117
97- const fileContents = fs . readFileSync ( filePath , 'utf-8' ) ;
98- const data = JSON . parse ( fileContents ) ;
99- const results = config . processor ( data ) ;
118+ const fileContents = fs . readFileSync ( filePath , 'utf-8' ) ;
119+ const data = JSON . parse ( fileContents ) ;
120+ const results = config . processor ( data , libraryInfo ) ;
100121
101- const outputFile = path . join ( outputBase , `${ path . parse ( fileName ) . name } - ${ metricType } -metric.json` ) ;
102- fs . writeFileSync ( outputFile , JSON . stringify ( results , null , 2 ) ) ;
122+ const outputFile = path . join ( outputBase , `${ metricType } -metric.json` ) ;
123+ fs . writeFileSync ( outputFile , JSON . stringify ( results , null , 2 ) ) ;
103124
104- if ( options . chart ) {
105- const chartConfigs = config . chartGenerator ( results , options ) ;
106- if ( chartConfigs ?. length ) {
107- await generateHtmlChart ( outputFile , chartConfigs ) ;
108- } else {
109- console . warn ( `⚠️ No chart data generated for metric '${ metricType } '.` ) ;
110- }
125+ if ( options . chart && config . chartGenerator ) {
126+ const chartConfigs = config . chartGenerator ( results , options ) ;
127+ if ( chartConfigs ?. length ) {
128+ await generateHtmlChart ( outputFile , chartConfigs ) ;
129+ } else {
130+ console . warn ( `⚠️ No chart data generated for metric '${ metricType } '.` ) ;
111131 }
112-
113- console . log ( `✅ Metric calculated for ${ filePath } and chart generated (if requested).` ) ;
114132 }
133+
134+ console . log ( `✅ Metric calculated for ${ filePath } and chart generated (if requested).` ) ;
115135}
0 commit comments