-
Notifications
You must be signed in to change notification settings - Fork 673
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
[css-cascade] Specify how @import
cycles work
#9171
Comments
Edit : I was wrong :)
This is green for me in all browsers. I filed a bug report for that failure here : https://bugzilla.mozilla.org/show_bug.cgi?id=1844683 To prevent infinite loops/expansion I looked at the WebKit source code and found that they create a chain of ancestor stylesheets. https://github.com/WebKit/WebKit/blob/main/Source/WebCore/css/StyleRuleImport.cpp#L123-L131 I agree that this seems underspecified. But I do think there is interop between browser implementations. |
I attributed the difference to the wrong thing. Firefox gives the same result as Chrome/Safari when opening files from the local file system. If a web server is used it indeed shows it as red. |
I commented on Firefox's behavior in https://bugzilla.mozilla.org/show_bug.cgi?id=1844683#c9. TLDR, this is Firefox more aggressively reusing stylesheets than other browsers... For the file origin it seems our cache is not as aggressive as it could (each stylesheet ends up being in a different origin, and we avoid sharing stylesheets loaded by different origins, which means that the parent stylesheet ends up implicitly being part of the cache key). |
I'm the author of esbuild and I'm trying to write a CSS bundler. As far as I can tell, there is no CSS specification that explains what to do when there's an import cycle. This is surprising to me so I'm guessing I'm wrong and there is such a specification. If so, please disregard this issue!
Background
Here's everything I could find about how to interpret
@import
rules from the CSS cascade specification (where the behavior of@import
appears to be specified):(Click for quotes from the specification)
From https://drafts.csswg.org/css-cascade-5/#at-import:
and from https://drafts.csswg.org/css-cascade-5/#import-processing:
This doesn't say anything about how cycles work. It also doesn't disallow them, and indeed they are allowed by all browsers. A literal interpretation of the specification would cause a hang when a cycle is encountered due to infinite expansion. One such implementation is
postcss-import
which can have this exact problem: postcss/postcss-import#462. But browsers don't hang, so they must be doing something else. The only hint that I've found about how browsers might do this is in the description for issue #4287:This makes sense. You can traverse the import graph in reverse to find this "furthest-down" ordering and you can handle cycles by not visiting a given node more than once. That lets you handle cases like this:
(Click for example code with a cycle)
entry.css
foreground.css
background.css
reset.css
This example should set both the body's
color
andbackground
to green. Following the "furthest-down" algorithm gives the orderforeground.css
+reset.css
+background.css
+entry.css
which successfully reproduces the behavior observed in browsers. This is what esbuild currently implements.Problem 1
The "furthest-down" algorithm doesn't actually work. The problem is that
@layer
is specified to take effect in the "furthest-up" location instead of the "furthest-down" location (defined here). For example, this doesn't work:(Click for example code with this edge case)
entry.css
a.css
b.css
Following the "furthest-down" algorithm gives the order
b.css
+a.css
+entry.css
which is incorrect. It causes layerb
to come beforea
(and therefore the colorred
wins) while in the browser layera
comes beforeb
(and therefore the colorgreen
wins).Problem 2
Browsers don't even have consistent behavior in the presence of import cycles. This is understandable because the behavior import cycles doesn't appear to be specified, but it seems undesirable to leave this unspecified and for browsers to have divergent behavior. Here's an example of a case with inconsistent behavior:
(Click for example code with inconsistent browser behavior)
entry.css
a.css
b.css
c.css
red.css
green.css
This CSS sets the body to green in Chrome but red in Firefox. Following the "furthest-down" algorithm gives the order
red.css
+green.css
+b.css
+a.css
+c.css
+entry.css
which results in a green body. But it's not clear which browser is "correct" without a specification.Conclusion
It would be helpful for me if this behavior was specified. Then I could build esbuild to a specification instead of what I have been doing, which is trying to reverse-engineer what the behavior is supposed to be from how existing browsers work. What got me to file this issue was a) the realization that browsers aren't even consistent so reverse-engineering won't work and b) the realization that I have no idea how to implement
@layer
in combination with@import
in a CSS bundler, especially in the presence of cycles. I'm creating a new issue for this because while #4287 is related, it's discussing how the JavaScript API should behave while I'm interested in how CSS is supposed to behave.The text was updated successfully, but these errors were encountered: