2021-04-30 16:58:40 +02:00
|
|
|
import selectorParser from 'postcss-selector-parser'
|
|
|
|
|
import escapeCommas from './escapeCommas'
|
2021-05-14 13:07:56 -04:00
|
|
|
import { withAlphaValue } from './withAlphaVariable'
|
2021-08-16 17:25:30 +01:00
|
|
|
import isKeyframeRule from './isKeyframeRule'
|
2021-09-24 18:45:42 +02:00
|
|
|
import {
|
|
|
|
|
normalize,
|
|
|
|
|
length,
|
|
|
|
|
number,
|
|
|
|
|
percentage,
|
|
|
|
|
url,
|
|
|
|
|
color as validateColor,
|
|
|
|
|
genericName,
|
|
|
|
|
familyName,
|
|
|
|
|
image,
|
|
|
|
|
absoluteSize,
|
|
|
|
|
relativeSize,
|
|
|
|
|
position,
|
|
|
|
|
lineWidth,
|
|
|
|
|
} from './dataTypes'
|
2021-04-02 11:16:15 -04:00
|
|
|
|
2021-06-04 10:32:49 -04:00
|
|
|
export function applyPseudoToMarker(selector, marker, state, join) {
|
|
|
|
|
let states = [state]
|
|
|
|
|
|
|
|
|
|
let markerIdx = selector.indexOf(marker + ':')
|
|
|
|
|
|
|
|
|
|
if (markerIdx !== -1) {
|
|
|
|
|
let existingMarker = selector.slice(markerIdx, selector.indexOf(' ', markerIdx))
|
|
|
|
|
|
|
|
|
|
states = states.concat(
|
|
|
|
|
selector.slice(markerIdx + marker.length + 1, existingMarker.length).split(':')
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
selector = selector.replace(existingMarker, '')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return join(`${[marker, ...states].join(':')}`, selector)
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-30 16:58:40 +02:00
|
|
|
export function updateAllClasses(selectors, updateClass) {
|
2021-04-02 11:16:15 -04:00
|
|
|
let parser = selectorParser((selectors) => {
|
|
|
|
|
selectors.walkClasses((sel) => {
|
|
|
|
|
let updatedClass = updateClass(sel.value, {
|
|
|
|
|
withPseudo(className, pseudo) {
|
2021-05-26 08:57:02 -04:00
|
|
|
sel.parent.insertAfter(sel, selectorParser.pseudo({ value: `${pseudo}` }))
|
2021-04-02 11:16:15 -04:00
|
|
|
return className
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
sel.value = updatedClass
|
|
|
|
|
if (sel.raws && sel.raws.value) {
|
|
|
|
|
sel.raws.value = escapeCommas(sel.raws.value)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
let result = parser.processSync(selectors)
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-30 16:58:40 +02:00
|
|
|
export function updateLastClasses(selectors, updateClass) {
|
2021-04-02 11:16:15 -04:00
|
|
|
let parser = selectorParser((selectors) => {
|
|
|
|
|
selectors.each((sel) => {
|
|
|
|
|
let lastClass = sel.filter(({ type }) => type === 'class').pop()
|
|
|
|
|
|
|
|
|
|
if (lastClass === undefined) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let updatedClass = updateClass(lastClass.value, {
|
|
|
|
|
withPseudo(className, pseudo) {
|
2021-05-26 08:57:02 -04:00
|
|
|
lastClass.parent.insertAfter(lastClass, selectorParser.pseudo({ value: `${pseudo}` }))
|
2021-04-02 11:16:15 -04:00
|
|
|
return className
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
lastClass.value = updatedClass
|
|
|
|
|
if (lastClass.raws && lastClass.raws.value) {
|
|
|
|
|
lastClass.raws.value = escapeCommas(lastClass.raws.value)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
let result = parser.processSync(selectors)
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-18 01:06:31 +01:00
|
|
|
function splitByNotEscapedCommas(str) {
|
|
|
|
|
let chunks = []
|
|
|
|
|
let currentChunk = ''
|
|
|
|
|
for (let i = 0; i < str.length; i++) {
|
|
|
|
|
if (str[i] === ',' && str[i - 1] !== '\\') {
|
|
|
|
|
chunks.push(currentChunk)
|
|
|
|
|
currentChunk = ''
|
|
|
|
|
} else {
|
|
|
|
|
currentChunk += str[i]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
chunks.push(currentChunk)
|
|
|
|
|
return chunks
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-26 08:57:02 -04:00
|
|
|
export function transformAllSelectors(transformSelector, { wrap, withRule } = {}) {
|
2021-04-02 11:16:15 -04:00
|
|
|
return ({ container }) => {
|
|
|
|
|
container.walkRules((rule) => {
|
2021-08-16 17:25:30 +01:00
|
|
|
if (isKeyframeRule(rule)) {
|
|
|
|
|
return rule
|
|
|
|
|
}
|
2021-08-18 01:06:31 +01:00
|
|
|
let transformed = splitByNotEscapedCommas(rule.selector).map(transformSelector).join(',')
|
2021-04-02 11:16:15 -04:00
|
|
|
rule.selector = transformed
|
2021-05-26 08:57:02 -04:00
|
|
|
if (withRule) {
|
|
|
|
|
withRule(rule)
|
|
|
|
|
}
|
2021-04-02 11:16:15 -04:00
|
|
|
return rule
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (wrap) {
|
|
|
|
|
let wrapper = wrap()
|
2021-06-18 15:37:52 +02:00
|
|
|
let nodes = container.nodes
|
|
|
|
|
container.removeAll()
|
|
|
|
|
wrapper.append(nodes)
|
2021-04-02 11:16:15 -04:00
|
|
|
container.append(wrapper)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-26 08:57:02 -04:00
|
|
|
export function transformAllClasses(transformClass, { wrap, withRule } = {}) {
|
2021-04-02 11:16:15 -04:00
|
|
|
return ({ container }) => {
|
|
|
|
|
container.walkRules((rule) => {
|
|
|
|
|
let selector = rule.selector
|
|
|
|
|
let variantSelector = updateAllClasses(selector, transformClass)
|
|
|
|
|
rule.selector = variantSelector
|
2021-05-26 08:57:02 -04:00
|
|
|
if (withRule) {
|
|
|
|
|
withRule(rule)
|
|
|
|
|
}
|
2021-04-02 11:16:15 -04:00
|
|
|
return rule
|
|
|
|
|
})
|
2021-05-26 08:57:02 -04:00
|
|
|
|
|
|
|
|
if (wrap) {
|
|
|
|
|
let wrapper = wrap()
|
2021-06-18 15:37:52 +02:00
|
|
|
let nodes = container.nodes
|
|
|
|
|
container.removeAll()
|
|
|
|
|
wrapper.append(nodes)
|
2021-05-26 08:57:02 -04:00
|
|
|
container.append(wrapper)
|
|
|
|
|
}
|
2021-04-02 11:16:15 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-26 08:57:02 -04:00
|
|
|
export function transformLastClasses(transformClass, { wrap, withRule } = {}) {
|
2021-04-02 11:16:15 -04:00
|
|
|
return ({ container }) => {
|
|
|
|
|
container.walkRules((rule) => {
|
|
|
|
|
let selector = rule.selector
|
|
|
|
|
let variantSelector = updateLastClasses(selector, transformClass)
|
|
|
|
|
rule.selector = variantSelector
|
2021-05-26 08:57:02 -04:00
|
|
|
if (withRule) {
|
|
|
|
|
withRule(rule)
|
|
|
|
|
}
|
2021-04-02 11:16:15 -04:00
|
|
|
return rule
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (wrap) {
|
|
|
|
|
let wrapper = wrap()
|
2021-06-18 15:37:52 +02:00
|
|
|
let nodes = container.nodes
|
|
|
|
|
container.removeAll()
|
|
|
|
|
wrapper.append(nodes)
|
2021-04-02 11:16:15 -04:00
|
|
|
container.append(wrapper)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-09 18:10:04 +02:00
|
|
|
export function asValue(modifier, lookup = {}, { validate = () => true } = {}) {
|
2021-04-02 11:16:15 -04:00
|
|
|
let value = lookup[modifier]
|
|
|
|
|
|
|
|
|
|
if (value !== undefined) {
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-24 18:45:42 +02:00
|
|
|
if (!isArbitraryValue(modifier)) {
|
2021-04-02 11:16:15 -04:00
|
|
|
return undefined
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value = modifier.slice(1, -1)
|
|
|
|
|
|
|
|
|
|
if (!validate(value)) {
|
|
|
|
|
return undefined
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-24 18:45:42 +02:00
|
|
|
return normalize(value)
|
2021-04-30 16:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
2021-05-14 13:07:56 -04:00
|
|
|
function isArbitraryValue(input) {
|
|
|
|
|
return input.startsWith('[') && input.endsWith(']')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function splitAlpha(modifier) {
|
|
|
|
|
let slashIdx = modifier.lastIndexOf('/')
|
|
|
|
|
|
|
|
|
|
if (slashIdx === -1 || slashIdx === modifier.length - 1) {
|
|
|
|
|
return [modifier]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [modifier.slice(0, slashIdx), modifier.slice(slashIdx + 1)]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function asColor(modifier, lookup = {}, tailwindConfig = {}) {
|
|
|
|
|
if (lookup[modifier] !== undefined) {
|
|
|
|
|
return lookup[modifier]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let [color, alpha] = splitAlpha(modifier)
|
|
|
|
|
|
|
|
|
|
if (lookup[color] !== undefined) {
|
|
|
|
|
if (isArbitraryValue(alpha)) {
|
|
|
|
|
return withAlphaValue(lookup[color], alpha.slice(1, -1))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tailwindConfig.theme?.opacity?.[alpha] === undefined) {
|
|
|
|
|
return undefined
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return withAlphaValue(lookup[color], tailwindConfig.theme.opacity[alpha])
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-24 18:45:42 +02:00
|
|
|
return asValue(modifier, lookup, { validate: validateColor })
|
2021-04-30 16:58:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function asLookupValue(modifier, lookup = {}) {
|
|
|
|
|
return lookup[modifier]
|
2021-04-02 11:16:15 -04:00
|
|
|
}
|
2021-05-05 15:23:32 +02:00
|
|
|
|
2021-09-24 18:45:42 +02:00
|
|
|
function guess(validate) {
|
|
|
|
|
return (modifier, lookup) => {
|
|
|
|
|
return asValue(modifier, lookup, { validate })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-05 15:23:32 +02:00
|
|
|
let typeMap = {
|
|
|
|
|
any: asValue,
|
|
|
|
|
color: asColor,
|
2021-09-24 18:45:42 +02:00
|
|
|
url: guess(url),
|
|
|
|
|
image: guess(image),
|
|
|
|
|
length: guess(length),
|
|
|
|
|
percentage: guess(percentage),
|
|
|
|
|
position: guess(position),
|
2021-05-05 15:23:32 +02:00
|
|
|
lookup: asLookupValue,
|
2021-09-24 18:45:42 +02:00
|
|
|
'generic-name': guess(genericName),
|
|
|
|
|
'family-name': guess(familyName),
|
|
|
|
|
number: guess(number),
|
|
|
|
|
'line-width': guess(lineWidth),
|
|
|
|
|
'absolute-size': guess(absoluteSize),
|
|
|
|
|
'relative-size': guess(relativeSize),
|
2021-05-05 15:23:32 +02:00
|
|
|
}
|
|
|
|
|
|
2021-09-24 18:18:48 +02:00
|
|
|
let supportedTypes = Object.keys(typeMap)
|
|
|
|
|
|
2021-05-07 08:59:24 -04:00
|
|
|
function splitAtFirst(input, delim) {
|
2021-09-24 18:18:48 +02:00
|
|
|
let idx = input.indexOf(delim)
|
|
|
|
|
if (idx === -1) return [undefined, input]
|
|
|
|
|
return [input.slice(0, idx), input.slice(idx + 1)]
|
2021-05-07 08:59:24 -04:00
|
|
|
}
|
|
|
|
|
|
2021-09-24 18:45:42 +02:00
|
|
|
export function coerceValue(types, modifier, values, tailwindConfig) {
|
2021-05-14 13:07:56 -04:00
|
|
|
if (isArbitraryValue(modifier)) {
|
2021-05-07 08:59:24 -04:00
|
|
|
let [explicitType, value] = splitAtFirst(modifier.slice(1, -1), ':')
|
|
|
|
|
|
2021-09-24 18:18:48 +02:00
|
|
|
if (explicitType !== undefined && !supportedTypes.includes(explicitType)) {
|
|
|
|
|
return []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (value.length > 0 && supportedTypes.includes(explicitType)) {
|
2021-05-14 13:07:56 -04:00
|
|
|
return [asValue(`[${value}]`, values, tailwindConfig), explicitType]
|
2021-05-07 08:59:24 -04:00
|
|
|
}
|
2021-09-24 18:45:42 +02:00
|
|
|
}
|
2021-05-14 13:07:56 -04:00
|
|
|
|
2021-09-24 18:45:42 +02:00
|
|
|
// Find first matching type
|
|
|
|
|
for (let type of [].concat(types)) {
|
|
|
|
|
let result = typeMap[type](modifier, values, tailwindConfig)
|
|
|
|
|
if (result) return [result, type]
|
2021-05-07 08:59:24 -04:00
|
|
|
}
|
|
|
|
|
|
2021-09-24 18:45:42 +02:00
|
|
|
return []
|
2021-05-05 15:23:32 +02:00
|
|
|
}
|