From 71bd613552ac26b83be1857fb39ff610cdb2dd97 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Tue, 2 Jul 2024 11:03:35 +0200 Subject: [PATCH] avoid merging global css in a way that leaks into other chunk groups --- .../webpack/plugins/css-chunking-plugin.ts | 34 +++++++++++++++++++ test/e2e/app-dir/css-order/app/base.css | 3 ++ .../css-order/app/global-first/page.tsx | 12 +++++++ .../css-order/app/global-first/style.css | 3 ++ .../css-order/app/global-second/page.tsx | 12 +++++++ .../css-order/app/global-second/style.css | 3 ++ test/e2e/app-dir/css-order/app/nav.tsx | 10 ++++++ test/e2e/app-dir/css-order/css-order.test.ts | 14 ++++++++ 8 files changed, 91 insertions(+) create mode 100644 test/e2e/app-dir/css-order/app/base.css create mode 100644 test/e2e/app-dir/css-order/app/global-first/page.tsx create mode 100644 test/e2e/app-dir/css-order/app/global-first/style.css create mode 100644 test/e2e/app-dir/css-order/app/global-second/page.tsx create mode 100644 test/e2e/app-dir/css-order/app/global-second/style.css 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..9f9c537f1e7c4 --- /dev/null +++ b/test/e2e/app-dir/css-order/app/base.css @@ -0,0 +1,3 @@ +#hello { + 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..ed85d26cc98d6 --- /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..e63db2797a021 --- /dev/null +++ b/test/e2e/app-dir/css-order/app/global-first/style.css @@ -0,0 +1,3 @@ +#hello { + 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..ed85d26cc98d6 --- /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..7b3ae5841e74e --- /dev/null +++ b/test/e2e/app-dir/css-order/app/global-second/style.css @@ -0,0 +1,3 @@ +#hello { + 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