Skip to content

Commit 3cbd214

Browse files
committed
move to calculate_stylesheet_coverage
1 parent 2b65190 commit 3cbd214

File tree

1 file changed

+111
-105
lines changed

1 file changed

+111
-105
lines changed

src/index.ts

Lines changed: 111 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,116 @@ function ratio(fraction: number, total: number) {
3939
return fraction / total
4040
}
4141

42+
function calculate_stylesheet_coverage(text: string, ranges: Range[], url: string): StylesheetCoverage {
43+
function is_line_covered(line: string, start_offset: number) {
44+
let end = start_offset + line.length
45+
let next_offset = end + 1 // account for newline character
46+
let is_empty = /^\s*$/.test(line)
47+
let is_closing_brace = line.endsWith('}')
48+
49+
if (!is_empty && !is_closing_brace) {
50+
for (let range of ranges) {
51+
if (range.start > end || range.end < start_offset) {
52+
continue
53+
}
54+
if (range.start <= start_offset && range.end >= end) {
55+
return true
56+
} else if (line.startsWith('@') && range.start > start_offset && range.start < next_offset) {
57+
return true
58+
}
59+
}
60+
}
61+
return false
62+
}
63+
64+
let lines = text.split('\n')
65+
let total_file_lines = lines.length
66+
let line_coverage = new Uint8Array(total_file_lines)
67+
let file_lines_covered = 0
68+
let file_total_bytes = text.length
69+
let file_bytes_covered = 0
70+
let offset = 0
71+
72+
for (let index = 0; index < lines.length; index++) {
73+
let line = lines[index]
74+
if (line === undefined) continue
75+
76+
let start = offset
77+
let end = offset + line.length
78+
let next_offset = end + 1 // +1 for the newline character
79+
let is_empty = /^\s*$/.test(line)
80+
let is_closing_brace = line.endsWith('}')
81+
let is_in_range = is_line_covered(line, start)
82+
let is_covered = false
83+
84+
let prev_is_covered = index > 0 && line_coverage[index - 1] === 1
85+
86+
if (is_in_range && !is_closing_brace && !is_empty) {
87+
is_covered = true
88+
} else if ((is_empty || is_closing_brace) && prev_is_covered) {
89+
is_covered = true
90+
} else if (is_empty && !prev_is_covered && is_line_covered(lines[index + 1]!, next_offset)) {
91+
// If the next line is covered, mark this empty line as covered
92+
is_covered = true
93+
}
94+
95+
line_coverage[index] = is_covered ? 1 : 0
96+
97+
if (is_covered) {
98+
file_lines_covered++
99+
file_bytes_covered += line.length + 1
100+
}
101+
102+
offset = next_offset
103+
}
104+
105+
// Create "chunks" of covered/uncovered lines for easier rendering later on
106+
let chunks = [
107+
{
108+
start_line: 1,
109+
is_covered: line_coverage[0] === 1,
110+
end_line: 1,
111+
total_lines: 1,
112+
},
113+
]
114+
115+
for (let index = 1; index < line_coverage.length; index++) {
116+
let is_covered = line_coverage[index]
117+
if (is_covered !== line_coverage[index - 1]) {
118+
let last_chunk = chunks.at(-1)!
119+
last_chunk.end_line = index
120+
last_chunk.total_lines = index - last_chunk.start_line + 1
121+
122+
chunks.push({
123+
start_line: index + 1,
124+
is_covered: is_covered === 1,
125+
end_line: index,
126+
total_lines: 0,
127+
})
128+
}
129+
}
130+
131+
let last_chunk = chunks.at(-1)!
132+
last_chunk.total_lines = line_coverage.length + 1 - last_chunk.start_line
133+
last_chunk.end_line = line_coverage.length
134+
135+
return {
136+
url,
137+
text,
138+
ranges,
139+
unused_bytes: file_total_bytes - file_bytes_covered,
140+
used_bytes: file_bytes_covered,
141+
total_bytes: file_total_bytes,
142+
line_coverage_ratio: ratio(file_lines_covered, total_file_lines),
143+
byte_coverage_ratio: ratio(file_bytes_covered, file_total_bytes),
144+
line_coverage,
145+
total_lines: total_file_lines,
146+
covered_lines: file_lines_covered,
147+
uncovered_lines: total_file_lines - file_lines_covered,
148+
chunks,
149+
}
150+
}
151+
42152
/**
43153
* @description
44154
* CSS Code Coverage calculation
@@ -63,111 +173,7 @@ export function calculate_coverage(coverage: Coverage[], parse_html?: Parser): C
63173

64174
// Calculate coverage for each individual stylesheet we found
65175
let coverage_per_stylesheet = Array.from(deduplicated).map(([text, { url, ranges }]) => {
66-
function is_line_covered(line: string, start_offset: number) {
67-
let end = start_offset + line.length
68-
let next_offset = end + 1 // account for newline character
69-
let is_empty = /^\s*$/.test(line)
70-
let is_closing_brace = line.endsWith('}')
71-
72-
if (!is_empty && !is_closing_brace) {
73-
for (let range of ranges) {
74-
if (range.start > end || range.end < start_offset) {
75-
continue
76-
}
77-
if (range.start <= start_offset && range.end >= end) {
78-
return true
79-
} else if (line.startsWith('@') && range.start > start_offset && range.start < next_offset) {
80-
return true
81-
}
82-
}
83-
}
84-
return false
85-
}
86-
87-
let lines = text.split('\n')
88-
let total_file_lines = lines.length
89-
let line_coverage = new Uint8Array(total_file_lines)
90-
let file_lines_covered = 0
91-
let file_total_bytes = text.length
92-
let file_bytes_covered = 0
93-
let offset = 0
94-
95-
for (let index = 0; index < lines.length; index++) {
96-
let line = lines[index]!
97-
let start = offset
98-
let end = offset + line.length
99-
let next_offset = end + 1 // +1 for the newline character
100-
let is_empty = /^\s*$/.test(line)
101-
let is_closing_brace = line.endsWith('}')
102-
let is_in_range = is_line_covered(line, start)
103-
let is_covered = false
104-
105-
let prev_is_covered = index > 0 && line_coverage[index - 1] === 1
106-
107-
if (is_in_range && !is_closing_brace && !is_empty) {
108-
is_covered = true
109-
} else if ((is_empty || is_closing_brace) && prev_is_covered) {
110-
is_covered = true
111-
} else if (is_empty && !prev_is_covered && is_line_covered(lines[index + 1]!, next_offset)) {
112-
// If the next line is covered, mark this empty line as covered
113-
is_covered = true
114-
}
115-
116-
line_coverage[index] = is_covered ? 1 : 0
117-
118-
if (is_covered) {
119-
file_lines_covered++
120-
file_bytes_covered += line.length + 1
121-
}
122-
123-
offset = next_offset
124-
}
125-
126-
// Create "chunks" of covered/uncovered lines for easier rendering later on
127-
let chunks = [
128-
{
129-
start_line: 1,
130-
is_covered: line_coverage[0] === 1,
131-
end_line: 1,
132-
total_lines: 1,
133-
},
134-
]
135-
136-
for (let index = 1; index < line_coverage.length; index++) {
137-
let is_covered = line_coverage[index]
138-
if (is_covered !== line_coverage[index - 1]) {
139-
let last_chunk = chunks.at(-1)!
140-
last_chunk.end_line = index
141-
last_chunk.total_lines = index - last_chunk.start_line + 1
142-
143-
chunks.push({
144-
start_line: index + 1,
145-
is_covered: is_covered === 1,
146-
end_line: index,
147-
total_lines: 0,
148-
})
149-
}
150-
}
151-
152-
let last_chunk = chunks.at(-1)!
153-
last_chunk.total_lines = line_coverage.length + 1 - last_chunk.start_line
154-
last_chunk.end_line = line_coverage.length
155-
156-
return {
157-
url,
158-
text,
159-
ranges,
160-
unused_bytes: file_total_bytes - file_bytes_covered,
161-
used_bytes: file_bytes_covered,
162-
total_bytes: file_total_bytes,
163-
line_coverage_ratio: ratio(file_lines_covered, total_file_lines),
164-
byte_coverage_ratio: ratio(file_bytes_covered, file_total_bytes),
165-
line_coverage,
166-
total_lines: total_file_lines,
167-
covered_lines: file_lines_covered,
168-
uncovered_lines: total_file_lines - file_lines_covered,
169-
chunks,
170-
}
176+
return calculate_stylesheet_coverage(text, ranges, url)
171177
})
172178

173179
// Calculate total coverage for all stylesheets combined

0 commit comments

Comments
 (0)