From 046050c0414bc1d8518322450e00a213c4ba2681 Mon Sep 17 00:00:00 2001 From: Zack Tanner <1939140+ztanner@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:18:24 -0800 Subject: [PATCH] add option to disable experimental CssChunkingPlugin --- .../01-next-config-js/cssChunking.mdx | 1 + packages/next/src/build/webpack-config.ts | 1 + packages/next/src/server/config-schema.ts | 2 +- packages/next/src/server/config-shared.ts | 3 ++- test/e2e/app-dir/css-chunking/app/globals.css | 12 +++++++++++ test/e2e/app-dir/css-chunking/app/layout.tsx | 20 ++++++++++++++++++ .../app/other/otherPage.module.css | 3 +++ .../app-dir/css-chunking/app/other/page.tsx | 10 +++++++++ .../app-dir/css-chunking/app/page.module.css | 4 ++++ test/e2e/app-dir/css-chunking/app/page.tsx | 16 ++++++++++++++ .../components/inner-wrapper/index.tsx | 10 +++++++++ .../inner-wrapper/innerWrapper.module.css | 7 +++++++ .../app-dir/css-chunking/css-chunking.test.ts | 21 +++++++++++++++++++ test/e2e/app-dir/css-chunking/next.config.js | 10 +++++++++ 14 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 test/e2e/app-dir/css-chunking/app/globals.css create mode 100644 test/e2e/app-dir/css-chunking/app/layout.tsx create mode 100644 test/e2e/app-dir/css-chunking/app/other/otherPage.module.css create mode 100644 test/e2e/app-dir/css-chunking/app/other/page.tsx create mode 100644 test/e2e/app-dir/css-chunking/app/page.module.css create mode 100644 test/e2e/app-dir/css-chunking/app/page.tsx create mode 100644 test/e2e/app-dir/css-chunking/components/inner-wrapper/index.tsx create mode 100644 test/e2e/app-dir/css-chunking/components/inner-wrapper/innerWrapper.module.css create mode 100644 test/e2e/app-dir/css-chunking/css-chunking.test.ts create mode 100644 test/e2e/app-dir/css-chunking/next.config.js diff --git a/docs/01-app/03-api-reference/05-config/01-next-config-js/cssChunking.mdx b/docs/01-app/03-api-reference/05-config/01-next-config-js/cssChunking.mdx index 54a2a6c73c77a..76042dd49a07a 100644 --- a/docs/01-app/03-api-reference/05-config/01-next-config-js/cssChunking.mdx +++ b/docs/01-app/03-api-reference/05-config/01-next-config-js/cssChunking.mdx @@ -35,6 +35,7 @@ module.exports = nextConfig - **`'loose'` (default)**: Next.js will try to merge CSS files whenever possible, determining explicit and implicit dependencies between files from import order to reduce the number of chunks and therefore the number of requests. - **`'strict'`**: Next.js will load CSS files in the correct order they are imported into your files, which can lead to more chunks and requests. +- **`'disabled'**: Next.js will not attempt to merge or re-order your CSS files. You may consider using `'strict'` if you run into unexpected CSS behavior. For example, if you import `a.css` and `b.css` in different files using a different `import` order (`a` before `b`, or `b` before `a`), `'loose'` will merge the files in any order and assume there are no dependencies between them. However, if `b.css` depends on `a.css`, you may want to use `'strict'` to prevent the files from being merged, and instead, load them in the order they are imported - which can result in more chunks and requests. diff --git a/packages/next/src/build/webpack-config.ts b/packages/next/src/build/webpack-config.ts index b36da1033e656..89c9463e29879 100644 --- a/packages/next/src/build/webpack-config.ts +++ b/packages/next/src/build/webpack-config.ts @@ -1927,6 +1927,7 @@ export default async function getBaseWebpackConfig( }), !dev && isClient && + config.experimental.cssChunking !== 'disabled' && new CssChunkingPlugin(config.experimental.cssChunking === 'strict'), !dev && isClient && diff --git a/packages/next/src/server/config-schema.ts b/packages/next/src/server/config-schema.ts index ea6f795db6bc8..f75b77e7bdaa4 100644 --- a/packages/next/src/server/config-schema.ts +++ b/packages/next/src/server/config-schema.ts @@ -328,7 +328,7 @@ export const configSchema: zod.ZodType = z.lazy(() => manualClientBasePath: z.boolean().optional(), middlewarePrefetch: z.enum(['strict', 'flexible']).optional(), multiZoneDraftMode: z.boolean().optional(), - cssChunking: z.enum(['strict', 'loose']).optional(), + cssChunking: z.enum(['strict', 'loose', 'disabled']).optional(), nextScriptWorkers: z.boolean().optional(), // The critter option is unknown, use z.any() here optimizeCss: z.union([z.boolean(), z.any()]).optional(), diff --git a/packages/next/src/server/config-shared.ts b/packages/next/src/server/config-shared.ts index ff89b74a06866..81445c2134b24 100644 --- a/packages/next/src/server/config-shared.ts +++ b/packages/next/src/server/config-shared.ts @@ -302,7 +302,7 @@ export interface ExperimentalConfig { * An alternative is 'strict', which will try to keep correct ordering as * much as possible, even when this leads to many requests. */ - cssChunking?: 'strict' | 'loose' + cssChunking?: 'strict' | 'loose' | 'disabled' disablePostcssPresetEnv?: boolean cpus?: number memoryBasedWorkersCount?: boolean @@ -1090,6 +1090,7 @@ export const defaultConfig: NextConfig = { remote: process.env.NEXT_REMOTE_CACHE_HANDLER_PATH, static: process.env.NEXT_STATIC_CACHE_HANDLER_PATH, }, + cssChunking: 'loose', multiZoneDraftMode: false, appNavFailHandling: Boolean(process.env.NEXT_PRIVATE_FLYING_SHUTTLE), flyingShuttle: Boolean(process.env.NEXT_PRIVATE_FLYING_SHUTTLE) diff --git a/test/e2e/app-dir/css-chunking/app/globals.css b/test/e2e/app-dir/css-chunking/app/globals.css new file mode 100644 index 0000000000000..08aff9a17c6d3 --- /dev/null +++ b/test/e2e/app-dir/css-chunking/app/globals.css @@ -0,0 +1,12 @@ +:root { + --maxWidth: 1210px; +} + +html, +body { + background: #000; +} + +a { + color: aqua; +} diff --git a/test/e2e/app-dir/css-chunking/app/layout.tsx b/test/e2e/app-dir/css-chunking/app/layout.tsx new file mode 100644 index 0000000000000..165992ba9d314 --- /dev/null +++ b/test/e2e/app-dir/css-chunking/app/layout.tsx @@ -0,0 +1,20 @@ +import type { Metadata } from 'next' +import React from 'react' +import './globals.css' + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode +}>) { + return ( + + {children} + + ) +} diff --git a/test/e2e/app-dir/css-chunking/app/other/otherPage.module.css b/test/e2e/app-dir/css-chunking/app/other/otherPage.module.css new file mode 100644 index 0000000000000..5a91af7c83861 --- /dev/null +++ b/test/e2e/app-dir/css-chunking/app/other/otherPage.module.css @@ -0,0 +1,3 @@ +.otherPage { + color: crimson; +} diff --git a/test/e2e/app-dir/css-chunking/app/other/page.tsx b/test/e2e/app-dir/css-chunking/app/other/page.tsx new file mode 100644 index 0000000000000..04c93300f1154 --- /dev/null +++ b/test/e2e/app-dir/css-chunking/app/other/page.tsx @@ -0,0 +1,10 @@ +import InnerWrapper from '../../components/inner-wrapper' +import styles from './otherPage.module.css' + +export default async function OtherPage() { + return ( + +

Other Page Title

+
+ ) +} diff --git a/test/e2e/app-dir/css-chunking/app/page.module.css b/test/e2e/app-dir/css-chunking/app/page.module.css new file mode 100644 index 0000000000000..29af0a214589c --- /dev/null +++ b/test/e2e/app-dir/css-chunking/app/page.module.css @@ -0,0 +1,4 @@ +.h1 { + background: antiquewhite; + color: #1a1a1a; +} diff --git a/test/e2e/app-dir/css-chunking/app/page.tsx b/test/e2e/app-dir/css-chunking/app/page.tsx new file mode 100644 index 0000000000000..b16af53e6e777 --- /dev/null +++ b/test/e2e/app-dir/css-chunking/app/page.tsx @@ -0,0 +1,16 @@ +import styles from './page.module.css' +import InnerWrapper from '../components/inner-wrapper' +import Link from 'next/link' + +export default function Home() { + return ( +
+ +

Home Page

+ + Other page + +
+
+ ) +} diff --git a/test/e2e/app-dir/css-chunking/components/inner-wrapper/index.tsx b/test/e2e/app-dir/css-chunking/components/inner-wrapper/index.tsx new file mode 100644 index 0000000000000..bd33205bb66db --- /dev/null +++ b/test/e2e/app-dir/css-chunking/components/inner-wrapper/index.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import styles from './innerWrapper.module.css' + +export default function InnerWrapper({ + children, +}: { + children: React.ReactNode +}) { + return
{children}
+} diff --git a/test/e2e/app-dir/css-chunking/components/inner-wrapper/innerWrapper.module.css b/test/e2e/app-dir/css-chunking/components/inner-wrapper/innerWrapper.module.css new file mode 100644 index 0000000000000..d67ef59e98c4d --- /dev/null +++ b/test/e2e/app-dir/css-chunking/components/inner-wrapper/innerWrapper.module.css @@ -0,0 +1,7 @@ +.innerWrapper { + max-width: var(--maxWidth); + margin: 0 auto; + position: relative; + padding-left: 30px; + padding-right: 30px; +} diff --git a/test/e2e/app-dir/css-chunking/css-chunking.test.ts b/test/e2e/app-dir/css-chunking/css-chunking.test.ts new file mode 100644 index 0000000000000..a6338a8b4ae9e --- /dev/null +++ b/test/e2e/app-dir/css-chunking/css-chunking.test.ts @@ -0,0 +1,21 @@ +import { nextTestSetup } from 'e2e-utils' + +describe('css-chunking', () => { + const { next } = nextTestSetup({ files: __dirname }) + + // this test asserts that all the emitted CSS files for the index page + // do not contain styles for the `/other` page, which can happen + // when the CSSChunkingPlugin is enabled and styles are shared across + // both routes. + it('should be possible to disable the chunking plugin', async () => { + const $ = await next.render$('/') + const stylesheets = $('link[rel="stylesheet"]') + stylesheets.each(async (_, element) => { + const href = element.attribs.href + const result = await next.fetch(href) + const css = await result.text() + + expect(css).not.toContain('.otherPage') + }) + }) +}) diff --git a/test/e2e/app-dir/css-chunking/next.config.js b/test/e2e/app-dir/css-chunking/next.config.js new file mode 100644 index 0000000000000..335fd9726dfd8 --- /dev/null +++ b/test/e2e/app-dir/css-chunking/next.config.js @@ -0,0 +1,10 @@ +/** + * @type {import('next').NextConfig} + */ +const nextConfig = { + experimental: { + cssChunking: 'disabled', + }, +} + +module.exports = nextConfig