Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
},
"dependencies": {
"@projectwallace/format-css": "^2.1.1",
"css-tree": "^3.1.0",
"valibot": "^1.1.0"
}
}
101 changes: 101 additions & 0 deletions src/chunkify.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { test, expect } from '@playwright/test'
import { chunkify_stylesheet, type ChunkedCoverage } from './chunkify'

test('creates chunks with outer chunks covered', () => {
let coverage = {
text: 'a { color: red; } b { color: green; } c { color: blue; }',
ranges: [
{ start: 0, end: 17 },
{ start: 38, end: 56 },
],
url: 'https://example.com',
}
let result = chunkify_stylesheet(coverage)
expect(result).toEqual({
...coverage,
chunks: [
{
start_offset: 0,
end_offset: 17,
is_covered: true,
},
{
start_offset: 17,
end_offset: 38,
is_covered: false,
},
{
start_offset: 38,
end_offset: 56,
is_covered: true,
},
],
} satisfies ChunkedCoverage)
})

test('creates chunks with only middle chunk covered', () => {
let coverage = {
text: 'a { color: red; } b { color: green; } c { color: blue; }',
ranges: [{ start: 17, end: 38 }],
url: 'https://example.com',
}
let result = chunkify_stylesheet(coverage)
expect(result).toEqual({
...coverage,
chunks: [
{
start_offset: 0,
end_offset: 17,
is_covered: false,
},
{
start_offset: 17,
end_offset: 38,
is_covered: true,
},
{
start_offset: 38,
end_offset: 56,
is_covered: false,
},
],
} satisfies ChunkedCoverage)
})

test('creates a single chunk when all is covered', () => {
let coverage = {
text: 'a { color: red; } b { color: green; } c { color: blue; }',
ranges: [{ start: 0, end: 56 }],
url: 'https://example.com',
}
let result = chunkify_stylesheet(coverage)
expect(result).toEqual({
...coverage,
chunks: [
{
start_offset: 0,
end_offset: 56,
is_covered: true,
},
],
} satisfies ChunkedCoverage)
})

test('creates a single chunk when none is covered', () => {
let coverage = {
text: 'a { color: red; } b { color: green; } c { color: blue; }',
ranges: [],
url: 'https://example.com',
}
let result = chunkify_stylesheet(coverage)
expect(result).toEqual({
...coverage,
chunks: [
{
start_offset: 0,
end_offset: 56,
is_covered: false,
},
],
} satisfies ChunkedCoverage)
})
48 changes: 48 additions & 0 deletions src/chunkify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { Coverage } from './parse-coverage'

export type ChunkedCoverage = Coverage & {
chunks: {
start_offset: number
end_offset: number
is_covered: boolean
}[]
}

// TODO: get rid of empty chunks, merge first/last with adjecent covered block
export function chunkify_stylesheet(stylesheet: Coverage): ChunkedCoverage {
let chunks = []
let offset = 0

for (let range of stylesheet.ranges) {
// Create non-covered chunk
if (offset !== range.start) {
chunks.push({
start_offset: offset,
end_offset: range.start,
is_covered: false,
})
offset = range.start
}

chunks.push({
start_offset: range.start,
end_offset: range.end,
is_covered: true,
})
offset = range.end
}

// fill up last chunk if necessary:
if (offset !== stylesheet.text.length) {
chunks.push({
start_offset: offset,
end_offset: stylesheet.text.length,
is_covered: false,
})
}

return {
...stylesheet,
chunks,
}
}
61 changes: 0 additions & 61 deletions src/css-tree.d.ts

This file was deleted.

6 changes: 3 additions & 3 deletions src/decuplicate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import type { Coverage, Range } from './parse-coverage.ts'
* - if a duplicate stylesheet enters the room, we add it's ranges to the existing stylesheet's ranges
* - only bytes of deduplicated stylesheets are counted
*/
export function deduplicate_entries(entries: Coverage[]): Map<NonNullable<Coverage['text']>, Pick<Coverage, 'ranges' | 'url'>> {
export function deduplicate_entries(entries: Coverage[]): Coverage[] {
let checked_stylesheets = new Map<string, { url: string; ranges: Range[] }>()

for (let entry of entries) {
let text = entry.text || ''
let text = entry.text
if (checked_stylesheets.has(text)) {
let sheet = checked_stylesheets.get(text)!
let ranges = sheet.ranges
Expand All @@ -36,5 +36,5 @@ export function deduplicate_entries(entries: Coverage[]): Map<NonNullable<Covera
}
}

return checked_stylesheets
return Array.from(checked_stylesheets, ([text, { url, ranges }]) => ({ text, url, ranges }))
}
38 changes: 16 additions & 22 deletions src/deduplicate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ test('handles a single entry', () => {
ranges: [{ start: 0, end: 4 }],
url: 'example.com',
}
expect(deduplicate_entries([entry])).toEqual(new Map([[entry.text, { url: entry.url, ranges: entry.ranges }]]))
expect(deduplicate_entries([entry])).toEqual([entry])
})

