WIP
This commit is contained in:
parent
3cb47aaab6
commit
5bcd9d2f69
5 changed files with 175 additions and 145 deletions
|
|
@ -7,10 +7,9 @@ import { updateAllClasses } from '../util/pluginUtils'
|
|||
import log from '../util/log'
|
||||
import { formatVariantSelector, finalizeSelector } from '../util/formatVariantSelector'
|
||||
import { asClass } from '../util/nameClass'
|
||||
import { normalize } from '../util/dataTypes'
|
||||
import { isValidVariantFormatString, parseVariant } from './setupContextUtils'
|
||||
import { isParsableNode } from '../util/css-validation.js'
|
||||
import { parseCandidate } from '../lib/candidate.js'
|
||||
import { isParsableNode } from '../util/css-validation'
|
||||
import { parseCandidate } from './candidate'
|
||||
|
||||
let classNameParser = selectorParser((selectors) => {
|
||||
return selectors.first.filter(({ type }) => type === 'class').pop().value
|
||||
|
|
@ -20,42 +19,6 @@ function getClassNameFromSelector(selector) {
|
|||
return classNameParser.transformSync(selector)
|
||||
}
|
||||
|
||||
// Generate match permutations for a class candidate, like:
|
||||
// ['ring-offset-blue', '100']
|
||||
// ['ring-offset', 'blue-100']
|
||||
// ['ring', 'offset-blue-100']
|
||||
// Example with dynamic classes:
|
||||
// ['grid-cols', '[[linename],1fr,auto]']
|
||||
// ['grid', 'cols-[[linename],1fr,auto]']
|
||||
function* candidatePermutations(candidate) {
|
||||
let lastIndex = Infinity
|
||||
|
||||
while (lastIndex >= 0) {
|
||||
let dashIdx
|
||||
|
||||
if (lastIndex === Infinity && candidate.endsWith(']')) {
|
||||
let bracketIdx = candidate.indexOf('[')
|
||||
|
||||
// If character before `[` isn't a dash or a slash, this isn't a dynamic class
|
||||
// eg. string[]
|
||||
dashIdx = ['-', '/'].includes(candidate[bracketIdx - 1]) ? bracketIdx - 1 : -1
|
||||
} else {
|
||||
dashIdx = candidate.lastIndexOf('-', lastIndex)
|
||||
}
|
||||
|
||||
if (dashIdx < 0) {
|
||||
break
|
||||
}
|
||||
|
||||
let prefix = candidate.slice(0, dashIdx)
|
||||
let modifier = candidate.slice(dashIdx + 1)
|
||||
|
||||
yield [prefix, modifier]
|
||||
|
||||
lastIndex = dashIdx - 1
|
||||
}
|
||||
}
|
||||
|
||||
function applyPrefix(matches, context) {
|
||||
if (matches.length === 0 || context.tailwindConfig.prefix === '') {
|
||||
return matches
|
||||
|
|
@ -314,21 +277,21 @@ function extractArbitraryProperty(parsed, context) {
|
|||
]
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('./candidate').Candidate} parsed
|
||||
* @param {any} context
|
||||
*/
|
||||
function* resolveMatchedPlugins(parsed, context) {
|
||||
let classCandidate = parsed.withoutVariants
|
||||
|
||||
if (context.candidateRuleMap.has(classCandidate)) {
|
||||
yield [context.candidateRuleMap.get(classCandidate), 'DEFAULT']
|
||||
}
|
||||
|
||||
if (parsed.type === 'custom') {
|
||||
yield [extractArbitraryProperty(parsed, context), 'DEFAULT']
|
||||
}
|
||||
|
||||
if (parsed.type !== 'constrained' && parsed.type !== 'partial') {
|
||||
if (parsed.type !== 'constrained') {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Can this be dropped?
|
||||
let candidatePrefix = parsed.negative
|
||||
? parsed.prefix + parsed.withoutVariants.slice(parsed.prefix.length + 1)
|
||||
: parsed.withoutVariants
|
||||
|
|
@ -337,9 +300,13 @@ function* resolveMatchedPlugins(parsed, context) {
|
|||
yield [context.candidateRuleMap.get(candidatePrefix), '-DEFAULT']
|
||||
}
|
||||
|
||||
for (let [prefix, modifier] of candidatePermutations(candidatePrefix)) {
|
||||
if (context.candidateRuleMap.has(prefix)) {
|
||||
yield [context.candidateRuleMap.get(prefix), parsed.negative ? `-${modifier}` : modifier]
|
||||
for (let plugin of parsed.plugins) {
|
||||
let ruleGenerator =
|
||||
context.candidateRuleMap.get(parsed.prefix + plugin.plugin) ||
|
||||
context.candidateRuleMap.get(plugin.plugin)
|
||||
|
||||
if (ruleGenerator) {
|
||||
yield [ruleGenerator, plugin.value, plugin]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -358,15 +325,15 @@ function* recordCandidates(matches, candidate) {
|
|||
}
|
||||
}
|
||||
|
||||
function* resolveMatches(candidate, context) {
|
||||
let parsed = parseCandidate(candidate, context)
|
||||
|
||||
if (parsed === null) {
|
||||
return
|
||||
function* resolveMatches(candidateRaw, context) {
|
||||
for (const candidate of parseCandidate(candidateRaw, context)) {
|
||||
yield* resolveMatchesForCandidate(candidate, context)
|
||||
}
|
||||
}
|
||||
|
||||
let important = parsed.important
|
||||
let variants = parsed.variants
|
||||
function* resolveMatchesForCandidate(candidate, context) {
|
||||
let important = candidate.important
|
||||
let variants = candidate.variants
|
||||
|
||||
// TODO: Reintroduce this in ways that doesn't break on false positives
|
||||
// function sortAgainst(toSort, against) {
|
||||
|
|
@ -380,18 +347,20 @@ function* resolveMatches(candidate, context) {
|
|||
// throw new Error(`Class ${candidate} should be written as ${corrected}`)
|
||||
// }
|
||||
|
||||
for (let matchedPlugins of resolveMatchedPlugins(parsed, context)) {
|
||||
for (let matchedPlugins of resolveMatchedPlugins(candidate, context)) {
|
||||
let matches = []
|
||||
let typesByMatches = new Map()
|
||||
|
||||
let [plugins, modifier] = matchedPlugins
|
||||
let [plugins, modifier, candidatePlugin] = matchedPlugins
|
||||
let isOnlyPlugin = plugins.length === 1
|
||||
|
||||
for (let [sort, plugin] of plugins) {
|
||||
let matchesPerPlugin = []
|
||||
|
||||
if (typeof plugin === 'function') {
|
||||
for (let ruleSet of [].concat(plugin(modifier, { isOnlyPlugin }))) {
|
||||
for (let ruleSet of [].concat(
|
||||
plugin(modifier, { isOnlyPlugin, candidate, candidatePlugin })
|
||||
)) {
|
||||
let [rules, options] = parseRules(ruleSet, context.postCssNodeCache)
|
||||
for (let rule of rules) {
|
||||
matchesPerPlugin.push([{ ...sort, options: { ...sort.options, ...options } }, rule])
|
||||
|
|
@ -413,7 +382,7 @@ function* resolveMatches(candidate, context) {
|
|||
}
|
||||
}
|
||||
|
||||
if (isArbitraryValue(modifier)) {
|
||||
if (typeof modifier === 'object') {
|
||||
// When generated arbitrary values are ambiguous, we can't know
|
||||
// which to pick so don't generate any utilities for them
|
||||
if (matches.length > 1) {
|
||||
|
|
@ -456,16 +425,16 @@ function* resolveMatches(candidate, context) {
|
|||
.join('\n\n')
|
||||
|
||||
messages.push(
|
||||
` Use \`${candidate.replace('[', `[${type}:`)}\` for \`${rules.trim()}\``
|
||||
` Use \`${candidate.raw.replace('[', `[${type}:`)}\` for \`${rules.trim()}\``
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
log.warn([
|
||||
`The class \`${candidate}\` is ambiguous and matches multiple utilities.`,
|
||||
`The class \`${candidate.raw}\` is ambiguous and matches multiple utilities.`,
|
||||
...messages,
|
||||
`If this is content and not a class, replace it with \`${candidate
|
||||
`If this is content and not a class, replace it with \`${candidate.raw
|
||||
.replace('[', '[')
|
||||
.replace(']', ']')}\` to silence this warning.`,
|
||||
])
|
||||
|
|
@ -476,19 +445,22 @@ function* resolveMatches(candidate, context) {
|
|||
}
|
||||
|
||||
matches = matches.flat()
|
||||
matches = Array.from(recordCandidates(matches, parsed))
|
||||
matches = Array.from(recordCandidates(matches, candidate))
|
||||
matches = applyPrefix(matches, context)
|
||||
|
||||
if (important) {
|
||||
matches = applyImportant(matches, parsed)
|
||||
matches = applyImportant(matches, candidate)
|
||||
}
|
||||
|
||||
for (let variant of variants) {
|
||||
matches = applyVariant(variant, matches, context)
|
||||
}
|
||||
|
||||
let foundMatches = false
|
||||
for (let match of matches) {
|
||||
match[1].raws.tailwind = { ...match[1].raws.tailwind, candidate }
|
||||
foundMatches = true
|
||||
|
||||
match[1].raws.tailwind = { ...match[1].raws.tailwind, candidate: candidate.raw }
|
||||
|
||||
// Apply final format selector
|
||||
if (match[0].collectedFormats) {
|
||||
|
|
@ -499,7 +471,7 @@ function* resolveMatches(candidate, context) {
|
|||
|
||||
rule.selector = finalizeSelector(finalFormat, {
|
||||
selector: rule.selector,
|
||||
candidate,
|
||||
candidate: candidate.raw,
|
||||
context,
|
||||
})
|
||||
})
|
||||
|
|
@ -508,6 +480,25 @@ function* resolveMatches(candidate, context) {
|
|||
|
||||
yield match
|
||||
}
|
||||
|
||||
// We always want to emit matches for `DEFAULT`-ish values
|
||||
// so we'll continue matching in that case
|
||||
if (
|
||||
candidatePlugin &&
|
||||
(candidatePlugin.value === 'DEFAULT' || candidatePlugin.value === '-DEFAULT')
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
// If we've emitted matches we're done searching
|
||||
// This is because the order of matched values is as follows:
|
||||
// - Real values from the config
|
||||
// - Real values from the config accounting for modifiers
|
||||
// - Arbitrary values
|
||||
// - Arbitrary values accounting for modifiers
|
||||
if (foundMatches) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -579,8 +570,4 @@ function generateRules(candidates, context) {
|
|||
})
|
||||
}
|
||||
|
||||
function isArbitraryValue(input) {
|
||||
return input.startsWith('[') && input.endsWith(']')
|
||||
}
|
||||
|
||||
export { resolveMatches, generateRules }
|
||||
|
|
|
|||
|
|
@ -371,10 +371,17 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|||
|
||||
classList.add([prefixedIdentifier, options])
|
||||
|
||||
function wrapped(modifier, { isOnlyPlugin }) {
|
||||
function wrapped(modifier, { isOnlyPlugin, candidate, candidatePlugin }) {
|
||||
let { type = 'any' } = options
|
||||
type = [].concat(type)
|
||||
let [value, coercedType] = coerceValue(type, modifier, options, tailwindConfig)
|
||||
let [value, coercedType] = coerceValue(
|
||||
type,
|
||||
modifier,
|
||||
options,
|
||||
tailwindConfig,
|
||||
candidate,
|
||||
candidatePlugin
|
||||
)
|
||||
|
||||
if (value === undefined) {
|
||||
return []
|
||||
|
|
@ -384,15 +391,36 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|||
return []
|
||||
}
|
||||
|
||||
// TODO: Can we remove this?
|
||||
// This check is done in the candidate section
|
||||
if (!isValidArbitraryValue(value)) {
|
||||
return []
|
||||
}
|
||||
|
||||
let tempIdentifier = identifier
|
||||
|
||||
if (typeof modifier === 'object') {
|
||||
modifier = modifier.raw
|
||||
if (candidate.negative && !tempIdentifier.startsWith('-')) {
|
||||
tempIdentifier = `-${tempIdentifier}`
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove and use .className
|
||||
let alphaModifierValue = candidatePlugin?.modifiers[0]?.value
|
||||
if (alphaModifierValue !== undefined) {
|
||||
if (typeof alphaModifierValue === 'object') {
|
||||
alphaModifierValue = alphaModifierValue.raw
|
||||
}
|
||||
|
||||
modifier += `/${alphaModifierValue}`
|
||||
}
|
||||
|
||||
let ruleSets = []
|
||||
.concat(rule(value))
|
||||
.filter(Boolean)
|
||||
.map((declaration) => ({
|
||||
[nameClass(identifier, modifier)]: declaration,
|
||||
[nameClass(tempIdentifier, modifier)]: declaration,
|
||||
}))
|
||||
|
||||
return ruleSets
|
||||
|
|
@ -423,10 +451,17 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|||
|
||||
classList.add([prefixedIdentifier, options])
|
||||
|
||||
function wrapped(modifier, { isOnlyPlugin }) {
|
||||
function wrapped(modifier, { isOnlyPlugin, candidate, candidatePlugin }) {
|
||||
let { type = 'any' } = options
|
||||
type = [].concat(type)
|
||||
let [value, coercedType] = coerceValue(type, modifier, options, tailwindConfig)
|
||||
let [value, coercedType] = coerceValue(
|
||||
type,
|
||||
modifier,
|
||||
options,
|
||||
tailwindConfig,
|
||||
candidate,
|
||||
candidatePlugin
|
||||
)
|
||||
|
||||
if (value === undefined) {
|
||||
return []
|
||||
|
|
@ -446,15 +481,26 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Can we remove this?
|
||||
// This check is done in the candidate section
|
||||
if (!isValidArbitraryValue(value)) {
|
||||
return []
|
||||
}
|
||||
|
||||
let tempIdentifier = identifier
|
||||
|
||||
if (typeof modifier === 'object') {
|
||||
modifier = modifier.raw
|
||||
if (candidate.negative && !tempIdentifier.startsWith('-')) {
|
||||
tempIdentifier = `-${tempIdentifier}`
|
||||
}
|
||||
}
|
||||
|
||||
let ruleSets = []
|
||||
.concat(rule(value))
|
||||
.filter(Boolean)
|
||||
.map((declaration) => ({
|
||||
[nameClass(identifier, modifier)]: declaration,
|
||||
[nameClass(tempIdentifier, modifier)]: declaration,
|
||||
}))
|
||||
|
||||
return ruleSets
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ let cssFunctions = ['min', 'max', 'clamp', 'calc']
|
|||
// Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Types
|
||||
|
||||
let COMMA = /,(?![^(]*\))/g // Comma separator that is not located between brackets. E.g.: `cubiz-bezier(a, b, c)` these don't count.
|
||||
let UNDERSCORE = /_(?![^(]*\))/g // Underscore separator that is not located between brackets. E.g.: `rgba(255,_255,_255)_black` these don't count.
|
||||
let SPACE = /[ ](?![^(]*\))/g // space separator that is not located between brackets. E.g.: `rgba(255, 255, 255) black` these don't count.
|
||||
|
||||
// This is not a data type, but rather a function that can normalize the
|
||||
// correct values.
|
||||
|
|
@ -63,7 +63,7 @@ export function number(value) {
|
|||
}
|
||||
|
||||
export function percentage(value) {
|
||||
return value.split(UNDERSCORE).every((part) => {
|
||||
return value.split(SPACE).every((part) => {
|
||||
return /%$/g.test(part) || cssFunctions.some((fn) => new RegExp(`^${fn}\\(.+?%`).test(part))
|
||||
})
|
||||
}
|
||||
|
|
@ -88,7 +88,7 @@ let lengthUnits = [
|
|||
]
|
||||
let lengthUnitsPattern = `(?:${lengthUnits.join('|')})`
|
||||
export function length(value) {
|
||||
return value.split(UNDERSCORE).every((part) => {
|
||||
return value.split(SPACE).every((part) => {
|
||||
return (
|
||||
part === '0' ||
|
||||
new RegExp(`${lengthUnitsPattern}$`).test(part) ||
|
||||
|
|
@ -117,7 +117,7 @@ export function shadow(value) {
|
|||
export function color(value) {
|
||||
let colors = 0
|
||||
|
||||
let result = value.split(UNDERSCORE).every((part) => {
|
||||
let result = value.split(SPACE).every((part) => {
|
||||
part = normalize(part)
|
||||
|
||||
if (part.startsWith('var(')) return true
|
||||
|
|
@ -173,7 +173,7 @@ export function gradient(value) {
|
|||
let validPositions = new Set(['center', 'top', 'right', 'bottom', 'left'])
|
||||
export function position(value) {
|
||||
let positions = 0
|
||||
let result = value.split(UNDERSCORE).every((part) => {
|
||||
let result = value.split(SPACE).every((part) => {
|
||||
part = normalize(part)
|
||||
|
||||
if (part.startsWith('var(')) return true
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ export function asClass(name) {
|
|||
}
|
||||
|
||||
export default function nameClass(classPrefix, key) {
|
||||
if (typeof key === 'object') {
|
||||
key = key.raw
|
||||
}
|
||||
|
||||
return asClass(formatClass(classPrefix, key))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,17 +36,15 @@ export function updateAllClasses(selectors, updateClass) {
|
|||
}
|
||||
|
||||
function resolveArbitraryValue(modifier, validate) {
|
||||
if (!isArbitraryValue(modifier)) {
|
||||
return undefined
|
||||
if (isArbitraryValue(modifier)) {
|
||||
if (!validate(modifier.value)) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return modifier.value
|
||||
}
|
||||
|
||||
let value = modifier.slice(1, -1)
|
||||
|
||||
if (!validate(value)) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return normalize(value)
|
||||
return undefined
|
||||
}
|
||||
|
||||
function asNegativeValue(modifier, lookup = {}, validate) {
|
||||
|
|
@ -55,7 +53,9 @@ function asNegativeValue(modifier, lookup = {}, validate) {
|
|||
if (positiveValue !== undefined) {
|
||||
return negateValue(positiveValue)
|
||||
}
|
||||
}
|
||||
|
||||
export function asValue(modifier, options = {}, { candidate, validate = () => true } = {}) {
|
||||
if (isArbitraryValue(modifier)) {
|
||||
let resolved = resolveArbitraryValue(modifier, validate)
|
||||
|
||||
|
|
@ -63,11 +63,13 @@ function asNegativeValue(modifier, lookup = {}, validate) {
|
|||
return undefined
|
||||
}
|
||||
|
||||
return negateValue(resolved)
|
||||
}
|
||||
}
|
||||
if (options.supportsNegativeValues && candidate.negative) {
|
||||
return negateValue(resolved)
|
||||
}
|
||||
|
||||
return resolved
|
||||
}
|
||||
|
||||
export function asValue(modifier, options = {}, { validate = () => true } = {}) {
|
||||
let value = options.values?.[modifier]
|
||||
|
||||
if (value !== undefined) {
|
||||
|
|
@ -78,50 +80,56 @@ export function asValue(modifier, options = {}, { validate = () => true } = {})
|
|||
return asNegativeValue(modifier.slice(1), options.values, validate)
|
||||
}
|
||||
|
||||
return resolveArbitraryValue(modifier, validate)
|
||||
return undefined
|
||||
}
|
||||
|
||||
function isArbitraryValue(input) {
|
||||
return input.startsWith('[') && input.endsWith(']')
|
||||
return typeof input === 'object'
|
||||
}
|
||||
|
||||
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, options = {}, { tailwindConfig = {} } = {}) {
|
||||
if (options.values?.[modifier] !== undefined) {
|
||||
/**
|
||||
*
|
||||
* @param {string} modifier
|
||||
* @param {any} options
|
||||
* @param {object} param2
|
||||
* @param {any} param2.tailwindConfig
|
||||
* @param {import('../lib/candidate').Candidate} param2.candidate
|
||||
* @param {import('../lib/candidate').Plugin} param2.candidatePlugin
|
||||
* @returns
|
||||
*/
|
||||
export function asColor(
|
||||
modifier,
|
||||
options = {},
|
||||
{ tailwindConfig = {}, candidate, candidatePlugin } = {}
|
||||
) {
|
||||
if (options.values?.[modifier] !== undefined && candidatePlugin.modifiers.length === 0) {
|
||||
return options.values?.[modifier]
|
||||
}
|
||||
|
||||
let [color, alpha] = splitAlpha(modifier)
|
||||
let alpha = candidatePlugin.modifiers[0]?.value ?? undefined
|
||||
|
||||
if (typeof alpha === 'string') {
|
||||
alpha = tailwindConfig.theme?.opacity?.[alpha] ?? undefined
|
||||
|
||||
if (alpha === undefined) {
|
||||
return undefined
|
||||
}
|
||||
} else if (typeof alpha === 'object') {
|
||||
alpha = alpha.value
|
||||
}
|
||||
|
||||
if (alpha !== undefined) {
|
||||
let normalizedColor =
|
||||
options.values?.[color] ?? (isArbitraryValue(color) ? color.slice(1, -1) : undefined)
|
||||
options.values?.[modifier] ?? (isArbitraryValue(modifier) ? modifier.value : undefined)
|
||||
|
||||
if (normalizedColor === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (isArbitraryValue(alpha)) {
|
||||
return withAlphaValue(normalizedColor, alpha.slice(1, -1))
|
||||
}
|
||||
|
||||
if (tailwindConfig.theme?.opacity?.[alpha] === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return withAlphaValue(normalizedColor, tailwindConfig.theme.opacity[alpha])
|
||||
return withAlphaValue(normalizedColor, alpha)
|
||||
}
|
||||
|
||||
return asValue(modifier, options, { validate: validateColor })
|
||||
return asValue(modifier, options, { candidate, validate: validateColor })
|
||||
}
|
||||
|
||||
export function asLookupValue(modifier, options = {}) {
|
||||
|
|
@ -129,8 +137,8 @@ export function asLookupValue(modifier, options = {}) {
|
|||
}
|
||||
|
||||
function guess(validate) {
|
||||
return (modifier, options) => {
|
||||
return asValue(modifier, options, { validate })
|
||||
return (modifier, options, { candidate }) => {
|
||||
return asValue(modifier, options, { candidate, validate })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -154,37 +162,22 @@ let typeMap = {
|
|||
|
||||
let supportedTypes = Object.keys(typeMap)
|
||||
|
||||
function splitAtFirst(input, delim) {
|
||||
let idx = input.indexOf(delim)
|
||||
if (idx === -1) return [undefined, input]
|
||||
return [input.slice(0, idx), input.slice(idx + 1)]
|
||||
}
|
||||
|
||||
export function coerceValue(types, modifier, options, tailwindConfig) {
|
||||
export function coerceValue(types, modifier, options, tailwindConfig, candidate, candidatePlugin) {
|
||||
if (isArbitraryValue(modifier)) {
|
||||
let arbitraryValue = modifier.slice(1, -1)
|
||||
let [explicitType, value] = splitAtFirst(arbitraryValue, ':')
|
||||
|
||||
// It could be that this resolves to `url(https` which is not a valid
|
||||
// identifier. We currently only support "simple" words with dashes or
|
||||
// underscores. E.g.: family-name
|
||||
if (!/^[\w-_]+$/g.test(explicitType)) {
|
||||
value = arbitraryValue
|
||||
}
|
||||
|
||||
//
|
||||
else if (explicitType !== undefined && !supportedTypes.includes(explicitType)) {
|
||||
if (!supportedTypes.includes(modifier.dataType)) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (value.length > 0 && supportedTypes.includes(explicitType)) {
|
||||
return [asValue(`[${value}]`, options), explicitType]
|
||||
// 'any' is the default data type for arbitrary values
|
||||
// TODO: We should probably guess them when parsing candidates
|
||||
if (modifier.dataType !== 'any') {
|
||||
return [asValue(modifier, options), modifier.dataType]
|
||||
}
|
||||
}
|
||||
|
||||
// Find first matching type
|
||||
for (let type of [].concat(types)) {
|
||||
let result = typeMap[type](modifier, options, { tailwindConfig })
|
||||
let result = typeMap[type](modifier, options, { tailwindConfig, candidate, candidatePlugin })
|
||||
if (result !== undefined) return [result, type]
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue