@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
package/src/index.js
DELETED
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
import * as csstree from 'css-tree'
|
|
2
|
-
import { compareSpecificity } from './selectors/specificity.js'
|
|
3
|
-
import { analyzeRules } from './rules/rules.js'
|
|
4
|
-
import { analyzeColors, colorFunctions, colorNames } from './values/colors.js'
|
|
5
|
-
import { analyzeFontFamilies } from './values/font-families.js'
|
|
6
|
-
import { analyzeFontSizes } from './values/font-sizes.js'
|
|
7
|
-
import { analyzeDeclarations } from './declarations/declarations.js'
|
|
8
|
-
import { analyzeSelectors } from './selectors/selectors.js'
|
|
9
|
-
import { analyzeProperties } from './properties/properties.js'
|
|
10
|
-
import { analyzeValues } from './values/values.js'
|
|
11
|
-
import { analyzeAnimations } from './values/animations.js'
|
|
12
|
-
import { analyzeVendorPrefixes } from './values/vendor-prefix.js'
|
|
13
|
-
import { analyzeAtRules } from './atrules/atrules.js'
|
|
14
|
-
import { CountableCollection } from './countable-collection.js'
|
|
15
|
-
import { ContextCollection } from './context-collection.js'
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
*
|
|
19
|
-
* @param {string} css
|
|
20
|
-
* @returns
|
|
21
|
-
*/
|
|
22
|
-
const analyze = (css) => {
|
|
23
|
-
const start = new Date()
|
|
24
|
-
|
|
25
|
-
// We need all lines later on when we need to stringify the AST again
|
|
26
|
-
// e.g. for Selectors
|
|
27
|
-
const lines = css.split(/\r?\n/)
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Recreate the authored CSS from a CSSTree node
|
|
31
|
-
* @param {import('css-tree').Node} node - Node from CSSTree AST to stringify
|
|
32
|
-
* @returns {string} str - The stringified node
|
|
33
|
-
*/
|
|
34
|
-
function stringifyNode(node) {
|
|
35
|
-
const start = node.loc.start
|
|
36
|
-
const end = node.loc.end
|
|
37
|
-
const lineCount = end.line - start.line
|
|
38
|
-
|
|
39
|
-
// Single-line nodes
|
|
40
|
-
if (lineCount === 0) {
|
|
41
|
-
return lines[start.line - 1].substring(start.column - 1, end.column - 1)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Multi-line nodes
|
|
45
|
-
const value = []
|
|
46
|
-
|
|
47
|
-
for (let i = start.line; i <= end.line; i++) {
|
|
48
|
-
const line = lines[i - 1]
|
|
49
|
-
// First line
|
|
50
|
-
if (i === start.line) {
|
|
51
|
-
value.push(line.substring(start.column - 1))
|
|
52
|
-
continue
|
|
53
|
-
}
|
|
54
|
-
// Last line
|
|
55
|
-
if (i === end.line) {
|
|
56
|
-
value.push(line.substring(0, end.column - 1))
|
|
57
|
-
continue
|
|
58
|
-
}
|
|
59
|
-
// All lines in between first and last
|
|
60
|
-
value.push(line)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return value.join('\n')
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const startParse = new Date()
|
|
67
|
-
let totalComments = 0
|
|
68
|
-
let commentsSize = 0
|
|
69
|
-
|
|
70
|
-
const ast = csstree.parse(css, {
|
|
71
|
-
parseAtrulePrelude: false,
|
|
72
|
-
parseCustomProperty: true, // To find font-families, colors, etc.
|
|
73
|
-
positions: true, // So we can use stringifyNode()
|
|
74
|
-
onComment: function (comment) {
|
|
75
|
-
totalComments++
|
|
76
|
-
commentsSize += comment.length
|
|
77
|
-
},
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
const startAnalysis = new Date()
|
|
81
|
-
const atrules = []
|
|
82
|
-
const rules = []
|
|
83
|
-
const selectors = []
|
|
84
|
-
const declarations = []
|
|
85
|
-
const properties = []
|
|
86
|
-
const values = []
|
|
87
|
-
const zindex = []
|
|
88
|
-
const textShadows = []
|
|
89
|
-
const boxShadows = []
|
|
90
|
-
const fontValues = []
|
|
91
|
-
const fontFamilyValues = []
|
|
92
|
-
const fontSizeValues = []
|
|
93
|
-
const animations = []
|
|
94
|
-
const timingFunctions = []
|
|
95
|
-
const durations = []
|
|
96
|
-
const colors = new ContextCollection()
|
|
97
|
-
const units = new ContextCollection()
|
|
98
|
-
|
|
99
|
-
csstree.walk(ast, {
|
|
100
|
-
enter: function (node) {
|
|
101
|
-
switch (node.type) {
|
|
102
|
-
case 'Atrule': {
|
|
103
|
-
atrules.push(node)
|
|
104
|
-
break
|
|
105
|
-
}
|
|
106
|
-
case 'Rule': {
|
|
107
|
-
rules.push(node)
|
|
108
|
-
break
|
|
109
|
-
}
|
|
110
|
-
case 'Selector': {
|
|
111
|
-
selectors.push({
|
|
112
|
-
...node,
|
|
113
|
-
isKeyframeSelector: this.atrule && this.atrule.name.endsWith('keyframes')
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
// Avoid further walking of selectors to not mess with
|
|
117
|
-
// our specificity calculations in case of a selector
|
|
118
|
-
// with :where() or :is() that contain SelectorLists
|
|
119
|
-
// as children
|
|
120
|
-
return this.skip
|
|
121
|
-
}
|
|
122
|
-
case 'Dimension': {
|
|
123
|
-
if (!this.declaration) {
|
|
124
|
-
break
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
units.push(node.unit, this.declaration.property)
|
|
128
|
-
|
|
129
|
-
return this.skip
|
|
130
|
-
}
|
|
131
|
-
case 'Declaration': {
|
|
132
|
-
declarations.push({
|
|
133
|
-
...node,
|
|
134
|
-
inKeyframe: this.atrule && this.atrule.name.endsWith('keyframes')
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
const { value, property } = node
|
|
138
|
-
const fullProperty = {
|
|
139
|
-
authored: property,
|
|
140
|
-
...csstree.property(property)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
properties.push(fullProperty)
|
|
144
|
-
values.push(value)
|
|
145
|
-
|
|
146
|
-
switch (fullProperty.basename) {
|
|
147
|
-
case 'z-index': {
|
|
148
|
-
zindex.push(value)
|
|
149
|
-
break
|
|
150
|
-
}
|
|
151
|
-
case 'text-shadow': {
|
|
152
|
-
textShadows.push(value)
|
|
153
|
-
break
|
|
154
|
-
}
|
|
155
|
-
case 'box-shadow': {
|
|
156
|
-
boxShadows.push(value)
|
|
157
|
-
break
|
|
158
|
-
}
|
|
159
|
-
case 'font': {
|
|
160
|
-
fontValues.push(value)
|
|
161
|
-
break
|
|
162
|
-
}
|
|
163
|
-
case 'font-family': {
|
|
164
|
-
fontFamilyValues.push(stringifyNode(value))
|
|
165
|
-
// Prevent analyzer to find color names in this property
|
|
166
|
-
return this.skip
|
|
167
|
-
}
|
|
168
|
-
case 'font-size': {
|
|
169
|
-
fontSizeValues.push(stringifyNode(value))
|
|
170
|
-
break
|
|
171
|
-
}
|
|
172
|
-
case 'transition':
|
|
173
|
-
case 'animation': {
|
|
174
|
-
animations.push(node)
|
|
175
|
-
break
|
|
176
|
-
}
|
|
177
|
-
case 'animation-duration':
|
|
178
|
-
case 'transition-duration': {
|
|
179
|
-
durations.push(value)
|
|
180
|
-
break
|
|
181
|
-
}
|
|
182
|
-
case 'transition-timing-function':
|
|
183
|
-
case 'animation-timing-function': {
|
|
184
|
-
timingFunctions.push(value)
|
|
185
|
-
break
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
csstree.walk(node.value, {
|
|
190
|
-
enter: function (valueNode) {
|
|
191
|
-
switch (valueNode.type) {
|
|
192
|
-
case 'Hash': {
|
|
193
|
-
colors.push(stringifyNode(valueNode), property)
|
|
194
|
-
|
|
195
|
-
return this.skip
|
|
196
|
-
}
|
|
197
|
-
case 'Identifier': {
|
|
198
|
-
const { name } = valueNode
|
|
199
|
-
// Bail out if it can't be a color name
|
|
200
|
-
// 20 === 'lightgoldenrodyellow'.length
|
|
201
|
-
// 3 === 'red'.length
|
|
202
|
-
if (name.length > 20 || name.length < 3) {
|
|
203
|
-
return this.skip
|
|
204
|
-
}
|
|
205
|
-
if (colorNames[name.toLowerCase()]) {
|
|
206
|
-
colors.push(stringifyNode(valueNode), property)
|
|
207
|
-
}
|
|
208
|
-
return this.skip
|
|
209
|
-
}
|
|
210
|
-
case 'Function': {
|
|
211
|
-
if (colorFunctions[valueNode.name.toLowerCase()]) {
|
|
212
|
-
colors.push(stringifyNode(valueNode), property)
|
|
213
|
-
}
|
|
214
|
-
// No this.skip here intentionally,
|
|
215
|
-
// otherwise we'll miss colors in linear-gradient() etc.
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
})
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
return {
|
|
226
|
-
stylesheet: {
|
|
227
|
-
sourceLinesOfCode: atrules.length + selectors.length + declarations.length,
|
|
228
|
-
linesOfCode: lines.length,
|
|
229
|
-
size: css.length,
|
|
230
|
-
comments: {
|
|
231
|
-
total: totalComments,
|
|
232
|
-
size: commentsSize,
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
|
-
atrules: analyzeAtRules({ atrules, stringifyNode }),
|
|
236
|
-
rules: analyzeRules({ rules }),
|
|
237
|
-
selectors: analyzeSelectors({ stringifyNode, selectors }),
|
|
238
|
-
declarations: analyzeDeclarations({ stringifyNode, declarations }),
|
|
239
|
-
properties: analyzeProperties({ properties }),
|
|
240
|
-
values: {
|
|
241
|
-
colors: analyzeColors({ colors }),
|
|
242
|
-
fontFamilies: analyzeFontFamilies({ stringifyNode, fontValues, fontFamilyValues }),
|
|
243
|
-
fontSizes: analyzeFontSizes({ stringifyNode, fontValues, fontSizeValues }),
|
|
244
|
-
zindexes: analyzeValues({ values: zindex, stringifyNode }),
|
|
245
|
-
textShadows: analyzeValues({ values: textShadows, stringifyNode }),
|
|
246
|
-
boxShadows: analyzeValues({ values: boxShadows, stringifyNode }),
|
|
247
|
-
animations: analyzeAnimations({ animations, timingFunctions, durations, stringifyNode }),
|
|
248
|
-
prefixes: analyzeVendorPrefixes({ values, stringifyNode }),
|
|
249
|
-
units: units.count(),
|
|
250
|
-
},
|
|
251
|
-
__meta__: {
|
|
252
|
-
parseTime: startAnalysis - startParse,
|
|
253
|
-
analyzeTime: new Date() - startAnalysis,
|
|
254
|
-
total: new Date() - start
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
export { analyze, compareSpecificity }
|
package/src/index.test.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { suite } from 'uvu';
|
|
2
|
-
import * as assert from 'uvu/assert';
|
|
3
|
-
import { analyze, compareSpecificity } from './index.js'
|
|
4
|
-
import { readFileSync } from 'fs'
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const fixtures = [
|
|
8
|
-
'bol-com-20190617.css',
|
|
9
|
-
'css-tricks-20190319.css',
|
|
10
|
-
'facebook-20190319.css',
|
|
11
|
-
'gazelle-20210905.css',
|
|
12
|
-
'github-20210501.css',
|
|
13
|
-
'lego-20190617.css',
|
|
14
|
-
'smashing-magazine-20190319.css',
|
|
15
|
-
'trello-20190617.css',
|
|
16
|
-
].map(fileName => {
|
|
17
|
-
const css = readFileSync(`./src/__fixtures__/${fileName}`, 'utf-8')
|
|
18
|
-
return {
|
|
19
|
-
css,
|
|
20
|
-
fileName
|
|
21
|
-
}
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
const Api = suite('Public API')
|
|
25
|
-
|
|
26
|
-
Api('exposes the analyze method', () => {
|
|
27
|
-
assert.is(typeof compareSpecificity, 'function')
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
Api('exposes the compareSpecificity method', () => {
|
|
31
|
-
assert.is(typeof compareSpecificity, 'function')
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
Api('does not break on CSS Syntax Errors', () => {
|
|
35
|
-
assert.not.throws(() => analyze('test, {}'))
|
|
36
|
-
assert.not.throws(() => analyze('test { color red }'))
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
Api('serializes into small objects', () => {
|
|
40
|
-
for (const { css, fileName } of fixtures) {
|
|
41
|
-
const actual = JSON.stringify(analyze(css))
|
|
42
|
-
assert.ok(
|
|
43
|
-
actual.length < css.length * 0.51,
|
|
44
|
-
`Expected serialized size to be smaller (${actual.length} serialized vs. ${css.length} raw css (${actual.length / css.length}) on ${fileName})`
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
Api('it runs fast', () => {
|
|
50
|
-
const start = Date.now()
|
|
51
|
-
for (const { css } of fixtures) {
|
|
52
|
-
analyze(css)
|
|
53
|
-
}
|
|
54
|
-
const end = Date.now()
|
|
55
|
-
const actual = end - start
|
|
56
|
-
|
|
57
|
-
assert.ok(actual < 2500, `Expected to be fast, but took ${actual}ms`)
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
Api.run()
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { CountableCollection } from '../countable-collection.js'
|
|
2
|
-
|
|
3
|
-
const analyzeProperties = ({ properties }) => {
|
|
4
|
-
const all = new CountableCollection(properties.map(p => p.authored))
|
|
5
|
-
const prefixed = new CountableCollection()
|
|
6
|
-
const hacks = new CountableCollection()
|
|
7
|
-
const customs = new CountableCollection()
|
|
8
|
-
const totalProperties = properties.length
|
|
9
|
-
|
|
10
|
-
for (let i = 0; i < totalProperties; i++) {
|
|
11
|
-
const property = properties[i]
|
|
12
|
-
|
|
13
|
-
if (property.vendor) {
|
|
14
|
-
prefixed.push(property.authored)
|
|
15
|
-
continue
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (property.hack) {
|
|
19
|
-
hacks.push(property.authored)
|
|
20
|
-
continue
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (property.custom) {
|
|
24
|
-
customs.push(property.authored)
|
|
25
|
-
continue
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
...all.count(),
|
|
31
|
-
prefixed: {
|
|
32
|
-
...prefixed.count(),
|
|
33
|
-
ratio: prefixed.size() / totalProperties,
|
|
34
|
-
},
|
|
35
|
-
custom: {
|
|
36
|
-
...customs.count(),
|
|
37
|
-
ratio: customs.size() / totalProperties,
|
|
38
|
-
},
|
|
39
|
-
browserhacks: {
|
|
40
|
-
...hacks.count(),
|
|
41
|
-
ratio: hacks.size() / totalProperties,
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export {
|
|
47
|
-
analyzeProperties
|
|
48
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { suite } from 'uvu'
|
|
2
|
-
import * as assert from 'uvu/assert'
|
|
3
|
-
import { analyze } from '../index.js'
|
|
4
|
-
|
|
5
|
-
const Properties = suite('Properties')
|
|
6
|
-
|
|
7
|
-
Properties('counts totals', () => {
|
|
8
|
-
const fixture = `
|
|
9
|
-
properties {
|
|
10
|
-
margin: 0;
|
|
11
|
-
--custom: 1;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
@media print {
|
|
15
|
-
nested {
|
|
16
|
-
--custom: 2;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
`
|
|
20
|
-
const actual = analyze(fixture).properties.total
|
|
21
|
-
|
|
22
|
-
assert.is(actual, 3)
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
Properties('calculates uniqueness', () => {
|
|
26
|
-
const fixture = `
|
|
27
|
-
properties {
|
|
28
|
-
margin: 0;
|
|
29
|
-
--custom: 1;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
@media print {
|
|
33
|
-
nested {
|
|
34
|
-
--custom: 1;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
`
|
|
38
|
-
const actual = analyze(fixture).properties
|
|
39
|
-
const expected = {
|
|
40
|
-
'margin': 1,
|
|
41
|
-
'--custom': 2,
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
assert.is(actual.totalUnique, 2)
|
|
45
|
-
assert.equal(actual.unique, expected)
|
|
46
|
-
assert.is(actual.uniquenessRatio, 2 / 3)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
Properties('counts vendor prefixes', () => {
|
|
50
|
-
const fixture = `
|
|
51
|
-
prefixed {
|
|
52
|
-
border-radius: 2px;
|
|
53
|
-
-webkit-border-radius: 2px;
|
|
54
|
-
-khtml-border-radius: 2px;
|
|
55
|
-
-o-border-radius: 2px;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
@media (min-width: 0) {
|
|
59
|
-
@supports (-o-border-radius: 2px) {
|
|
60
|
-
prefixed2 {
|
|
61
|
-
-o-border-radius: 4px;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
`
|
|
66
|
-
const actual = analyze(fixture).properties.prefixed
|
|
67
|
-
const expected = {
|
|
68
|
-
'-webkit-border-radius': 1,
|
|
69
|
-
'-khtml-border-radius': 1,
|
|
70
|
-
'-o-border-radius': 2,
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
assert.is(actual.total, 4)
|
|
74
|
-
assert.is(actual.totalUnique, 3)
|
|
75
|
-
assert.equal(actual.unique, expected)
|
|
76
|
-
assert.is(actual.ratio, 4 / 5)
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
Properties('counts browser hacks', () => {
|
|
80
|
-
const fixture = `
|
|
81
|
-
hacks {
|
|
82
|
-
margin: 0;
|
|
83
|
-
*zoom: 1;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
@media (min-width: 0) {
|
|
87
|
-
@supports (-o-border-radius: 2px) {
|
|
88
|
-
hacks2 {
|
|
89
|
-
*zoom: 1;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
`
|
|
94
|
-
const actual = analyze(fixture).properties.browserhacks
|
|
95
|
-
const expected = {
|
|
96
|
-
'*zoom': 2
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
assert.is(actual.total, 2)
|
|
100
|
-
assert.is(actual.totalUnique, 1)
|
|
101
|
-
assert.equal(actual.unique, expected)
|
|
102
|
-
assert.is(actual.ratio, 2 / 3)
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
Properties('counts custom properties', () => {
|
|
106
|
-
const fixture = `
|
|
107
|
-
:root {
|
|
108
|
-
--yellow-400: yellow;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
custom {
|
|
112
|
-
margin: 0;
|
|
113
|
-
--yellow-400: yellow;
|
|
114
|
-
color: var(--yellow-400);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
@media (min-width: 0) {
|
|
118
|
-
@supports (-o-border-radius: 2px) {
|
|
119
|
-
custom2 {
|
|
120
|
-
--green-400: green;
|
|
121
|
-
color: var(--green-400);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
`
|
|
126
|
-
const actual = analyze(fixture).properties.custom
|
|
127
|
-
const expected = {
|
|
128
|
-
'--yellow-400': 2,
|
|
129
|
-
'--green-400': 1,
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
assert.is(actual.total, 3)
|
|
133
|
-
assert.is(actual.totalUnique, 2)
|
|
134
|
-
assert.equal(actual.unique, expected)
|
|
135
|
-
assert.is(actual.ratio, 3 / 6)
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
Properties.run()
|
package/src/rules/rules.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import * as csstree from 'css-tree'
|
|
2
|
-
import { AggregateCollection } from '../aggregate-collection.js'
|
|
3
|
-
|
|
4
|
-
const analyzeRules = ({ rules }) => {
|
|
5
|
-
const totalRules = rules.length
|
|
6
|
-
const selectorsPerRule = new AggregateCollection(totalRules)
|
|
7
|
-
const declarationsPerRule = new AggregateCollection(totalRules)
|
|
8
|
-
|
|
9
|
-
let emptyRules = 0
|
|
10
|
-
|
|
11
|
-
for (let i = 0; i < totalRules; i++) {
|
|
12
|
-
let selectors = 0
|
|
13
|
-
let declarations = 0
|
|
14
|
-
|
|
15
|
-
csstree.walk(rules[i], {
|
|
16
|
-
enter: function (childNode) {
|
|
17
|
-
if (childNode.type === 'Selector') {
|
|
18
|
-
selectors++
|
|
19
|
-
return this.skip
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (childNode.type === 'Declaration') {
|
|
23
|
-
declarations++
|
|
24
|
-
return this.skip
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
if (declarations === 0) {
|
|
30
|
-
emptyRules++
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// For later aggregations
|
|
34
|
-
selectorsPerRule.add(selectors)
|
|
35
|
-
declarationsPerRule.add(declarations)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
total: totalRules,
|
|
40
|
-
empty: {
|
|
41
|
-
total: emptyRules,
|
|
42
|
-
ratio: emptyRules / totalRules
|
|
43
|
-
},
|
|
44
|
-
selectors: {
|
|
45
|
-
...selectorsPerRule.aggregate(),
|
|
46
|
-
items: selectorsPerRule.toArray(),
|
|
47
|
-
},
|
|
48
|
-
declarations: {
|
|
49
|
-
...declarationsPerRule.aggregate(),
|
|
50
|
-
items: declarationsPerRule.toArray()
|
|
51
|
-
},
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export {
|
|
56
|
-
analyzeRules
|
|
57
|
-
}
|