From 3618b9017e6172fbb9038303e12c1dfe7a9365f2 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Thu, 27 Oct 2022 16:50:46 -0700 Subject: [PATCH] Merge app internal chunk into main chunk for layouts (#41902) When emitting the client components entry from server compiler, merging app internal entry into main-app to avoid duplicated chunks like react are generated in both sides Related: https://github.com/vercel/next.js/issues/41870 --- packages/next/build/index.ts | 14 +++++++++++++- packages/next/build/webpack-config.ts | 19 ++++++++++++------- .../plugins/flight-client-entry-plugin.ts | 19 +++++++++---------- packages/next/client/app-next.js | 5 ++--- packages/next/shared/lib/constants.ts | 2 ++ test/.stats-app/app/app-edge-ssr/page.js | 2 +- test/.stats-app/app/app-ssr/page.js | 5 +++++ 7 files changed, 44 insertions(+), 22 deletions(-) create mode 100644 test/.stats-app/app/app-ssr/page.js diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 0e6cde7e2365..df16898eb5fb 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -59,6 +59,8 @@ import { FLIGHT_SERVER_CSS_MANIFEST, RSC_MODULE_TYPES, FONT_LOADER_MANIFEST, + CLIENT_STATIC_FILES_RUNTIME_MAIN_APP, + APP_CLIENT_INTERNALS, } from '../shared/lib/constants' import { getSortedRoutes, isDynamicRoute } from '../shared/lib/router/utils' import { __ApiPreviewProps } from '../server/api-utils' @@ -975,7 +977,17 @@ export default async function build( // Only continue if there were no errors if (!serverResult.errors.length && !edgeServerResult?.errors.length) { injectedClientEntries.forEach((value, key) => { - ;(clientConfig.entry as webpack.EntryObject)[key] = value + const clientEntry = clientConfig.entry as webpack.EntryObject + if (key === APP_CLIENT_INTERNALS) { + clientEntry[CLIENT_STATIC_FILES_RUNTIME_MAIN_APP] = [ + // TODO-APP: cast clientEntry[CLIENT_STATIC_FILES_RUNTIME_MAIN_APP] to type EntryDescription once it's available from webpack + // @ts-expect-error clientEntry['main-app'] is type EntryDescription { import: ... } + ...clientEntry[CLIENT_STATIC_FILES_RUNTIME_MAIN_APP].import, + value, + ] + } else { + clientEntry[key] = value + } }) clientResult = await runCompiler(clientConfig, { diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index bc90b23a0eac..278508819632 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -795,13 +795,18 @@ export default async function getBaseWebpackConfig( ) .replace(/\\/g, '/'), ] - : `./` + - path - .relative( - dir, - path.join(NEXT_PROJECT_ROOT_DIST_CLIENT, 'app-next.js') - ) - .replace(/\\/g, '/'), + : [ + `./` + + path + .relative( + dir, + path.join( + NEXT_PROJECT_ROOT_DIST_CLIENT, + 'app-next.js' + ) + ) + .replace(/\\/g, '/'), + ], } : {}), } as ClientEntries) 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 fd8a299dcd14..9865fc21bdf0 100644 --- a/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts @@ -13,6 +13,7 @@ import type { } from '../loaders/next-flight-client-entry-loader' import { APP_DIR_ALIAS, WEBPACK_LAYERS } from '../../../lib/constants' import { + APP_CLIENT_INTERNALS, COMPILER_NAMES, EDGE_RUNTIME_WEBPACK, FLIGHT_SERVER_CSS_MANIFEST, @@ -219,7 +220,7 @@ export class FlightClientEntryPlugin { compilation, entryName: name, clientComponentImports: [...internalClientComponentEntryImports], - bundlePath: 'app-internals', + bundlePath: APP_CLIENT_INTERNALS, }) ) }) @@ -517,28 +518,26 @@ export class FlightClientEntryPlugin { addEntry( compilation: any, context: string, - entry: any /* Dependency */, - options: { - name: string - layer: string | undefined - } /* EntryOptions */ + dependency: any /* Dependency */, + options: any /* EntryOptions */ ): Promise /* Promise */ { return new Promise((resolve, reject) => { - compilation.entries.get(options.name).includeDependencies.push(entry) + const entry = compilation.entries.get(options.name) + entry.includeDependencies.push(dependency) compilation.hooks.addEntry.call(entry, options) compilation.addModuleTree( { context, - dependency: entry, + dependency, contextInfo: { issuerLayer: options.layer }, }, (err: Error | undefined, module: any) => { if (err) { - compilation.hooks.failedEntry.call(entry, options, err) + compilation.hooks.failedEntry.call(dependency, options, err) return reject(err) } - compilation.hooks.succeedEntry.call(entry, options, module) + compilation.hooks.succeedEntry.call(dependency, options, module) return resolve(module) } ) diff --git a/packages/next/client/app-next.js b/packages/next/client/app-next.js index 9243abffc1e5..ed8088e0bf62 100644 --- a/packages/next/client/app-next.js +++ b/packages/next/client/app-next.js @@ -2,9 +2,8 @@ import { appBootstrap } from './app-bootstrap' appBootstrap(() => { // Include app-router and layout-router in the main chunk - import('next/dist/client/components/app-router.js') - import('next/dist/client/components/layout-router.js') - + require('next/dist/client/components/app-router') + require('next/dist/client/components/layout-router') const { hydrate } = require('./app-index') hydrate() }) diff --git a/packages/next/shared/lib/constants.ts b/packages/next/shared/lib/constants.ts index 1b8dee89e279..f1fe5d7330f0 100644 --- a/packages/next/shared/lib/constants.ts +++ b/packages/next/shared/lib/constants.ts @@ -76,6 +76,8 @@ export const MIDDLEWARE_REACT_LOADABLE_MANIFEST = // static/runtime/main.js export const CLIENT_STATIC_FILES_RUNTIME_MAIN = `main` export const CLIENT_STATIC_FILES_RUNTIME_MAIN_APP = `${CLIENT_STATIC_FILES_RUNTIME_MAIN}-app` +// next internal client components chunk for layouts +export const APP_CLIENT_INTERNALS = 'app-client-internals' // static/runtime/react-refresh.js export const CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH = `react-refresh` // static/runtime/amp.js diff --git a/test/.stats-app/app/app-edge-ssr/page.js b/test/.stats-app/app/app-edge-ssr/page.js index 8151cdff9bd9..1e074ce268e1 100644 --- a/test/.stats-app/app/app-edge-ssr/page.js +++ b/test/.stats-app/app/app-edge-ssr/page.js @@ -1,5 +1,5 @@ export default function page() { - return 'edge-ssr' + return 'app-edge-ssr' } export const runtime = 'experimental-edge' diff --git a/test/.stats-app/app/app-ssr/page.js b/test/.stats-app/app/app-ssr/page.js new file mode 100644 index 000000000000..e4af94768566 --- /dev/null +++ b/test/.stats-app/app/app-ssr/page.js @@ -0,0 +1,5 @@ +export default function page() { + return 'app-ssr' +} + +export const revalidate = 0