postcss / postcss-import

PostCSS plugin to inline at-import rules content
MIT License
1.38k stars 115 forks source link

implement `supports` conditions #548

Closed romainmenke closed 11 months ago

romainmenke commented 11 months ago

supersedes : https://github.com/postcss/postcss-import/pull/532 fixes : https://github.com/postcss/postcss-import/issues/529


The core issue is how multiple statements are combined.

The previous implementation tried to squash media queries and layer conditions.

becomes :

@media screen and (min-width: 1000px) {
  @layer foo.bar {}
}

In this example the foo layer is only defined when the media is screen and has a width of 1000px. While it should actually be defined even when the media is screen and smaller.

becomes :

@media screen and not print {}

In this example the media query becomes invalid because and can not be used to combine screen and not print


The implementation for this feature was also fairly complex.

Adding supports into this would have made the implementation very unwieldy and it would have increased the chance that users experience bugs.


The correct way to combine is actually much simpler.

becomes :

@media screen {
  @layer foo {
    @media (min-width: 1000px) {
      @layer foo.bar {}
    }
  }
}

becomes :

@media screen {
  @media not print {}
}

By using nested statements we ensure that the order of declaration is respected and that no invalid statements are produced.

At this point it also becomes trivial to add support for supports conditions.


This method does break one aspect.

@import statements can not be nested syntactically in CSS. They must appear first at the root of the AST.

valid :

@imports "http://something-external.com" screen;

invalid :

@media screen {
  @imports "http://something-external.com";
}

To restore support for this we can use nested stylesheets. @import statement urls can be base64 encoded data urls. This doesn't actually have to be base64 encoded, but encoding makes it less prone to errors.

Without encoding it looks like this :

@import url("data:text/css,@imports url('http://something-external.com') not print;") screen;

This technique was first thought of by the maintainer of esbuild while fixing issues brought to light by https://github.com/romainmenke/css-import-tests


This implementation change fixes a lot of subtle bugs and as far as I can tell the output is almost always smaller.

It also eliminates some complexity for users. They no longer need to define things like nameLayer for anonymous layers to work correctly. The issue for which we introduced that option no longer exists in this implementation.

So I think this is a pure win :)

romainmenke commented 11 months ago

Thank you for the review 🙇

Just to clarify, base64 encoding is only used for external URL imports, right?

Yes. Only when an import can not be inlined and conditions or cascade layers need to be applied.

RyanZim commented 11 months ago

Thanks a lot! Ready to publish as v16?

RyanZim commented 11 months ago

I think I'll wait until after the New Year to publish, just in case something goes wrong.

RyanZim commented 10 months ago

https://github.com/postcss/postcss-import/commit/025f27a86018d052d1de62bfe636931bc0f28a2f :tada:

romainmenke commented 10 months ago

Awesome! thank you so much for all the careful reviews 🙇