diff --git a/packages/next/build/webpack/plugins/build-manifest-plugin.ts b/packages/next/build/webpack/plugins/build-manifest-plugin.ts index be640cbed31f613..75a3b2500d027c3 100644 --- a/packages/next/build/webpack/plugins/build-manifest-plugin.ts +++ b/packages/next/build/webpack/plugins/build-manifest-plugin.ts @@ -33,7 +33,9 @@ const generateClientManifest = ( // Filter out dependencies in the _app entry, because those will have already // been loaded by the client prior to a navigation event const filteredDeps = dependencies.filter( - dep => !appDependencies.has(dep) && /\.module\.js$/.test(dep) === isModern + dep => + !appDependencies.has(dep) && + (!dep.endsWith('.js') || dep.endsWith('.module.js') === isModern) ) // The manifest can omit the page if it has no requirements diff --git a/test/integration/css-client-nav/test/index.test.js b/test/integration/css-client-nav/test/index.test.js index d5dfc8cba184a0a..4f35823bc276cad 100644 --- a/test/integration/css-client-nav/test/index.test.js +++ b/test/integration/css-client-nav/test/index.test.js @@ -171,3 +171,159 @@ describe('CSS Module client-side navigation in Production', () => { } }) }) + +describe('CSS Module client-side navigation in Production (Modern)', () => { + const appDir = join(fixturesDir, 'multi-module-modern') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + + let appPort + let app + beforeAll(async () => { + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) + + it('should be able to client-side navigate from red to blue', async () => { + let browser + try { + browser = await webdriver(appPort, '/red') + + await browser.eval(`window.__did_not_ssr = 'make sure this is set'`) + + const redColor = await browser.eval( + `window.getComputedStyle(document.querySelector('#verify-red')).color` + ) + expect(redColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`) + + await browser.elementByCss('#link-blue').click() + + await browser.waitForElementByCss('#verify-blue') + + const blueColor = await browser.eval( + `window.getComputedStyle(document.querySelector('#verify-blue')).color` + ) + expect(blueColor).toMatchInlineSnapshot(`"rgb(0, 0, 255)"`) + + expect(await browser.eval(`window.__did_not_ssr`)).toMatchInlineSnapshot( + `"make sure this is set"` + ) + } finally { + if (browser) { + await browser.close() + } + } + }) + + it('should be able to client-side navigate from blue to red', async () => { + const content = await renderViaHTTP(appPort, '/blue') + const $ = cheerio.load(content) + + // Ensure only `/blue` page's CSS is preloaded + const serverCssPreloads = $('link[rel="preload"][as="style"]') + expect(serverCssPreloads.length).toBe(1) + + const serverCssPrefetches = $('link[rel="prefetch"][as="style"]') + expect(serverCssPrefetches.length).toBe(0) + + let browser + try { + browser = await webdriver(appPort, '/blue') + await browser.eval(`window.__did_not_ssr = 'make sure this is set'`) + + const redColor = await browser.eval( + `window.getComputedStyle(document.querySelector('#verify-blue')).color` + ) + expect(redColor).toMatchInlineSnapshot(`"rgb(0, 0, 255)"`) + + // Check that Red was preloaded + const result = await browser.eval( + `[].slice.call(document.querySelectorAll('link[rel="prefetch"][as="style"]')).map(e=>({href:e.href})).sort()` + ) + expect(result.length).toBe(1) + + // Check that CSS was not loaded as script + const cssPreloads = await browser.eval( + `[].slice.call(document.querySelectorAll('link[rel=preload][href*=".css"]')).map(e=>e.as)` + ) + expect(cssPreloads.every(e => e === 'style')).toBe(true) + const cssPreloads2 = await browser.eval( + `[].slice.call(document.querySelectorAll('link[rel=prefetch][href*=".css"]')).map(e=>e.as)` + ) + expect(cssPreloads2.every(e => e === 'style')).toBe(true) + + await browser.elementByCss('#link-red').click() + + await browser.waitForElementByCss('#verify-red') + + const blueColor = await browser.eval( + `window.getComputedStyle(document.querySelector('#verify-red')).color` + ) + expect(blueColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`) + + expect(await browser.eval(`window.__did_not_ssr`)).toMatchInlineSnapshot( + `"make sure this is set"` + ) + } finally { + if (browser) { + await browser.close() + } + } + }) + + it('should be able to client-side navigate from none to red', async () => { + let browser + try { + browser = await webdriver(appPort, '/none') + + await browser.eval(`window.__did_not_ssr = 'make sure this is set'`) + + await browser.elementByCss('#link-red').click() + await browser.waitForElementByCss('#verify-red') + + const blueColor = await browser.eval( + `window.getComputedStyle(document.querySelector('#verify-red')).color` + ) + expect(blueColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`) + + expect(await browser.eval(`window.__did_not_ssr`)).toMatchInlineSnapshot( + `"make sure this is set"` + ) + } finally { + if (browser) { + await browser.close() + } + } + }) + + it('should be able to client-side navigate from none to blue', async () => { + let browser + try { + browser = await webdriver(appPort, '/none') + + await browser.eval(`window.__did_not_ssr = 'make sure this is set'`) + + await browser.elementByCss('#link-blue').click() + await browser.waitForElementByCss('#verify-blue') + + const blueColor = await browser.eval( + `window.getComputedStyle(document.querySelector('#verify-blue')).color` + ) + expect(blueColor).toMatchInlineSnapshot(`"rgb(0, 0, 255)"`) + + expect(await browser.eval(`window.__did_not_ssr`)).toMatchInlineSnapshot( + `"make sure this is set"` + ) + } finally { + if (browser) { + await browser.close() + } + } + }) +}) diff --git a/test/integration/css-fixtures/multi-module-modern/next.config.js b/test/integration/css-fixtures/multi-module-modern/next.config.js new file mode 100644 index 000000000000000..a6e26e2467154a2 --- /dev/null +++ b/test/integration/css-fixtures/multi-module-modern/next.config.js @@ -0,0 +1,9 @@ +const parent = require('../next.config') + +module.exports = { + ...parent, + experimental: { + ...parent.experimental, + modern: true, + }, +} diff --git a/test/integration/css-fixtures/multi-module-modern/pages/blue.js b/test/integration/css-fixtures/multi-module-modern/pages/blue.js new file mode 100644 index 000000000000000..49dff85067fa323 --- /dev/null +++ b/test/integration/css-fixtures/multi-module-modern/pages/blue.js @@ -0,0 +1,20 @@ +import Link from 'next/link' +import { blueText } from './blue.module.css' + +export default function Blue() { + return ( + <> +
+ This text should be blue. +
+
+ + Red + +
+ + None + + + ) +} diff --git a/test/integration/css-fixtures/multi-module-modern/pages/blue.module.css b/test/integration/css-fixtures/multi-module-modern/pages/blue.module.css new file mode 100644 index 000000000000000..4c5ac28ce4ef8d9 --- /dev/null +++ b/test/integration/css-fixtures/multi-module-modern/pages/blue.module.css @@ -0,0 +1,3 @@ +.blueText { + color: blue; +} diff --git a/test/integration/css-fixtures/multi-module-modern/pages/none.js b/test/integration/css-fixtures/multi-module-modern/pages/none.js new file mode 100644 index 000000000000000..f26c2d80b3ae0a1 --- /dev/null +++ b/test/integration/css-fixtures/multi-module-modern/pages/none.js @@ -0,0 +1,19 @@ +import Link from 'next/link' + +export default function None() { + return ( + <> +
+ This text should be black. +
+
+ + Red + +
+ + Blue + + + ) +} diff --git a/test/integration/css-fixtures/multi-module-modern/pages/red.js b/test/integration/css-fixtures/multi-module-modern/pages/red.js new file mode 100644 index 000000000000000..d67276dda2d765a --- /dev/null +++ b/test/integration/css-fixtures/multi-module-modern/pages/red.js @@ -0,0 +1,20 @@ +import Link from 'next/link' +import { redText } from './red.module.css' + +export default function Red() { + return ( + <> +
+ This text should be red. +
+
+ + Blue + +
+ + None + + + ) +} diff --git a/test/integration/css-fixtures/multi-module-modern/pages/red.module.css b/test/integration/css-fixtures/multi-module-modern/pages/red.module.css new file mode 100644 index 000000000000000..08a38e09ef8eac4 --- /dev/null +++ b/test/integration/css-fixtures/multi-module-modern/pages/red.module.css @@ -0,0 +1,3 @@ +.redText { + color: red; +} diff --git a/test/integration/css-fixtures/multi-module/next.config.js b/test/integration/css-fixtures/multi-module/next.config.js new file mode 100644 index 000000000000000..b0f299cd7564071 --- /dev/null +++ b/test/integration/css-fixtures/multi-module/next.config.js @@ -0,0 +1,9 @@ +const parent = require('../next.config') + +module.exports = { + ...parent, + experimental: { + ...parent.experimental, + modern: false, + }, +}