@projectwallace/css-analyzer 5.0.0-alpha.1 → 5.0.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/dist/analyzer.cjs +1 -1
- package/dist/analyzer.cjs.map +1 -1
- package/dist/analyzer.modern.js +2 -0
- package/dist/analyzer.modern.js.map +1 -0
- package/dist/analyzer.module.js +1 -1
- package/dist/analyzer.module.js.map +1 -1
- package/dist/analyzer.umd.js +1 -1
- package/dist/analyzer.umd.js.map +1 -1
- package/package.json +10 -10
- package/dist/analyzer.js +0 -2
- package/dist/analyzer.js.map +0 -1
- 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
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import * as csstree from 'css-tree'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {[number, number, number]} Specificity
|
|
5
|
-
*
|
|
6
|
-
* @typedef {import('css-tree').Selector} Selector
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Compare specificity A to Specificity B
|
|
11
|
-
* @param {Specificity} a - Specificity A
|
|
12
|
-
* @param {Specificity} b - Specificity B
|
|
13
|
-
* @returns {number} sortIndex - 0 when a==b, 1 when a<b, -1 when a>b
|
|
14
|
-
*/
|
|
15
|
-
function compareSpecificity(a, b) {
|
|
16
|
-
if (a[0] === b[0]) {
|
|
17
|
-
if (a[1] === b[1]) {
|
|
18
|
-
return b[2] - a[2]
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return b[1] - a[1]
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return b[0] - a[0]
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
*
|
|
29
|
-
* @param {import('css-tree').SelectorList} selectorListAst
|
|
30
|
-
* @returns {Selector} topSpecificitySelector
|
|
31
|
-
*/
|
|
32
|
-
function selectorListSpecificities(selectorListAst) {
|
|
33
|
-
const childSelectors = []
|
|
34
|
-
csstree.walk(selectorListAst, {
|
|
35
|
-
visit: 'Selector',
|
|
36
|
-
enter(node) {
|
|
37
|
-
childSelectors.push(analyzeSpecificity(node))
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
return childSelectors.sort((a, b) => compareSpecificity(a.specificity, b.specificity))
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Get the Specificity for the AST of a Selector Node
|
|
46
|
-
* @param {import('css-tree').Node} ast - AST Node for a Selector
|
|
47
|
-
* @return {Object}
|
|
48
|
-
* @property {Specificity} specificity
|
|
49
|
-
* @property {number} complexity
|
|
50
|
-
*/
|
|
51
|
-
const analyzeSpecificity = (ast) => {
|
|
52
|
-
let A = 0
|
|
53
|
-
let B = 0
|
|
54
|
-
let C = 0
|
|
55
|
-
let complexity = 0
|
|
56
|
-
let isA11y = false
|
|
57
|
-
|
|
58
|
-
csstree.walk(ast, {
|
|
59
|
-
enter: function (selector) {
|
|
60
|
-
switch (selector.type) {
|
|
61
|
-
case 'IdSelector': {
|
|
62
|
-
A++
|
|
63
|
-
complexity++
|
|
64
|
-
break
|
|
65
|
-
}
|
|
66
|
-
case 'ClassSelector': {
|
|
67
|
-
B++
|
|
68
|
-
complexity++
|
|
69
|
-
break
|
|
70
|
-
}
|
|
71
|
-
case 'AttributeSelector': {
|
|
72
|
-
B++
|
|
73
|
-
complexity++
|
|
74
|
-
|
|
75
|
-
if (Boolean(selector.value)) {
|
|
76
|
-
complexity++
|
|
77
|
-
}
|
|
78
|
-
isA11y = selector.name.name === 'role' || selector.name.name.startsWith('aria-')
|
|
79
|
-
break
|
|
80
|
-
}
|
|
81
|
-
case 'PseudoElementSelector':
|
|
82
|
-
case 'TypeSelector': {
|
|
83
|
-
if (selector.name !== '*') {
|
|
84
|
-
C++
|
|
85
|
-
}
|
|
86
|
-
complexity++
|
|
87
|
-
break
|
|
88
|
-
}
|
|
89
|
-
case 'PseudoClassSelector': {
|
|
90
|
-
if (['before', 'after', 'first-letter', 'first-line'].includes(selector.name)) {
|
|
91
|
-
C++
|
|
92
|
-
complexity++
|
|
93
|
-
return this.skip
|
|
94
|
-
}
|
|
95
|
-
// The specificity of an :is(), :not(), or :has() pseudo-class is
|
|
96
|
-
// replaced by the specificity of the most specific complex
|
|
97
|
-
// selector in its selector list argument.
|
|
98
|
-
|
|
99
|
-
// CSSTree doesn't parse the arguments of :is, :has and :matches,
|
|
100
|
-
// so we need to create an AST out of them ourselves
|
|
101
|
-
if (['is', 'has', 'matches'].includes(selector.name)) {
|
|
102
|
-
const rawSelectorList = csstree.find(selector, ({ type }) => type === 'Raw')
|
|
103
|
-
const childAst = csstree.parse(rawSelectorList.value, { context: 'selectorList' })
|
|
104
|
-
const selectorList = selectorListSpecificities(childAst)
|
|
105
|
-
const [topA, topB, topC] = selectorList[0].specificity
|
|
106
|
-
A += topA
|
|
107
|
-
B += topB
|
|
108
|
-
C += topC
|
|
109
|
-
|
|
110
|
-
for (let i = 0; i < selectorList.length; i++) {
|
|
111
|
-
complexity += selectorList[i].complexity
|
|
112
|
-
}
|
|
113
|
-
complexity++
|
|
114
|
-
return
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// CSSTree *does* parse the arguments of the :not() pseudo-class,
|
|
118
|
-
// so we have direct access to the AST, instead of having to parse
|
|
119
|
-
// the arguments ourselves.
|
|
120
|
-
if (selector.name === 'not') {
|
|
121
|
-
const selectorList = selectorListSpecificities(selector)
|
|
122
|
-
const [topA, topB, topC] = selectorList[0].specificity
|
|
123
|
-
A += topA
|
|
124
|
-
B += topB
|
|
125
|
-
C += topC
|
|
126
|
-
|
|
127
|
-
for (let i = 0; i < selectorList.length; i++) {
|
|
128
|
-
complexity += selectorList[i].complexity
|
|
129
|
-
}
|
|
130
|
-
complexity++
|
|
131
|
-
return this.skip
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// The specificity of an :nth-child() or :nth-last-child() selector
|
|
135
|
-
// is the specificity of the pseudo class itself (counting as one
|
|
136
|
-
// pseudo-class selector) plus the specificity of the most
|
|
137
|
-
// specific complex selector in its selector list argument (if any).
|
|
138
|
-
if (['nth-child', 'nth-last-child'].includes(selector.name)) {
|
|
139
|
-
// +1 for the pseudo class itself
|
|
140
|
-
B++
|
|
141
|
-
|
|
142
|
-
const childSelectors = selectorListSpecificities(selector)
|
|
143
|
-
|
|
144
|
-
if (childSelectors.length === 0) {
|
|
145
|
-
return
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const [topA, topB, topC] = childSelectors[0].specificity
|
|
149
|
-
A += topA
|
|
150
|
-
B += topB
|
|
151
|
-
C += topC
|
|
152
|
-
|
|
153
|
-
for (let i = 0; i < childSelectors.length; i++) {
|
|
154
|
-
complexity += childSelectors[i].complexity;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
complexity++
|
|
158
|
-
return
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// The specificity of a :where() pseudo-class is replaced by zero,
|
|
162
|
-
// but it does count towards complexity.
|
|
163
|
-
if (selector.name === 'where') {
|
|
164
|
-
const rawSelectorList = csstree.find(selector, ({ type }) => type === 'Raw')
|
|
165
|
-
const childAst = csstree.parse(rawSelectorList.value, { context: 'selectorList' })
|
|
166
|
-
const childSelectors = selectorListSpecificities(childAst)
|
|
167
|
-
|
|
168
|
-
for (let i = 0; i < childSelectors.length; i++) {
|
|
169
|
-
complexity += childSelectors[i].complexity;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
complexity++
|
|
173
|
-
return
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Regular pseudo classes have specificity [0,1,0]
|
|
177
|
-
complexity++
|
|
178
|
-
B++
|
|
179
|
-
break
|
|
180
|
-
}
|
|
181
|
-
case 'Combinator': {
|
|
182
|
-
complexity++
|
|
183
|
-
break
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
return {
|
|
190
|
-
/** @type Specificity */
|
|
191
|
-
specificity: [A, B, C],
|
|
192
|
-
complexity,
|
|
193
|
-
isId: A > 0,
|
|
194
|
-
isA11y
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export {
|
|
199
|
-
analyzeSpecificity,
|
|
200
|
-
compareSpecificity,
|
|
201
|
-
}
|
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
import { suite } from 'uvu'
|
|
2
|
-
import * as assert from 'uvu/assert'
|
|
3
|
-
import { analyze } from '../index.js'
|
|
4
|
-
|
|
5
|
-
const Specificity = suite('Specificity')
|
|
6
|
-
|
|
7
|
-
Specificity('handles the universal selector', () => {
|
|
8
|
-
const fixture = `
|
|
9
|
-
* {}
|
|
10
|
-
`
|
|
11
|
-
const actual = analyze(fixture).selectors.specificity.items
|
|
12
|
-
const expected = [
|
|
13
|
-
[0, 0, 0],
|
|
14
|
-
]
|
|
15
|
-
assert.equal(actual, expected)
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
Specificity('handles ID selectors', () => {
|
|
19
|
-
const fixture = `
|
|
20
|
-
#id,
|
|
21
|
-
.Foo > .Bar ~ .Baz [type="text"] + span::before #bazz #fizz #buzz #brick #house,
|
|
22
|
-
|
|
23
|
-
/* https://drafts.csswg.org/selectors-4/#example-d97bd125 */
|
|
24
|
-
:not(span,strong#foo), /* a=1 b=0 c=1 */
|
|
25
|
-
#x34y, /* a=1 b=0 c=0 */
|
|
26
|
-
#s12:not(FOO) /* a=1 b=0 c=1 */
|
|
27
|
-
{}
|
|
28
|
-
`
|
|
29
|
-
const actual = analyze(fixture).selectors.specificity.items
|
|
30
|
-
const expected = [
|
|
31
|
-
[1, 0, 0],
|
|
32
|
-
[5, 4, 2],
|
|
33
|
-
[1, 0, 1],
|
|
34
|
-
[1, 0, 0],
|
|
35
|
-
[1, 0, 1],
|
|
36
|
-
]
|
|
37
|
-
assert.equal(actual, expected)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
Specificity('handles class selectors', () => {
|
|
41
|
-
const fixture = `
|
|
42
|
-
.class,
|
|
43
|
-
.class.class
|
|
44
|
-
{}
|
|
45
|
-
`
|
|
46
|
-
const actual = analyze(fixture).selectors.specificity.items
|
|
47
|
-
const expected = [
|
|
48
|
-
[0, 1, 0],
|
|
49
|
-
[0, 2, 0],
|
|
50
|
-
]
|
|
51
|
-
assert.equal(actual, expected)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
Specificity('handles element selectors', () => {
|
|
55
|
-
const fixture = `
|
|
56
|
-
element,
|
|
57
|
-
element element
|
|
58
|
-
{}
|
|
59
|
-
`
|
|
60
|
-
const actual = analyze(fixture).selectors.specificity.items
|
|
61
|
-
const expected = [
|
|
62
|
-
[0, 0, 1],
|
|
63
|
-
[0, 0, 2],
|
|
64
|
-
]
|
|
65
|
-
assert.equal(actual, expected)
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
Specificity('handles the :not, :is and :has pseudo classes', () => {
|
|
69
|
-
const fixture = `
|
|
70
|
-
/* https://drafts.csswg.org/selectors-4/#example-bd54871c */
|
|
71
|
-
:is(em, #foo), /* [1,0,0] like an ID selector (#foo)—when matched against any of <em>, <p id=foo>, or <em id=foo>. */
|
|
72
|
-
:not(span, strong#foo), /* [1,0,1] like a tag selector (strong) combined with an ID selector (#foo)—when matched against any element. */
|
|
73
|
-
|
|
74
|
-
/* https://drafts.csswg.org/selectors-4/#example-d97bd125 */
|
|
75
|
-
#s12:not(FOO), /* a=1 b=0 c=1 */
|
|
76
|
-
.foo :is(.bar, #baz) /* a=1 b=1 c=0 */
|
|
77
|
-
{}
|
|
78
|
-
`
|
|
79
|
-
const actual = analyze(fixture).selectors.specificity.items
|
|
80
|
-
const expected = [
|
|
81
|
-
[1, 0, 0],
|
|
82
|
-
[1, 0, 1],
|
|
83
|
-
[1, 0, 1],
|
|
84
|
-
[1, 1, 0],
|
|
85
|
-
]
|
|
86
|
-
assert.equal(actual, expected)
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
Specificity('handles attribute selectors', () => {
|
|
90
|
-
const fixture = `
|
|
91
|
-
[attribute],
|
|
92
|
-
.Foo > .Bar ~ .Baz [type="text"] + span::before #bazz #fizz #buzz #brick #house,
|
|
93
|
-
H1 + *[REL=up],
|
|
94
|
-
/* https://drafts.csswg.org/selectors-4/#attribute-representation */
|
|
95
|
-
a[rel~="copyright"],
|
|
96
|
-
a[hreflang=fr],
|
|
97
|
-
a[hreflang|="en"],
|
|
98
|
-
/* https://drafts.csswg.org/selectors-4/#attribute-substrings */
|
|
99
|
-
object[type^="image"],
|
|
100
|
-
a[href$=".html"],
|
|
101
|
-
p[title*="hello"],
|
|
102
|
-
/* https://drafts.csswg.org/selectors-4/#attribute-case */
|
|
103
|
-
[frame=hsides i],
|
|
104
|
-
[type="a" s],
|
|
105
|
-
[type="A" s],
|
|
106
|
-
/* https://drafts.csswg.org/selectors-4/#attrnmsp */
|
|
107
|
-
[foo|att=val],
|
|
108
|
-
[*|att],
|
|
109
|
-
[|att]
|
|
110
|
-
{}
|
|
111
|
-
`
|
|
112
|
-
const actual = analyze(fixture).selectors.specificity.items
|
|
113
|
-
const expected = [
|
|
114
|
-
[0, 1, 0],
|
|
115
|
-
[5, 4, 2],
|
|
116
|
-
[0, 1, 1],
|
|
117
|
-
|
|
118
|
-
[0, 1, 1],
|
|
119
|
-
[0, 1, 1],
|
|
120
|
-
[0, 1, 1],
|
|
121
|
-
|
|
122
|
-
[0, 1, 1],
|
|
123
|
-
[0, 1, 1],
|
|
124
|
-
[0, 1, 1],
|
|
125
|
-
|
|
126
|
-
[0, 1, 0],
|
|
127
|
-
[0, 1, 0],
|
|
128
|
-
[0, 1, 0],
|
|
129
|
-
|
|
130
|
-
[0, 1, 0],
|
|
131
|
-
[0, 1, 0],
|
|
132
|
-
[0, 1, 0],
|
|
133
|
-
]
|
|
134
|
-
assert.equal(actual, expected)
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
Specificity('handles the :where pseudo class', () => {
|
|
138
|
-
const fixture = `
|
|
139
|
-
.qux:where(em, #foo#bar#baz) /* [0,1,0] only the .qux outside the :where() contributes to selector specificity. */
|
|
140
|
-
{}
|
|
141
|
-
`
|
|
142
|
-
const actual = analyze(fixture).selectors.specificity.items
|
|
143
|
-
const expected = [
|
|
144
|
-
[0, 1, 0]
|
|
145
|
-
]
|
|
146
|
-
assert.equal(actual, expected)
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
Specificity('handles pseudo element selectors', () => {
|
|
150
|
-
const fixture = `
|
|
151
|
-
element::before,
|
|
152
|
-
element:before,
|
|
153
|
-
element::first-letter,
|
|
154
|
-
element:first-letter,
|
|
155
|
-
element::after,
|
|
156
|
-
element:after,
|
|
157
|
-
element::first-line,
|
|
158
|
-
element:first-line,
|
|
159
|
-
:nth-child(2n+1)
|
|
160
|
-
{}
|
|
161
|
-
`
|
|
162
|
-
const actual = analyze(fixture).selectors.specificity.items
|
|
163
|
-
const expected = [
|
|
164
|
-
[0, 0, 2],
|
|
165
|
-
[0, 0, 2],
|
|
166
|
-
[0, 0, 2],
|
|
167
|
-
[0, 0, 2],
|
|
168
|
-
[0, 0, 2],
|
|
169
|
-
[0, 0, 2],
|
|
170
|
-
[0, 0, 2],
|
|
171
|
-
[0, 0, 2],
|
|
172
|
-
[0, 1, 0]
|
|
173
|
-
]
|
|
174
|
-
assert.equal(actual, expected)
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
// TODO: test this whenever CSSTree contains 'native' specificity analysis
|
|
178
|
-
// https://twitter.com/csstree/status/1386799196355825664
|
|
179
|
-
Specificity.skip('handles multiple :where or :is parts')
|
|
180
|
-
|
|
181
|
-
Specificity('calculates the lowest value', () => {
|
|
182
|
-
const fixture = `
|
|
183
|
-
#test,
|
|
184
|
-
.me,
|
|
185
|
-
now,
|
|
186
|
-
[crazy] ~ .selector > for [no|="good"] {}
|
|
187
|
-
`
|
|
188
|
-
const actual = analyze(fixture).selectors.specificity.min
|
|
189
|
-
assert.equal(actual, [0, 0, 1])
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
Specificity('calculates the highest value', () => {
|
|
193
|
-
const fixture = `
|
|
194
|
-
#test,
|
|
195
|
-
.me,
|
|
196
|
-
now,
|
|
197
|
-
[crazy] ~ .selector > for [no|="good"] {}
|
|
198
|
-
`
|
|
199
|
-
const actual = analyze(fixture).selectors.specificity.max
|
|
200
|
-
assert.equal(actual, [1, 0, 0])
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
Specificity('calculates the mean value', () => {
|
|
204
|
-
const fixture = `
|
|
205
|
-
#test,
|
|
206
|
-
.me,
|
|
207
|
-
now,
|
|
208
|
-
[crazy] ~ .selector > for [no|="good"] {}
|
|
209
|
-
`
|
|
210
|
-
const actual = analyze(fixture).selectors.specificity.mean
|
|
211
|
-
assert.equal(actual, [.25, 1, 0.5])
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
Specificity('calculates the mode value', () => {
|
|
215
|
-
const fixture = `
|
|
216
|
-
#test,
|
|
217
|
-
.me,
|
|
218
|
-
now,
|
|
219
|
-
[crazy] ~ .selector > for [no|="good"] {}
|
|
220
|
-
`
|
|
221
|
-
const actual = analyze(fixture).selectors.specificity.mode
|
|
222
|
-
assert.equal(actual, [0, 0, 0.5])
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
Specificity('calculates the median value', () => {
|
|
226
|
-
const fixture = `
|
|
227
|
-
#test,
|
|
228
|
-
.me,
|
|
229
|
-
now,
|
|
230
|
-
[crazy] ~ .selector > check {}
|
|
231
|
-
`
|
|
232
|
-
const actual = analyze(fixture).selectors.specificity.median
|
|
233
|
-
assert.equal(actual, [0, 0.5, 0.5])
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
Specificity('calculates total specificity', () => {
|
|
237
|
-
const fixture = `
|
|
238
|
-
#test,
|
|
239
|
-
.me,
|
|
240
|
-
now,
|
|
241
|
-
[crazy] ~ .selector > for [no|="good"] {}
|
|
242
|
-
`
|
|
243
|
-
const actual = analyze(fixture).selectors.specificity.sum
|
|
244
|
-
assert.equal(actual, [1, 4, 2])
|
|
245
|
-
})
|
|
246
|
-
|
|
247
|
-
Specificity.run()
|
package/src/smoke.test.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs'
|
|
2
|
-
import { suite } from 'uvu';
|
|
3
|
-
import * as assert from 'uvu/assert';
|
|
4
|
-
import { analyze } from './index.js'
|
|
5
|
-
|
|
6
|
-
const Smoke = suite('Smoke testing')
|
|
7
|
-
|
|
8
|
-
Object.entries({
|
|
9
|
-
'Bol.com': 'bol-com-20190617',
|
|
10
|
-
'Bootstrap v5.0.0': 'bootstrap-5.0.0',
|
|
11
|
-
'CSS Tricks': 'css-tricks-20190319',
|
|
12
|
-
'Facebook': 'facebook-20190319',
|
|
13
|
-
'GitHub': 'github-20210501',
|
|
14
|
-
'Lego.com': 'lego-20190617',
|
|
15
|
-
'Trello': 'trello-20190617',
|
|
16
|
-
'Gazelle': 'gazelle-20210905',
|
|
17
|
-
'Smashing Magazine': 'smashing-magazine-20190319',
|
|
18
|
-
}).map(([name, fileName]) => {
|
|
19
|
-
const css = fs.readFileSync(`./src/__fixtures__/${fileName}.css`, 'utf-8')
|
|
20
|
-
const json = fs.readFileSync(`./src/__fixtures__/${fileName}.json`, 'utf-8')
|
|
21
|
-
return {
|
|
22
|
-
name,
|
|
23
|
-
fileName,
|
|
24
|
-
json,
|
|
25
|
-
css,
|
|
26
|
-
}
|
|
27
|
-
}).forEach(({ name, fileName, css, json }) => {
|
|
28
|
-
// const result = analyze(css)
|
|
29
|
-
// delete result.__meta__
|
|
30
|
-
// fs.writeFileSync(`./src/__fixtures__/${fileName}.json`, JSON.stringify(result, null, 2))
|
|
31
|
-
Smoke(name, () => {
|
|
32
|
-
assert.not.throws(() => analyze(css))
|
|
33
|
-
const result = analyze(css)
|
|
34
|
-
delete result.__meta__
|
|
35
|
-
assert.fixture(JSON.stringify(result, null, 2), json)
|
|
36
|
-
})
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
Smoke.run()
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { suite } from 'uvu'
|
|
2
|
-
import * as assert from 'uvu/assert'
|
|
3
|
-
import { analyze } from '../index.js'
|
|
4
|
-
|
|
5
|
-
const Stylesheet = suite('Stylesheet')
|
|
6
|
-
|
|
7
|
-
Stylesheet('counts Lines of Code', () => {
|
|
8
|
-
const fixture = `
|
|
9
|
-
/* doc */
|
|
10
|
-
|
|
11
|
-
html {
|
|
12
|
-
nothing: here;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
@done {
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
`
|
|
19
|
-
const actual = analyze(fixture).stylesheet.linesOfCode
|
|
20
|
-
assert.is(actual, 11)
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
Stylesheet('counts Source Lines of Code', () => {
|
|
24
|
-
const fixture = `
|
|
25
|
-
rule {
|
|
26
|
-
color: green;
|
|
27
|
-
color: orange !important;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
@media print {
|
|
31
|
-
@media (min-width: 1000px) {
|
|
32
|
-
@supports (display: grid) {
|
|
33
|
-
@keyframes test {
|
|
34
|
-
from {
|
|
35
|
-
opacity: 1;
|
|
36
|
-
}
|
|
37
|
-
to {
|
|
38
|
-
opacity: 0;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
another-rule {
|
|
43
|
-
color: purple;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
`
|
|
49
|
-
const actual = analyze(fixture).stylesheet.sourceLinesOfCode
|
|
50
|
-
assert.is(actual, 13)
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
Stylesheet('calculates filesize', () => {
|
|
54
|
-
const fixture = `test {}`
|
|
55
|
-
const actual = analyze(fixture).stylesheet.size
|
|
56
|
-
assert.is(actual, 7)
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
Stylesheet('counts comments', () => {
|
|
60
|
-
const fixture = `
|
|
61
|
-
/* comment 1 */
|
|
62
|
-
test1,
|
|
63
|
-
/* comment 2 */
|
|
64
|
-
test2 {
|
|
65
|
-
/* comment 3 */
|
|
66
|
-
color: /* comment 4 */ green;
|
|
67
|
-
background:
|
|
68
|
-
red,
|
|
69
|
-
/* comment 5 */
|
|
70
|
-
yellow
|
|
71
|
-
;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
@media all {
|
|
75
|
-
/* comment 6 */
|
|
76
|
-
}
|
|
77
|
-
`
|
|
78
|
-
const result = analyze(fixture)
|
|
79
|
-
const actual = result.stylesheet.comments
|
|
80
|
-
const expected = {
|
|
81
|
-
total: 6,
|
|
82
|
-
size: 66,
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
assert.equal(actual, expected)
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
Stylesheet.run()
|
package/src/values/animations.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { CountableCollection } from '../countable-collection.js'
|
|
2
|
-
|
|
3
|
-
const timingKeywords = {
|
|
4
|
-
'linear': 1,
|
|
5
|
-
'ease': 1,
|
|
6
|
-
'ease-in': 1,
|
|
7
|
-
'ease-out': 1,
|
|
8
|
-
'ease-in-out': 1,
|
|
9
|
-
'step-start': 1,
|
|
10
|
-
'step-end': 1,
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const analyzeAnimations = ({ animations, durations, timingFunctions, stringifyNode }) => {
|
|
14
|
-
const allDurations = new CountableCollection(durations.map(stringifyNode))
|
|
15
|
-
const allTimingFunctions = new CountableCollection(timingFunctions.map(stringifyNode))
|
|
16
|
-
|
|
17
|
-
for (let index = 0; index < animations.length; index++) {
|
|
18
|
-
const node = animations[index]
|
|
19
|
-
// Flag to know if we've grabbed the first Duration
|
|
20
|
-
// yet (the first Dimension in a shorthand)
|
|
21
|
-
let durationFound = false
|
|
22
|
-
|
|
23
|
-
node.value.children.forEach(child => {
|
|
24
|
-
// Right after a ',' we start over again
|
|
25
|
-
if (child.type === 'Operator') {
|
|
26
|
-
return durationFound = false
|
|
27
|
-
}
|
|
28
|
-
if (child.type === 'Dimension' && durationFound === false) {
|
|
29
|
-
durationFound = true
|
|
30
|
-
return allDurations.push(stringifyNode(child))
|
|
31
|
-
}
|
|
32
|
-
if (child.type === 'Identifier' && timingKeywords[child.name]) {
|
|
33
|
-
return allTimingFunctions.push(stringifyNode(child))
|
|
34
|
-
}
|
|
35
|
-
if (child.type === 'Function'
|
|
36
|
-
&& (
|
|
37
|
-
child.name === 'cubic-bezier' || child.name === 'steps'
|
|
38
|
-
)
|
|
39
|
-
) {
|
|
40
|
-
return allTimingFunctions.push(stringifyNode(child))
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
durations: allDurations.count(),
|
|
47
|
-
timingFunctions: allTimingFunctions.count(),
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export {
|
|
52
|
-
analyzeAnimations
|
|
53
|
-
}
|