Skip to content

Commit 56551c2

Browse files
authored
add selectors-per-rule metric (#124)
1 parent fbaeb03 commit 56551c2

File tree

8 files changed

+87
-20
lines changed

8 files changed

+87
-20
lines changed

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@
3030
},
3131
"xo": {
3232
"space": true,
33-
"semicolon": false
33+
"semicolon": false,
34+
"rules": {
35+
"operator-linebreak": "off"
36+
}
3437
},
3538
"prettier": {
3639
"semi": false,

src/analyzer/rules/index.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,20 @@ module.exports = rules => {
66
const all = rules
77
const empty = rules.filter(isEmpty)
88

9+
const selectorsPerRule = rules.map(({selectorsCount}) => selectorsCount)
10+
911
return {
1012
total: all.length,
1113
empty: {
1214
total: empty.length
15+
},
16+
selectors: {
17+
min: selectorsPerRule.length > 0 ? Math.min(...selectorsPerRule) : 0,
18+
max: selectorsPerRule.length > 0 ? Math.max(...selectorsPerRule) : 0,
19+
average:
20+
selectorsPerRule.length > 0
21+
? selectorsPerRule.reduce((acc, curr) => acc + curr, 0) / rules.length
22+
: 0
1323
}
1424
}
1525
}

src/parser/atrules.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,16 @@ module.exports = tree => {
2525
params: rule.params.trim()
2626
}
2727

28-
return atrules.push(
29-
addDescriptorsToAtRuleFromTree(atRule, rule)
30-
)
28+
return atrules.push(addDescriptorsToAtRuleFromTree(atRule, rule))
3129
})
3230

3331
return atrules
3432
}
33+
34+
module.exports.isKeyframes = rule => {
35+
return (
36+
rule.parent &&
37+
rule.parent.type === 'atrule' &&
38+
rule.parent.name === 'keyframes'
39+
)
40+
}

src/parser/rules.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
1+
const {isKeyframes} = require('./atrules')
2+
13
module.exports = tree => {
24
const rules = []
35

46
tree.walkRules(rule => {
57
// Count declarations per rule
68
let declarationsCount = 0
9+
710
rule.walkDecls(() => {
811
declarationsCount += 1
912
})
10-
rules.push({
11-
declarationsCount
12-
})
13+
14+
// Count selectors per rule, but don't include the 'selectors'
15+
// (from, 50%, to, etc.) inside @keyframes
16+
const selectorsCount = isKeyframes(rule)
17+
? 0
18+
: rule.selector.split(',').length
19+
20+
rules.push({declarationsCount, selectorsCount})
1321
})
1422

1523
return rules

src/parser/selectors.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
1+
const {isKeyframes} = require('./atrules')
2+
13
module.exports = tree => {
24
const selectors = []
35

46
tree.walkRules(rule => {
57
// Don't include the 'selectors' (from, 50%, to, etc.) inside @keyframes
6-
if (rule.parent &&
7-
rule.parent.type === 'atrule' &&
8-
rule.parent.name === 'keyframes') {
8+
if (isKeyframes(rule)) {
99
return
1010
}
1111

1212
// Get selectors: flatten the list, split each by ',' and trim the results
13-
selectors.push(
14-
...rule.selector
15-
.split(',')
16-
.map(s => s.trim())
17-
)
13+
selectors.push(...rule.selector.split(',').map(s => s.trim()))
1814
})
1915

2016
return selectors

test/analyzer/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ test('Returns the correct analysis object structure', async t => {
6767
'properties.unique': [],
6868
'rules.total': 1,
6969
'rules.empty.total': 1,
70+
'rules.selectors.max': 1,
71+
'rules.selectors.min': 1,
72+
'rules.selectors.average': 1,
7073
'selectors.accessibility.total': 0,
7174
'selectors.accessibility.totalUnique': 0,
7275
'selectors.accessibility.unique': [],

test/analyzer/rules/index.js

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ test('it responds with the correct structure', t => {
88
total: 0,
99
empty: {
1010
total: 0
11+
},
12+
selectors: {
13+
min: 0,
14+
max: 0,
15+
average: 0
1116
}
1217
})
1318
})
@@ -18,6 +23,36 @@ test('it counts basic rules', t => {
1823
})
1924

2025
test('it counts empty rules', t => {
21-
const actual = analyze([{declarationsCount: 1}, {declarationsCount: 0}])
22-
t.is(actual.empty.total, 1)
26+
const {
27+
empty: {total}
28+
} = analyze([{declarationsCount: 1}, {declarationsCount: 0}])
29+
const expected = 1
30+
t.is(total, expected)
31+
})
32+
33+
test('it counts the average selectors per rule', t => {
34+
const {
35+
selectors: {average}
36+
} = analyze([{selectorsCount: 1}, {selectorsCount: 4}])
37+
const expected = 2.5
38+
39+
t.is(average, expected)
40+
})
41+
42+
test('it counts the min selectors per rule', t => {
43+
const {
44+
selectors: {min}
45+
} = analyze([{selectorsCount: 1}, {selectorsCount: 4}])
46+
const expected = 1
47+
48+
t.is(min, expected)
49+
})
50+
51+
test('it counts the max selectors per rule', t => {
52+
const {
53+
selectors: {max}
54+
} = analyze([{selectorsCount: 1}, {selectorsCount: 4}])
55+
const expected = 4
56+
57+
t.is(max, expected)
2358
})

test/parser/rules.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,21 @@ const parser = require('../../src/parser')
44
test('basic rules are parsed', async t => {
55
const fixture = 'html {color:red} @media screen { html {} }'
66
const actual = await parser(fixture)
7-
const expected = [{declarationsCount: 1}, {declarationsCount: 0}]
7+
const expected = [
8+
{declarationsCount: 1, selectorsCount: 1},
9+
{declarationsCount: 0, selectorsCount: 1}
10+
]
811

912
t.deepEqual(actual.rules, expected)
1013
})
1114

1215
test('declarations per rule are counted', async t => {
1316
const fixture = 'html, body {color:red; font-size : 12px} .foo {color: red;}'
1417
const actual = await parser(fixture)
15-
const expected = [2, 1].map(num => ({declarationsCount: num}))
18+
const expected = [
19+
{declarationsCount: 2, selectorsCount: 2},
20+
{declarationsCount: 1, selectorsCount: 1}
21+
]
1622
t.deepEqual(actual.rules, expected)
1723
})
1824

@@ -29,6 +35,6 @@ test('heavily nested rules are parsed', async t => {
2935
}
3036
`
3137
const actual = await parser(fixture)
32-
const expected = [{declarationsCount: 1}]
38+
const expected = [{declarationsCount: 1, selectorsCount: 1}]
3339
t.deepEqual(actual.rules, expected)
3440
})

0 commit comments

Comments
 (0)