From b1669e69313f313504eb85ab4abdf9b804b80bee Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 13 Dec 2023 13:01:51 -0700 Subject: [PATCH] Revert "Revert "Page Info Cleanup (#59430)" (#59592)" This reverts commit b345e1b01ef4fac660ba666a0eec00a1784c04c3. --- .../next/src/build/collect-build-traces.ts | 4 +- packages/next/src/build/index.ts | 149 +++++++++--------- packages/next/src/build/page-info.ts | 30 ++++ packages/next/src/build/utils.ts | 43 ++--- 4 files changed, 115 insertions(+), 111 deletions(-) create mode 100644 packages/next/src/build/page-info.ts diff --git a/packages/next/src/build/collect-build-traces.ts b/packages/next/src/build/collect-build-traces.ts index 87ca40ece1752..90c692dd62074 100644 --- a/packages/next/src/build/collect-build-traces.ts +++ b/packages/next/src/build/collect-build-traces.ts @@ -15,10 +15,10 @@ import { import path from 'path' import fs from 'fs/promises' import { - deserializePageInfos, type PageInfos, type SerializedPageInfos, -} from './utils' + deserializePageInfos, +} from './page-info' import { loadBindings } from './swc' import { nonNullable } from '../lib/non-nullable' import * as ciEnvironment from '../telemetry/ci-info' diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 45da66f31ee95..a68336c51f3bd 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -6,6 +6,7 @@ import type { MiddlewareManifest } from './webpack/plugins/middleware-plugin' import type { ActionManifest } from './webpack/plugins/flight-client-entry-plugin' import type { ExportAppOptions, ExportAppWorker } from '../export/types' import type { Revalidate } from '../server/lib/revalidate' +import { serializePageInfos, type PageInfo, type PageInfos } from './page-info' import '../lib/setup-exception-listeners' @@ -114,9 +115,8 @@ import { copyTracedFiles, isReservedPage, isAppBuiltinNotFoundPage, - serializePageInfos, } from './utils' -import type { PageInfo, PageInfos, AppConfig } from './utils' +import type { AppConfig } from './utils' import { writeBuildId } from './write-build-id' import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path' import isError from '../lib/is-error' @@ -162,6 +162,7 @@ import { formatManifest } from './manifests/formatter/format-manifest' import { getStartServerInfo, logStartInfo } from '../server/lib/app-info-log' import type { NextEnabledDirectories } from '../server/base-server' import { hasCustomExportOutput } from '../export/utils' +import { RouteKind } from '../server/future/route-kind' interface ExperimentalBypassForInfo { experimentalBypassFor?: RouteHas[] @@ -2283,22 +2284,30 @@ export default async function build( appConfig.revalidate === 0 || exportResult.byPath.get(page)?.revalidate === 0 - if (hasDynamicData && pageInfos.get(page)?.isStatic) { - // if the page was marked as being static, but it contains dynamic data - // (ie, in the case of a static generation bailout), then it should be marked dynamic - pageInfos.set(page, { - ...(pageInfos.get(page) as PageInfo), - isStatic: false, - isSSG: false, - }) + const pageInfo = pageInfos.get(page) + if (!pageInfo) { + throw new Error( + `Invariant: page info for ${page} is missing from registry` + ) } - const isRouteHandler = isAppRouteRoute(originalAppPath) + if (hasDynamicData && pageInfo.isStatic) { + // If the page was marked as being static, but it contains dynamic + // data (ie, in the case of a static generation bailout), then it + // should be marked dynamic. + pageInfo.isStatic = false + pageInfo.isSSG = false + } + + const isDynamic = isDynamicRoute(page) + const kind = isAppRouteRoute(originalAppPath) + ? RouteKind.APP_ROUTE + : RouteKind.APP_PAGE // When this is an app page and PPR is enabled, the route supports // partial pre-rendering. const experimentalPPR = - !isRouteHandler && config.experimental.ppr === true + kind === RouteKind.APP_PAGE && config.experimental.ppr === true ? true : undefined @@ -2324,34 +2333,27 @@ export default async function build( hasPostponed, } = exportResult.byPath.get(route) ?? {} - pageInfos.set(route, { - ...(pageInfos.get(route) as PageInfo), - hasPostponed, - hasEmptyPrelude, - }) + // If this route postponed and/or had an empty prelude, then + // mark the page as having postponed and/or an empty prelude. + pageInfo.hasPostponed ||= hasPostponed + pageInfo.hasEmptyPrelude ||= hasEmptyPrelude - // update the page (eg /blog/[slug]) to also have the postpone metadata - pageInfos.set(page, { - ...(pageInfos.get(page) as PageInfo), - hasPostponed, - hasEmptyPrelude, - }) + // Link the same pageInfo used for the `page` to this specific + // `route` as they should all share the same characteristics. + pageInfos.set(route, pageInfo) if (revalidate !== 0) { - const normalizedRoute = normalizePagePath(route) - - let dataRoute: string | null - if (isRouteHandler) { - dataRoute = null - } else { - dataRoute = path.posix.join(`${normalizedRoute}${RSC_SUFFIX}`) - } - - let prefetchDataRoute: string | null | undefined - if (experimentalPPR) { - prefetchDataRoute = path.posix.join( - `${normalizedRoute}${RSC_PREFETCH_SUFFIX}` - ) + let dataRoute: string | null = null + let prefetchDataRoute: string | undefined + + // If this is for an app page, then we should associate a data + // route (and prefetch if PPR is enabled) for it. + if (kind === RouteKind.APP_PAGE) { + const normalized = normalizePagePath(route) + dataRoute = `${normalized}${RSC_SUFFIX}` + if (experimentalPPR) { + prefetchDataRoute = `${normalized}${RSC_PREFETCH_SUFFIX}` + } } const routeMeta: Partial = {} @@ -2398,34 +2400,30 @@ export default async function build( hasDynamicData = true // we might have determined during prerendering that this page // used dynamic data - pageInfos.set(route, { - ...(pageInfos.get(route) as PageInfo), - isSSG: false, - isStatic: false, - }) + pageInfo.isStatic = false + pageInfo.isSSG = false } }) - if (!hasDynamicData && isDynamicRoute(originalAppPath)) { - const normalizedRoute = normalizePagePath(page) - const dataRoute = path.posix.join( - `${normalizedRoute}${RSC_SUFFIX}` - ) + if (!hasDynamicData && isDynamic) { + let dataRoute: string | null = null + let prefetchDataRoute: string | undefined - let prefetchDataRoute: string | null | undefined - if (experimentalPPR) { - prefetchDataRoute = path.posix.join( - `${normalizedRoute}${RSC_PREFETCH_SUFFIX}` - ) + // If this is for an app page, then we should associate a data + // route (and prefetch if PPR is enabled) for it. + if (kind === RouteKind.APP_PAGE) { + const normalized = normalizePagePath(page) + dataRoute = `${normalized}${RSC_SUFFIX}` + if (experimentalPPR) { + prefetchDataRoute = `${normalized}${RSC_PREFETCH_SUFFIX}` + } } - pageInfos.set(page, { - ...(pageInfos.get(page) as PageInfo), - isDynamicAppRoute: true, - // if PPR is turned on and the route contains a dynamic segment, - // we assume it'll be partially prerendered - hasPostponed: experimentalPPR, - }) + pageInfo.isDynamicAppRoute = true + + // If PPR is turned on and the route contains a dynamic segment, + // we assume it'll be partially prerendered + pageInfo.hasPostponed = experimentalPPR // TODO: create a separate manifest to allow enforcing // dynamicParams for non-static paths? @@ -2435,33 +2433,32 @@ export default async function build( routeRegex: normalizeRouteRegex( getNamedRouteRegex(page, false).re.source ), - dataRoute, // if dynamicParams are enabled treat as fallback: // 'blocking' if not it's fallback: false fallback: appDynamicParamPaths.has(originalAppPath) ? null : false, - dataRouteRegex: isRouteHandler - ? null - : normalizeRouteRegex( + dataRoute, + dataRouteRegex: dataRoute + ? normalizeRouteRegex( getNamedRouteRegex( dataRoute.replace(/\.rsc$/, ''), false ).re.source.replace(/\(\?:\\\/\)\?\$$/, '\\.rsc$') - ), + ) + : null, prefetchDataRoute, - prefetchDataRouteRegex: - isRouteHandler || !prefetchDataRoute - ? undefined - : normalizeRouteRegex( - getNamedRouteRegex( - prefetchDataRoute.replace(/\.prefetch\.rsc$/, ''), - false - ).re.source.replace( - /\(\?:\\\/\)\?\$$/, - '\\.prefetch\\.rsc$' - ) - ), + prefetchDataRouteRegex: prefetchDataRoute + ? normalizeRouteRegex( + getNamedRouteRegex( + prefetchDataRoute.replace(/\.prefetch\.rsc$/, ''), + false + ).re.source.replace( + /\(\?:\\\/\)\?\$$/, + '\\.prefetch\\.rsc$' + ) + ) + : undefined, } } } diff --git a/packages/next/src/build/page-info.ts b/packages/next/src/build/page-info.ts new file mode 100644 index 0000000000000..65c4e572c7f43 --- /dev/null +++ b/packages/next/src/build/page-info.ts @@ -0,0 +1,30 @@ +import type { ServerRuntime } from '../../types' + +export interface PageInfo { + isHybridAmp?: boolean + size: number + totalSize: number + isStatic: boolean + isSSG: boolean + isPPR: boolean + ssgPageRoutes: string[] | null + initialRevalidateSeconds: number | false + pageDuration: number | undefined + ssgPageDurations: number[] | undefined + runtime: ServerRuntime + hasEmptyPrelude?: boolean + hasPostponed?: boolean + isDynamicAppRoute?: boolean +} + +export type PageInfos = Map + +export type SerializedPageInfos = [string, PageInfo][] + +export function serializePageInfos(input: PageInfos): SerializedPageInfos { + return Array.from(input.entries()) +} + +export function deserializePageInfos(input: SerializedPageInfos): PageInfos { + return new Map(input) +} diff --git a/packages/next/src/build/utils.ts b/packages/next/src/build/utils.ts index 8a7d94b35cc63..19846cebd82fd 100644 --- a/packages/next/src/build/utils.ts +++ b/packages/next/src/build/utils.ts @@ -23,6 +23,7 @@ import type { AppPageModule } from '../server/future/route-modules/app-page/modu import type { RouteModule } from '../server/future/route-modules/route-module' import type { LoaderTree } from '../server/lib/app-dir-module' import type { NextComponentType } from '../shared/lib/utils' +import type { PageInfo } from './page-info' import '../server/require-hook' import '../server/node-polyfill-crypto' @@ -326,35 +327,6 @@ const filterAndSortList = ( return pages.sort((a, b) => a.localeCompare(b)) } -export interface PageInfo { - isHybridAmp?: boolean - size: number - totalSize: number - isStatic: boolean - isSSG: boolean - isPPR: boolean - ssgPageRoutes: string[] | null - initialRevalidateSeconds: number | false - pageDuration: number | undefined - ssgPageDurations: number[] | undefined - runtime: ServerRuntime - hasEmptyPrelude?: boolean - hasPostponed?: boolean - isDynamicAppRoute?: boolean -} - -export type PageInfos = Map - -export type SerializedPageInfos = [string, PageInfo][] - -export function serializePageInfos(input: PageInfos): SerializedPageInfos { - return Array.from(input.entries()) -} - -export function deserializePageInfos(input: SerializedPageInfos): PageInfos { - return new Map(input) -} - export async function printTreeView( lists: { pages: ReadonlyArray @@ -659,10 +631,15 @@ export async function printTreeView( messages.push(['', '', '']) } - pageInfos.set('/404', { - ...(pageInfos.get('/404') || pageInfos.get('/_error'))!, - isStatic: useStaticPages404, - }) + // We should update the `isStatic` part of the pageInfo for the /404 page. + // When we're using experimental compile, this won't be available. + const pageInfo = pageInfos.get('/404') || pageInfos.get('/_error') + if (pageInfo) { + pageInfos.set('/404', { + ...pageInfo, + isStatic: useStaticPages404, + }) + } // If there's no app /_notFound page present, then the 404 is still using the pages/404 if (!lists.pages.includes('/404') && !lists.app?.includes('/_not-found')) {