Fix negative utility generation and detection when using a prefix (#7295)
* Add failing tests for negative utility detection We're not generating them properly in all cases, when using at-apply we sometimes crash, and safelisting doesn't currently work as expected. * Refactor * Generate utilities for negatives before and after the prefix * Properly detect negative utilities with prefixes in the safelist * Refactor test a bit * Add class list tests * Update changelog
This commit is contained in:
parent
ab9fd951dd
commit
01fbe196c4
6 changed files with 384 additions and 23 deletions
|
|
@ -63,9 +63,23 @@ function applyPrefix(matches, context) {
|
|||
let [meta] = match
|
||||
if (meta.options.respectPrefix) {
|
||||
let container = postcss.root({ nodes: [match[1].clone()] })
|
||||
let classCandidate = match[1].raws.tailwind.classCandidate
|
||||
|
||||
container.walkRules((r) => {
|
||||
r.selector = prefixSelector(context.tailwindConfig.prefix, r.selector)
|
||||
// If this is a negative utility with a dash *before* the prefix we
|
||||
// have to ensure that the generated selector matches the candidate
|
||||
|
||||
// Not doing this will cause `-tw-top-1` to generate the class `.tw--top-1`
|
||||
// The disconnect between candidate <-> class can cause @apply to hard crash.
|
||||
let shouldPrependNegative = classCandidate.startsWith('-')
|
||||
|
||||
r.selector = prefixSelector(
|
||||
context.tailwindConfig.prefix,
|
||||
r.selector,
|
||||
shouldPrependNegative
|
||||
)
|
||||
})
|
||||
|
||||
match[1] = container.nodes[0]
|
||||
}
|
||||
}
|
||||
|
|
@ -371,6 +385,14 @@ function splitWithSeparator(input, separator) {
|
|||
return input.split(new RegExp(`\\${separator}(?![^[]*\\])`, 'g'))
|
||||
}
|
||||
|
||||
function* recordCandidates(matches, classCandidate) {
|
||||
for (const match of matches) {
|
||||
match[1].raws.tailwind = { classCandidate }
|
||||
|
||||
yield match
|
||||
}
|
||||
}
|
||||
|
||||
function* resolveMatches(candidate, context) {
|
||||
let separator = context.tailwindConfig.separator
|
||||
let [classCandidate, ...variants] = splitWithSeparator(candidate, separator).reverse()
|
||||
|
|
@ -482,7 +504,9 @@ function* resolveMatches(candidate, context) {
|
|||
continue
|
||||
}
|
||||
|
||||
matches = applyPrefix(matches.flat(), context)
|
||||
matches = matches.flat()
|
||||
matches = Array.from(recordCandidates(matches, classCandidate))
|
||||
matches = applyPrefix(matches, context)
|
||||
|
||||
if (important) {
|
||||
matches = applyImportant(matches, context)
|
||||
|
|
|
|||
|
|
@ -666,17 +666,30 @@ function registerPlugins(plugins, context) {
|
|||
|
||||
if (checks.length > 0) {
|
||||
let patternMatchingCount = new Map()
|
||||
let prefixLength = context.tailwindConfig.prefix.length
|
||||
|
||||
for (let util of classList) {
|
||||
let utils = Array.isArray(util)
|
||||
? (() => {
|
||||
let [utilName, options] = util
|
||||
let classes = Object.keys(options?.values ?? {}).map((value) =>
|
||||
formatClass(utilName, value)
|
||||
)
|
||||
let values = Object.keys(options?.values ?? {})
|
||||
let classes = values.map((value) => formatClass(utilName, value))
|
||||
|
||||
if (options?.supportsNegativeValues) {
|
||||
// This is the normal negated version
|
||||
// e.g. `-inset-1` or `-tw-inset-1`
|
||||
classes = [...classes, ...classes.map((cls) => '-' + cls)]
|
||||
|
||||
// This is the negated version *after* the prefix
|
||||
// e.g. `tw--inset-1`
|
||||
// The prefix is already attached to util name
|
||||
// So we add the negative after the prefix
|
||||
classes = [
|
||||
...classes,
|
||||
...classes.map(
|
||||
(cls) => cls.slice(0, prefixLength) + '-' + cls.slice(prefixLength)
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
return classes
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import parser from 'postcss-selector-parser'
|
||||
import { tap } from './tap'
|
||||
|
||||
export default function (prefix, selector) {
|
||||
export default function (prefix, selector, prependNegative = false) {
|
||||
return parser((selectors) => {
|
||||
selectors.walkClasses((classSelector) => {
|
||||
tap(classSelector.value, (baseClass) => {
|
||||
classSelector.value = `${prefix}${baseClass}`
|
||||
})
|
||||
let baseClass = classSelector.value
|
||||
let shouldPlaceNegativeBeforePrefix = prependNegative && baseClass.startsWith('-')
|
||||
|
||||
classSelector.value = shouldPlaceNegativeBeforePrefix
|
||||
? `-${prefix}${baseClass.slice(1)}`
|
||||
: `${prefix}${baseClass}`
|
||||
})
|
||||
}).processSync(selector)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue