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

Make css-declaration-sorter safer #580

Closed
wants to merge 9 commits into from
Closed

Make css-declaration-sorter safer #580

wants to merge 9 commits into from

Conversation

Siilwyn
Copy link
Contributor

@Siilwyn Siilwyn commented Aug 11, 2018

Don't change the order of short- & longhand properties with the exception of margin,
padding & border since these are handled by the mergeLonghand optimisation.

Closes #535

@zanettin
Copy link

any estimates, when this get merged? 👼

@alexander-akait
Copy link
Member

/cc @Siilwyn what is status?

@Siilwyn
Copy link
Contributor Author

Siilwyn commented Sep 2, 2018

Still in progress, mostly doing local stuff. Will try to push more often. Though I'm also thinking of implementing it at the module instead of here.

@zanettin
Copy link

thanks for your work on this. the "new" sort logic blocks us from upgrading to postcss v7 because the working version of cssnano (v3.10) relys on a fn which is no longer available on postcss v7.

@coveralls
Copy link

coveralls commented Sep 16, 2018

Coverage Status

Coverage increased (+0.0005%) to 98.559% when pulling 3025267 on Siilwyn:safer-sorting into cd94901 on cssnano:master.

@Siilwyn Siilwyn changed the title WIP: make css-declaration-sorter safer Make css-declaration-sorter safer Sep 16, 2018
@Siilwyn
Copy link
Contributor Author

Siilwyn commented Sep 16, 2018

@andyjansson ready for review! :)

@Siilwyn Siilwyn mentioned this pull request Sep 16, 2018
'sorts both short- and longhand properties',
processCSS,
'h1{animation-name:a;animation:b}',
'h1{animation:b;animation-name:a}',
Copy link
Contributor

Choose a reason for hiding this comment

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

This is wrong. The code needs to respect precedence. You can't just sort based on the relationship between the properties (what's a longhand or a shorthand of what), but you need to respect the order in which they are declared.

@@ -49,6 +51,9 @@ const defaultOpts = {
normalizeCharset: {
add: false,
},
cssDeclarationSorter: {
customOrder: path.join(__dirname, 'safe-order.json'),
Copy link
Contributor

Choose a reason for hiding this comment

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

The declarations cannot be sorted based on a sort order defined in a json file. They need to respect the order of declaration in the CSS, else the precedence will be different and the CSS will render differently in the browser.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note that I'm leaving out the declarations that need to be kept in their original order, so this will work. Do you have an example that will fail?

processCSS,
'h1{animation-name:a;z-index:0;animation:b;color:0;}',
'h1{animation-name:a;animation:b;color:0;z-index:0}'
);
Copy link
Contributor

Choose a reason for hiding this comment

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

We need a lot more tests here.

Copy link
Contributor Author

@Siilwyn Siilwyn Sep 16, 2018

Choose a reason for hiding this comment

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

Could you provide a bit more descriptive feedback? Like what should be tested & what kind of tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

We need a range of tests to ensure that the module functions as expected, and to detect any future regressions. I expect tests covering cases where declarations should be rearranged, cases where they shouldn't, and cases with declarations of mixed precedence.

Copy link
Contributor Author

@Siilwyn Siilwyn Sep 16, 2018

Choose a reason for hiding this comment

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

The first test covers "cases where they shouldn't", the second has mixed precedence and declarations that should be rearranged right? I can split up the 2nd test to clarify the difference, that might be better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry but I still don't get what you're after. Both tests cover the cases you mentioned earlier.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm after more tests so we can have confidence in the feature. We need tests to capture its function and any edge cases that might lurk about. Two test cases aren't nearly enough for that.

Copy link
Contributor

Choose a reason for hiding this comment

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

@Siilwyn when can you add more tests?

\cc @evilebottnawi

Copy link
Member

Choose a reason for hiding this comment

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

Yep need more tests, example with border/padding/margin, also all tests from related issues and prefixes

Copy link
Contributor Author

@Siilwyn Siilwyn Sep 17, 2018

Choose a reason for hiding this comment

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

Once all other comments and questions are resolved I'll add tests for the border/padding/margin. 👍

"all tests from related issues and prefixes", there is a test for the prefixing, any related issues I need to be aware of?

@@ -18,13 +18,13 @@ th{font-weight:700;text-align:left}
td,th{line-height:inherit;padding:.25rem 1rem}
th{vertical-align:bottom}
td{vertical-align:top}
body{font-size:100%;line-height:1.5}
body{line-height:1.5;font-size:100%}
Copy link
Contributor

Choose a reason for hiding this comment

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

Some strange reordering of declarations in all integration tests. Shouldn't these be grouped alphabetically?

Copy link
Contributor Author

@Siilwyn Siilwyn Sep 16, 2018

Choose a reason for hiding this comment

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

No, since font-size could conflict with font it is not being moved. (Check my comment on the issue too: "For now keep all properties that have a shorthand in the same order")

Copy link
Contributor

Choose a reason for hiding this comment

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

What do you mean? There' s no font declaration here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It doesn't look at a selector by selector basis, the way this PR makes it safer is by not sorting properties that could conflict at all.

Copy link
Contributor

Choose a reason for hiding this comment

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

I really don't know what you mean, and there's no tests to infer meaning from either. Can you expand on what you're saying?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah sorry I missed this reply.

I'll add an extra test, what I mean is that any property that has a conflicting property will not be sorted.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand what you mean by conflicting. Conflicting with what? The functionality, as it stands, is completely opaque and non-intuitive. The tests aren't communicating its behaviour, and that means we can't have any confidence in its result.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Clarified this behaviour with a test: move properties that are not in safe-order.json to the start and sort remaining. Properties that are not 'safe', in other words: properties that have a shorthand/longhand are moved to the start. What I meant with conflicting is for a value that can be changed by multiple properties such as the font-size in this example because it can also be changed with font. These properties 'conflict', maybe it's a bad word to describe this, I'm not a native speaker. :)

@zanettin
Copy link

thanks @Siilwyn for all your work on this ❤️ just one tiny input about sorting (not sure if you already covered this case), but in the current version of cssnano, all vendor prefixed properties are pushed to the beginning which breaks some code of us:

working code:

background: red;
 -webkit-background-clip: text;

broken after sorting:

 -webkit-background-clip: text;
background: red;

would be very nice, if you could make sure that this works as well ❤️ and again - thank you very much!!!

@Siilwyn
Copy link
Contributor Author

Siilwyn commented Sep 16, 2018

Thank you for your reply @zanettin your reply is much appreciated. :)

This is covered by this PR, sharp! I'll add a test to clarify this.

