-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Use amount of properties when sorting #16715
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
3be98da to
74799f6
Compare
packages/tailwindcss/src/compile.ts
Outdated
| break | ||
| count++ | ||
|
|
||
| if (!seenTwSort) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We were using a break before once we noticed a --tw-sort, but now we want to make sure we count all properties. So once we notice a --tw-sort we don't want to collect new properties in the order set anymore, but we still want to keep walking to count other properties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we invert this condition so we reduce the level of nesting here?
| if (!seenTwSort) { | |
| if (teenTwSort) continue; |
philipp-spiess
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM but I'm confused why two utilities got reordered in the tests even though they set the same number of properties
packages/tailwindcss/src/compile.ts
Outdated
| break | ||
| count++ | ||
|
|
||
| if (!seenTwSort) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we invert this condition so we reduce the level of nesting here?
| if (!seenTwSort) { | |
| if (teenTwSort) continue; |
| --tw-translate-y: 1px; | ||
| translate: var(--tw-translate-x) var(--tw-translate-y); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused why the order here changed, didn't both classes define the same number of properties before? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is related to the bug we had originally where only known properties are counted, but we have to think if this is what we want though 🤔
So, if you look at the the property-order file, you see this:
'translate',
'--tw-translate-x',
'--tw-translate-y',
and --tw-translate-z doesn't exist in that list (we can add it though).
This means that even though we have 2 properties, in the previous code this would result in a sort order of [53] for the translate-z-* class (53 is the order of the translate property).
And for translate-y-* it would result in [53, 55] where 55 is the order of the --tw-translate-y property.
In the old code, that also means that the translate-z-* class counted 1 property, and the translate-y-* counted 2 properties, therefore it is sorted this way.
In the new code, this still results in [53, 55] and [53] but now we track the count which is 2 in both cases.
Another thing that already existed is that we only compare those numbers up to where they have the same length. So in both scenarios that would be comparing 53. Then we compare the property count, and then we compare alphabetically.
So previously we would compare the property count which was 1 vs 2, now they are both 2 and therefore we fallback to the alphabetical sort where the negative -translate-z is sorted before the other one.
The current code handling all of that:
astNodes.sort((a, z) => {
// SAFETY: At this point it is safe to use TypeScript's non-null assertion
// operator because if the ast nodes didn't exist, we introduced a bug
// above, but there is no need to re-check just to be sure. If this relied
// on pure user input, then we would need to check for its existence.
let aSorting = nodeSorting.get(a)!
let zSorting = nodeSorting.get(z)!
// Sort by variant order first
if (aSorting.variants - zSorting.variants !== 0n) {
return Number(aSorting.variants - zSorting.variants)
}
// Find the first property that is different between the two rules
let offset = 0
while (
aSorting.properties.order.length < offset &&
zSorting.properties.order.length < offset &&
aSorting.properties.order[offset] === zSorting.properties.order[offset]
) {
offset += 1
}
return (
// Sort by lowest property index first
(aSorting.properties.order[offset] ?? Infinity) -
(zSorting.properties.order[offset] ?? Infinity) ||
// Sort by most properties first, then by least properties
zSorting.properties.count - aSorting.properties.count ||
// Sort alphabetically
compare(aSorting.candidate, zSorting.candidate)
)
})Looking at the count/order result, if I add the --tw-translate-z to the property-order list, then this would be the result:
--tw-translate-x: 1px;
2 [ 53, 54 ]
--tw-translate-y: 1px;
2 [ 53, 55 ]
--tw-translate-z: calc(var(--value) * -1);
2 [ 53, 56 ]
Where 2 is the count of properties, and the array is the sorted list based on the property order.
So uhm, I think this code is completely wrong:
// Find the first property that is different between the two rules
let offset = 0
while (
aSorting.properties.order.length < offset &&
zSorting.properties.order.length < offset &&
aSorting.properties.order[offset] === zSorting.properties.order[offset]
) {
offset += 1
}Because the offset will start at 0, so the aSorting.properties.order.length and zSorting.properties.order.length will never be less than 0.
Thanks for being my rubber ducky! 😅
Complete sidenote: this test is wrong because this is the translate-z-* test and we use translate-y-* 😅
| .font-sans { | ||
| font-family: var(--font-sans); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also wouldn't expect this moving.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aha! I accidentally counted properties that were set to undefined | null. So we counted 3 for this guy (font-family, font-feature-settings, font-variation-settings)
| ".translate-y-px { | ||
| --tw-translate-y: 1px; | ||
| translate: var(--tw-translate-x) var(--tw-translate-y); | ||
| expect(await run(['translate-z-px', '-translate-z-[var(--value)]'])).toMatchInlineSnapshot(` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the correct translate-z-px instead. (previous commits also show the fix for the order change)
| 'translate', | ||
| '--tw-translate-x', | ||
| '--tw-translate-y', | ||
| '--tw-translate-z', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was missing, but we do have it for --tw-scale-z and --tw-rotate-z so I figured why don't we just add this.
| translate: var(--tw-translate-x) var(--tw-translate-y) var(--tw-translate-z); | ||
| translate: var(--tw-translate-x) var(--tw-translate-y) var(--tw-translate-z); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to this PR but we found translate-z-px emits two css classes right now. Needs a follow-up fix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR: #16718
| container: sidebar / size; | ||
| } | ||
| .\\@container-\\[size\\] { | ||
| container-type: size; | ||
| .\\@container-normal\\/sidebar { | ||
| container: sidebar; | ||
| } | ||
| .\\@container-\\[size\\]\\/sidebar { | ||
| container: sidebar / size; | ||
| .\\@container\\/sidebar { | ||
| container: sidebar / inline-size; | ||
| } | ||
| .\\@container-normal { | ||
| container-type: normal; | ||
| .\\@container { | ||
| container-type: inline-size; | ||
| } | ||
| .\\@container-normal\\/sidebar { | ||
| container: sidebar; | ||
| .\\@container-\\[size\\] { | ||
| container-type: size; | ||
| } | ||
| .\\@container\\/sidebar { | ||
| container: sidebar / inline-size; | ||
| .\\@container-normal { | ||
| container-type: normal; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah nice so this previously counted container and container-type as the same but isn't doing that anymore. Neat!
Right now we sort the nodes based on a pre-defined sort order based on the properties that are being used. The property sort order is defined in a list we maintain.
We also have to make sure that the property count is taken into account such that if all the "sorts" are the same, that we fallback to the property count. Most amount of properties should be first such that we can override it with more specific utilities that have fewer properties.
However, if a property doesn't exist, then it wouldn't be included in a list of properties therefore the total count was off.
This PR fixes that by counting all the used properties. If a property already exists it is counted twice. E.g.:
In this case, we have 2 properties, not 1 even though it's the same
colorproperty.Test plan:
prose-invertis defined after theprose-stoneclasses when using the@tailwindcss/typographyplugin where this problem originated from.Note how in this play (https://play.tailwindcss.com/wt3LYDaljN) the
prose-invertcomes before theprose-stonewhich means that you can't apply theprose-invertclasses to invertprose-stone.