Skip to content

Commit

Permalink
fix(preset-mini): spread mutiple pseudo-elements for correct variant …
Browse files Browse the repository at this point in the history
…handling (#4298)
  • Loading branch information
zyyv authored Nov 28, 2024
1 parent 01994e5 commit 95a9f69
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 49 deletions.
2 changes: 2 additions & 0 deletions packages/core/test/__snapshots__/order.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ exports[`order > multiple variant sorting 1`] = `

exports[`order > pseudo-elements sorting 1`] = `
"/* layer: default */
.dark .dark\\:hover\\:file\\:marker\\:bg-red-600::file-selector-button:hover *::marker{--un-bg-opacity:1;background-color:rgb(220 38 38 / var(--un-bg-opacity));}
.dark .dark\\:hover\\:file\\:marker\\:bg-red-600::file-selector-button:hover::marker{--un-bg-opacity:1;background-color:rgb(220 38 38 / var(--un-bg-opacity));}
.dark .dark\\:file\\:marker\\:hover\\:bg-red-600:hover::file-selector-button *::marker{--un-bg-opacity:1;background-color:rgb(220 38 38 / var(--un-bg-opacity));}
.dark .dark\\:file\\:marker\\:hover\\:bg-red-600:hover::file-selector-button::marker{--un-bg-opacity:1;background-color:rgb(220 38 38 / var(--un-bg-opacity));}"
`;

Expand Down
2 changes: 1 addition & 1 deletion packages/preset-mini/src/_variants/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function variants(options: PresetMiniOptions): Variant<Theme>[] {
variantBreakpoints(),
...variantCombinators,

variantPseudoClassesAndElements(),
...variantPseudoClassesAndElements(),
variantPseudoClassFunctions(),
...variantTaggedPseudoClasses(options),

Expand Down
4 changes: 2 additions & 2 deletions packages/preset-mini/src/_variants/pseudo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { variantPseudoClassesAndElements } from './pseudo'
it('pseudo variant order', async () => {
const uno = await createGenerator({
variants: [
variantPseudoClassesAndElements(),
...variantPseudoClassesAndElements(),
],
rules: [
[/^foo-(\d)$/, ([_, a]) => ({ text: `foo-${a}` })],
Expand Down Expand Up @@ -53,7 +53,7 @@ it('pseudo variant order', async () => {
it('focus-visible:', async () => {
const uno = await createGenerator({
variants: [
variantPseudoClassesAndElements(),
...variantPseudoClassesAndElements(),
],
rules: [
[/^foo-(\d)$/, ([_, a]) => ({ text: `foo-${a}` })],
Expand Down
114 changes: 73 additions & 41 deletions packages/preset-mini/src/_variants/pseudo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ const PseudoClasses: Record<string, string> = Object.fromEntries([
['placeholder', '::placeholder'],
['before', '::before'],
['after', '::after'],
['selection', ' ::selection'],
['marker', '::marker'],
['file', '::file-selector-button'],
].map(key => Array.isArray(key) ? key : [key, `:${key}`]))

Expand All @@ -89,6 +87,11 @@ const PseudoClassFunctions = [
'has',
]

const PseudoClassesMulti: Record<string, string[]> = Object.fromEntries([
['selection', ['::selection', ' *::selection']],
['marker', ['::marker', ' *::marker']],
])

const PseudoClassesStr = Object.entries(PseudoClasses)
.filter(([, pseudo]) => !pseudo.startsWith('::'))
.map(([key]) => key)
Expand All @@ -100,6 +103,7 @@ const PseudoClassesColonStr = Object.entries(PseudoClassesColon)
.sort((a, b) => b.length - a.length)
.join('|')
const PseudoClassFunctionsStr = PseudoClassFunctions.join('|')
const PseudoClassesMultiStr = Object.keys(PseudoClassesMulti).sort((a, b) => b.length - a.length).join('|')

function taggedPseudoClassMatcher(tag: string, parent: string, combinator: string): VariantObject {
const rawRE = new RegExp(`^(${escapeRegExp(parent)}:)(\\S+)${escapeRegExp(combinator)}\\1`)
Expand Down Expand Up @@ -213,52 +217,80 @@ const PseudoClassesAndElementsColonStr = Object.entries(PseudoClassesColon)
.sort((a, b) => b.length - a.length)
.join('|')

export function variantPseudoClassesAndElements(): VariantObject {
export function variantPseudoClassesAndElements(): VariantObject[] {
let PseudoClassesAndElementsRE: RegExp
let PseudoClassesAndElementsColonRE: RegExp
return {
name: 'pseudo',
match(input, ctx) {
if (!(PseudoClassesAndElementsRE && PseudoClassesAndElementsRE)) {
PseudoClassesAndElementsRE = new RegExp(`^(${PseudoClassesAndElementsStr})(?:${ctx.generator.config.separators.join('|')})`)
PseudoClassesAndElementsColonRE = new RegExp(`^(${PseudoClassesAndElementsColonStr})(?:${ctx.generator.config.separators.filter(x => x !== '-').join('|')})`)
}
let PseudoClassesMultiRE: RegExp

const match = input.match(PseudoClassesAndElementsRE) || input.match(PseudoClassesAndElementsColonRE)
if (match) {
const pseudo = PseudoClasses[match[1]] || PseudoClassesColon[match[1]] || `:${match[1]}`
return [
{
name: 'pseudo',
match(input, ctx) {
if (!(PseudoClassesAndElementsRE && PseudoClassesAndElementsColonRE)) {
PseudoClassesAndElementsRE = new RegExp(`^(${PseudoClassesAndElementsStr})(?:${ctx.generator.config.separators.join('|')})`)
PseudoClassesAndElementsColonRE = new RegExp(`^(${PseudoClassesAndElementsColonStr})(?:${ctx.generator.config.separators.filter(x => x !== '-').join('|')})`)
}

// order of pseudo classes
let index: number | undefined = PseudoClassesKeys.indexOf(match[1])
if (index === -1)
index = PseudoClassesColonKeys.indexOf(match[1])
if (index === -1)
index = undefined
const match = input.match(PseudoClassesAndElementsRE) || input.match(PseudoClassesAndElementsColonRE)
if (match) {
const pseudo = PseudoClasses[match[1]] || PseudoClassesColon[match[1]] || `:${match[1]}`

// order of pseudo classes
let index: number | undefined = PseudoClassesKeys.indexOf(match[1])
if (index === -1)
index = PseudoClassesColonKeys.indexOf(match[1])
if (index === -1)
index = undefined

return {
matcher: input.slice(match[0].length),
handle: (input, next) => {
const selectors = (pseudo.includes('::') && !excludedPseudo.includes(pseudo))
? {
pseudo: `${input.pseudo}${pseudo}`,
}
: {
selector: `${input.selector}${pseudo}`,
}

return next({
...input,
...selectors,
sort: index,
noMerge: true,
})
},
}
}
},
multiPass: true,
autocomplete: `(${PseudoClassesAndElementsStr}|${PseudoClassesAndElementsColonStr}):`,
},
{
name: 'pseudo:multi',
match(input, ctx) {
if (!PseudoClassesMultiRE) {
PseudoClassesMultiRE = new RegExp(`^(${PseudoClassesMultiStr})(?:${ctx.generator.config.separators.join('|')})`)
}

return {
matcher: input.slice(match[0].length),
handle: (input, next) => {
const selectors = (pseudo.startsWith('::') && !excludedPseudo.includes(pseudo))
? {
pseudo: `${input.pseudo}${pseudo}`,
}
: {
selector: `${input.selector}${pseudo}`,
}

return next({
...input,
...selectors,
sort: index,
noMerge: true,
})
},
const match = input.match(PseudoClassesMultiRE)
if (match) {
const pseudos = PseudoClassesMulti[match[1]]
return pseudos.map((pseudo) => {
return {
matcher: input.slice(match[0].length),
handle: (input, next) => next({
...input,
pseudo: `${input.pseudo}${pseudo}`,
}),
}
})
}
}
},
multiPass: false,
autocomplete: `(${PseudoClassesMultiStr}):`,
},
multiPass: true,
autocomplete: `(${PseudoClassesAndElementsStr}|${PseudoClassesAndElementsColonStr}):`,
}
]
}

export function variantPseudoClassFunctions(): VariantObject {
Expand Down
3 changes: 1 addition & 2 deletions packages/preset-wind/src/postprocessors/important.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ export function important(option: PresetWindOptions['important']): Postprocessor

// handle pseudo
if (selector.includes('::'))
// eslint-disable-next-line regexp/no-super-linear-backtracking
return selector.replace(/(.*?)(\s*::.*)/, ':is($1)$2')
return selector.replace(/(.*?)((?:\s\*)?::.*)/, ':is($1)$2')

return `:is(${selector})`
}
Expand Down
6 changes: 4 additions & 2 deletions test/assets/output/preset-mini-targets.css
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,8 @@ unocss .scope-\[unocss\]\:block{display:block;}
.bg-teal-400\/\[\.55\]{background-color:rgb(45 212 191 / .55);}
.bg-teal-500\/\[55\%\]{background-color:rgb(20 184 166 / 55%);}
.first-letter\:bg-green-400::first-letter{--un-bg-opacity:1;background-color:rgb(74 222 128 / var(--un-bg-opacity));}
.marker\:bg-violet-200 *::marker,
.marker\:bg-violet-200::marker{--un-bg-opacity:1;background-color:rgb(221 214 254 / var(--un-bg-opacity));}
.first-line\:bg-green-400::first-line{--un-bg-opacity:1;background-color:rgb(74 222 128 / var(--un-bg-opacity));}
.peer-aria:checked~.peer-aria-checked\:bg-blue-500,
.peer:checked~.peer-checked\:bg-blue-500{--un-bg-opacity:1;background-color:rgb(59 130 246 / var(--un-bg-opacity));}
Expand All @@ -606,7 +608,6 @@ unocss .scope-\[unocss\]\:block{display:block;}
.hover\:is-first\:checked\:bg-true-gray\/10:checked:is(:first-child):hover{background-color:rgb(163 163 163 / 0.1);}
.hover\:not-first\:checked\:bg-red\/10:checked:not(:first-child):hover{background-color:rgb(248 113 113 / 0.1);}
.hover\:not-first\:checked\:bg-true-gray\/10:checked:not(:first-child):hover{background-color:rgb(163 163 163 / 0.1);}
.marker\:bg-violet-200::marker{--un-bg-opacity:1;background-color:rgb(221 214 254 / var(--un-bg-opacity));}
.file\:bg-violet-50::file-selector-button{--un-bg-opacity:1;background-color:rgb(245 243 255 / var(--un-bg-opacity));}
.bg-opacity-\[--opacity-variable\],
.bg-opacity-\$opacity-variable{--un-bg-opacity:var(--opacity-variable);}
Expand Down Expand Up @@ -816,13 +817,14 @@ unocss .scope-\[unocss\]\:block{display:block;}
.color-blue-gray\/10,
.color-bluegray-400\/10,
.color-bluegray\/10{color:rgb(148 163 184 / 0.1);}
.selection\:color-\[var\(--select-color\)\] *::selection,
.selection\:color-\[var\(--select-color\)\]::selection{color:var(--select-color);}
.open\:color-pink-100[open]{--un-text-opacity:1;color:rgb(252 231 243 / var(--un-text-opacity));}
.placeholder-shown-color-transparent:placeholder-shown{color:transparent;}
.in-range\:color-pink-100:in-range{--un-text-opacity:1;color:rgb(252 231 243 / var(--un-text-opacity));}
.out-of-range\:color-pink-100:out-of-range{--un-text-opacity:1;color:rgb(252 231 243 / var(--un-text-opacity));}
.placeholder-color-red-1::placeholder{--un-text-opacity:1;color:rgb(254 226 226 / var(--un-text-opacity));}
.placeholder\:color-transparent::placeholder{color:transparent;}
.selection\:color-\[var\(--select-color\)\] ::selection{color:var(--select-color);}
.color-inherit,
.text-inherit{color:inherit;}
.color-initial{color:initial;}
Expand Down
3 changes: 2 additions & 1 deletion test/assets/output/preset-wind-important-string.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 95a9f69

Please sign in to comment.