From ce92d7c9448fd670ef777a60901efd17bf64bdf2 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Fri, 30 Sep 2022 16:51:42 +0200 Subject: [PATCH 1/2] use deterministic module ids for ssr --- packages/next/build/webpack-config.ts | 2 +- .../plugins/flight-client-entry-plugin.ts | 61 ++++++++++++++++++- .../webpack/plugins/flight-manifest-plugin.ts | 25 +++++--- 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 831df6a50c194..b18b4a403dfdc 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -1274,7 +1274,7 @@ export default async function getBaseWebpackConfig( ...(hasServerComponents ? { // We have to use the names here instead of hashes to ensure the consistency between compilers. - moduleIds: isClient ? 'deterministic' : 'named', + moduleIds: 'deterministic', // isClient ? 'deterministic' : 'named', } : {}), splitChunks: ((): 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 1e16b917be012..538f92ff87de3 100644 --- a/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts @@ -1,5 +1,6 @@ import { stringify } from 'querystring' import path from 'path' +import { relative } from 'path' import { webpack, sources } from 'next/dist/compiled/webpack/webpack' import { getInvalidator, @@ -11,7 +12,7 @@ import type { ClientComponentImports, NextFlightClientEntryLoaderOptions, } from '../loaders/next-flight-client-entry-loader' -import { APP_DIR_ALIAS } from '../../../lib/constants' +import { APP_DIR_ALIAS, WEBPACK_LAYERS } from '../../../lib/constants' import { COMPILER_NAMES, EDGE_RUNTIME_WEBPACK, @@ -31,6 +32,9 @@ const PLUGIN_NAME = 'ClientEntryPlugin' export const injectedClientEntries = new Map() +export const serverModuleIds = new Map() +export const edgeServerModuleIds = new Map() + // TODO-APP: ensure .scss / .sass also works. const regexCSS = /\.css$/ @@ -77,6 +81,60 @@ export class FlightClientEntryPlugin { } } }) + + const recordModule = (id: number | string, mod: any) => { + const modResource = mod.resourceResolveData?.path || mod.resource + + if ( + mod.resourceResolveData?.context?.issuerLayer === + WEBPACK_LAYERS.server + ) { + return + } + + if (typeof id !== 'undefined' && modResource) { + // Note that this isn't that reliable as webpack is still possible to assign + // additional queries to make sure there's no conflict even using the `named` + // module ID strategy. + let ssrNamedModuleId = relative(compiler.context, modResource) + if (!ssrNamedModuleId.startsWith('.')) { + // TODO use getModuleId instead + ssrNamedModuleId = `./${ssrNamedModuleId.replace(/\\/g, '/')}` + } + + if (this.isEdgeServer) { + edgeServerModuleIds.set( + ssrNamedModuleId.replace(/\/next\/dist\/esm\//, '/next/dist/'), + id + ) + } else { + serverModuleIds.set(ssrNamedModuleId, id) + } + } + } + + compilation.chunkGroups.forEach((chunkGroup) => { + chunkGroup.chunks.forEach((chunk: webpack.Chunk) => { + const chunkModules = compilation.chunkGraph.getChunkModulesIterable( + chunk + ) as Iterable + + for (const mod of chunkModules) { + const modId = compilation.chunkGraph.getModuleId(mod) + + recordModule(modId, mod) + + // If this is a concatenation, register each child to the parent ID. + // TODO: remove any + const anyModule = mod as any + if (anyModule.modules) { + anyModule.modules.forEach((concatenatedMod: any) => { + recordModule(modId, concatenatedMod) + }) + } + } + }) + }) }) } @@ -323,6 +381,7 @@ export class FlightClientEntryPlugin { // Check if request is for css file. if ((!inClientComponentBoundary && isClientComponent) || isCSS) { clientComponentImports.push(modRequest) + return } diff --git a/packages/next/build/webpack/plugins/flight-manifest-plugin.ts b/packages/next/build/webpack/plugins/flight-manifest-plugin.ts index fb09198806891..c3432035bca5d 100644 --- a/packages/next/build/webpack/plugins/flight-manifest-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-manifest-plugin.ts @@ -10,6 +10,11 @@ import { FLIGHT_MANIFEST } from '../../../shared/lib/constants' import { relative } from 'path' import { isClientComponentModule } from '../loaders/utils' +import { + edgeServerModuleIds, + serverModuleIds, +} from './flight-client-entry-plugin' + // This is the module that will be used to anchor all client references to. // I.e. it will have all the client files as async deps from this point on. // We use the Flight client implementation because you can't get to these @@ -304,16 +309,20 @@ export class FlightManifestPlugin { } } - moduleIdMapping[id] = moduleIdMapping[id] || {} - moduleIdMapping[id][name] = { - ...moduleExports[name], - id: ssrNamedModuleId, + if (serverModuleIds.has(ssrNamedModuleId)) { + moduleIdMapping[id] = moduleIdMapping[id] || {} + moduleIdMapping[id][name] = { + ...moduleExports[name], + id: serverModuleIds.get(ssrNamedModuleId)!, + } } - edgeModuleIdMapping[id] = edgeModuleIdMapping[id] || {} - edgeModuleIdMapping[id][name] = { - ...moduleExports[name], - id: ssrNamedModuleId.replace(/\/next\/dist\//, '/next/dist/esm/'), + if (edgeServerModuleIds.has(ssrNamedModuleId)) { + edgeModuleIdMapping[id] = edgeModuleIdMapping[id] || {} + edgeModuleIdMapping[id][name] = { + ...moduleExports[name], + id: edgeServerModuleIds.get(ssrNamedModuleId)!, + } } }) From d6110dfb10da34ef9ac5cbe421f37381d222892e Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Fri, 30 Sep 2022 17:47:11 +0200 Subject: [PATCH 2/2] remove comments --- packages/next/build/webpack-config.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index b18b4a403dfdc..0ebd082f123cb 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -1271,12 +1271,7 @@ export default async function getBaseWebpackConfig( emitOnErrors: !dev, checkWasmTypes: false, nodeEnv: false, - ...(hasServerComponents - ? { - // We have to use the names here instead of hashes to ensure the consistency between compilers. - moduleIds: 'deterministic', // isClient ? 'deterministic' : 'named', - } - : {}), + ...(hasServerComponents ? { moduleIds: 'deterministic' } : {}), splitChunks: ((): | Required['optimization']['splitChunks'] | false => {