7
7
PluginReport ,
8
8
Report ,
9
9
} from '@code-pushup/models' ;
10
+ import { deepClone } from './transformation' ;
10
11
11
12
type EnrichedAuditReport = AuditReport & { plugin : string } ;
12
13
type ScoredCategoryConfig = CategoryConfig & { score : number } ;
@@ -24,103 +25,77 @@ export type ScoredReport = Omit<Report, 'plugins' | 'categories'> & {
24
25
categories : ScoredCategoryConfig [ ] ;
25
26
} ;
26
27
27
- function groupRefToScore (
28
- audits : AuditReport [ ] ,
29
- ) : ( ref : AuditGroupRef ) => number {
30
- return ref => {
31
- const score = audits . find ( audit => audit . slug === ref . slug ) ?. score ;
32
- if ( score == null ) {
33
- throw new Error (
34
- `Group has invalid ref - audit with slug ${ ref . slug } not found` ,
35
- ) ;
36
- }
37
- return score ;
38
- } ;
39
- }
40
-
41
- function categoryRefToScore (
42
- audits : EnrichedAuditReport [ ] ,
43
- groups : EnrichedScoredAuditGroup [ ] ,
44
- ) : ( ref : CategoryRef ) => number {
45
- return ( ref : CategoryRef ) : number => {
46
- switch ( ref . type ) {
47
- case 'audit' :
48
- // eslint-disable-next-line no-case-declarations
49
- const audit = audits . find (
50
- a => a . slug === ref . slug && a . plugin === ref . plugin ,
51
- ) ;
52
- if ( ! audit ) {
53
- throw new Error (
54
- `Category has invalid ref - audit with slug ${ ref . slug } not found in ${ ref . plugin } plugin` ,
55
- ) ;
56
- }
57
- return audit . score ;
58
-
59
- case 'group' :
60
- // eslint-disable-next-line no-case-declarations
61
- const group = groups . find (
62
- g => g . slug === ref . slug && g . plugin === ref . plugin ,
63
- ) ;
64
- if ( ! group ) {
65
- throw new Error (
66
- `Category has invalid ref - group with slug ${ ref . slug } not found in ${ ref . plugin } plugin` ,
67
- ) ;
68
- }
69
- return group . score ;
70
- default :
71
- throw new Error ( `Type ${ ref . type } is unknown` ) ;
72
- }
73
- } ;
74
- }
75
-
76
28
export function calculateScore < T extends { weight : number } > (
77
29
refs : T [ ] ,
78
30
scoreFn : ( ref : T ) => number ,
79
31
) : number {
80
- const numerator = refs . reduce (
81
- ( sum , ref ) => sum + scoreFn ( ref ) * ref . weight ,
82
- 0 ,
32
+ const { numerator, denominator } = refs . reduce (
33
+ ( acc , ref ) => {
34
+ const score = scoreFn ( ref ) ;
35
+ return {
36
+ numerator : acc . numerator + score * ref . weight ,
37
+ denominator : acc . denominator + ref . weight ,
38
+ } ;
39
+ } ,
40
+ { numerator : 0 , denominator : 0 } ,
83
41
) ;
84
- const denominator = refs . reduce ( ( sum , ref ) => sum + ref . weight , 0 ) ;
85
42
return numerator / denominator ;
86
43
}
87
44
88
45
export function scoreReport ( report : Report ) : ScoredReport {
89
- const scoredPlugins = report . plugins . map ( plugin => {
90
- const { groups, audits } = plugin ;
91
- const preparedAudits = audits . map ( audit => ( {
92
- ...audit ,
93
- plugin : plugin . slug ,
94
- } ) ) ;
95
- const preparedGroups =
96
- groups ?. map ( group => ( {
97
- ...group ,
98
- score : calculateScore ( group . refs , groupRefToScore ( preparedAudits ) ) ,
99
- plugin : plugin . slug ,
100
- } ) ) || [ ] ;
46
+ const scoredReport = deepClone ( report ) as ScoredReport ;
47
+ const allScoredAuditsAndGroups = new Map ( ) ;
48
+
49
+ scoredReport . plugins ?. forEach ( plugin => {
50
+ const { audits } = plugin ;
51
+ const groups = plugin . groups || [ ] ;
52
+
53
+ audits . forEach ( audit => {
54
+ const key = `${ plugin . slug } -${ audit . slug } -audit` ;
55
+ audit . plugin = plugin . slug ;
56
+ allScoredAuditsAndGroups . set ( key , audit ) ;
57
+ } ) ;
58
+
59
+ function groupScoreFn ( ref : AuditGroupRef ) {
60
+ const score = allScoredAuditsAndGroups . get (
61
+ `${ plugin . slug } -${ ref . slug } -audit` ,
62
+ ) ?. score ;
63
+ if ( score == null ) {
64
+ throw new Error (
65
+ `Group has invalid ref - audit with slug ${ plugin . slug } -${ ref . slug } -audit not found` ,
66
+ ) ;
67
+ }
68
+ return score ;
69
+ }
101
70
102
- return {
103
- ...plugin ,
104
- audits : preparedAudits ,
105
- groups : preparedGroups ,
106
- } ;
71
+ groups . forEach ( group => {
72
+ const key = `${ plugin . slug } -${ group . slug } -group` ;
73
+ group . score = calculateScore ( group . refs , groupScoreFn ) ;
74
+ group . plugin = plugin . slug ;
75
+ allScoredAuditsAndGroups . set ( key , group ) ;
76
+ } ) ;
77
+ plugin . groups = groups ;
107
78
} ) ;
108
79
109
- // @TODO intro dict to avoid multiple find calls in the scoreFn
110
- const allScoredAudits = scoredPlugins . flatMap ( ( { audits } ) => audits ) ;
111
- const allScoredGroups = scoredPlugins . flatMap ( ( { groups } ) => groups ) ;
80
+ function catScoreFn ( ref : CategoryRef ) {
81
+ const key = `${ ref . plugin } -${ ref . slug } -${ ref . type } ` ;
82
+ const item = allScoredAuditsAndGroups . get ( key ) ;
83
+ if ( ! item ) {
84
+ throw new Error (
85
+ `Category has invalid ref - ${ ref . type } with slug ${ key } not found in ${ ref . plugin } plugin` ,
86
+ ) ;
87
+ }
88
+ return item . score ;
89
+ }
90
+
91
+ const scoredCategoriesMap = new Map ( ) ;
92
+ // eslint-disable-next-line functional/no-loop-statements
93
+ for ( const category of scoredReport . categories ) {
94
+ category . score = calculateScore ( category . refs , catScoreFn ) ;
95
+ scoredCategoriesMap . set ( category . slug , category ) ;
96
+ }
112
97
113
- const scoredCategories = report . categories . map ( category => ( {
114
- ...category ,
115
- score : calculateScore (
116
- category . refs ,
117
- categoryRefToScore ( allScoredAudits , allScoredGroups ) ,
118
- ) ,
119
- } ) ) ;
98
+ scoredReport . categories = Array . from ( scoredCategoriesMap . values ( ) ) ;
120
99
121
- return {
122
- ...report ,
123
- categories : scoredCategories ,
124
- plugins : scoredPlugins ,
125
- } ;
100
+ return scoredReport ;
126
101
}
0 commit comments