@@ -2,7 +2,9 @@ import { is_valid_coverage, type Coverage, type Range } from './parse-coverage.t
22import { deduplicate_entries } from './decuplicate.ts'
33import { filter_coverage } from './filter-entries.ts'
44import type { Parser } from './types.ts'
5- import { format } from '@projectwallace/format-css'
5+ import { chunkify_stylesheet } from './chunkify.ts'
6+ import { extend_ranges } from './extend-ranges.ts'
7+ import { prettify , type PrettifiedCoverage , type PrettifiedChunk } from './prettify.ts'
68
79export type CoverageData = {
810 uncovered_bytes : number
@@ -19,7 +21,7 @@ export type StylesheetCoverage = CoverageData & {
1921 url : string
2022 text : string
2123 ranges : Range [ ]
22- chunks : CoverageChunk [ ]
24+ chunks : PrettifiedChunk [ ]
2325}
2426
2527export type CoverageResult = CoverageData & {
@@ -33,7 +35,8 @@ function ratio(fraction: number, total: number) {
3335 return fraction / total
3436}
3537
36- function calculate_stylesheet_coverage ( { text, ranges, url, chunks } : LineCoverage ) : StylesheetCoverage {
38+ function calculate_stylesheet_coverage ( stylesheet : PrettifiedCoverage ) : StylesheetCoverage {
39+ let { text, ranges, url, chunks } = stylesheet
3740 let uncovered_bytes = 0
3841 let covered_bytes = 0
3942 let total_bytes = 0
@@ -73,136 +76,6 @@ function calculate_stylesheet_coverage({ text, ranges, url, chunks }: LineCovera
7376 }
7477}
7578
76- /**
77- * WARNING: mutates the ranges array
78- */
79- function include_atrule_name_in_ranges ( coverage : Coverage [ ] ) : Coverage [ ] {
80- // Adjust ranges to include @-rule name (only preludes included)
81- // Note: Cannot reliably include closing } because it may not be the end of the range
82- const LONGEST_ATRULE_NAME = '@-webkit-font-feature-values' . length
83-
84- for ( let stylesheet of coverage ) {
85- for ( let range of stylesheet . ranges ) {
86- // Heuristic: atrule names are no longer than LONGEST_ATRULE_NAME
87- for ( let i = 1 ; i >= - LONGEST_ATRULE_NAME ; i -- ) {
88- let char_position = range . start + i
89- if ( stylesheet . text . charAt ( char_position ) === '@' ) {
90- range . start = char_position
91- break
92- }
93- }
94- }
95- }
96-
97- return coverage
98- }
99-
100- type OffsetChunk = {
101- start_offset : number
102- end_offset : number
103- is_covered : boolean
104- }
105-
106- type CoverageChunk = OffsetChunk & {
107- start_line : number
108- end_line : number
109- total_lines : number
110- css : string
111- }
112-
113- type ChunkifiedCoverage = Coverage & { chunks : OffsetChunk [ ] }
114- type LineCoverage = Coverage & { chunks : CoverageChunk [ ] }
115-
116- // TODO: get rid of empty chunks, merge first/last with adjecent covered block
117- function chunkify_stylesheet ( stylesheet : Coverage ) : ChunkifiedCoverage {
118- let chunks = [ ]
119- let offset = 0
120-
121- for ( let range of stylesheet . ranges ) {
122- // Create non-covered chunk
123- if ( offset !== range . start ) {
124- chunks . push ( {
125- start_offset : offset ,
126- end_offset : range . start ,
127- is_covered : false ,
128- } )
129- offset = range . start
130- }
131-
132- chunks . push ( {
133- start_offset : range . start ,
134- end_offset : range . end ,
135- is_covered : true ,
136- } )
137- offset = range . end
138- }
139-
140- // fill up last chunk if necessary:
141- if ( offset !== stylesheet . text . length ) {
142- chunks . push ( {
143- start_offset : offset ,
144- end_offset : stylesheet . text . length ,
145- is_covered : false ,
146- } )
147- }
148-
149- return {
150- ...stylesheet ,
151- chunks,
152- }
153- }
154-
155- function prettify ( stylesheet : ChunkifiedCoverage ) : LineCoverage {
156- let line = 1
157- let offset = 0
158-
159- let pretty_chunks = stylesheet . chunks . map ( ( offset_chunk , index ) => {
160- let css = format ( stylesheet . text . slice ( offset_chunk . start_offset , offset_chunk . end_offset ) )
161-
162- if ( offset_chunk . is_covered ) {
163- if ( index === 0 ) {
164- // mark the line between this chunk and the next on as covered
165- css = css + '\n'
166- } else if ( index === stylesheet . chunks . length - 1 ) {
167- // mark the newline after the previous uncovered block as covered
168- css = '\n' + css
169- } else {
170- // mark the newline after the previous uncovered block as covered
171- // and mark the line between this chunk and the next on as covered
172- css = '\n' + css + '\n'
173- }
174- }
175-
176- let line_count = css . split ( '\n' ) . length
177- let start_offset = offset
178- let end_offset = Math . max ( offset + css . length - 1 , 0 )
179- let start_line = line
180- let end_line = line + line_count
181-
182- line = end_line
183- offset = end_offset
184-
185- return {
186- ...offset_chunk ,
187- start_offset,
188- start_line,
189- end_line : end_line - 1 ,
190- end_offset,
191- css,
192- total_lines : end_line - start_line ,
193- }
194- } )
195-
196- let updated_stylesheet = {
197- ...stylesheet ,
198- // TODO: update ranges as well?? Or remove them because we have chunks now
199- chunks : pretty_chunks ,
200- text : pretty_chunks . map ( ( { css } ) => css ) . join ( '' ) ,
201- }
202-
203- return updated_stylesheet
204- }
205-
20679/**
20780 * @description
20881 * CSS Code Coverage calculation
@@ -223,11 +96,9 @@ export function calculate_coverage(coverage: Coverage[], parse_html?: Parser): C
22396
22497 let filtered_coverage = filter_coverage ( coverage , parse_html )
22598 let deduplicated = deduplicate_entries ( filtered_coverage )
226- let range_extended = include_atrule_name_in_ranges ( deduplicated )
99+ let range_extended = extend_ranges ( deduplicated )
227100 let chunkified = range_extended . map ( ( stylesheet ) => chunkify_stylesheet ( stylesheet ) )
228101 let prettified = chunkified . map ( ( stylesheet ) => prettify ( stylesheet ) )
229-
230- // Calculate coverage for each individual stylesheet we found
231102 let coverage_per_stylesheet = prettified . map ( ( stylesheet ) => calculate_stylesheet_coverage ( stylesheet ) )
232103
233104 // Calculate total coverage for all stylesheets combined
0 commit comments