File size: 5,082 Bytes
fea495a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
const arbitraryValueRegex = /^\[(?:(\w[\w-]*):)?(.+)\]$/i
const arbitraryVariableRegex = /^\((?:(\w[\w-]*):)?(.+)\)$/i
const fractionRegex = /^\d+(?:\.\d+)?\/\d+(?:\.\d+)?$/
const tshirtUnitRegex = /^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/
const lengthUnitRegex =
    /\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/
const colorFunctionRegex = /^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/
// Shadow always begins with x and y offset separated by underscore optionally prepended by inset
const shadowRegex = /^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/
const imageRegex =
    /^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/

export const isFraction = (value: string) => fractionRegex.test(value)

export const isNumber = (value: string) => !!value && !Number.isNaN(Number(value))

export const isInteger = (value: string) => !!value && Number.isInteger(Number(value))

export const isPercent = (value: string) => value.endsWith('%') && isNumber(value.slice(0, -1))

export const isTshirtSize = (value: string) => tshirtUnitRegex.test(value)

export const isAny = () => true

const isLengthOnly = (value: string) =>
    // `colorFunctionRegex` check is necessary because color functions can have percentages in them which which would be incorrectly classified as lengths.
    // For example, `hsl(0 0% 0%)` would be classified as a length without this check.
    // I could also use lookbehind assertion in `lengthUnitRegex` but that isn't supported widely enough.
    lengthUnitRegex.test(value) && !colorFunctionRegex.test(value)

const isNever = () => false

const isShadow = (value: string) => shadowRegex.test(value)

const isImage = (value: string) => imageRegex.test(value)

export const isAnyNonArbitrary = (value: string) =>
    !isArbitraryValue(value) && !isArbitraryVariable(value)

export const isArbitrarySize = (value: string) => getIsArbitraryValue(value, isLabelSize, isNever)

export const isArbitraryValue = (value: string) => arbitraryValueRegex.test(value)

export const isArbitraryLength = (value: string) =>
    getIsArbitraryValue(value, isLabelLength, isLengthOnly)

export const isArbitraryNumber = (value: string) =>
    getIsArbitraryValue(value, isLabelNumber, isNumber)

export const isArbitraryWeight = (value: string) => getIsArbitraryValue(value, isLabelWeight, isAny)

export const isArbitraryFamilyName = (value: string) =>
    getIsArbitraryValue(value, isLabelFamilyName, isNever)

export const isArbitraryPosition = (value: string) =>
    getIsArbitraryValue(value, isLabelPosition, isNever)

export const isArbitraryImage = (value: string) => getIsArbitraryValue(value, isLabelImage, isImage)

export const isArbitraryShadow = (value: string) =>
    getIsArbitraryValue(value, isLabelShadow, isShadow)

export const isArbitraryVariable = (value: string) => arbitraryVariableRegex.test(value)

export const isArbitraryVariableLength = (value: string) =>
    getIsArbitraryVariable(value, isLabelLength)

export const isArbitraryVariableFamilyName = (value: string) =>
    getIsArbitraryVariable(value, isLabelFamilyName)

export const isArbitraryVariablePosition = (value: string) =>
    getIsArbitraryVariable(value, isLabelPosition)

export const isArbitraryVariableSize = (value: string) => getIsArbitraryVariable(value, isLabelSize)

export const isArbitraryVariableImage = (value: string) =>
    getIsArbitraryVariable(value, isLabelImage)

export const isArbitraryVariableShadow = (value: string) =>
    getIsArbitraryVariable(value, isLabelShadow, true)

export const isArbitraryVariableWeight = (value: string) =>
    getIsArbitraryVariable(value, isLabelWeight, true)

// Helpers

const getIsArbitraryValue = (
    value: string,
    testLabel: (label: string) => boolean,
    testValue: (value: string) => boolean,
) => {
    const result = arbitraryValueRegex.exec(value)

    if (result) {
        if (result[1]) {
            return testLabel(result[1])
        }

        return testValue(result[2]!)
    }

    return false
}

const getIsArbitraryVariable = (
    value: string,
    testLabel: (label: string) => boolean,
    shouldMatchNoLabel = false,
) => {
    const result = arbitraryVariableRegex.exec(value)

    if (result) {
        if (result[1]) {
            return testLabel(result[1])
        }
        return shouldMatchNoLabel
    }

    return false
}

// Labels

const isLabelPosition = (label: string) => label === 'position' || label === 'percentage'

const isLabelImage = (label: string) => label === 'image' || label === 'url'

const isLabelSize = (label: string) => label === 'length' || label === 'size' || label === 'bg-size'

const isLabelLength = (label: string) => label === 'length'

const isLabelNumber = (label: string) => label === 'number'

const isLabelFamilyName = (label: string) => label === 'family-name'

const isLabelWeight = (label: string) => label === 'number' || label === 'weight'

const isLabelShadow = (label: string) => label === 'shadow'