Skip to content

Commit a62c7c6

Browse files
authored
case-sensitivity fixes, additional tests (#318)
- Added tests for line-heights - Removed obsolete isValueKeyword check - Added lots of CSS global keywords to value tests - Fixed case sensitivity check for animation-timing-functions (`StEpS`, `cubic-bEzIeR`) - Added some JSDoc examples to confusing functions
1 parent b7f2fa5 commit a62c7c6

17 files changed

+245
-63
lines changed

src/declarations/declarations.test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ import { analyze } from '../index.js'
44

55
const Declarations = suite('Declarations')
66

7+
Declarations('handles empty values', () => {
8+
let css = `
9+
thing {
10+
height:;
11+
width: ;
12+
}
13+
`
14+
15+
assert.not.throws(() => analyze(css))
16+
})
17+
718
Declarations('should be counted', () => {
819
const fixture = `
920
rule {

src/index.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { calculate } from '@bramus/specificity/core'
44
import { isSupportsBrowserhack, isMediaBrowserhack } from './atrules/atrules.js'
55
import { getComplexity, isAccessibility, compareSpecificity } from './selectors/utils.js'
66
import { colorFunctions, colorKeywords, namedColors, systemColors } from './values/colors.js'
7-
import { destructure, isFontKeyword } from './values/destructure-font-shorthand.js'
7+
import { destructure, isSystemFont } from './values/destructure-font-shorthand.js'
88
import { isValueKeyword } from './values/values.js'
99
import { analyzeAnimation } from './values/animations.js'
1010
import { isAstVendorPrefixed } from './values/vendor-prefix.js'
@@ -343,12 +343,10 @@ function analyze(css) {
343343
// Process properties first that don't have colors,
344344
// so we can avoid further walking them;
345345
if (isProperty('z-index', property)) {
346-
if (!isValueKeyword(node)) {
347-
zindex.push(stringifyNode(node))
348-
}
346+
zindex.push(stringifyNode(node))
349347
return this.skip
350348
} else if (isProperty('font', property)) {
351-
if (isFontKeyword(node)) return
349+
if (isSystemFont(node)) return
352350

353351
let { font_size, line_height, font_family } = destructure(node, stringifyNode)
354352

@@ -364,12 +362,12 @@ function analyze(css) {
364362

365363
break
366364
} else if (isProperty('font-size', property)) {
367-
if (!isFontKeyword(node)) {
365+
if (!isSystemFont(node)) {
368366
fontSizes.push(stringifyNode(node))
369367
}
370368
break
371369
} else if (isProperty('font-family', property)) {
372-
if (!isFontKeyword(node)) {
370+
if (!isSystemFont(node)) {
373371
fontFamilies.push(stringifyNode(node))
374372
}
375373
break

src/properties/property-utils.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,20 @@ export function isCustom(property) {
2525
return property.charCodeAt(0) === 45 && property.charCodeAt(1) === 45
2626
}
2727

28+
/**
29+
* A check to verify that a propery is `basename` or a prefixed
30+
* version of that, but never a custom property that accidentally
31+
* ends with the same substring.
32+
*
33+
* @example
34+
* isProperty('animation', 'animation') // true
35+
* isProperty('animation', '-webkit-animation') // true
36+
* isProperty('animation', '--my-animation') // false
37+
*
38+
* @param {string} basename
39+
* @param {string} property
40+
* @returns {boolean} True if `property` equals `basename` without prefix
41+
*/
2842
export function isProperty(basename, property) {
2943
if (isCustom(property)) return false
3044
return endsWith(basename, property)

src/properties/property-utils.test.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { suite } from 'uvu'
22
import * as assert from 'uvu/assert'
3-
import { analyze } from '../index.js'
43
import { isHack, isCustom, isProperty } from './property-utils.js'
54

65
const PropertyUtils = suite('Property Utils')

src/string-utils.js

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* Case-insensitive compare two character codes
3-
* @param {string} charA
4-
* @param {string} charB
3+
* @param {string} referenceCode
4+
* @param {string} testCode
55
* @see https://github.com/csstree/csstree/blob/41f276e8862d8223eeaa01a3d113ab70bb13d2d9/lib/tokenizer/utils.js#L22
66
*/
77
function compareChar(referenceCode, testCode) {
@@ -15,16 +15,22 @@ function compareChar(referenceCode, testCode) {
1515

1616
/**
1717
* Case-insensitive string-comparison
18-
* @param {string} base
19-
* @param {string} test
18+
* @example
19+
* strEquals('test', 'test') // true
20+
* strEquals('test', 'TEST') // true
21+
* strEquals('test', 'TesT') // true
22+
* strEquals('test', 'derp') // false
23+
*
24+
* @param {string} base The string to check against
25+
* @param {string} maybe The test string, possibly containing uppercased characters
2026
* @returns {boolean} true if the two strings are the same, false otherwise
2127
*/
22-
export function strEquals(base, test) {
28+
export function strEquals(base, maybe) {
2329
let len = base.length;
24-
if (len !== test.length) return false
30+
if (len !== maybe.length) return false
2531

2632
for (let i = 0; i < len; i++) {
27-
if (compareChar(base.charCodeAt(i), test.charCodeAt(i)) === false) {
33+
if (compareChar(base.charCodeAt(i), maybe.charCodeAt(i)) === false) {
2834
return false
2935
}
3036
}
@@ -34,20 +40,25 @@ export function strEquals(base, test) {
3440

3541
/**
3642
* Case-insensitive testing whether a string ends with a given substring
43+
*
44+
* @example
45+
* endsWith('test', 'my-test') // true
46+
* endsWith('test', 'est') // false
47+
*
3748
* @param {string} base e.g. '-webkit-transform'
38-
* @param {string} test e.g. 'transform'
49+
* @param {string} maybe e.g. 'transform'
3950
* @returns {boolean} true if `test` ends with `base`, false otherwise
4051
*/
41-
export function endsWith(base, test) {
42-
let len = test.length
52+
export function endsWith(base, maybe) {
53+
let len = maybe.length
4354
let offset = len - base.length
4455

4556
if (offset < 0) {
4657
return false
4758
}
4859

4960
for (let i = len - 1; i >= offset; i--) {
50-
if (compareChar(base.charCodeAt(i - offset), test.charCodeAt(i)) === false) {
61+
if (compareChar(base.charCodeAt(i - offset), maybe.charCodeAt(i)) === false) {
5162
return false
5263
}
5364
}
@@ -58,15 +69,15 @@ export function endsWith(base, test) {
5869
/**
5970
* Case-insensitive testing whether a string starts with a given substring
6071
* @param {string} base
61-
* @param {string} test
72+
* @param {string} maybe
6273
* @returns {boolean} true if `test` starts with `base`, false otherwise
6374
*/
64-
export function startsWith(base, test) {
75+
export function startsWith(base, maybe) {
6576
let len = base.length
66-
if (test.length < len) return false
77+
if (maybe.length < len) return false
6778

6879
for (let i = 0; i < len; i++) {
69-
if (compareChar(base.charCodeAt(i), test.charCodeAt(i)) === false) {
80+
if (compareChar(base.charCodeAt(i), maybe.charCodeAt(i)) === false) {
7081
return false
7182
}
7283
}

src/values/animations.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { KeywordSet } from "../keyword-set.js"
22

3-
const timingKeywords = new KeywordSet([
3+
const TIMING_KEYWORDS = new KeywordSet([
44
'linear',
55
'ease',
66
'ease-in',
@@ -10,6 +10,11 @@ const timingKeywords = new KeywordSet([
1010
'step-end',
1111
])
1212

13+
const TIMING_FUNCTION_VALUES = new KeywordSet([
14+
'cubic-bezier',
15+
'steps'
16+
])
17+
1318
export function analyzeAnimation(children, stringifyNode) {
1419
let durationFound = false
1520
let durations = []
@@ -26,14 +31,10 @@ export function analyzeAnimation(children, stringifyNode) {
2631
durationFound = true
2732
return durations.push(stringifyNode(child))
2833
}
29-
if (type === 'Identifier' && timingKeywords.has(child.name)) {
34+
if (type === 'Identifier' && TIMING_KEYWORDS.has(child.name)) {
3035
return timingFunctions.push(stringifyNode(child))
3136
}
32-
if (type === 'Function'
33-
&& (
34-
child.name === 'cubic-bezier' || child.name === 'steps'
35-
)
36-
) {
37+
if (type === 'Function' && TIMING_FUNCTION_VALUES.has(child.name)) {
3738
return timingFunctions.push(stringifyNode(child))
3839
}
3940
})

src/values/animations.test.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,19 +94,22 @@ Animations('finds shorthand timing functions', () => {
9494
transition: all 4s cubic-bezier(0,1,0,1);
9595
transition: all 5s linear 5000ms;
9696
97+
transition: all 6s Cubic-Bezier(0,1,0,1);
98+
9799
--my-animation: invalid;
98100
--my-transition: invalid;
99101
}
100102
`
101103
const actual = analyze(fixture).values.animations.timingFunctions
102104
const expected = {
103-
total: 4,
104-
totalUnique: 2,
105+
total: 5,
106+
totalUnique: 3,
105107
unique: {
106108
'linear': 2,
107109
'cubic-bezier(0,1,0,1)': 2,
110+
'Cubic-Bezier(0,1,0,1)': 1,
108111
},
109-
uniquenessRatio: 2 / 4
112+
uniquenessRatio: 3 / 5
110113
}
111114
assert.equal(actual, expected)
112115
})

src/values/box-shadows.test.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,14 @@ BoxShadows('finds vendor prefixed values', () => {
6464
BoxShadows('ignores keywords', () => {
6565
const fixture = `
6666
box-shadows-keyword {
67-
box-shadow: initial;
6867
box-shadow: none;
68+
69+
/* Global keywords */
70+
box-shadow: initial;
71+
box-shadow: inherit;
72+
box-shadow: revert;
73+
box-shadow: revert-layer;
74+
box-shadow: unset;
6975
}
7076
`
7177
const actual = analyze(fixture).values.boxShadows

src/values/colors.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,8 @@ Colors('ignores CSS keywords', () => {
892892
color: unset;
893893
color: initial;
894894
color: transparent;
895+
color: revert;
896+
color: revert-layer;
895897
background: none;
896898
}
897899

src/values/destructure-font-shorthand.js

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
import { KeywordSet } from "../keyword-set.js"
22

3-
const FONT_KEYWORDS = new KeywordSet([
4-
// Global CSS keywords
5-
'inherit',
6-
'initial',
7-
'unset',
8-
'revert',
9-
10-
// System font keywords
3+
const SYSTEM_FONTS = new KeywordSet([
114
'caption',
125
'icon',
136
'menu',
@@ -36,9 +29,9 @@ const SLASH = 47 // '/'.charCodeAt(0) === 47
3629
const TYPE_OPERATOR = 'Operator'
3730
const TYPE_IDENTIFIER = 'Identifier'
3831

39-
export function isFontKeyword(node) {
32+
export function isSystemFont(node) {
4033
let firstChild = node.children.first
41-
return firstChild.type === TYPE_IDENTIFIER && FONT_KEYWORDS.has(firstChild.name)
34+
return firstChild.type === TYPE_IDENTIFIER && SYSTEM_FONTS.has(firstChild.name)
4235
}
4336

4437
export function destructure(value, stringifyNode) {

0 commit comments

Comments
 (0)