diff --git a/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts b/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts index b8c483ceba37..622d837a01b0 100644 --- a/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts @@ -3,6 +3,8 @@ import type { ClientComponentImports, NextFlightClientEntryLoaderOptions, } from '../loaders/next-flight-client-entry-loader' +import type { FlightCSSManifest } from './flight-manifest-plugin' + import { webpack } from 'next/dist/compiled/webpack/webpack' import { stringify } from 'querystring' import path from 'path' @@ -19,7 +21,6 @@ import { EDGE_RUNTIME_WEBPACK, FLIGHT_SERVER_CSS_MANIFEST, } from '../../../shared/lib/constants' -import { FlightCSSManifest } from './flight-manifest-plugin' import { ASYNC_CLIENT_MODULES } from './flight-manifest-plugin' import { isClientComponentModule, regexCSS } from '../loaders/utils' import { traverseModules } from '../utils' @@ -37,8 +38,8 @@ export const injectedClientEntries = new Map() export const serverModuleIds = new Map() export const edgeServerModuleIds = new Map() -// TODO-APP: move CSS manifest generation to the flight manifest plugin. -const flightCSSManifest: FlightCSSManifest = {} +let serverCSSManifest: FlightCSSManifest = {} +let edgeServerCSSManifest: FlightCSSManifest = {} export class FlightClientEntryPlugin { dev: boolean @@ -112,7 +113,7 @@ export class FlightClientEntryPlugin { } traverseModules(compilation, (mod, _chunk, _chunkGroup, modId) => { - recordModule(modId + '', mod) + recordModule(String(modId), mod) }) }) } @@ -234,6 +235,15 @@ export class FlightClientEntryPlugin { // by the certain chunk. compilation.hooks.afterOptimizeModules.tap(PLUGIN_NAME, () => { const cssImportsForChunk: Record = {} + if (this.isEdgeServer) { + edgeServerCSSManifest = {} + } else { + serverCSSManifest = {} + } + + let cssManifest = this.isEdgeServer + ? edgeServerCSSManifest + : serverCSSManifest function collectModule(entryName: string, mod: any) { const resource = mod.resource @@ -272,10 +282,10 @@ export class FlightClientEntryPlugin { } const entryCSSInfo: Record = - flightCSSManifest.__entry_css__ || {} + cssManifest.__entry_css__ || {} entryCSSInfo[entryName] = cssImportsForChunk[entryName] - Object.assign(flightCSSManifest, { + Object.assign(cssManifest, { __entry_css__: entryCSSInfo, }) }) @@ -320,7 +330,7 @@ export class FlightClientEntryPlugin { clientEntryDependencyMap, }) - Object.assign(flightCSSManifest, cssImports) + Object.assign(cssManifest, cssImports) } }) }) @@ -333,7 +343,14 @@ export class FlightClientEntryPlugin { stage: webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_HASH, }, (assets: webpack.Compilation['assets']) => { - const manifest = JSON.stringify(flightCSSManifest) + const manifest = JSON.stringify({ + ...serverCSSManifest, + ...edgeServerCSSManifest, + __entry_css__: { + ...serverCSSManifest.__entry_css__, + ...edgeServerCSSManifest.__entry_css__, + }, + }) assets[FLIGHT_SERVER_CSS_MANIFEST + '.json'] = new sources.RawSource( manifest ) as unknown as webpack.sources.RawSource diff --git a/test/e2e/app-dir/index.test.ts b/test/e2e/app-dir/index.test.ts index c9f0bcd1b34f..012435662951 100644 --- a/test/e2e/app-dir/index.test.ts +++ b/test/e2e/app-dir/index.test.ts @@ -382,6 +382,49 @@ describe('app dir', () => { } } ) + ;(isDev ? describe : describe.skip)('HMR', () => { + it('should HMR correctly for client component', async () => { + const filePath = 'app/client-component-route/page.js' + const origContent = await next.readFile(filePath) + + try { + const browser = await webdriver(next.url, '/client-component-route') + + const ssrInitial = await renderViaHTTP( + next.url, + '/client-component-route' + ) + + expect(ssrInitial).toContain('hello from app/client-component-route') + + expect(await browser.elementByCss('p').text()).toContain( + 'hello from app/client-component-route' + ) + + await next.patchFile( + filePath, + origContent.replace('hello from', 'swapped from') + ) + + await check(() => browser.elementByCss('p').text(), /swapped from/) + + const ssrUpdated = await renderViaHTTP( + next.url, + '/client-component-route' + ) + expect(ssrUpdated).toContain('swapped from') + + await next.patchFile(filePath, origContent) + + await check(() => browser.elementByCss('p').text(), /hello from/) + expect( + await renderViaHTTP(next.url, '/client-component-route') + ).toContain('hello from') + } finally { + await next.patchFile(filePath, origContent) + } + }) + }) it('should handle hash in initial url', async () => { const browser = await webdriver(next.url, '/dashboard#abc') @@ -1332,52 +1375,6 @@ describe('app dir', () => { }) }) }) - - if (isDev) { - it('should HMR correctly for client component', async () => { - const filePath = 'app/client-component-route/page.js' - const origContent = await next.readFile(filePath) - - try { - const browser = await webdriver(next.url, '/client-component-route') - - const ssrInitial = await renderViaHTTP( - next.url, - '/client-component-route' - ) - - expect(ssrInitial).toContain( - 'hello from app/client-component-route' - ) - - expect(await browser.elementByCss('p').text()).toContain( - 'hello from app/client-component-route' - ) - - await next.patchFile( - filePath, - origContent.replace('hello from', 'swapped from') - ) - - await check(() => browser.elementByCss('p').text(), /swapped from/) - - const ssrUpdated = await renderViaHTTP( - next.url, - '/client-component-route' - ) - expect(ssrUpdated).toContain('swapped from') - - await next.patchFile(filePath, origContent) - - await check(() => browser.elementByCss('p').text(), /hello from/) - expect( - await renderViaHTTP(next.url, '/client-component-route') - ).toContain('hello from') - } finally { - await next.patchFile(filePath, origContent) - } - }) - } }) describe('css support', () => {