@@ -9,6 +9,9 @@ const defaultOpts = {
autoprefixer: {
add: false,
},
cssDeclarationSorter: {
customOrder: null,
Copy link
Contributor

Choose a reason for hiding this comment

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

Explain your reasoning behind having a different sorting order to the advanced preset and the consequences it'll have.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The advanced order can make unsafe changes to save more bytes, so it orders everything.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's not really what the advanced preset is about, though. See the website on 'advanced transforms' for more info.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From the advanced transform page: "Certain optimizations are not suitable for all use cases; unlike those that we bundle by default, advanced transforms all carry a certain amount of risk.". This exactly falls into this category. When using legacy code with double properties that override each other sorting everything comes with a certain amount of risk.

Copy link
Contributor

Choose a reason for hiding this comment

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

The risk with the advanced preset comes from mixing sources. When working within the same stylesheet, it should be safe. The advanced preset is not "bug mode".

Copy link
Contributor Author

@Siilwyn Siilwyn Sep 18, 2018

Choose a reason for hiding this comment

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

It does not only come from mixing sources, to be specific quoting the same page:

This transform changes CSS semantically; it may remove styles based on certain characteristics of the CSS, or it might update values to make the CSS smaller overall. Depending on exactly what the transform does, this may be undesirable because it might change the appearance of your website in certain use cases.

Is what this optimization falls under, I think reducing the size here further is worth the tiny risk of breaking legacy code which other optimizations in this preset also do.

Thoughts @ai @evilebottnawi ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Certain optimizations are not suitable for all use cases [...] we've documented the assumptions that it must make about your CSS in order for the transform to be safe. [...] these transforms fall into either or both of categories.

Assumes concatenation
[...] assumes that the CSS passed through cssnano is all that is needed for a website to run [...]
may be problematic if the styles you are writing in some way interact with third party styles, or you are using multiple stylesheets instead of concatenating them

Changes semantics

may remove styles based on certain characteristics of the CSS, or it might update values to make the CSS smaller overall. [...] this may be undesirable [...]
A good example is autoprefixer

What you are going is against the spirit of the preset. It has clear qualifiers on what it takes to use the advanced preset safely. It's not supposed to be a roll of the dice on whether it works or not. Please respect that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's not a "roll of the dice" though, it is not safe when the CSS contains conflicting properties in one selector. I don't see how this is different from e.g. autoprefixer that removes outdated properties that is unsafe for CSS that contains outdated properties.

Copy link
Contributor

Choose a reason for hiding this comment

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

it is not safe when the CSS contains conflicting properties in one selector.

Yeah, we can't have that. It's a super-common thing in CSS to have properties that affect the same space. Bringing this into the advanced preset would destroy any use case it has, rendering it useless.

Copy link
Contributor Author

@Siilwyn Siilwyn Sep 18, 2018

Choose a reason for hiding this comment

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

So being not super-common it would be useful... Is it super-common? For one, this issue took a while to surface which I think partly indicates this is uncommon. Secondly as far as I know CSS selectors normally do not contain properties that override each other, it's usually a mistake, e.g:

animation: bla;
animation-name: bla;

The animation property also contains the name so instead of putting animation-name after it normally I would just change the name in the animation shorthand value.

I understand your concern though, this at least should be clarified in the docs. It comes down to if the benefit outweighs the risk, if it is common as you said then I see how it is not worth it. For me seeing CSS like this is pretty new, however I've also only been a front-end dev for 5 years and mainly worked with newish codebases.

@@ -76,7 +76,7 @@ test(
h2, h1 {
font-weight: 400;
}`,
`h1,h2{color:red;font-weight:400}`
`h1,h2{font-weight:400;color:red}`
Copy link
Member

Choose a reason for hiding this comment

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

Why it is happens?

Copy link
Contributor Author

@Siilwyn Siilwyn Sep 22, 2018

Choose a reason for hiding this comment

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

Clarified this behaviour with a test: move properties that are not in safe-order.json to the start and sort remaining in bfe1257. Properties that are not 'safe', in other words properties that have a shorthand/longhand are moved to the start.

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.

The main idea of this plugin is to maximize the alphabetical order to better gzip, here i find many strange cases where we broke alphabetical order without reasons. Maybe we need use better logic here.


```js
{
customOrder: null
Copy link
Member

Choose a reason for hiding this comment

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

Don't think what we need this option, no one will constantly keep their list, it's too time consuming and takes a lot of time and effort, let's remove it from here

Copy link
Contributor Author

@Siilwyn Siilwyn Sep 17, 2018

Choose a reason for hiding this comment

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

What do you mean? It's there so that the advanced preset does full sorting resulting in more optimization. Note that this is generated documentation too, no way to hide it.

@@ -4,7 +4,7 @@
"main": "dist/index.js",
"description": "Safe defaults for cssnano which require minimal configuration.",
"scripts": {
"prepublish": "cross-env BABEL_ENV=publish babel src --out-dir dist --ignore /__tests__/"
"prepublish": "cross-env BABEL_ENV=publish babel src --out-dir dist --ignore /__tests__/ --copy-files"
Copy link
Member

Choose a reason for hiding this comment

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

Why it is here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So that the custom order will get copied, babel normally ignores JSON files.

processCSS,
'h1{animation-name:a;z-index:0;animation:b;color:0;}',
'h1{animation-name:a;animation:b;color:0;z-index:0}'
);
Copy link
Member

Choose a reason for hiding this comment

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

Yep need more tests, example with border/padding/margin, also all tests from related issues and prefixes

@Siilwyn
Copy link
Contributor Author

Siilwyn commented Sep 17, 2018

@evilebottnawi please read my last replies on the related issue, this PR uses simple ordering for now. As you see in some cases it can't order alphabetically, this is intended.

@ai
Copy link
Member

ai commented Sep 18, 2018

The main idea of this plugin is to maximize the alphabetical order to better gzip, here i find many strange cases where we broke alphabetical order without reasons. Maybe we need use better logic here.

Unfortunately, it doesn’t work :(. We have an issue with this sorter right now in our current project (temporarily fixed with !important).

To be honest, this issue is really a high priority and I recommend to fix it ASAP.

@ai
Copy link
Member

ai commented Sep 18, 2018

Maybe we should disable the rule, release a fix and then continue to discuss?

CSS minifier must be safe. People should trust it. So it is very important to fix safety problems ASAP.

@andyjansson
Copy link
Contributor

@ai merging #599 would disable css-declaration-sorter.

@evilebottnawi @ai should we proceed with this course of action?

@ai
Copy link
Member

ai commented Sep 19, 2018

@andyjansson sorry, what do you mean about course of actions? (I am very bad in language ileomes)

@andyjansson
Copy link
Contributor

@ai whether we should proceed with disabling css-declaration-sorter

@ai
Copy link
Member

ai commented Sep 19, 2018

I think we should disable this plugin at least for default preset

@Siilwyn
Copy link
Contributor Author

Siilwyn commented Sep 19, 2018

@ai do you still have this issue with the changes made in this PR?

If not, we can merge it with safe sorting in default and advanced, and continue the discussion about the advanced preset after since you want to fix it ASAP. 🐎

@andyjansson thoughts?

@ai
Copy link
Member

ai commented Sep 19, 2018

@Siilwyn yeap, accepting this PR is an option too

@andyjansson please, could we release it faster. It is a few months until the issue got reported. It is critical. We need to fix it ASAP. Disable plugin or accept this PR.

@Siilwyn
Copy link
Contributor Author

Siilwyn commented Sep 20, 2018

Hey it seems it got disabled looking at master, would like to know what needs to change for this PR to be accepted. It's been open for so long it's weighing down on me. :/

@andyjansson
Copy link
Contributor

Read the changes we've requested. You've marked the threads as "fixed" although they haven't been.

@Siilwyn
Copy link
Contributor Author

Siilwyn commented Sep 22, 2018

@andyjansson it is unclear for me when conversations are resolved, I have unresolved and replied to all of them. Could you go through them to resolve or give feedback? That would be great.

Edit: I've removed border properties because of #592.

@Siilwyn
Copy link
Contributor Author

Siilwyn commented Oct 2, 2018

So @evilebottnawi if I understand it correctly this PR will not be merged because it is not optimal? Even though the current state of disabling the plugin is even worse in terms of optimizations.

Or does it depend on Siilwyn/css-declaration-sorter#64? We can discuss it there too if you want.

@Siilwyn
Copy link
Contributor Author

Siilwyn commented Oct 29, 2018

☝️ would love a reply.

Will close and delete this PR if there is no chance of getting merged.

@alexander-akait
Copy link
Member

@Siilwyn sorry, don't have time right now

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.

cssDeclarationSorter is not safe when shorthand after longhand property
6 participants