From 49204b86836c6a8d8a75b4e47ffda200dc2a9e75 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Wed, 26 Oct 2022 14:29:11 -0700 Subject: [PATCH 1/5] Merge app internal chunk into main chunk for layouts --- packages/next/build/index.ts | 15 ++++++++++- packages/next/build/webpack-config.ts | 25 +++++++++++++------ .../plugins/flight-client-entry-plugin.ts | 20 +++++++-------- packages/next/client/app-next.js | 10 +++++--- 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, 56 insertions(+), 23 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 0e20c2a9e7db..4ebe6b0d281e 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_INTERNALS, } from '../shared/lib/constants' import { getSortedRoutes, isDynamicRoute } from '../shared/lib/router/utils' import { __ApiPreviewProps } from '../server/api-utils' @@ -121,6 +123,7 @@ import { RemotePattern } from '../shared/lib/image-config' import { eventSwcPlugins } from '../telemetry/events/swc-plugins' import { normalizeAppPath } from '../shared/lib/router/utils/app-paths' import { AppBuildManifest } from './webpack/plugins/app-build-manifest-plugin' +import { EntryObject } from 'webpack' export type SsgRoute = { initialRevalidateSeconds: number | false @@ -975,7 +978,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_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-ignore 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..b861ce14ef4c 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -28,6 +28,7 @@ import { SERVER_DIRECTORY, COMPILER_NAMES, CompilerNameValues, + APP_INTERNALS, } from '../shared/lib/constants' import { execOnce } from '../shared/lib/utils' import { NextConfigComplete } from '../server/config-shared' @@ -78,6 +79,11 @@ const babelIncludeRegexes: RegExp[] = [ const reactPackagesRegex = /^(react(?:$|\/)|react-dom(?:$|\/))/ +const builtinReactPackages = [ + /next[\\/]dist[\\/]compiled[\\/](react|react-dom)[\\/]/, + /next[\\/]dist[\\/]compiled[\\/]react-server-dom-webpack[\\/]server.browser/, +] + const staticGenerationAsyncStorageRegex = /next[\\/]dist[\\/]client[\\/]components[\\/]static-generation-async-storage/ @@ -795,13 +801,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..e68851ad7580 100644 --- a/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts @@ -1,6 +1,7 @@ import { stringify } from 'querystring' import path from 'path' import { webpack, sources } from 'next/dist/compiled/webpack/webpack' +import type { Dependency, EntryOptions } from 'webpack' import { getInvalidator, entries, @@ -13,6 +14,7 @@ import type { } from '../loaders/next-flight-client-entry-loader' import { APP_DIR_ALIAS, WEBPACK_LAYERS } from '../../../lib/constants' import { + APP_INTERNALS, COMPILER_NAMES, EDGE_RUNTIME_WEBPACK, FLIGHT_SERVER_CSS_MANIFEST, @@ -219,7 +221,7 @@ export class FlightClientEntryPlugin { compilation, entryName: name, clientComponentImports: [...internalClientComponentEntryImports], - bundlePath: 'app-internals', + bundlePath: APP_INTERNALS, }) ) }) @@ -517,28 +519,26 @@ export class FlightClientEntryPlugin { addEntry( compilation: any, context: string, - entry: any /* Dependency */, - options: { - name: string - layer: string | undefined - } /* EntryOptions */ + dependency: Dependency, + options: 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..94a8824ff4d1 100644 --- a/packages/next/client/app-next.js +++ b/packages/next/client/app-next.js @@ -1,10 +1,12 @@ 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') +// Include app-router and layout-router in the main chunk +require('next/dist/client/components/app-router') +require('next/dist/client/components/layout-router') +require('react') +require('next/dist/compiled/react-server-dom-webpack/client') +appBootstrap(() => { const { hydrate } = require('./app-index') hydrate() }) diff --git a/packages/next/shared/lib/constants.ts b/packages/next/shared/lib/constants.ts index f567505b9857..80ea6a02d53a 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_INTERNALS = 'app-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 From 661b182ea6257f6e5f83ac28dd133f8059b4ac50 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Wed, 26 Oct 2022 14:55:18 -0700 Subject: [PATCH 2/5] fix lint --- packages/next/build/index.ts | 1 - packages/next/build/webpack-config.ts | 7 +------ .../build/webpack/plugins/flight-client-entry-plugin.ts | 4 ++-- packages/next/shared/lib/constants.ts | 2 +- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 4ebe6b0d281e..0896b18814ca 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -123,7 +123,6 @@ import { RemotePattern } from '../shared/lib/image-config' import { eventSwcPlugins } from '../telemetry/events/swc-plugins' import { normalizeAppPath } from '../shared/lib/router/utils/app-paths' import { AppBuildManifest } from './webpack/plugins/app-build-manifest-plugin' -import { EntryObject } from 'webpack' export type SsgRoute = { initialRevalidateSeconds: number | false diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index b861ce14ef4c..f4f6409ec8e6 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -28,7 +28,7 @@ import { SERVER_DIRECTORY, COMPILER_NAMES, CompilerNameValues, - APP_INTERNALS, + APP_CLIENT_INTERNALS, } from '../shared/lib/constants' import { execOnce } from '../shared/lib/utils' import { NextConfigComplete } from '../server/config-shared' @@ -79,11 +79,6 @@ const babelIncludeRegexes: RegExp[] = [ const reactPackagesRegex = /^(react(?:$|\/)|react-dom(?:$|\/))/ -const builtinReactPackages = [ - /next[\\/]dist[\\/]compiled[\\/](react|react-dom)[\\/]/, - /next[\\/]dist[\\/]compiled[\\/]react-server-dom-webpack[\\/]server.browser/, -] - const staticGenerationAsyncStorageRegex = /next[\\/]dist[\\/]client[\\/]components[\\/]static-generation-async-storage/ 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 e68851ad7580..5b33cbf1945f 100644 --- a/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts @@ -14,7 +14,7 @@ import type { } from '../loaders/next-flight-client-entry-loader' import { APP_DIR_ALIAS, WEBPACK_LAYERS } from '../../../lib/constants' import { - APP_INTERNALS, + APP_CLIENT_INTERNALS, COMPILER_NAMES, EDGE_RUNTIME_WEBPACK, FLIGHT_SERVER_CSS_MANIFEST, @@ -221,7 +221,7 @@ export class FlightClientEntryPlugin { compilation, entryName: name, clientComponentImports: [...internalClientComponentEntryImports], - bundlePath: APP_INTERNALS, + bundlePath: APP_CLIENT_INTERNALS, }) ) }) diff --git a/packages/next/shared/lib/constants.ts b/packages/next/shared/lib/constants.ts index 80ea6a02d53a..044d196d87ed 100644 --- a/packages/next/shared/lib/constants.ts +++ b/packages/next/shared/lib/constants.ts @@ -77,7 +77,7 @@ export const MIDDLEWARE_REACT_LOADABLE_MANIFEST = 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_INTERNALS = 'app-internals' +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 From ed26f79cc9fbba9a1b1eb015ec82e0a2b324c9ed Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Wed, 26 Oct 2022 15:04:10 -0700 Subject: [PATCH 3/5] fix build --- packages/next/build/index.ts | 4 ++-- .../next/build/webpack/plugins/flight-client-entry-plugin.ts | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 0896b18814ca..46d7e0294b32 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -60,7 +60,7 @@ import { RSC_MODULE_TYPES, FONT_LOADER_MANIFEST, CLIENT_STATIC_FILES_RUNTIME_MAIN_APP, - APP_INTERNALS, + APP_CLIENT_INTERNALS, } from '../shared/lib/constants' import { getSortedRoutes, isDynamicRoute } from '../shared/lib/router/utils' import { __ApiPreviewProps } from '../server/api-utils' @@ -978,7 +978,7 @@ export default async function build( if (!serverResult.errors.length && !edgeServerResult?.errors.length) { injectedClientEntries.forEach((value, key) => { const clientEntry = clientConfig.entry as webpack.EntryObject - if (key === APP_INTERNALS) { + 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-ignore clientEntry['main-app'] is type EntryDescription { import: ... } 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 5b33cbf1945f..9865fc21bdf0 100644 --- a/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts @@ -1,7 +1,6 @@ import { stringify } from 'querystring' import path from 'path' import { webpack, sources } from 'next/dist/compiled/webpack/webpack' -import type { Dependency, EntryOptions } from 'webpack' import { getInvalidator, entries, @@ -519,8 +518,8 @@ export class FlightClientEntryPlugin { addEntry( compilation: any, context: string, - dependency: Dependency, - options: EntryOptions + dependency: any /* Dependency */, + options: any /* EntryOptions */ ): Promise /* Promise */ { return new Promise((resolve, reject) => { const entry = compilation.entries.get(options.name) From b9b6254a021a65c474d55b3f383125f3908fd1ec Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Thu, 27 Oct 2022 23:35:43 +0200 Subject: [PATCH 4/5] merge canary --- packages/next/build/webpack-config.ts | 1 - packages/next/client/app-next.js | 9 +++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index f4f6409ec8e6..278508819632 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -28,7 +28,6 @@ import { SERVER_DIRECTORY, COMPILER_NAMES, CompilerNameValues, - APP_CLIENT_INTERNALS, } from '../shared/lib/constants' import { execOnce } from '../shared/lib/utils' import { NextConfigComplete } from '../server/config-shared' diff --git a/packages/next/client/app-next.js b/packages/next/client/app-next.js index 94a8824ff4d1..ed8088e0bf62 100644 --- a/packages/next/client/app-next.js +++ b/packages/next/client/app-next.js @@ -1,12 +1,9 @@ import { appBootstrap } from './app-bootstrap' -// Include app-router and layout-router in the main chunk -require('next/dist/client/components/app-router') -require('next/dist/client/components/layout-router') -require('react') -require('next/dist/compiled/react-server-dom-webpack/client') - appBootstrap(() => { + // Include app-router and layout-router in the main chunk + require('next/dist/client/components/app-router') + require('next/dist/client/components/layout-router') const { hydrate } = require('./app-index') hydrate() }) From 5f58e4aee0cd42a30094e959e82f10a3a96de392 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Fri, 28 Oct 2022 00:13:47 +0200 Subject: [PATCH 5/5] use ts-expect-error --- packages/next/build/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 46d7e0294b32..1a77998d1c2b 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -981,7 +981,7 @@ export default async function build( 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-ignore clientEntry['main-app'] is type EntryDescription { import: ... } + // @ts-expect-error clientEntry['main-app'] is type EntryDescription { import: ... } ...clientEntry[CLIENT_STATIC_FILES_RUNTIME_MAIN_APP].import, value, ]