fix(rule-utils): keep -1 out of the tagged pseudo variant sort - #5339
Merged
Merged
Conversation
`createTaggedPseudoClassMatcher` computed its sort with `PseudoClassesKeys.indexOf(name) ?? PseudoClassesColonKeys.indexOf(name)`. `indexOf` returns -1 rather than a nullish value, so the right-hand side was dead and the -1 miss value was handed on as a real sort. For `group-[...]`, `peer-[...]`, `parent-[...]`, `previous-[...]` and the `*-not-[...]` forms there is no pseudo class name at all, so the sort was always -1. That hoisted those utilities out of the alphabetical order their dynamic rule is documented to use, and, because -1 is not nullish, it also replaced the rule's own `symbols.sort` in `applyVariants`. Reuse the normalisation `createPseudoClassesAndElements` already performs, so a miss becomes `undefined` and the sort falls back to the rule's own value.
✅ Deploy Preview for unocss ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
commit: |
hannoeru
approved these changes
Sep 13, 2026
zyyv
approved these changes
Sep 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
createTaggedPseudoClassMatcherbuilds the variantsortwithindexOfreturns-1on a miss, nevernull/undefined, so??never falls through: thesecond lookup is dead code and the
-1is passed on as if it were a real sort index.The bracket form (
group-[...]) and the pseudo var form (group-not-[...]) carry no pseudoclass name at all (
pseudoNamedefaults to''), so their sort is always-1. Two thingsfollow:
-1sorts ahead of every utility that has no sort, so those variants are lifted out of thealphabetical order a dynamic rule is documented to use ("the output of those matched under a
single dynamic rule will be sorted alphabetically within the group").
-1is not nullish, so atsort: variantSort ?? metaSortit also replaces the rule's ownsymbols.sort. A rule that orders its own tokens loses that order entirely under anarbitrary tagged variant, and since the selectors involved have identical specificity the
emitted order is what decides the cascade.
Affects
group-[...],peer-[...],parent-[...],previous-[...]and the*-not-[...],*-is-[...],*-where-[...]forms inpreset-mini,preset-wind3andpreset-wind4.Reproduction
preset-wind4, withfont-11 font-12 font-13 font-14 font-16 font-17 font-18 font-19written asgroup-[:hover]:font-11,group-[[data-attr]]:font-12,group-[.as-parent_&]:font-13,group-[.not-parent]:font-14,group-[:hover]/label:font-16,group-[[data-attr]]/label:font-17,group-[.as-parent_&]/label:font-18,group-[.not-parent]/label:font-19, alongside the plainfont-[system-ui] font-$font-name font-550 font-mono font-sans font-thin(these are already intest/assets/preset-wind4-targets.ts).Produced now, all eight arbitrary variants jump to the head of the
font-group:Expected, and what this PR generates, is the alphabetical position inside the group:
The same shift shows up in
preset-mini, where it moves two rules of equal specificity past eachother:
.group\/label[data-attr] .group-\[\[data-attr\]\]\/label\:font-17used to be emittedbefore
.group[data-state=open] .group-data-\[state\=open\]\:font-bold, even though thealphabetical order of the two selectors puts
font-boldfirst. Both are(0,3,0)and both setfont-weight, so the emitted order is what wins.Fix
createPseudoClassesAndElementsa few lines below already normalises the same lookup(
indexOfto-1toundefined). This extracts that intogetPseudoSortIndexand uses it onboth paths, so a miss yields
undefinedand the sort falls back to the rule's own value. Knownpseudo class names keep the index they have today.
Tests
Three cases in
packages-presets/rule-utils/test/pseudo.test.ts:arbitrary tagged pseudo variant order:foo-1,group-hover:foo-2,group-[.on]:foo-3,group-not-[.off]:foo-4,peer-[.on]:foo-5from one dynamic rule. Assertsfoo-1is emittedbefore the three arbitrary variants, and that
group-hoverstill sorts by its pseudo index.Fails on
mainwithexpected 170 to be less than 106.arbitrary tagged pseudo variant keeps the rule sort: one rule whosesymbols.sortvalues arethe reverse of the tokens' alphabetical order, so the assertion can only pass if the rule sort
survives the variant. On
mainthe two rules come out alphabetically instead.colon-only pseudo class sorts by its own index: pins thePseudoClassesColonKeysbranch ofthe shared helper through
backdrop:, which exists only in that map.Two snapshot files (
preset-mini-targets.css,preset-wind4-targets.css) move the affected rulesinto their alphabetical position; the diff is ordering only, no rule gained, lost or changed a
declaration.
pnpm build && pnpm test: 730 passed, 3 skipped, up from 727 passed, 3 skipped onmain, noother test changed.
pnpm typecheckandeslintclean.Note
#5332 edits the
prefix:line directly above the one changed here, so the two will conflicttextually if both land. They are independent fixes and either rebase is a one line resolution.