test('deduplicats a simple duplicate entry', () => {
test('deduplicates a simple duplicate entry', () => {
let entry = {
text: 'a {}',
ranges: [{ start: 0, end: 4 }],
url: 'example.com',
}
expect(deduplicate_entries([entry, entry])).toEqual(new Map([[entry.text, { url: entry.url, ranges: entry.ranges }]]))
expect(deduplicate_entries([entry, entry])).toEqual([entry])
})

test('merges two identical texts with different URLs and identical ranges', () => {
Expand All @@ -33,7 +33,7 @@ test('merges two identical texts with different URLs and identical ranges', () =
},
]
let first = entries.at(0)!
expect(deduplicate_entries(entries)).toEqual(new Map([[first.text, { url: first.url, ranges: first.ranges }]]))
expect(deduplicate_entries(entries)).toEqual([{ text: first.text, url: first.url, ranges: first.ranges }])
})

test('merges different ranges on identical CSS, different URLs', () => {
Expand All @@ -50,9 +50,7 @@ test('merges different ranges on identical CSS, different URLs', () => {
},
]
let first = entries.at(0)!
expect(deduplicate_entries(entries)).toEqual(
new Map([[first.text, { url: first.url, ranges: [first.ranges[0], entries[1]!.ranges[0]] }]]),
)
expect(deduplicate_entries(entries)).toEqual([{ text: first.text, url: first.url, ranges: [first.ranges[0], entries[1]!.ranges[0]] }])
})

test('merges different ranges on identical CSS, identical URLs', () => {
Expand All @@ -68,9 +66,9 @@ test('merges different ranges on identical CSS, identical URLs', () => {
url: 'example.com',
},
]
expect(deduplicate_entries(entries)).toEqual(
new Map([[entries[0]!.text, { url: entries[0]!.url, ranges: [entries[0]!.ranges[0], entries[1]!.ranges[0]] }]]),
)
expect(deduplicate_entries(entries)).toEqual([
{ text: entries[0]!.text, url: entries[0]!.url, ranges: [entries[0]!.ranges[0], entries[1]!.ranges[0]] },
])
})

test('does not merge different CSS with different URLs and identical ranges', () => {
Expand All @@ -86,12 +84,10 @@ test('does not merge different CSS with different URLs and identical ranges', ()
url: 'example.com/b',
},
]
expect(deduplicate_entries(entries)).toEqual(
new Map([
[entries[0]!.text, { url: entries[0]!.url, ranges: entries[0]!.ranges }],
[entries[1]!.text, { url: entries[1]!.url, ranges: entries[1]!.ranges }],
]),
)
expect(deduplicate_entries(entries)).toEqual([
{ text: entries[0]!.text, url: entries[0]!.url, ranges: entries[0]!.ranges },
{ text: entries[1]!.text, url: entries[1]!.url, ranges: entries[1]!.ranges },
])
})

test('does not merge different CSS with same URLs and identical ranges', () => {
Expand All @@ -107,10 +103,8 @@ test('does not merge different CSS with same URLs and identical ranges', () => {
url: 'example.com',
},
]
expect(deduplicate_entries(entries)).toEqual(
new Map([
[entries[0]!.text, { url: entries[0]!.url, ranges: entries[0]!.ranges }],
[entries[1]!.text, { url: entries[1]!.url, ranges: entries[1]!.ranges }],
]),
)
expect(deduplicate_entries(entries)).toEqual([
{ text: entries[0]!.text, url: entries[0]!.url, ranges: entries[0]!.ranges },
{ text: entries[1]!.text, url: entries[1]!.url, ranges: entries[1]!.ranges },
])
})
4 changes: 4 additions & 0 deletions src/extend-ranges.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { test, expect } from '@playwright/test'
import { extend_ranges } from './extend-ranges'

// TODO
25 changes: 25 additions & 0 deletions src/extend-ranges.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { Coverage } from './parse-coverage'

/**
* WARNING: mutates the ranges array
*/
export function extend_ranges(coverage: Coverage[]): Coverage[] {
// Adjust ranges to include @-rule name (only preludes included)
// Note: Cannot reliably include closing } because it may not be the end of the range
const LONGEST_ATRULE_NAME = '@-webkit-font-feature-values'.length

for (let stylesheet of coverage) {
for (let range of stylesheet.ranges) {
// Heuristic: atrule names are no longer than LONGEST_ATRULE_NAME
for (let i = 1; i >= -LONGEST_ATRULE_NAME; i--) {
let char_position = range.start + i
if (stylesheet.text.charAt(char_position) === '@') {
range.start = char_position
break
}
}
}
}

return coverage
}
1 change: 0 additions & 1 deletion src/filter-entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export function filter_coverage(coverage: Coverage[], parse_html?: Parser): Cove
let result = []

for (let entry of coverage) {
if (!entry.text) continue
let extension = ext(entry.url).toLowerCase()
if (extension === 'js') continue

Expand Down
Loading