Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Page Info Cleanup v2 #59780

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 4 additions & 13 deletions packages/next/src/build/collect-build-traces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ import {

import path from 'path'
import fs from 'fs/promises'
import {
deserializePageInfos,
type PageInfos,
type SerializedPageInfos,
} from './utils'
import { loadBindings } from './swc'
import { nonNullable } from '../lib/non-nullable'
import * as ciEnvironment from '../telemetry/ci-info'
Expand Down Expand Up @@ -71,7 +66,7 @@ export async function collectBuildTraces({
dir,
config,
distDir,
pageInfos,
edgeRoutes,
staticPages,
nextBuildSpan = new Span({ name: 'build' }),
hasSsrAmpPages,
Expand All @@ -83,8 +78,7 @@ export async function collectBuildTraces({
staticPages: string[]
hasSsrAmpPages: boolean
outputFileTracingRoot: string
// pageInfos is serialized when this function runs in a worker.
pageInfos: PageInfos | SerializedPageInfos
edgeRoutes: Readonly<Record<string, boolean>> | undefined
nextBuildSpan?: Span
config: NextConfigComplete
buildTraceContext?: BuildTraceContext
Expand Down Expand Up @@ -628,8 +622,6 @@ export async function collectBuildTraces({
}

const { entryNameFilesMap } = buildTraceContext?.chunksTrace || {}
const infos =
pageInfos instanceof Map ? pageInfos : deserializePageInfos(pageInfos)

await Promise.all(
[
Expand All @@ -649,9 +641,8 @@ export async function collectBuildTraces({
return
}

// edge routes have no trace files
const pageInfo = infos.get(route)
if (pageInfo?.runtime === 'edge') {
// Edge routes have no trace files.
if (edgeRoutes && route in edgeRoutes) {
return
}

Expand Down
166 changes: 85 additions & 81 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 type { PageInfo } from './page-info'

import '../lib/setup-exception-listeners'

Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -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[]
Expand Down Expand Up @@ -1133,8 +1134,7 @@ export default async function build(
dir,
config,
distDir,
// Serialize Map as this is sent to the worker.
pageInfos: serializePageInfos(new Map()),
edgeRoutes: undefined,
staticPages: [],
hasSsrAmpPages: false,
buildTraceContext,
Expand Down Expand Up @@ -1208,7 +1208,7 @@ export default async function build(
const appNormalizedPaths = new Map<string, string>()
const appDynamicParamPaths = new Set<string>()
const appDefaultConfigs = new Map<string, AppConfig>()
const pageInfos: PageInfos = new Map<string, PageInfo>()
const pageInfos = new Map<string, PageInfo>()
const pagesManifest = JSON.parse(
await fs.readFile(pagesManifestPath, 'utf8')
) as PagesManifest
Expand Down Expand Up @@ -1944,12 +1944,20 @@ export default async function build(
}

if (!isGenerateMode && config.outputFileTracing && !buildTracesPromise) {
// Get all the edge routes from the pageInfos. If the runtime is set as
// edge, then we know it's an edge route.
const edgeRoutes: Record<string, boolean> = {}
pageInfos.forEach((pageInfo, route) => {
if (pageInfo.runtime !== 'edge') return
edgeRoutes[route] = true
})

buildTracesPromise = collectBuildTraces({
dir,
config,
distDir,
pageInfos,
staticPages: [...staticPages],
edgeRoutes,
staticPages: Array.from(staticPages),
nextBuildSpan,
hasSsrAmpPages,
buildTraceContext,
Expand Down Expand Up @@ -2283,22 +2291,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

Expand All @@ -2324,34 +2340,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<SsgRoute> = {}
Expand Down Expand Up @@ -2398,34 +2407,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?
Expand All @@ -2435,33 +2440,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,
}
}
}
Expand Down
18 changes: 18 additions & 0 deletions packages/next/src/build/page-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
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
}
Loading