Skip to content
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

fix(merge-rules): avoid merging conflicting borders #1450

Merged
merged 3 commits into from Feb 15, 2023

Conversation

ludofischer
Copy link
Collaborator

@ludofischer ludofischer commented Nov 4, 2022

The previous logic assumed properties did not conflict if their
names were not equal but contained the same number of dash-separated parts.
This works for margin and padding but does not work for border,
because for example border-left and border-color can conflict.

This change prevents a valid optimization when the conflicting properties
re-declared the same value (for example border-width: 10px and
border: 10px solid blue), but that's the same as for other properties.

Related to #1000 and #701

I think a similar problem is going to happen for place-items and align-items or justify-items since they conflict but escape the current conflict detection algorithm (it assumes no conflict if the names property names start with a different word).

return true;
}
const nextConflictInIntersection = intersection
.slice(intersectIndex + 1)
.filter((d) => isConflictingProp(d.prop, decl.prop));
if (!nextConflictInIntersection.length) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Next condition ensures that nextConflictInIntersection.length !== 0 since already nextConflictInFirst.length !== 0, but in fact it even does not matter, as long as the number of conflicts is the same we can still hope the merge will work.

// Conflict if rest-count mismatches
if (a.rest.length !== b.rest.length) {
return true;
}

/* Do not merge conflicting border properties */
if (a.base === 'border') {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this function uses some non-obvious terminology. base is the first part of the property name excluding the vendor prefix. rest all the rest of the name.
I could not find an alternative to using a special case, CSS syntax is just not that regular. I think in fact conflict if count mismatches or if every part is the same is only true for the margin- and padding- properties.

Copy link
Member

@alexander-akait alexander-akait left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am fine with it, but I am afraid it will not fix all possible cases, I think we need rewrite logic, I want to implement this in swc too, so in theory we can rewrite the whole logic together (it will be just different languages, but logic will be same)

@ludofischer
Copy link
Collaborator Author

I am fine with it, but I am afraid it will not fix all possible cases, I think we need rewrite logic, I want to implement this in swc too, so in theory we can rewrite the whole logic together (it will be just different languages, but logic will be same)

I would rather rewrite the logic when we drop Node 12 and 14 and release 6.0, this is fix for people who will remain on 5.x. Do you think there's a more general method to merge the rules? I think you can likely do more than merging only groups of adjacent two rules, but you still need to make special cases for the properties with different names but can override each other

@ludofischer
Copy link
Collaborator Author

'Evil' case:

.a { place-content: center; justify-content: start; } .b { justify-content: start; place-content: center; }

@codecov-commenter
Copy link

codecov-commenter commented Nov 4, 2022

Codecov Report

Base: 97.61% // Head: 97.62% // Increases project coverage by +0.00% 🎉

Coverage data is based on head (ea403e2) compared to base (773cfb6).
Patch coverage: 100.00% of modified lines in pull request are covered.

❗ Current head ea403e2 differs from pull request most recent head 1e5e63a. Consider uploading reports for the commit 1e5e63a to get more accurate results

📣 This organization is not using Codecov’s GitHub App Integration. We recommend you install it so Codecov can continue to function properly for your repositories. Learn more

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1450   +/-   ##
=======================================
  Coverage   97.61%   97.62%           
=======================================
  Files         122      122           
  Lines        9999    10010   +11     
=======================================
+ Hits         9761     9772   +11     
  Misses        238      238           
Impacted Files Coverage Δ
packages/postcss-merge-rules/src/index.js 97.18% <100.00%> (+0.07%) ⬆️

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@ludofischer
Copy link
Collaborator Author

I've added a fix for the place- justify align- conflict.

@alexander-akait
Copy link
Member

@ludofischer

I would rather rewrite the logic when we drop Node 12 and 14 and release 6.0, this is fix for people who will remain on 5.x. Do you think there's a more general method to merge the rules? I think you can likely do more than merging only groups of adjacent two rules, but you still need to make special cases for the properties with different names but can override each other

Yeah, I am fine with it after drop Node 12 and Node 14

.a { place-content: center; justify-content: start; } .b { justify-content: start; place-content: center; }

I think we should order them before merge, and if reorder is failed (i.e. keeped) we just don't merge them, because I found our partial logic is rely on things which should not, for perf reason, we can reorder only potential to merge candidates

@ludofischer
Copy link
Collaborator Author

I think we should order them before merge, and if reorder is failed (i.e. keeped) we just don't merge them, because I found our partial logic is rely on things which should not, for perf reason, we can reorder only potential to merge candidates

Does this reorder logic exist some inside cssnano? What I did was detecting if one of the properties was place-*, then don't merge. ea403e2#diff-106bd8df0f811c5c47fcb5841936b1a954ea692da68c1b246384bec798328d5fR168 From looking at the list of all shorthands, I think place is the only case where a property can override another property which starts with a different word. But yes, it is kind of a hack.

@alexander-akait
Copy link
Member

Does this reorder logic exist some inside cssnano? What I did was detecting if one of the properties was place-*, then don't merge.

css-declaration-sorter should do it, even if developer disable use it directly, we can create fake root with required properties and sort them and then check it

I think if we move it https://github.com/cssnano/cssnano/blob/master/packages/cssnano-preset-default/src/index.js#L124 before merge-rules we will fix most of cases

@ludofischer
Copy link
Collaborator Author

I think if we move it https://github.com/cssnano/cssnano/blob/master/packages/cssnano-preset-default/src/index.js#L124 before merge-rules we will fix most of cases

I see. What do you want to do with the current PR? Merge it for 5.x or just leave it out?

@alexander-akait
Copy link
Member

alexander-akait commented Nov 7, 2022

@ludofischer Let's add TODO/create an issue about it to not lose context and we can merge as patch for fast fix

* add function return type
* explicitly check array size
* We've already checked that nextConflictInFirst is not 0,
so we only care if the number of conflicts in the intersection
is different.
The previous logic assumed properties did not conflict if their
names were not equal but contained the same number of dash-separated parts.
This works for margin and padding but does not work for border,
because for example border-left and border-color can conflict.

This change prevents a valid optimization when the conflicting properties
re-declared the same value (for example border-width: 10px and
border: 10px solid blue), but that's the same as for other properties.
`place` conflicts with `justify` and `align`, even if they don't start
with the same property.
@ludofischer
Copy link
Collaborator Author

I have create an issue for a better fix as you've suggested: #1466 Do you think we can merge this? Then I can make a new release with the other fixes that have been contributed recently.

@alexander-akait
Copy link
Member

@ludofischer yeah, let's do it 👍

@ludofischer ludofischer merged commit b69b8e9 into master Feb 15, 2023
@ludofischer ludofischer deleted the merge-rules-refactor branch February 15, 2023 20:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants