Skip to content

Commit 8b97e4c

Browse files
authored
Wrap CSS chunk items in a @layer (vercel/turborepo#3542)
In Turbopack, as a consequence of our lazy compilation model, CSS chunks can contain duplicate CSS chunk items. This can cause issues with precedence. Take the following example: Initial CSS chunk: ```css /* ... */ /* chunk item A */ h1 { font-size: 2rem; } /* ... */ /* other chunk item */ h1 { font-size: 4rem; } /* ... */ ``` Dynamic CSS chunk (loaded after the first page load completes) ```css /* ... */ /* chunk item A */ h1 { font-size: 2rem; } /* ... */ ``` In this example, when the page first loads, the following rule will be applied: ```css h1 { font-size: 4rem; } ``` But as soon as the dynamic CSS chunk loads, the following rule will be applied instead: ```css h1 { font-size: 2rem; } ``` However, from the order of rules in the initial load, we know that the former should still apply. We can remedy this particular issue by wrapping each CSS chunk item into its own [`@layer`](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer) (thanks @sokra for the idea!). This ensures that when a CSS chunk item is re-encountered at a later time, it is automatically de-duplicated thanks to the inherent CSS layering algorithm. This is not an issue in Next.js as we can't have duplicated CSS chunk items.
1 parent eff285e commit 8b97e4c

File tree

1 file changed

+22
-6
lines changed
  • crates/next-dev-tests/tests/integration/next/font-google/basic/pages

1 file changed

+22
-6
lines changed

crates/next-dev-tests/tests/integration/next/font-google/basic/pages/index.js

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ function runTests() {
5555
async function getRuleMatchingClassName(className) {
5656
const selector = `.${CSS.escape(className)}`;
5757

58-
let matchingRule;
5958
for (const stylesheet of document.querySelectorAll("link[rel=stylesheet]")) {
6059
if (stylesheet.sheet == null) {
6160
// Wait for the stylesheet to load completely if it hasn't already
@@ -64,13 +63,30 @@ async function getRuleMatchingClassName(className) {
6463
});
6564
}
6665

67-
for (const rule of stylesheet.sheet.cssRules) {
68-
if (rule.selectorText === selector) {
69-
matchingRule = rule;
70-
break;
66+
const sheet = stylesheet.sheet;
67+
68+
const res = getRuleMatchingClassNameRec(selector, sheet.cssRules);
69+
if (res != null) {
70+
return res;
71+
}
72+
}
73+
74+
return null;
75+
}
76+
77+
function getRuleMatchingClassNameRec(selector, rules) {
78+
for (const rule of rules) {
79+
if (rule instanceof CSSStyleRule && rule.selectorText === selector) {
80+
return rule;
81+
}
82+
83+
if (rule instanceof CSSLayerBlockRule) {
84+
const res = getRuleMatchingClassNameRec(selector, rule.cssRules);
85+
if (res != null) {
86+
return res;
7187
}
7288
}
7389
}
7490

75-
return matchingRule;
91+
return null;
7692
}

0 commit comments

Comments
 (0)