diff --git a/packages/next/src/build/webpack/plugins/css-chunking-plugin.ts b/packages/next/src/build/webpack/plugins/css-chunking-plugin.ts index 3a2c48e9cf5fa..a4699d04b7b76 100644 --- a/packages/next/src/build/webpack/plugins/css-chunking-plugin.ts +++ b/packages/next/src/build/webpack/plugins/css-chunking-plugin.ts @@ -11,6 +11,10 @@ const MIN_CSS_CHUNK_SIZE = 30 * 1024 */ const MAX_CSS_CHUNK_SIZE = 100 * 1024 +function isGlobalCss(module: Module) { + return !/\.module\.(css|scss|sass)$/.test(module.nameForCondition() || '') +} + type ChunkState = { chunk: Chunk modules: Module[] @@ -125,6 +129,8 @@ export class CssChunkingPlugin { // Process through all modules for (const startModule of remainingModules) { + let globalCssMode = isGlobalCss(startModule) + // The current position of processing in all selected chunks let allChunkStates = new Map(chunkStatesByModule.get(startModule)!) @@ -225,8 +231,36 @@ export class CssChunkingPlugin { } } } + + // Global CSS must not leak into unrelated chunks + const nextIsGlobalCss = isGlobalCss(nextModule) + if (nextIsGlobalCss && globalCssMode) { + if (allChunkStates.size !== nextChunkStates.size) { + // Fast check + continue + } + } + if (globalCssMode) { + for (const chunkState of nextChunkStates.keys()) { + if (!allChunkStates.has(chunkState)) { + // Global CSS would leak into chunkState + continue loop + } + } + } + if (nextIsGlobalCss) { + for (const chunkState of allChunkStates.keys()) { + if (!nextChunkStates.has(chunkState)) { + // Global CSS would leak into chunkState + continue loop + } + } + } potentialNextModules.delete(nextModule) currentSize += size + if (nextIsGlobalCss) { + globalCssMode = true + } for (const [chunkState, i] of nextChunkStates) { if (allChunkStates.has(chunkState)) { // This reduces the request count of the chunk group diff --git a/test/e2e/app-dir/css-order/app/base.css b/test/e2e/app-dir/css-order/app/base.css new file mode 100644 index 0000000000000..af0ff0ad5314a --- /dev/null +++ b/test/e2e/app-dir/css-order/app/base.css @@ -0,0 +1,4 @@ +#hello1, +#hello2 { + color: rgb(255, 0, 0); +} diff --git a/test/e2e/app-dir/css-order/app/global-first/page.tsx b/test/e2e/app-dir/css-order/app/global-first/page.tsx new file mode 100644 index 0000000000000..400b6d2143b1b --- /dev/null +++ b/test/e2e/app-dir/css-order/app/global-first/page.tsx @@ -0,0 +1,12 @@ +import '../base.css' +import './style.css' +import Nav from '../nav' + +export default function Page() { + return ( +
+

hello world

+
+ ) +} diff --git a/test/e2e/app-dir/css-order/app/global-first/style.css b/test/e2e/app-dir/css-order/app/global-first/style.css new file mode 100644 index 0000000000000..fc1883550ea55 --- /dev/null +++ b/test/e2e/app-dir/css-order/app/global-first/style.css @@ -0,0 +1,4 @@ +#hello1, +#hello2 { + color: rgb(0, 255, 0); +} diff --git a/test/e2e/app-dir/css-order/app/global-second/page.tsx b/test/e2e/app-dir/css-order/app/global-second/page.tsx new file mode 100644 index 0000000000000..5c7acc28a9c47 --- /dev/null +++ b/test/e2e/app-dir/css-order/app/global-second/page.tsx @@ -0,0 +1,12 @@ +import '../base.css' +import './style.css' +import Nav from '../nav' + +export default function Page() { + return ( +
+

hello world

+
+ ) +} diff --git a/test/e2e/app-dir/css-order/app/global-second/style.css b/test/e2e/app-dir/css-order/app/global-second/style.css new file mode 100644 index 0000000000000..82401e75bdbc1 --- /dev/null +++ b/test/e2e/app-dir/css-order/app/global-second/style.css @@ -0,0 +1,4 @@ +#hello1, +#hello2 { + color: rgb(0, 0, 255); +} diff --git a/test/e2e/app-dir/css-order/app/nav.tsx b/test/e2e/app-dir/css-order/app/nav.tsx index d47adc22cf204..d10d8e3618297 100644 --- a/test/e2e/app-dir/css-order/app/nav.tsx +++ b/test/e2e/app-dir/css-order/app/nav.tsx @@ -70,6 +70,16 @@ export default function Nav() { Partial Reversed B +
  • + + Global First + +
  • +
  • + + Global Second + +
  • Pages