Repository: vercel/next.js
Pull request: Destructure loadComponents where possible

## What?

More preparation work for removing manifest loading from loadComponents. No changes to how things function, only making it clearer what is used from loadComponents in each place it is used.

Closes PACK-5336

Changed files:
filename: packages/next/src/build/segment-config/app/app-segments.ts
patch: @@ -1,13 +1,6 @@
-import type { LoadComponentsReturnType } from '../../../server/load-components'
 import type { Params } from '../../../server/request/params'
-import type {
-  AppPageRouteModule,
-  AppPageModule,
-} from '../../../server/route-modules/app-page/module.compiled'
-import type {
-  AppRouteRouteModule,
-  AppRouteModule,
-} from '../../../server/route-modules/app-route/module.compiled'
+import type { AppPageRouteModule } from '../../../server/route-modules/app-page/module.compiled'
+import type { AppRouteRouteModule } from '../../../server/route-modules/app-route/module.compiled'
 import {
   type AppSegmentConfig,
   parseAppSegmentConfig,
@@ -25,6 +18,7 @@ import {
   type LoaderTree,
 } from '../../../server/lib/app-dir-module'
 import { PAGE_SEGMENT_KEY } from '../../../shared/lib/segment'
+import type { RouteModule } from '../../../server/route-modules/route-module'
 
 type GenerateStaticParams = (options: { params?: Params }) => Promise<Params[]>
 
@@ -187,11 +181,9 @@ function collectAppRouteSegments(
  * @param components the loaded components
  * @returns the segments for the route module
  */
-export function collectSegments({
-  routeModule,
-}: LoadComponentsReturnType<AppPageModule | AppRouteModule>):
-  | Promise<AppSegment[]>
-  | AppSegment[] {
+export function collectSegments(
+  routeModule: RouteModule
+): Promise<AppSegment[]> | AppSegment[] {
   if (isAppRouteRouteModule(routeModule)) {
     return collectAppRouteSegments(routeModule)
   }

filename: packages/next/src/build/segment-config/app/collect-root-param-keys.ts
patch: @@ -1,12 +1,10 @@
 import { getSegmentParam } from '../../../server/app-render/get-segment-param'
-import type { LoadComponentsReturnType } from '../../../server/load-components'
-import type { AppPageModule } from '../../../server/route-modules/app-page/module'
 import type AppPageRouteModule from '../../../server/route-modules/app-page/module'
-import type { AppRouteModule } from '../../../server/route-modules/app-route/module'
 import {
   isAppPageRouteModule,
   isAppRouteRouteModule,
 } from '../../../server/route-modules/checks'
+import type { RouteModule } from '../../../server/route-modules/route-module'
 import { InvariantError } from '../../../shared/lib/invariant-error'
 
 function collectAppPageRootParamKeys(
@@ -46,11 +44,9 @@ function collectAppPageRootParamKeys(
  * @param components the loaded components
  * @returns the segments for the route module
  */
-export function collectRootParamKeys({
-  routeModule,
-}: LoadComponentsReturnType<
-  AppPageModule | AppRouteModule
->): readonly string[] {
+export function collectRootParamKeys(
+  routeModule: RouteModule
+): readonly string[] {
   if (isAppRouteRouteModule(routeModule)) {
     return []
   }

filename: packages/next/src/build/utils.ts
patch: @@ -16,7 +16,6 @@ import type {
 } from './webpack/plugins/middleware-plugin'
 import type { WebpackLayerName } from '../lib/constants'
 import type { AppPageModule } from '../server/route-modules/app-page/module'
-import type { RouteModule } from '../server/route-modules/route-module'
 import type { NextComponentType } from '../shared/lib/utils'
 
 import '../server/require-hook'
@@ -1163,9 +1162,10 @@ export async function isPageStatic({
           sriEnabled,
         })
       }
-      const Comp = componentsResult.Component as NextComponentType | undefined
 
-      const routeModule: RouteModule = componentsResult.routeModule
+      const { Component, routeModule } = componentsResult
+
+      const Comp = Component as NextComponentType | undefined
 
       let isRoutePPREnabled: boolean = false
 
@@ -1176,7 +1176,7 @@ export async function isPageStatic({
 
         let segments
         try {
-          segments = await collectSegments(componentsResult)
+          segments = await collectSegments(routeModule)
         } catch (err) {
           throw new Error(`Failed to collect configuration for ${page}`, {
             cause: err,
@@ -1194,7 +1194,7 @@ export async function isPageStatic({
           )
         }
 
-        rootParamKeys = collectRootParamKeys(componentsResult)
+        rootParamKeys = collectRootParamKeys(routeModule)
 
         // A page supports partial prerendering if it is an app page and either
         // the whole app has PPR enabled or this page has PPR enabled when we're
@@ -1422,14 +1422,14 @@ export async function hasCustomGetInitialProps({
     require('../shared/lib/runtime-config.external') as typeof import('../shared/lib/runtime-config.external')
   ).setConfig(runtimeEnvConfig)
 
-  const components = await loadComponents({
+  const { ComponentMod } = await loadComponents({
     distDir,
     page: page,
     isAppPath: false,
     isDev: false,
     sriEnabled,
   })
-  let mod = components.ComponentMod
+  let mod = ComponentMod
 
   if (checkingApp) {
     mod = (await mod._app) || mod.default || mod
@@ -1454,16 +1454,16 @@ export async function getDefinedNamedExports({
   ;(
     require('../shared/lib/runtime-config.external') as typeof import('../shared/lib/runtime-config.external')
   ).setConfig(runtimeEnvConfig)
-  const components = await loadComponents({
+  const { ComponentMod } = await loadComponents({
     distDir,
     page: page,
     isAppPath: false,
     isDev: false,
     sriEnabled,
   })
 
-  return Object.keys(components.ComponentMod).filter((key) => {
-    return typeof components.ComponentMod[key] !== 'undefined'
+  return Object.keys(ComponentMod).filter((key) => {
+    return typeof ComponentMod[key] !== 'undefined'
   })
 }
 

filename: packages/next/src/server/dev/static-paths-worker.ts
patch: @@ -107,13 +107,14 @@ export async function loadStaticPaths({
   })
 
   if (isAppPath) {
-    const segments = await collectSegments(components)
+    const routeModule = components.routeModule
+    const segments = await collectSegments(routeModule)
 
     const isRoutePPREnabled =
-      isAppPageRouteModule(components.routeModule) &&
+      isAppPageRouteModule(routeModule) &&
       checkIsRoutePPREnabled(config.pprConfig, reduceAppConfig(segments))
 
-    const rootParamKeys = collectRootParamKeys(components)
+    const rootParamKeys = collectRootParamKeys(routeModule)
 
     return buildAppStaticPaths({
       dir,

filename: packages/next/src/server/next-server.ts
patch: @@ -361,37 +361,42 @@ export default class NextNodeServer extends BaseServer<
     await this.loadCustomCacheHandlers()
 
     for (const page of Object.keys(pagesManifest || {})) {
-      await loadComponents({
-        distDir: this.distDir,
-        page,
-        isAppPath: false,
-        isDev: this.isDev,
-        sriEnabled: this.sriEnabled,
-      }).catch(() => {})
+      try {
+        await loadComponents({
+          distDir: this.distDir,
+          page,
+          isAppPath: false,
+          isDev: this.isDev,
+          sriEnabled: this.sriEnabled,
+        })
+      } catch (_err) {
+        // Intentionally ignored because this is a preload step.
+      }
     }
 
     for (const page of Object.keys(appPathsManifest || {})) {
-      await loadComponents({
-        distDir: this.distDir,
-        page,
-        isAppPath: true,
-        isDev: this.isDev,
-        sriEnabled: this.sriEnabled,
-      })
-        .then(async ({ ComponentMod }) => {
-          // we need to ensure fetch is patched before we require the page,
-          // otherwise if the fetch is patched by user code, we will be patching it
-          // too late and there won't be any caching behaviors
-          ComponentMod.patchFetch()
-
-          const webpackRequire = ComponentMod.__next_app__.require
-          if (webpackRequire?.m) {
-            for (const id of Object.keys(webpackRequire.m)) {
-              await webpackRequire(id)
-            }
-          }
+      try {
+        const { ComponentMod } = await loadComponents({
+          distDir: this.distDir,
+          page,
+          isAppPath: true,
+          isDev: this.isDev,
+          sriEnabled: this.sriEnabled,
         })
-        .catch(() => {})
+        // we need to ensure fetch is patched before we require the page,
+        // otherwise if the fetch is patched by user code, we will be patching it
+        // too late and there won't be any caching behaviors
+        ComponentMod.patchFetch()
+
+        const webpackRequire = ComponentMod.__next_app__.require
+        if (webpackRequire?.m) {
+          for (const id of Object.keys(webpackRequire.m)) {
+            await webpackRequire(id)
+          }
+        }
+      } catch (_err) {
+        // Intentionally ignored because this is a preload step.
+      }
     }
   }