source: frontend/node_modules/tailwindcss/src/util/pluginUtils.js@ cdcff72

Last change on this file since cdcff72 was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 8.0 KB
Line 
1import escapeCommas from './escapeCommas'
2import { withAlphaValue } from './withAlphaVariable'
3import {
4 normalize,
5 length,
6 number,
7 percentage,
8 url,
9 color as validateColor,
10 genericName,
11 familyName,
12 image,
13 absoluteSize,
14 relativeSize,
15 position,
16 lineWidth,
17 shadow,
18} from './dataTypes'
19import negateValue from './negateValue'
20import { backgroundSize } from './validateFormalSyntax'
21import { flagEnabled } from '../featureFlags.js'
22
23/**
24 * @param {import('postcss-selector-parser').Container} selectors
25 * @param {(className: string) => string} updateClass
26 * @returns {string}
27 */
28export function updateAllClasses(selectors, updateClass) {
29 selectors.walkClasses((sel) => {
30 sel.value = updateClass(sel.value)
31
32 if (sel.raws && sel.raws.value) {
33 sel.raws.value = escapeCommas(sel.raws.value)
34 }
35 })
36}
37
38function resolveArbitraryValue(modifier, validate) {
39 if (!isArbitraryValue(modifier)) {
40 return undefined
41 }
42
43 let value = modifier.slice(1, -1)
44
45 if (!validate(value)) {
46 return undefined
47 }
48
49 return normalize(value)
50}
51
52function asNegativeValue(modifier, lookup = {}, validate) {
53 let positiveValue = lookup[modifier]
54
55 if (positiveValue !== undefined) {
56 return negateValue(positiveValue)
57 }
58
59 if (isArbitraryValue(modifier)) {
60 let resolved = resolveArbitraryValue(modifier, validate)
61
62 if (resolved === undefined) {
63 return undefined
64 }
65
66 return negateValue(resolved)
67 }
68}
69
70export function asValue(modifier, options = {}, { validate = () => true } = {}) {
71 let value = options.values?.[modifier]
72
73 if (value !== undefined) {
74 return value
75 }
76
77 if (options.supportsNegativeValues && modifier.startsWith('-')) {
78 return asNegativeValue(modifier.slice(1), options.values, validate)
79 }
80
81 return resolveArbitraryValue(modifier, validate)
82}
83
84function isArbitraryValue(input) {
85 return input.startsWith('[') && input.endsWith(']')
86}
87
88function splitUtilityModifier(modifier) {
89 let slashIdx = modifier.lastIndexOf('/')
90
91 // If the `/` is inside an arbitrary, we want to find the previous one if any
92 // This logic probably isn't perfect but it should work for most cases
93 let arbitraryStartIdx = modifier.lastIndexOf('[', slashIdx)
94 let arbitraryEndIdx = modifier.indexOf(']', slashIdx)
95
96 let isNextToArbitrary = modifier[slashIdx - 1] === ']' || modifier[slashIdx + 1] === '['
97
98 // Backtrack to the previous `/` if the one we found was inside an arbitrary
99 if (!isNextToArbitrary) {
100 if (arbitraryStartIdx !== -1 && arbitraryEndIdx !== -1) {
101 if (arbitraryStartIdx < slashIdx && slashIdx < arbitraryEndIdx) {
102 slashIdx = modifier.lastIndexOf('/', arbitraryStartIdx)
103 }
104 }
105 }
106
107 if (slashIdx === -1 || slashIdx === modifier.length - 1) {
108 return [modifier, undefined]
109 }
110
111 let arbitrary = isArbitraryValue(modifier)
112
113 // The modifier could be of the form `[foo]/[bar]`
114 // We want to handle this case properly
115 // without affecting `[foo/bar]`
116 if (arbitrary && !modifier.includes(']/[')) {
117 return [modifier, undefined]
118 }
119
120 return [modifier.slice(0, slashIdx), modifier.slice(slashIdx + 1)]
121}
122
123export function parseColorFormat(value) {
124 if (typeof value === 'string' && value.includes('<alpha-value>')) {
125 let oldValue = value
126
127 return ({ opacityValue = 1 }) => oldValue.replace(/<alpha-value>/g, opacityValue)
128 }
129
130 return value
131}
132
133function unwrapArbitraryModifier(modifier) {
134 return normalize(modifier.slice(1, -1))
135}
136
137export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) {
138 if (options.values?.[modifier] !== undefined) {
139 return parseColorFormat(options.values?.[modifier])
140 }
141
142 // TODO: Hoist this up to getMatchingTypes or something
143 // We do this here because we need the alpha value (if any)
144 let [color, alpha] = splitUtilityModifier(modifier)
145
146 if (alpha !== undefined) {
147 let normalizedColor =
148 options.values?.[color] ?? (isArbitraryValue(color) ? color.slice(1, -1) : undefined)
149
150 if (normalizedColor === undefined) {
151 return undefined
152 }
153
154 normalizedColor = parseColorFormat(normalizedColor)
155
156 if (isArbitraryValue(alpha)) {
157 return withAlphaValue(normalizedColor, unwrapArbitraryModifier(alpha))
158 }
159
160 if (tailwindConfig.theme?.opacity?.[alpha] === undefined) {
161 return undefined
162 }
163
164 return withAlphaValue(normalizedColor, tailwindConfig.theme.opacity[alpha])
165 }
166
167 return asValue(modifier, options, { validate: validateColor })
168}
169
170export function asLookupValue(modifier, options = {}) {
171 return options.values?.[modifier]
172}
173
174function guess(validate) {
175 return (modifier, options) => {
176 return asValue(modifier, options, { validate })
177 }
178}
179
180export let typeMap = {
181 any: asValue,
182 color: asColor,
183 url: guess(url),
184 image: guess(image),
185 length: guess(length),
186 percentage: guess(percentage),
187 position: guess(position),
188 lookup: asLookupValue,
189 'generic-name': guess(genericName),
190 'family-name': guess(familyName),
191 number: guess(number),
192 'line-width': guess(lineWidth),
193 'absolute-size': guess(absoluteSize),
194 'relative-size': guess(relativeSize),
195 shadow: guess(shadow),
196 size: guess(backgroundSize),
197}
198
199let supportedTypes = Object.keys(typeMap)
200
201function splitAtFirst(input, delim) {
202 let idx = input.indexOf(delim)
203 if (idx === -1) return [undefined, input]
204 return [input.slice(0, idx), input.slice(idx + 1)]
205}
206
207export function coerceValue(types, modifier, options, tailwindConfig) {
208 if (options.values && modifier in options.values) {
209 for (let { type } of types ?? []) {
210 let result = typeMap[type](modifier, options, {
211 tailwindConfig,
212 })
213
214 if (result === undefined) {
215 continue
216 }
217
218 return [result, type, null]
219 }
220 }
221
222 if (isArbitraryValue(modifier)) {
223 let arbitraryValue = modifier.slice(1, -1)
224 let [explicitType, value] = splitAtFirst(arbitraryValue, ':')
225
226 // It could be that this resolves to `url(https` which is not a valid
227 // identifier. We currently only support "simple" words with dashes or
228 // underscores. E.g.: family-name
229 if (!/^[\w-_]+$/g.test(explicitType)) {
230 value = arbitraryValue
231 }
232
233 //
234 else if (explicitType !== undefined && !supportedTypes.includes(explicitType)) {
235 return []
236 }
237
238 if (value.length > 0 && supportedTypes.includes(explicitType)) {
239 return [asValue(`[${value}]`, options), explicitType, null]
240 }
241 }
242
243 let matches = getMatchingTypes(types, modifier, options, tailwindConfig)
244
245 // Find first matching type
246 for (let match of matches) {
247 return match
248 }
249
250 return []
251}
252
253/**
254 *
255 * @param {{type: string}[]} types
256 * @param {string} rawModifier
257 * @param {any} options
258 * @param {any} tailwindConfig
259 * @returns {Iterator<[value: string, type: string, modifier: string | null]>}
260 */
261export function* getMatchingTypes(types, rawModifier, options, tailwindConfig) {
262 let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
263
264 let [modifier, utilityModifier] = splitUtilityModifier(rawModifier)
265
266 let canUseUtilityModifier =
267 modifiersEnabled &&
268 options.modifiers != null &&
269 (options.modifiers === 'any' ||
270 (typeof options.modifiers === 'object' &&
271 ((utilityModifier && isArbitraryValue(utilityModifier)) ||
272 utilityModifier in options.modifiers)))
273
274 if (!canUseUtilityModifier) {
275 modifier = rawModifier
276 utilityModifier = undefined
277 }
278
279 if (utilityModifier !== undefined && modifier === '') {
280 modifier = 'DEFAULT'
281 }
282
283 // Check the full value first
284 // TODO: Move to asValue… somehow
285 if (utilityModifier !== undefined) {
286 if (typeof options.modifiers === 'object') {
287 let configValue = options.modifiers?.[utilityModifier] ?? null
288 if (configValue !== null) {
289 utilityModifier = configValue
290 } else if (isArbitraryValue(utilityModifier)) {
291 utilityModifier = unwrapArbitraryModifier(utilityModifier)
292 }
293 }
294 }
295
296 for (let { type } of types ?? []) {
297 let result = typeMap[type](modifier, options, {
298 tailwindConfig,
299 })
300
301 if (result === undefined) {
302 continue
303 }
304
305 yield [result, type, utilityModifier ?? null]
306 }
307}
Note: See TracBrowser for help on using the repository browser.