@projectwallace/css-analyzer 5.0.0-alpha.1 → 5.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -5
- package/src/aggregate-collection.js +0 -113
- package/src/aggregate-collection.test.js +0 -47
- package/src/atrules/atrules.js +0 -76
- package/src/atrules/atrules.test.js +0 -289
- package/src/context-collection.js +0 -36
- package/src/countable-collection.js +0 -46
- package/src/declarations/declarations.js +0 -46
- package/src/declarations/declarations.test.js +0 -113
- package/src/index.js +0 -259
- package/src/index.test.js +0 -60
- package/src/properties/properties.js +0 -48
- package/src/properties/properties.test.js +0 -138
- package/src/rules/rules.js +0 -57
- package/src/rules/rules.test.js +0 -247
- package/src/selectors/complexity.test.js +0 -123
- package/src/selectors/selectors.js +0 -122
- package/src/selectors/selectors.test.js +0 -189
- package/src/selectors/specificity.js +0 -201
- package/src/selectors/specificity.test.js +0 -247
- package/src/smoke.test.js +0 -39
- package/src/stylesheet/stylesheet.test.js +0 -88
- package/src/values/animations.js +0 -53
- package/src/values/animations.test.js +0 -154
- package/src/values/box-shadows.test.js +0 -82
- package/src/values/colors.js +0 -192
- package/src/values/colors.test.js +0 -804
- package/src/values/font-families.js +0 -98
- package/src/values/font-families.test.js +0 -119
- package/src/values/font-sizes.js +0 -92
- package/src/values/font-sizes.test.js +0 -120
- package/src/values/text-shadows.test.js +0 -93
- package/src/values/units.test.js +0 -72
- package/src/values/values.js +0 -30
- package/src/values/vendor-prefix.js +0 -45
- package/src/values/vendor-prefix.test.js +0 -64
- package/src/values/z-index.test.js +0 -54
- package/src/vendor-prefix.js +0 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@projectwallace/css-analyzer",
|
|
3
|
-
"version": "5.0.0-alpha.
|
|
3
|
+
"version": "5.0.0-alpha.2",
|
|
4
4
|
"author": "Bart Veneman",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -37,10 +37,7 @@
|
|
|
37
37
|
"metrics"
|
|
38
38
|
],
|
|
39
39
|
"files": [
|
|
40
|
-
"dist"
|
|
41
|
-
"src",
|
|
42
|
-
"!src/__fixtures__",
|
|
43
|
-
"!*.test.js"
|
|
40
|
+
"dist"
|
|
44
41
|
],
|
|
45
42
|
"dependencies": {
|
|
46
43
|
"css-tree": "^2.0.1"
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Find the mode (most occurring value) in an array of Numbers
|
|
3
|
-
* Takes the mean/average of multiple values if multiple values occur the same amount of times.
|
|
4
|
-
*
|
|
5
|
-
* @see https://github.com/angus-c/just/blob/684af9ca0c7808bc78543ec89379b1fdfce502b1/packages/array-mode/index.js
|
|
6
|
-
* @param {Array} arr - Array to find the mode value for
|
|
7
|
-
* @returns {Number} mode - The `mode` value of `arr`
|
|
8
|
-
*/
|
|
9
|
-
function Mode(arr) {
|
|
10
|
-
const frequencies = Object.create(null)
|
|
11
|
-
let maxOccurrences = -1
|
|
12
|
-
let maxOccurenceCount = 0
|
|
13
|
-
let sum = 0
|
|
14
|
-
|
|
15
|
-
for (let i = 0; i < arr.length; i++) {
|
|
16
|
-
const element = arr[i]
|
|
17
|
-
const updatedCount = (frequencies[element] || 0) + 1
|
|
18
|
-
frequencies[element] = updatedCount
|
|
19
|
-
|
|
20
|
-
if (updatedCount > maxOccurrences) {
|
|
21
|
-
maxOccurrences = updatedCount
|
|
22
|
-
maxOccurenceCount = 0
|
|
23
|
-
sum = 0
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (updatedCount >= maxOccurrences) {
|
|
27
|
-
maxOccurenceCount++
|
|
28
|
-
sum += element
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return sum / maxOccurenceCount
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Find the middle number in an Array of Numbers
|
|
37
|
-
* Returns the average of 2 numbers if the Array length is an even number
|
|
38
|
-
* @see https://github.com/angus-c/just/blob/684af9ca0c7808bc78543ec89379b1fdfce502b1/packages/array-median/index.js
|
|
39
|
-
* @param {Array} arr - A sorted Array
|
|
40
|
-
* @returns {Number} - The array's Median
|
|
41
|
-
*/
|
|
42
|
-
function Median(arr) {
|
|
43
|
-
const middle = arr.length / 2
|
|
44
|
-
const lowerMiddleRank = Math.floor(middle)
|
|
45
|
-
|
|
46
|
-
if (middle !== lowerMiddleRank) {
|
|
47
|
-
return arr[lowerMiddleRank]
|
|
48
|
-
}
|
|
49
|
-
return (arr[lowerMiddleRank] + arr[lowerMiddleRank - 1]) / 2
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
class AggregateCollection {
|
|
53
|
-
constructor(size) {
|
|
54
|
-
/** @type Number[] */
|
|
55
|
-
this.items = new Uint8Array(size)
|
|
56
|
-
this.sum = 0
|
|
57
|
-
this.cursor = 0
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Add a new Integer to the AggregateCollection
|
|
62
|
-
* @param {number} item - The item to add to the AggregateCollection
|
|
63
|
-
*/
|
|
64
|
-
add(item) {
|
|
65
|
-
this.items[this.cursor] = (item)
|
|
66
|
-
this.sum += item
|
|
67
|
-
this.cursor++
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
aggregate() {
|
|
71
|
-
if (this.cursor === 0) {
|
|
72
|
-
return {
|
|
73
|
-
min: 0,
|
|
74
|
-
max: 0,
|
|
75
|
-
mean: 0,
|
|
76
|
-
mode: 0,
|
|
77
|
-
median: 0,
|
|
78
|
-
range: 0,
|
|
79
|
-
sum: 0,
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** @type Number[] */
|
|
84
|
-
const sorted = new Uint8Array(
|
|
85
|
-
this.items.slice(0, this.cursor)
|
|
86
|
-
).sort((a, b) => a - b)
|
|
87
|
-
const min = sorted[0]
|
|
88
|
-
const max = sorted[sorted.length - 1]
|
|
89
|
-
|
|
90
|
-
const mode = Mode(sorted)
|
|
91
|
-
const median = Median(sorted)
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
min,
|
|
95
|
-
max,
|
|
96
|
-
mean: this.sum / this.cursor,
|
|
97
|
-
mode,
|
|
98
|
-
median,
|
|
99
|
-
range: max - min,
|
|
100
|
-
sum: this.sum,
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
toArray() {
|
|
105
|
-
return Array.from(
|
|
106
|
-
this.items.subarray(0, this.cursor)
|
|
107
|
-
)
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export {
|
|
112
|
-
AggregateCollection
|
|
113
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { suite } from 'uvu';
|
|
2
|
-
import * as assert from 'uvu/assert';
|
|
3
|
-
import { AggregateCollection } from './aggregate-collection.js'
|
|
4
|
-
|
|
5
|
-
const CollectionSuite = suite('AggregateCollection')
|
|
6
|
-
|
|
7
|
-
CollectionSuite('aggregates correctly', () => {
|
|
8
|
-
const fixture = new AggregateCollection(6)
|
|
9
|
-
fixture.add(1)
|
|
10
|
-
fixture.add(2)
|
|
11
|
-
fixture.add(25)
|
|
12
|
-
fixture.add(3)
|
|
13
|
-
fixture.add(4)
|
|
14
|
-
fixture.add(4)
|
|
15
|
-
const actual = fixture.aggregate()
|
|
16
|
-
const expected = {
|
|
17
|
-
max: 25,
|
|
18
|
-
min: 1,
|
|
19
|
-
range: 24,
|
|
20
|
-
mean: 39 / 6,
|
|
21
|
-
median: 3.5,
|
|
22
|
-
mode: 4,
|
|
23
|
-
sum: 39,
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
assert.equal(actual, expected)
|
|
27
|
-
assert.equal(fixture.toArray(), [1, 2, 25, 3, 4, 4])
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
CollectionSuite('handles collections without values', () => {
|
|
31
|
-
const fixture = new AggregateCollection(0)
|
|
32
|
-
const aggregate = fixture.aggregate()
|
|
33
|
-
const items = fixture.toArray()
|
|
34
|
-
|
|
35
|
-
assert.equal(aggregate, {
|
|
36
|
-
max: 0,
|
|
37
|
-
min: 0,
|
|
38
|
-
range: 0,
|
|
39
|
-
mean: 0,
|
|
40
|
-
median: 0,
|
|
41
|
-
mode: 0,
|
|
42
|
-
sum: 0,
|
|
43
|
-
})
|
|
44
|
-
assert.equal(items, [])
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
CollectionSuite.run()
|
package/src/atrules/atrules.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { CountableCollection } from '../countable-collection.js'
|
|
2
|
-
import { hasVendorPrefix } from '../vendor-prefix.js'
|
|
3
|
-
|
|
4
|
-
const analyzeAtRules = ({ atrules, stringifyNode }) => {
|
|
5
|
-
const fontfaces = []
|
|
6
|
-
const imports = new CountableCollection()
|
|
7
|
-
const medias = new CountableCollection()
|
|
8
|
-
const charsets = new CountableCollection()
|
|
9
|
-
const supports = new CountableCollection()
|
|
10
|
-
const keyframes = new CountableCollection()
|
|
11
|
-
const prefixedKeyframes = new CountableCollection()
|
|
12
|
-
const containers = new CountableCollection()
|
|
13
|
-
|
|
14
|
-
const machine = {
|
|
15
|
-
'font-face': (node) => {
|
|
16
|
-
const descriptors = {}
|
|
17
|
-
|
|
18
|
-
node.block.children.forEach(descriptor => {
|
|
19
|
-
descriptors[descriptor.property] = stringifyNode(descriptor.value)
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
fontfaces.push(descriptors)
|
|
23
|
-
},
|
|
24
|
-
'media': node => medias.push(node.prelude.value),
|
|
25
|
-
'supports': node => supports.push(node.prelude.value),
|
|
26
|
-
'keyframes': node => keyframes.push(`@${node.name} ${node.prelude.value}`),
|
|
27
|
-
'import': node => imports.push(node.prelude.value),
|
|
28
|
-
'charset': node => charsets.push(node.prelude.value),
|
|
29
|
-
'container': node => containers.push(node.prelude.value),
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
for (let i = 0; i < atrules.length; i++) {
|
|
33
|
-
const node = atrules[i]
|
|
34
|
-
const nodeName = node.name
|
|
35
|
-
const action = machine[nodeName]
|
|
36
|
-
if (action) {
|
|
37
|
-
action(node)
|
|
38
|
-
continue
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (nodeName.endsWith('keyframes')) {
|
|
42
|
-
const name = `@${nodeName} ${node.prelude.value}`
|
|
43
|
-
keyframes.push(name)
|
|
44
|
-
|
|
45
|
-
if (hasVendorPrefix(nodeName)) {
|
|
46
|
-
prefixedKeyframes.push(name)
|
|
47
|
-
}
|
|
48
|
-
continue
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
fontface: {
|
|
54
|
-
total: fontfaces.length,
|
|
55
|
-
totalUnique: fontfaces.length,
|
|
56
|
-
unique: fontfaces,
|
|
57
|
-
uniquenessRatio: 1
|
|
58
|
-
},
|
|
59
|
-
import: imports.count(),
|
|
60
|
-
media: medias.count(),
|
|
61
|
-
charset: charsets.count(),
|
|
62
|
-
supports: supports.count(),
|
|
63
|
-
keyframes: {
|
|
64
|
-
...keyframes.count(),
|
|
65
|
-
prefixed: {
|
|
66
|
-
...prefixedKeyframes.count(),
|
|
67
|
-
ratio: prefixedKeyframes.size() / keyframes.size()
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
container: containers.count(),
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export {
|
|
75
|
-
analyzeAtRules
|
|
76
|
-
}
|
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
import { suite } from 'uvu'
|
|
2
|
-
import * as assert from 'uvu/assert'
|
|
3
|
-
import { analyze } from '../index.js'
|
|
4
|
-
|
|
5
|
-
const AtRules = suite('at-rules')
|
|
6
|
-
|
|
7
|
-
AtRules('finds @font-face', () => {
|
|
8
|
-
const fixture = `
|
|
9
|
-
@font-face {
|
|
10
|
-
font-family: Arial;
|
|
11
|
-
src: url("https://url-to-arial.woff");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
@font-face {
|
|
15
|
-
font-display: swap;
|
|
16
|
-
font-family: Test;
|
|
17
|
-
font-stretch: condensed;
|
|
18
|
-
font-style: italic;
|
|
19
|
-
font-weight: 700;
|
|
20
|
-
font-variant: no-common-ligatures proportional-nums;
|
|
21
|
-
font-feature-settings: "liga" 0;
|
|
22
|
-
font-variation-settings: "xhgt" 0.7;
|
|
23
|
-
src: local("Input Mono");
|
|
24
|
-
unicode-range: U+0025-00FF;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
@font-face {
|
|
28
|
-
font-family: 'Input Mono';
|
|
29
|
-
src: local('Input Mono') url("https://url-to-input-mono.woff");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
@font-face {
|
|
33
|
-
font-family: MyHelvetica;
|
|
34
|
-
src: local("Helvetica Neue Bold"), local("HelveticaNeue-Bold"), url(MgOpenModernaBold.ttf);
|
|
35
|
-
font-weight: bold;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/* Duplicate @font-face in Media Query */
|
|
39
|
-
@media (min-width: 1000px) {
|
|
40
|
-
@font-face {
|
|
41
|
-
font-family: 'Input Mono';
|
|
42
|
-
src: local('Input Mono') url("https://url-to-input-mono.woff");
|
|
43
|
-
}
|
|
44
|
-
}`
|
|
45
|
-
const actual = analyze(fixture).atrules.fontface
|
|
46
|
-
const expected = {
|
|
47
|
-
total: 5,
|
|
48
|
-
totalUnique: 5,
|
|
49
|
-
unique: [
|
|
50
|
-
{
|
|
51
|
-
"font-family": "Arial",
|
|
52
|
-
"src": "url(\"https://url-to-arial.woff\")"
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
"font-display": `swap`,
|
|
56
|
-
"font-family": `Test`,
|
|
57
|
-
"font-stretch": `condensed`,
|
|
58
|
-
"font-style": `italic`,
|
|
59
|
-
"font-weight": `700`,
|
|
60
|
-
"font-variant": `no-common-ligatures proportional-nums`,
|
|
61
|
-
"font-feature-settings": `"liga" 0`,
|
|
62
|
-
"font-variation-settings": `"xhgt" 0.7`,
|
|
63
|
-
"src": `local("Input Mono")`,
|
|
64
|
-
"unicode-range": `U+0025-00FF`,
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
"font-family": "'Input Mono'",
|
|
68
|
-
"src": "local('Input Mono') url(\"https://url-to-input-mono.woff\")"
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
'font-family': 'MyHelvetica',
|
|
72
|
-
'src': 'local("Helvetica Neue Bold"), local("HelveticaNeue-Bold"), url(MgOpenModernaBold.ttf)',
|
|
73
|
-
'font-weight': 'bold',
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
"font-family": "'Input Mono'",
|
|
77
|
-
"src": "local('Input Mono') url(\"https://url-to-input-mono.woff\")"
|
|
78
|
-
}
|
|
79
|
-
],
|
|
80
|
-
uniquenessRatio: 1
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
assert.equal(actual, expected)
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
AtRules('finds @imports', () => {
|
|
87
|
-
const fixture = `
|
|
88
|
-
@import "https://example.com/without-url";
|
|
89
|
-
@import url("https://example.com/with-url");
|
|
90
|
-
@import url("https://example.com/with-media-query") screen and (min-width: 33em);
|
|
91
|
-
@import url("https://example.com/with-multiple-media-queries") screen, projection;
|
|
92
|
-
`
|
|
93
|
-
const actual = analyze(fixture).atrules.import
|
|
94
|
-
const expected = {
|
|
95
|
-
total: 4,
|
|
96
|
-
totalUnique: 4,
|
|
97
|
-
unique: {
|
|
98
|
-
'"https://example.com/without-url"': 1,
|
|
99
|
-
'url("https://example.com/with-url")': 1,
|
|
100
|
-
'url("https://example.com/with-media-query") screen and (min-width: 33em)': 1,
|
|
101
|
-
'url("https://example.com/with-multiple-media-queries") screen, projection': 1,
|
|
102
|
-
},
|
|
103
|
-
uniquenessRatio: 4 / 4
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
assert.equal(actual, expected)
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
AtRules('finds @charsets', () => {
|
|
110
|
-
const fixture = `
|
|
111
|
-
@charset "UTF-8";
|
|
112
|
-
@charset "UTF-16";
|
|
113
|
-
`
|
|
114
|
-
const actual = analyze(fixture).atrules.charset
|
|
115
|
-
const expected = {
|
|
116
|
-
total: 2,
|
|
117
|
-
totalUnique: 2,
|
|
118
|
-
unique: {
|
|
119
|
-
'"UTF-8"': 1,
|
|
120
|
-
'"UTF-16"': 1,
|
|
121
|
-
},
|
|
122
|
-
uniquenessRatio: 2 / 2
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
assert.equal(actual, expected)
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
AtRules('finds @supports', () => {
|
|
129
|
-
const fixture = `
|
|
130
|
-
@supports (filter: blur(5px)) {}
|
|
131
|
-
@supports (display: table-cell) and (display: list-item) {}
|
|
132
|
-
@supports (-webkit-appearance: none) {}
|
|
133
|
-
|
|
134
|
-
@media (min-width: 0) {
|
|
135
|
-
@supports (-webkit-appearance: none) {}
|
|
136
|
-
}
|
|
137
|
-
`
|
|
138
|
-
const actual = analyze(fixture).atrules.supports
|
|
139
|
-
const expected = {
|
|
140
|
-
total: 4,
|
|
141
|
-
totalUnique: 3,
|
|
142
|
-
unique: {
|
|
143
|
-
'(filter: blur(5px))': 1,
|
|
144
|
-
'(display: table-cell) and (display: list-item)': 1,
|
|
145
|
-
'(-webkit-appearance: none)': 2,
|
|
146
|
-
},
|
|
147
|
-
uniquenessRatio: 3 / 4
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
assert.equal(actual, expected)
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
AtRules('finds @media', () => {
|
|
154
|
-
const fixture = `
|
|
155
|
-
@media screen {}
|
|
156
|
-
@media screen and (min-width: 33em) {}
|
|
157
|
-
@media (min-width: 20px) {}
|
|
158
|
-
@media (max-width: 200px) {}
|
|
159
|
-
@media screen or print {}
|
|
160
|
-
@media \\0 all {}
|
|
161
|
-
|
|
162
|
-
@supports (-webkit-appearance: none) {
|
|
163
|
-
@media (min-width: 0) {}
|
|
164
|
-
}
|
|
165
|
-
`
|
|
166
|
-
const actual = analyze(fixture).atrules.media
|
|
167
|
-
const expected = {
|
|
168
|
-
total: 7,
|
|
169
|
-
totalUnique: 7,
|
|
170
|
-
unique: {
|
|
171
|
-
'screen': 1,
|
|
172
|
-
'screen and (min-width: 33em)': 1,
|
|
173
|
-
'(min-width: 20px)': 1,
|
|
174
|
-
'(max-width: 200px)': 1,
|
|
175
|
-
'screen or print': 1,
|
|
176
|
-
'\\0 all': 1,
|
|
177
|
-
'(min-width: 0)': 1,
|
|
178
|
-
},
|
|
179
|
-
uniquenessRatio: 7 / 7
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
assert.equal(actual, expected)
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
AtRules('analyzes @keyframes', () => {
|
|
186
|
-
const fixture = `
|
|
187
|
-
@keyframes one {}
|
|
188
|
-
@keyframes one {}
|
|
189
|
-
@keyframes TWO {}
|
|
190
|
-
|
|
191
|
-
/* Prefixes */
|
|
192
|
-
@-webkit-keyframes animation {}
|
|
193
|
-
@-moz-keyframes animation {}
|
|
194
|
-
@-o-keyframes animation {}
|
|
195
|
-
`
|
|
196
|
-
const actual = analyze(fixture).atrules.keyframes
|
|
197
|
-
const expected = {
|
|
198
|
-
total: 6,
|
|
199
|
-
totalUnique: 5,
|
|
200
|
-
unique: {
|
|
201
|
-
'@keyframes one': 2,
|
|
202
|
-
'@keyframes TWO': 1,
|
|
203
|
-
'@-webkit-keyframes animation': 1,
|
|
204
|
-
'@-moz-keyframes animation': 1,
|
|
205
|
-
'@-o-keyframes animation': 1,
|
|
206
|
-
},
|
|
207
|
-
uniquenessRatio: 5 / 6,
|
|
208
|
-
prefixed: {
|
|
209
|
-
total: 3,
|
|
210
|
-
totalUnique: 3,
|
|
211
|
-
unique: {
|
|
212
|
-
'@-webkit-keyframes animation': 1,
|
|
213
|
-
'@-moz-keyframes animation': 1,
|
|
214
|
-
'@-o-keyframes animation': 1,
|
|
215
|
-
},
|
|
216
|
-
uniquenessRatio: 3 / 3,
|
|
217
|
-
ratio: 3 / 6
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
assert.equal(actual, expected)
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
AtRules('analyzes container queries', () => {
|
|
225
|
-
// Fixture contains examples from the spec.
|
|
226
|
-
// https://drafts.csswg.org/css-contain-3/
|
|
227
|
-
const fixture = `
|
|
228
|
-
/* Example 2 */
|
|
229
|
-
@container (inline-size > 45em) {
|
|
230
|
-
.media-object {
|
|
231
|
-
grid-template: 'img content' auto / auto 1fr;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/* Example 3 */
|
|
236
|
-
@container (width > 40em) {
|
|
237
|
-
h2 { font-size: 1.5em; }
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/* Example 4 */
|
|
241
|
-
@container (--cards) {
|
|
242
|
-
article {
|
|
243
|
-
border: thin solid silver;
|
|
244
|
-
border-radius: 0.5em;
|
|
245
|
-
padding: 1em;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/* Example 5 */
|
|
250
|
-
@container page-layout (block-size > 12em) {
|
|
251
|
-
.card { margin-block: 2em; }
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
@container component-library (inline-size > 30em) {
|
|
255
|
-
.card { margin-inline: 2em; }
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/* Example 8 */
|
|
259
|
-
@container card (inline-size > 30em) and (--responsive = true) {
|
|
260
|
-
/* styles */
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/* Example 11 */
|
|
264
|
-
@container type(inline-size) {
|
|
265
|
-
/* only applies when an inline-size container is available */
|
|
266
|
-
h2 { font-size: calc(1.2em + 1cqi); }
|
|
267
|
-
}
|
|
268
|
-
`
|
|
269
|
-
const result = analyze(fixture)
|
|
270
|
-
const actual = result.atrules.container
|
|
271
|
-
const expected = {
|
|
272
|
-
total: 7,
|
|
273
|
-
totalUnique: 7,
|
|
274
|
-
unique: {
|
|
275
|
-
'(inline-size > 45em)': 1,
|
|
276
|
-
'(width > 40em)': 1,
|
|
277
|
-
'(--cards)': 1,
|
|
278
|
-
'page-layout (block-size > 12em)': 1,
|
|
279
|
-
'component-library (inline-size > 30em)': 1,
|
|
280
|
-
'card (inline-size > 30em) and (--responsive = true)': 1,
|
|
281
|
-
'type(inline-size)': 1,
|
|
282
|
-
},
|
|
283
|
-
uniquenessRatio: 7 / 7
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
assert.equal(actual, expected)
|
|
287
|
-
})
|
|
288
|
-
|
|
289
|
-
AtRules.run()
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { CountableCollection } from './countable-collection.js'
|
|
2
|
-
|
|
3
|
-
class ContextCollection {
|
|
4
|
-
constructor() {
|
|
5
|
-
this.list = new CountableCollection()
|
|
6
|
-
this.contexts = {}
|
|
7
|
-
this.contextCount = 0
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
push(item, context) {
|
|
11
|
-
this.list.push(item)
|
|
12
|
-
|
|
13
|
-
if (!this.contexts[context]) {
|
|
14
|
-
this.contexts[context] = new CountableCollection()
|
|
15
|
-
this.contextCount++
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
this.contexts[context].push(item)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
count() {
|
|
22
|
-
const itemsPerContext = {}
|
|
23
|
-
|
|
24
|
-
for (let context in this.contexts) {
|
|
25
|
-
itemsPerContext[context] = this.contexts[context].count()
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return Object.assign(this.list.count(), {
|
|
29
|
-
itemsPerContext
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export {
|
|
35
|
-
ContextCollection
|
|
36
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
class CountableCollection {
|
|
2
|
-
constructor(initial) {
|
|
3
|
-
this.items = {}
|
|
4
|
-
this.total = 0
|
|
5
|
-
this.totalUnique = 0
|
|
6
|
-
|
|
7
|
-
if (initial) {
|
|
8
|
-
for (let index = 0; index < initial.length; index++) {
|
|
9
|
-
this.push(initial[index])
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
*
|
|
16
|
-
* @param {string} item
|
|
17
|
-
*/
|
|
18
|
-
push(item) {
|
|
19
|
-
this.total++
|
|
20
|
-
|
|
21
|
-
if (this.items[item]) {
|
|
22
|
-
this.items[item]++
|
|
23
|
-
return
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
this.items[item] = 1
|
|
27
|
-
this.totalUnique++
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
size() {
|
|
31
|
-
return this.total
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
count() {
|
|
35
|
-
return {
|
|
36
|
-
total: this.total,
|
|
37
|
-
totalUnique: this.totalUnique,
|
|
38
|
-
unique: this.items,
|
|
39
|
-
uniquenessRatio: this.total === 0 ? 0 : this.totalUnique / this.total,
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export {
|
|
45
|
-
CountableCollection
|
|
46
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
const analyzeDeclarations = ({ stringifyNode, declarations }) => {
|
|
2
|
-
const total = declarations.length
|
|
3
|
-
const cache = Object.create(null)
|
|
4
|
-
let importants = 0
|
|
5
|
-
let totalUnique = 0
|
|
6
|
-
let totalInKeyframes = 0
|
|
7
|
-
|
|
8
|
-
for (let i = 0; i < total; i++) {
|
|
9
|
-
const declaration = declarations[i]
|
|
10
|
-
|
|
11
|
-
if (declaration.important === true) {
|
|
12
|
-
importants++
|
|
13
|
-
|
|
14
|
-
if (declaration.inKeyframe) {
|
|
15
|
-
totalInKeyframes++
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const stringified = stringifyNode(declaration)
|
|
20
|
-
|
|
21
|
-
if (!cache[stringified]) {
|
|
22
|
-
cache[stringified] = 1
|
|
23
|
-
totalUnique++
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
total,
|
|
29
|
-
unique: {
|
|
30
|
-
total: totalUnique,
|
|
31
|
-
ratio: total === 0 ? 0 : totalUnique / total,
|
|
32
|
-
},
|
|
33
|
-
importants: {
|
|
34
|
-
total: importants,
|
|
35
|
-
ratio: total === 0 ? 0 : importants / total,
|
|
36
|
-
inKeyframes: {
|
|
37
|
-
total: totalInKeyframes,
|
|
38
|
-
ratio: importants === 0 ? 0 : totalInKeyframes / importants,
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export {
|
|
45
|
-
analyzeDeclarations
|
|
46
|
-
}
|