diff --git a/packages/next/build/webpack/loaders/next-flight-server-loader.ts b/packages/next/build/webpack/loaders/next-flight-server-loader.ts index 2dd7cde2fb12..83c4ac670f88 100644 --- a/packages/next/build/webpack/loaders/next-flight-server-loader.ts +++ b/packages/next/build/webpack/loaders/next-flight-server-loader.ts @@ -3,8 +3,8 @@ import { builtinModules } from 'module' import { parse } from '../../swc' import { buildExports, - createClientComponentFilter, - createServerComponentFilter, + clientComponentRegex, + serverComponentRegex, isNextBuiltinClientComponent, } from './utils' @@ -25,15 +25,11 @@ async function parseModuleInfo({ resourcePath, source, isClientCompilation, - isServerComponent, - isClientComponent, resolver, }: { resourcePath: string source: string isClientCompilation: boolean - isServerComponent: (name: string) => boolean - isClientComponent: (name: string) => boolean resolver: (req: string) => Promise }): Promise<{ source: string @@ -71,10 +67,10 @@ async function parseModuleInfo({ } function addClientImport(path: string) { - if (isServerComponent(path) || hasFlightLoader(path, 'server')) { + if (serverComponentRegex.test(path) || hasFlightLoader(path, 'server')) { // If it's a server component, we recursively import its dependencies. imports.push(path) - } else if (isClientComponent(path)) { + } else if (clientComponentRegex.test(path)) { // Client component. imports.push(path) } else { @@ -102,7 +98,7 @@ async function parseModuleInfo({ if (!isClientCompilation) { // Server compilation for .server.js. - if (isServerComponent(importSource)) { + if (serverComponentRegex.test(importSource)) { continue } @@ -111,7 +107,7 @@ async function parseModuleInfo({ node.source.span.start - beginPos ) - if (isClientComponent(importSource)) { + if (clientComponentRegex.test(importSource)) { transformedSource += importDeclarations transformedSource += JSON.stringify( `next-flight-client-loader!${importSource}` @@ -210,12 +206,10 @@ export default async function transformSource( throw new Error('Expected source to have been transformed to a string.') } - const isServerComponent = createServerComponentFilter() - const isClientComponent = createClientComponentFilter() const hasAppliedFlightServerLoader = this.loaders.some((loader: any) => { return hasFlightLoader(loader.path, 'server') }) - const isServerExt = isServerComponent(resourcePath) + const isServerExt = serverComponentRegex.test(resourcePath) if (!isClientCompilation) { // We only apply the loader to server components, or shared components that @@ -235,8 +229,6 @@ export default async function transformSource( resourcePath, source, isClientCompilation, - isServerComponent, - isClientComponent, resolver, }) diff --git a/packages/next/build/webpack/loaders/utils.ts b/packages/next/build/webpack/loaders/utils.ts index ba2c478b8af9..d6e113f1f93c 100644 --- a/packages/next/build/webpack/loaders/utils.ts +++ b/packages/next/build/webpack/loaders/utils.ts @@ -24,25 +24,18 @@ export function buildExports(moduleExports: any, isESM: boolean) { return ret } -export const createClientComponentFilter = () => { - // Special cases for Next.js APIs that are considered as client components: - // - .client.[ext] - // - next built-in client components - // - .[imageExt] - const regex = new RegExp( - '(' + - `\\.client(\\.(${defaultJsFileExtensions.join('|')}))?|` + - `next/(${nextClientComponents.join('|')})(\\.js)?|` + - `\\.(${imageExtensions.join('|')})` + - ')$' - ) - - return (importSource: string) => regex.test(importSource) -} +// Special cases for Next.js APIs that are considered as client components: +// - .client.[ext] +// - next built-in client components +// - .[imageExt] +export const clientComponentRegex = new RegExp( + '(' + + `\\.client(\\.(${defaultJsFileExtensions.join('|')}))?|` + + `next/(${nextClientComponents.join('|')})(\\.js)?|` + + `\\.(${imageExtensions.join('|')})` + + ')$' +) -export const createServerComponentFilter = () => { - const regex = new RegExp( - `\\.server(\\.(${defaultJsFileExtensions.join('|')}))?$` - ) - return (importSource: string) => regex.test(importSource) -} +export const serverComponentRegex = new RegExp( + `\\.server(\\.(${defaultJsFileExtensions.join('|')}))?$` +) diff --git a/packages/next/build/webpack/plugins/flight-manifest-plugin.ts b/packages/next/build/webpack/plugins/flight-manifest-plugin.ts index 7d28a5ed5192..1dea74472665 100644 --- a/packages/next/build/webpack/plugins/flight-manifest-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-manifest-plugin.ts @@ -7,7 +7,7 @@ import { webpack, sources } from 'next/dist/compiled/webpack/webpack' import { MIDDLEWARE_FLIGHT_MANIFEST } from '../../../shared/lib/constants' -import { createClientComponentFilter } from '../loaders/utils' +import { clientComponentRegex } from '../loaders/utils' // 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. @@ -27,7 +27,6 @@ const PLUGIN_NAME = 'FlightManifestPlugin' let edgeFlightManifest = {} let nodeFlightManifest = {} -const isClientComponent = createClientComponentFilter() export class FlightManifestPlugin { dev: boolean = false pageExtensions: string[] @@ -78,9 +77,14 @@ export class FlightManifestPlugin { // TODO: Hook into deps instead of the target module. // That way we know by the type of dep whether to include. // It also resolves conflicts when the same module is in multiple chunks. - if (!resource || !isClientComponent(resource)) { + if ( + !resource || + !clientComponentRegex.test(resource) || + !clientComponentRegex.test(id) + ) { return } + const moduleExports: any = manifest[resource] || {} const exportsInfo = compilation.moduleGraph.getExportsInfo(mod) @@ -113,13 +117,12 @@ export class FlightManifestPlugin { for (const mod of chunkModules) { let modId = compilation.chunkGraph.getModuleId(mod) - // Clean up the module id. - if (typeof modId === 'string') { - // Remove resource queries. - modId = modId.split('?')[0] - // Remove the loader prefix. - modId = modId.split('next-flight-client-loader.js!')[1] - } + if (typeof modId !== 'string') continue + + // Remove resource queries. + modId = modId.split('?')[0] + // Remove the loader prefix. + modId = modId.split('next-flight-client-loader.js!')[1] || modId recordModule(modId, chunk, mod) // If this is a concatenation, register each child to the parent ID. @@ -143,7 +146,9 @@ export class FlightManifestPlugin { ...nodeFlightManifest, ...edgeFlightManifest, } - const file = MIDDLEWARE_FLIGHT_MANIFEST + const file = + (!this.dev && !this.isEdgeServer ? '../' : '') + + MIDDLEWARE_FLIGHT_MANIFEST const json = JSON.stringify(mergedManifest) assets[file + '.js'] = new sources.RawSource('self.__RSC_MANIFEST=' + json)