Skip to content

Commit 23b90e7

Browse files
authored
fix(rule-utils): keep -1 out of the tagged pseudo variant sort (#5339)
1 parent e63873f commit 23b90e7

4 files changed

Lines changed: 125 additions & 35 deletions

File tree

‎packages-presets/rule-utils/src/pseudo.ts‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,15 @@ export const PseudoClassesColon: Record<string, string> = Object.fromEntries([
8787

8888
export const PseudoClassesColonKeys = Object.keys(PseudoClassesColon)
8989

90+
// `indexOf` returns -1 for unknown names, which would otherwise be passed on as a real sort value
91+
// and hoist the rule above utilities that carry no sort at all.
92+
function getPseudoSortIndex(name: string): number | undefined {
93+
let index = PseudoClassesKeys.indexOf(name)
94+
if (index === -1)
95+
index = PseudoClassesColonKeys.indexOf(name)
96+
return index === -1 ? undefined : index
97+
}
98+
9099
export const PseudoClassFunctions = [
91100
'not',
92101
'is',
@@ -243,7 +252,7 @@ export function createTaggedPseudoClassMatcher<T extends object = object>(
243252
handle: (input, next) => next({
244253
...input,
245254
prefix: `${prefix}${combinator}${input.prefix}`.replace(rawRE, '$1$2:'),
246-
sort: PseudoClassesKeys.indexOf(pseudoName) ?? PseudoClassesColonKeys.indexOf(pseudoName),
255+
sort: getPseudoSortIndex(pseudoName),
247256
}),
248257
}
249258
},
@@ -285,11 +294,7 @@ export function createPseudoClassesAndElements<T extends object = object>(utils:
285294
}
286295

287296
// order of pseudo classes
288-
let index: number | undefined = PseudoClassesKeys.indexOf(match[1])
289-
if (index === -1)
290-
index = PseudoClassesColonKeys.indexOf(match[1])
291-
if (index === -1)
292-
index = undefined
297+
const index = getPseudoSortIndex(match[1])
293298

294299
return {
295300
matcher: input.slice(match[0].length),

‎packages-presets/rule-utils/test/pseudo.test.ts‎

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { PseudoVariantUtilities } from '../src/pseudo'
2-
import { createGenerator } from '@unocss/core'
2+
import { createGenerator, symbols } from '@unocss/core'
33
import { h } from '@unocss/preset-wind4/utils'
44
import { expect, it } from 'vitest'
55
import {
@@ -135,6 +135,91 @@ it('nested named groups containing hyphens', async () => {
135135
`)
136136
})
137137

138+
it('arbitrary tagged pseudo variant order', async () => {
139+
const uno = await createGenerator({
140+
variants: [
141+
...createTaggedPseudoClasses({}, utils),
142+
],
143+
rules: [
144+
[/^foo-(\d)$/, ([_, a]) => ({ text: `foo-${a}` })],
145+
],
146+
})
147+
148+
const css = await uno.generate([
149+
'foo-1',
150+
'group-hover:foo-2',
151+
'group-[.on]:foo-3',
152+
'group-not-[.off]:foo-4',
153+
'peer-[.on]:foo-5',
154+
]).then(r => r.css)
155+
156+
expect(css.indexOf('foo-1')).toBeLessThan(css.indexOf('foo-3'))
157+
expect(css.indexOf('foo-1')).toBeLessThan(css.indexOf('foo-4'))
158+
expect(css.indexOf('foo-1')).toBeLessThan(css.indexOf('foo-5'))
159+
expect(css.indexOf('foo-5')).toBeLessThan(css.indexOf('foo-2'))
160+
expect(css)
161+
.toMatchInlineSnapshot(`
162+
"/* layer: default */
163+
.foo-1{text:foo-1;}
164+
.group:not(.off) .group-not-\\[\\.off\\]\\:foo-4{text:foo-4;}
165+
.group.on .group-\\[\\.on\\]\\:foo-3{text:foo-3;}
166+
.peer.on~.peer-\\[\\.on\\]\\:foo-5{text:foo-5;}
167+
.group:hover .group-hover\\:foo-2{text:foo-2;}"
168+
`)
169+
})
170+
171+
it('arbitrary tagged pseudo variant keeps the rule sort', async () => {
172+
const uno = await createGenerator({
173+
variants: [
174+
...createTaggedPseudoClasses({}, utils),
175+
],
176+
rules: [
177+
// the declared sorts are the reverse of the alphabetical order of the tokens,
178+
// so only a rule sort that survives the variant can produce `foo-b` first
179+
[/^foo-(\w)$/, ([_, a]) => ({ [symbols.sort]: a === 'a' ? 20 : 10, color: `foo-${a}` })],
180+
],
181+
})
182+
183+
const css = await uno.generate([
184+
'group-[.on]:foo-a',
185+
'group-[.on]:foo-b',
186+
]).then(r => r.css)
187+
188+
expect(css.indexOf('foo-b')).toBeLessThan(css.indexOf('foo-a'))
189+
expect(css)
190+
.toMatchInlineSnapshot(`
191+
"/* layer: default */
192+
.group.on .group-\\[\\.on\\]\\:foo-b{color:foo-b;}
193+
.group.on .group-\\[\\.on\\]\\:foo-a{color:foo-a;}"
194+
`)
195+
})
196+
197+
it('colon-only pseudo class sorts by its own index', async () => {
198+
const uno = await createGenerator({
199+
variants: [
200+
...createPseudoClassesAndElements(utils),
201+
],
202+
rules: [
203+
[/^foo-(\w)$/, ([_, a]) => ({ [symbols.sort]: a === 'a' ? 20 : 10, color: `foo-${a}` })],
204+
],
205+
})
206+
207+
const css = await uno.generate([
208+
'backdrop:foo-a',
209+
'backdrop:foo-b',
210+
]).then(r => r.css)
211+
212+
// `backdrop` lives only in `PseudoClassesColon`, so finding its index there is what
213+
// keeps the pseudo order in charge instead of the rule sort
214+
expect(css.indexOf('foo-a')).toBeLessThan(css.indexOf('foo-b'))
215+
expect(css)
216+
.toMatchInlineSnapshot(`
217+
"/* layer: default */
218+
.backdrop\\:foo-a::backdrop{color:foo-a;}
219+
.backdrop\\:foo-b::backdrop{color:foo-b;}"
220+
`)
221+
})
222+
138223
it('pseudo class functions', async () => {
139224
const uno = await createGenerator({
140225
variants: [

‎test/assets/output/preset-mini-targets.css‎

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,6 @@
123123
.clear-both{clear:both;}
124124
.clear-none{clear:none;}
125125
.clear-revert{clear:revert;}
126-
.group:not([data-potato]) .group-not-\[\[data-potato\]\]\:m-1,
127-
.parent:not(#someId)>.parent-not-\[\#someId\]\:m-1,
128-
.peer:where(.child)~.peer-where-\[\.child\]\:m-1,
129-
.previous:is(div)+.previous-is-\[div\]\:m-1,
130-
.has-\[\:hover\]\:m-1:has(:hover){margin:0.25rem;}
131126
.-m-lg,
132127
.m--lg{margin:calc(var(--spacing-lg) * -1);}
133128
.-m-md,
@@ -143,6 +138,11 @@
143138
.children\:m-auto>*,
144139
.m-auto{margin:auto;}
145140
.all\:m1\/1 *{margin:100%;}
141+
.group:not([data-potato]) .group-not-\[\[data-potato\]\]\:m-1,
142+
.has-\[\:hover\]\:m-1:has(:hover),
143+
.parent:not(#someId)>.parent-not-\[\#someId\]\:m-1,
144+
.peer:where(.child)~.peer-where-\[\.child\]\:m-1,
145+
.previous:is(div)+.previous-is-\[div\]\:m-1{margin:0.25rem;}
146146
.m-\[3em\]{margin:3em;}
147147
.m-0,
148148
.m-none{margin:0;}
@@ -871,16 +871,6 @@ unocss .scope-\[unocss\]\:block{display:block;}
871871
.placeholder-color-opacity-60::placeholder{--un-text-opacity:0.6;}
872872
.as-parent .group .group-\[\.as-parent_\&\]\:font-13{font-weight:13;}
873873
.as-parent .group\/label .group-\[\.as-parent_\&\]\/label\:font-18{font-weight:18;}
874-
.group:hover .group-\[\:hover\]\:font-11{font-weight:11;}
875-
.group.not-parent .group-\[\.not-parent\]\:font-14{font-weight:14;}
876-
.group[data-attr] .group-\[\[data-attr\]\]\:font-12{font-weight:12;}
877-
.group\/label:hover .group-\[\:hover\]\/label\:font-16{font-weight:16;}
878-
.group\/label.not-parent .group-\[\.not-parent\]\/label\:font-19{font-weight:19;}
879-
.group\/label[data-attr] .group-\[\[data-attr\]\]\/label\:font-17,
880-
.group\/named[data-x=y] .group-data-\[x\=y\]\/named\:font-17,
881-
.parent\/named[data-x=y]>.parent-data-\[x\=y\]\/named\:font-17,
882-
.peer\/named[data-x=y]~.peer-data-\[x\=y\]\/named\:font-17,
883-
.previous\/named[data-x=y]+.previous-data-\[x\=y\]\/named\:font-17{font-weight:17;}
884874
.font-050,
885875
.font-50,
886876
.fw-050,
@@ -890,7 +880,17 @@ unocss .scope-\[unocss\]\:block{display:block;}
890880
.fw-900{font-weight:900;}
891881
.font-thin{font-weight:100;}
892882
.fw-inherit{font-weight:inherit;}
883+
.group:hover .group-\[\:hover\]\:font-11{font-weight:11;}
884+
.group.not-parent .group-\[\.not-parent\]\:font-14{font-weight:14;}
885+
.group[data-attr] .group-\[\[data-attr\]\]\:font-12{font-weight:12;}
893886
.group[data-state=open] .group-data-\[state\=open\]\:font-bold{font-weight:700;}
887+
.group\/label:hover .group-\[\:hover\]\/label\:font-16{font-weight:16;}
888+
.group\/label.not-parent .group-\[\.not-parent\]\/label\:font-19{font-weight:19;}
889+
.group\/label[data-attr] .group-\[\[data-attr\]\]\/label\:font-17,
890+
.group\/named[data-x=y] .group-data-\[x\=y\]\/named\:font-17,
891+
.parent\/named[data-x=y]>.parent-data-\[x\=y\]\/named\:font-17,
892+
.peer\/named[data-x=y]~.peer-data-\[x\=y\]\/named\:font-17,
893+
.previous\/named[data-x=y]+.previous-data-\[x\=y\]\/named\:font-17{font-weight:17;}
894894
.group\/named[aria-level="1"] .group-aria-\[level\=\"1\"\]\/named\:font-21,
895895
.parent\/named[aria-level="3"]>.parent-aria-\[level\=\"3\"\]\/named\:font-21,
896896
.peer\/named[aria-level="2"]~.peer-aria-\[level\=\"2\"\]\/named\:font-21{font-weight:21;}

‎test/assets/output/preset-wind4-targets.css‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -371,14 +371,6 @@
371371
.font-stretch-normal{font-stretch:normal;}
372372
.font-stretch-\[ultra-expanded\]{font-stretch:ultra-expanded;}
373373
.font-stretch-1\/2{font-stretch:50%;}
374-
.group:hover .group-\[\:hover\]\:font-11{--un-font-weight:11;font-weight:11;}
375-
.group[data-attr] .group-\[\[data-attr\]\]\:font-12{--un-font-weight:12;font-weight:12;}
376-
.as-parent .group .group-\[\.as-parent_\&\]\:font-13{--un-font-weight:13;font-weight:13;}
377-
.group.not-parent .group-\[\.not-parent\]\:font-14{--un-font-weight:14;font-weight:14;}
378-
.group\/label:hover .group-\[\:hover\]\/label\:font-16{--un-font-weight:16;font-weight:16;}
379-
.group\/label[data-attr] .group-\[\[data-attr\]\]\/label\:font-17{--un-font-weight:17;font-weight:17;}
380-
.as-parent .group\/label .group-\[\.as-parent_\&\]\/label\:font-18{--un-font-weight:18;font-weight:18;}
381-
.group\/label.not-parent .group-\[\.not-parent\]\/label\:font-19{--un-font-weight:19;font-weight:19;}
382374
.font-\[\"custom_fontFamily_name\"\]{font-family:"custom fontFamily name";}
383375
.font-\[custom-family-name\],
384376
.font-\[family\:custom-family-name\]{font-family:custom-family-name;}
@@ -388,6 +380,14 @@
388380
.font-\[number\:\$variable\]{--un-font-weight:var(--variable);font-weight:var(--variable);}
389381
.font-\[system-ui\]{font-family:system-ui;}
390382
.font-\$font-name{font-family:var(--font-name);}
383+
.group:hover .group-\[\:hover\]\:font-11{--un-font-weight:11;font-weight:11;}
384+
.group[data-attr] .group-\[\[data-attr\]\]\:font-12{--un-font-weight:12;font-weight:12;}
385+
.as-parent .group .group-\[\.as-parent_\&\]\:font-13{--un-font-weight:13;font-weight:13;}
386+
.group.not-parent .group-\[\.not-parent\]\:font-14{--un-font-weight:14;font-weight:14;}
387+
.group\/label:hover .group-\[\:hover\]\/label\:font-16{--un-font-weight:16;font-weight:16;}
388+
.group\/label[data-attr] .group-\[\[data-attr\]\]\/label\:font-17{--un-font-weight:17;font-weight:17;}
389+
.as-parent .group\/label .group-\[\.as-parent_\&\]\/label\:font-18{--un-font-weight:18;font-weight:18;}
390+
.group\/label.not-parent .group-\[\.not-parent\]\/label\:font-19{--un-font-weight:19;font-weight:19;}
391391
.font-550{--un-font-weight:550;font-weight:550;}
392392
.font-inherit{font-family:inherit;}
393393
.font-mono{font-family:var(--font-mono);}
@@ -418,16 +418,16 @@
418418
.text-shadow-color-op-30{--un-text-shadow-opacity:30%;}
419419
.text-shadow-color-op-50,
420420
.text-shadow-op-50{--un-text-shadow-opacity:50%;}
421-
.group:not([data-potato]) .group-not-\[\[data-potato\]\]\:m-1,
422-
.parent:not(#someId)>.parent-not-\[\#someId\]\:m-1,
423-
.peer:where(.child)~.peer-where-\[\.child\]\:m-1,
424-
.previous:is(div)+.previous-is-\[div\]\:m-1,
425-
.has-\[\:hover\]\:m-1:has(:hover){margin:calc(var(--spacing) * 1);}
426421
.-m-lg,
427422
.m--lg{margin:calc(var(--spacing-lg) * -1);}
428423
.m-\[3em\]{margin:3em;}
429424
.\!m-\$c-m{margin:var(--c-m) !important;}
430425
.m-0{margin:calc(var(--spacing) * 0);}
426+
.group:not([data-potato]) .group-not-\[\[data-potato\]\]\:m-1,
427+
.has-\[\:hover\]\:m-1:has(:hover),
428+
.parent:not(#someId)>.parent-not-\[\#someId\]\:m-1,
429+
.peer:where(.child)~.peer-where-\[\.child\]\:m-1,
430+
.previous:is(div)+.previous-is-\[div\]\:m-1{margin:calc(var(--spacing) * 1);}
431431
.m-1\/2{margin:50%;}
432432
.\[\&\:nth-child\(2\)\]\:m-10:nth-child(2){margin:calc(var(--spacing) * 10);}
433433
.\[\&\>\*\]\:m-11>*{margin:calc(var(--spacing) * 11);}

0 commit comments

Comments
 (0)
Sponsor
SponsoredKunjungi sekarang
Promo