diff --git a/packages/next/src/build/webpack-config.ts b/packages/next/src/build/webpack-config.ts index cf0306d4d6..698a2804ef 100644 --- a/packages/next/src/build/webpack-config.ts +++ b/packages/next/src/build/webpack-config.ts @@ -2557,12 +2557,6 @@ export default async function getBaseWebpackConfig( webpack5Config.output.enabledLibraryTypes = ['assign'] } - if (dev) { - // @ts-ignore unsafeCache exists - webpack5Config.module.unsafeCache = (module) => - !/[\\/]pages[\\/][^\\/]+(?:$|\?|#)/.test(module.resource) - } - // This enables managedPaths for all node_modules // and also for the unplugged folder when using yarn pnp // It also add the yarn cache to the immutable paths diff --git a/packages/next/src/build/webpack/loaders/next-flight-client-entry-loader.ts b/packages/next/src/build/webpack/loaders/next-flight-client-entry-loader.ts index afdba5cd6d..25ab2cf9d7 100644 --- a/packages/next/src/build/webpack/loaders/next-flight-client-entry-loader.ts +++ b/packages/next/src/build/webpack/loaders/next-flight-client-entry-loader.ts @@ -31,16 +31,9 @@ export default async function transformSource(this: any): Promise { .join(';\n') const buildInfo = getModuleBuildInfo(this._module) - const resolve = this.getResolve() - - // Resolve to absolute resource url for flight manifest to collect and use to determine client components - const resolvedRequests = await Promise.all( - requests.map(async (r) => await resolve(this.rootContext, r)) - ) buildInfo.rsc = { type: RSC_MODULE_TYPES.client, - requests: resolvedRequests, } return code diff --git a/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts b/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts index 6676f3e2f7..a6ba03460c 100644 --- a/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts +++ b/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts @@ -675,13 +675,13 @@ export class ClientReferenceEntryPlugin { // replace them. const clientLoader = `next-flight-client-entry-loader?${stringify({ modules: this.isEdgeServer - ? clientImports.map((importPath) => + ? loaderOptions.modules.map((importPath) => importPath.replace( /[\\/]next[\\/]dist[\\/]esm[\\/]/, '/next/dist/'.replace(/\//g, path.sep) ) ) - : clientImports, + : loaderOptions.modules, server: false, })}!` diff --git a/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts b/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts index 0f54539b8d..d0a49f12a6 100644 --- a/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts +++ b/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts @@ -12,10 +12,9 @@ import { SYSTEM_ENTRYPOINTS, } from '../../../shared/lib/constants' import { relative } from 'path' -import { isClientComponentEntryModule, isCSSMod } from '../loaders/utils' +import { isCSSMod } from '../loaders/utils' import { getProxiedPluginState } from '../../build-context' -import { traverseModules } from '../utils' import { nonNullable } from '../../../lib/non-nullable' import { WEBPACK_LAYERS } from '../../../lib/constants' @@ -131,20 +130,6 @@ export class ClientReferenceManifestPlugin { entryCSSFiles: {}, } - const clientRequestsSet = new Set() - - // Collect client requests - function collectClientRequest(mod: webpack.NormalModule) { - if (mod.resource === '' && mod.buildInfo?.rsc) { - const { requests = [] } = mod.buildInfo.rsc - requests.forEach((r: string) => { - clientRequestsSet.add(r) - }) - } - } - - traverseModules(compilation, (mod) => collectClientRequest(mod)) - compilation.chunkGroups.forEach((chunkGroup) => { function getAppPathRequiredChunks() { return chunkGroup.chunks @@ -189,12 +174,8 @@ export class ClientReferenceManifestPlugin { } const recordModule = (id: ModuleId, mod: webpack.NormalModule) => { - const isCSSModule = isCSSMod(mod) - - // Skip all modules from the pages folder. CSS modules are a special case - // as they are generated by mini-css-extract-plugin and these modules - // don't have layer information attached. - if (!isCSSModule && mod.layer !== WEBPACK_LAYERS.appClient) { + // Skip all modules from the pages folder. + if (mod.layer !== WEBPACK_LAYERS.appClient) { return } @@ -208,13 +189,6 @@ export class ClientReferenceManifestPlugin { return } - if (isCSSModule) { - if (chunkEntryName) { - manifest.entryCSSFiles[chunkEntryName].modules.push(resource) - } - return - } - const moduleReferences = manifest.clientModules const moduleIdMapping = manifest.ssrModuleMapping const edgeModuleIdMapping = manifest.edgeSSRModuleMapping @@ -230,15 +204,6 @@ export class ClientReferenceManifestPlugin { if (!ssrNamedModuleId.startsWith('.')) ssrNamedModuleId = `./${ssrNamedModuleId.replace(/\\/g, '/')}` - // Only apply following logic to client module requests from client entry, - // or if the module is marked as client module. - if ( - !clientRequestsSet.has(resource) && - !isClientComponentEntryModule(mod) - ) { - return - } - const isAsyncModule = this.ASYNC_CLIENT_MODULES.has(mod.resource) // The client compiler will always use the CJS Next.js build, so here we @@ -305,24 +270,73 @@ export class ClientReferenceManifestPlugin { manifest.edgeSSRModuleMapping = edgeModuleIdMapping } + // Only apply following logic to client module requests from client entry, + // or if the module is marked as client module. That's because other + // client modules don't need to be in the manifest at all as they're + // never be referenced by the server/client boundary. + // This saves a lot of bytes in the manifest. chunkGroup.chunks.forEach((chunk: webpack.Chunk) => { + const entryMods = + compilation.chunkGraph.getChunkEntryModulesIterable(chunk) + for (const mod of entryMods) { + if (mod.layer !== WEBPACK_LAYERS.appClient) continue + + const request = (mod as webpack.NormalModule).request + + if ( + !request || + !request.includes('next-flight-client-entry-loader.js?') + ) { + continue + } + + const connections = + compilation.moduleGraph.getOutgoingConnections(mod) + + for (const connection of connections) { + const dependency = connection.dependency + if (!dependency) continue + + const clientEntryMod = compilation.moduleGraph.getResolvedModule( + dependency + ) as webpack.NormalModule + const modId = compilation.chunkGraph.getModuleId(clientEntryMod) as + | string + | number + | null + + if (modId !== null) { + recordModule(modId, clientEntryMod) + } else { + // If this is a concatenation, register each child to the parent ID. + if ( + connection.module?.constructor.name === 'ConcatenatedModule' + ) { + const concatenatedMod = connection.module + const concatenatedModId = + compilation.chunkGraph.getModuleId(concatenatedMod) + recordModule(concatenatedModId, clientEntryMod) + } + } + } + } + + // Track CSS modules. This is necessary for next/font preloading. + // TODO: Unfortunately we have to keep this iteration for now but we + // will optimize it altogether in the future. const chunkModules = compilation.chunkGraph.getChunkModulesIterable( chunk - // TODO: Update type so that it doesn't have to be cast. ) as Iterable - for (const mod of chunkModules) { - const modId: string = 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) - }) + if (isCSSMod(mod)) { + if (chunkEntryName) { + const resource = + mod.type === 'css/mini-extract' + ? // @ts-expect-error TODO: use `identifier()` instead. + mod._identifier.slice(mod._identifier.lastIndexOf('!') + 1) + : mod.resource + manifest.entryCSSFiles[chunkEntryName].modules.push(resource) + } } } })