Skip to content

Commit

Permalink
feat: added partial shell generation using root params
Browse files Browse the repository at this point in the history
  • Loading branch information
wyattjoh committed Dec 16, 2024
1 parent 20b4910 commit e689a21
Show file tree
Hide file tree
Showing 15 changed files with 550 additions and 211 deletions.
7 changes: 6 additions & 1 deletion packages/next/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -613,5 +613,10 @@
"612": "ServerPrerenderStreamResult cannot be consumed as a stream because it is not yet complete. status: %s",
"613": "Expected the input to be `string | string[]`",
"614": "Route %s used \"unstable_rootParams\" inside \"use cache\". This is not currently supported.",
"615": "Missing workStore in unstable_rootParams"
"615": "Missing workStore in unstable_rootParams",
"616": "App config not found",
"617": "A required parameter (%s) was not provided as a string received %s in generateStaticParams for %s",
"618": "A required parameter (%s) was not provided as an array received %s in generateStaticParams for %s",
"619": "Page not found",
"620": "A required parameter (%s) was not provided as %s received %s in getStaticPaths for %s"
}
108 changes: 64 additions & 44 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ import {
collectMeta,
} from './utils'
import type { PageInfo, PageInfos } from './utils'
import type { PrerenderedRoute } from './static-paths/types'
import type { AppSegmentConfig } from './segment-config/app/app-segment-config'
import { writeBuildId } from './write-build-id'
import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path'
Expand Down Expand Up @@ -212,7 +213,7 @@ import {
formatNodeOptions,
getParsedNodeOptionsWithoutInspect,
} from '../server/lib/utils'
import type { PrerenderedRoute } from './static-paths/types'
import { InvariantError } from '../shared/lib/invariant-error'

type Fallback = null | boolean | string

Expand Down Expand Up @@ -2131,11 +2132,16 @@ export default async function build(
} else {
const isDynamic = isDynamicRoute(page)

if (
typeof workerResult.isRoutePPREnabled === 'boolean'
) {
isRoutePPREnabled = workerResult.isRoutePPREnabled
}

// If this route can be partially pre-rendered, then
// mark it as such and mark that it can be
// generated server-side.
if (workerResult.isRoutePPREnabled) {
isRoutePPREnabled = workerResult.isRoutePPREnabled
isSSG = true
isStatic = true

Expand All @@ -2160,7 +2166,7 @@ export default async function build(
workerResult.prerenderedRoutes
)
ssgPageRoutes = workerResult.prerenderedRoutes.map(
(route) => route.path
(route) => route.pathname
)
isSSG = true
}
Expand Down Expand Up @@ -2188,9 +2194,12 @@ export default async function build(
if (!isDynamic) {
staticPaths.set(originalAppPath, [
{
path: page,
encoded: page,
pathname: page,
encodedPathname: page,
fallbackRouteParams: undefined,
fallbackMode:
workerResult.prerenderFallbackMode,
fallbackRootParams: undefined,
},
])
isStatic = true
Expand Down Expand Up @@ -2256,7 +2265,7 @@ export default async function build(
workerResult.prerenderedRoutes
)
ssgPageRoutes = workerResult.prerenderedRoutes.map(
(route) => route.path
(route) => route.pathname
)
}

Expand Down Expand Up @@ -2692,7 +2701,7 @@ export default async function build(
new Map(
Array.from(additionalPaths.entries()).map(
([page, routes]): [string, string[]] => {
return [page, routes.map((route) => route.path)]
return [page, routes.map((route) => route.pathname)]
}
)
)
Expand Down Expand Up @@ -2743,9 +2752,9 @@ export default async function build(
// post slugs.
additionalPaths.forEach((routes, page) => {
routes.forEach((route) => {
defaultMap[route.path] = {
defaultMap[route.pathname] = {
page,
query: { __nextSsgPath: route.encoded },
query: { __nextSsgPath: route.encodedPathname },
}
})
})
Expand Down Expand Up @@ -2773,9 +2782,9 @@ export default async function build(
: undefined

routes.forEach((route) => {
defaultMap[route.path] = {
defaultMap[route.pathname] = {
page: originalAppPath,
query: { __nextSsgPath: route.encoded },
query: { __nextSsgPath: route.encodedPathname },
_fallbackRouteParams: route.fallbackRouteParams,
_isDynamicError: isDynamicError,
_isAppDir: true,
Expand Down Expand Up @@ -2885,8 +2894,11 @@ export default async function build(
}

staticPaths.forEach((prerenderedRoutes, originalAppPath) => {
const page = appNormalizedPaths.get(originalAppPath) || ''
const appConfig = appDefaultConfigs.get(originalAppPath) || {}
const page = appNormalizedPaths.get(originalAppPath)
if (!page) throw new InvariantError('Page not found')

const appConfig = appDefaultConfigs.get(originalAppPath)
if (!appConfig) throw new InvariantError('App config not found')

let hasRevalidateZero =
appConfig.revalidate === 0 ||
Expand Down Expand Up @@ -2928,8 +2940,8 @@ export default async function build(
// route), any routes that were generated with unknown route params
// should be collected and included in the dynamic routes part
// of the manifest instead.
const routes: string[] = []
const dynamicRoutes: string[] = []
const routes: PrerenderedRoute[] = []
const dynamicRoutes: PrerenderedRoute[] = []

// Sort the outputted routes to ensure consistent output. Any route
// though that has unknown route params will be pulled and sorted
Expand All @@ -2951,11 +2963,11 @@ export default async function build(

unknownPrerenderRoutes = getSortedRouteObjects(
unknownPrerenderRoutes,
(prerenderedRoute) => prerenderedRoute.path
(prerenderedRoute) => prerenderedRoute.pathname
)
knownPrerenderRoutes = getSortedRouteObjects(
knownPrerenderRoutes,
(prerenderedRoute) => prerenderedRoute.path
(prerenderedRoute) => prerenderedRoute.pathname
)

prerenderedRoutes = [
Expand All @@ -2966,7 +2978,7 @@ export default async function build(
for (const prerenderedRoute of prerenderedRoutes) {
// TODO: check if still needed?
// Exclude the /_not-found route.
if (prerenderedRoute.path === UNDERSCORE_NOT_FOUND_ROUTE) {
if (prerenderedRoute.pathname === UNDERSCORE_NOT_FOUND_ROUTE) {
continue
}

Expand All @@ -2977,28 +2989,28 @@ export default async function build(
) {
// If the route has unknown params, then we need to add it to
// the list of dynamic routes.
dynamicRoutes.push(prerenderedRoute.path)
dynamicRoutes.push(prerenderedRoute)
} else {
// If the route doesn't have unknown params, then we need to
// add it to the list of routes.
routes.push(prerenderedRoute.path)
routes.push(prerenderedRoute)
}
}

// Handle all the static routes.
for (const route of routes) {
if (isDynamicRoute(page) && route === page) continue
if (route === UNDERSCORE_NOT_FOUND_ROUTE) continue
if (isDynamicRoute(page) && route.pathname === page) continue
if (route.pathname === UNDERSCORE_NOT_FOUND_ROUTE) continue

const {
revalidate = appConfig.revalidate ?? false,
metadata = {},
hasEmptyPrelude,
hasPostponed,
} = exportResult.byPath.get(route) ?? {}
} = exportResult.byPath.get(route.pathname) ?? {}

pageInfos.set(route, {
...(pageInfos.get(route) as PageInfo),
pageInfos.set(route.pathname, {
...(pageInfos.get(route.pathname) as PageInfo),
hasPostponed,
hasEmptyPrelude,
})
Expand All @@ -3011,7 +3023,7 @@ export default async function build(
})

if (revalidate !== 0) {
const normalizedRoute = normalizePagePath(route)
const normalizedRoute = normalizePagePath(route.pathname)

let dataRoute: string | null
if (isAppRouteHandler) {
Expand All @@ -3033,7 +3045,7 @@ export default async function build(

const meta = collectMeta(metadata)

prerenderManifest.routes[route] = {
prerenderManifest.routes[route.pathname] = {
initialStatus: meta.status,
initialHeaders: meta.headers,
renderingMode: isAppPPREnabled
Expand All @@ -3053,8 +3065,8 @@ export default async function build(
hasRevalidateZero = true
// we might have determined during prerendering that this page
// used dynamic data
pageInfos.set(route, {
...(pageInfos.get(route) as PageInfo),
pageInfos.set(route.pathname, {
...(pageInfos.get(route.pathname) as PageInfo),
isSSG: false,
isStatic: false,
})
Expand All @@ -3066,14 +3078,22 @@ export default async function build(
// they are enabled, then it'll already be included in the
// prerendered routes.
if (!isRoutePPREnabled) {
dynamicRoutes.push(page)
dynamicRoutes.push({
pathname: page,
encodedPathname: page,
fallbackRouteParams: undefined,
fallbackMode:
fallbackModes.get(originalAppPath) ??
FallbackMode.NOT_FOUND,
fallbackRootParams: undefined,
})
}

for (const route of dynamicRoutes) {
const normalizedRoute = normalizePagePath(route)
const normalizedRoute = normalizePagePath(route.pathname)

const { metadata, revalidate } =
exportResult.byPath.get(route) ?? {}
exportResult.byPath.get(route.pathname) ?? {}

let dataRoute: string | null = null
if (!isAppRouteHandler) {
Expand All @@ -3087,16 +3107,16 @@ export default async function build(
)
}

pageInfos.set(route, {
...(pageInfos.get(route) as PageInfo),
pageInfos.set(route.pathname, {
...(pageInfos.get(route.pathname) as PageInfo),
isDynamicAppRoute: true,
// if PPR is turned on and the route contains a dynamic segment,
// we assume it'll be partially prerendered
hasPostponed: isRoutePPREnabled,
})

const fallbackMode =
fallbackModes.get(originalAppPath) ?? FallbackMode.NOT_FOUND
route.fallbackMode ?? FallbackMode.NOT_FOUND

// When we're configured to serve a prerender, we should use the
// fallback revalidate from the export result. If it can't be
Expand All @@ -3108,7 +3128,7 @@ export default async function build(

const fallback: Fallback = fallbackModeToFallbackField(
fallbackMode,
route
route.pathname
)

const meta =
Expand All @@ -3118,7 +3138,7 @@ export default async function build(
? collectMeta(metadata)
: {}

prerenderManifest.dynamicRoutes[route] = {
prerenderManifest.dynamicRoutes[route.pathname] = {
experimentalPPR: isRoutePPREnabled,
renderingMode: isAppPPREnabled
? isRoutePPREnabled
Expand All @@ -3127,7 +3147,7 @@ export default async function build(
: undefined,
experimentalBypassFor: bypassFor,
routeRegex: normalizeRouteRegex(
getNamedRouteRegex(route, false).re.source
getNamedRouteRegex(route.pathname, false).re.source
),
dataRoute,
fallback,
Expand Down Expand Up @@ -3417,18 +3437,18 @@ export default async function build(
// We must also copy specific versions of this page as defined by
// `getStaticPaths` (additionalSsgPaths).
for (const route of additionalPaths.get(page) ?? []) {
const pageFile = normalizePagePath(route.path)
const pageFile = normalizePagePath(route.pathname)
await moveExportedPage(
page,
route.path,
route.pathname,
pageFile,
isSsg,
'html',
true
)
await moveExportedPage(
page,
route.path,
route.pathname,
pageFile,
isSsg,
'json',
Expand Down Expand Up @@ -3456,21 +3476,21 @@ export default async function build(
}

const initialRevalidateSeconds =
exportResult.byPath.get(route.path)?.revalidate ?? false
exportResult.byPath.get(route.pathname)?.revalidate ?? false

if (typeof initialRevalidateSeconds === 'undefined') {
throw new Error("Invariant: page wasn't built")
}

prerenderManifest.routes[route.path] = {
prerenderManifest.routes[route.pathname] = {
initialRevalidateSeconds,
experimentalPPR: undefined,
renderingMode: undefined,
srcRoute: page,
dataRoute: path.posix.join(
'/_next/data',
buildId,
`${normalizePagePath(route.path)}.json`
`${normalizePagePath(route.pathname)}.json`
),
// Pages does not have a prefetch data route.
prefetchDataRoute: undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,5 @@
* JSON string, otherwise it will return a minified JSON string.
*/
export function formatManifest<T extends object>(manifest: T): string {
if (process.env.NODE_ENV === 'development') {
return JSON.stringify(manifest, null, 2)
}

return JSON.stringify(manifest)
return JSON.stringify(manifest, null, 2)
}
11 changes: 5 additions & 6 deletions packages/next/src/build/segment-config/app/app-segments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,15 @@ async function collectAppPageSegments(routeModule: AppPageRouteModule) {
// Process current node
const { mod: userland, filePath } = await getLayoutOrPageModule(loaderTree)
const isClientComponent = userland && isClientReference(userland)
const isDynamicSegment = /\[.*\]$/.test(name)
const param = isDynamicSegment ? getSegmentParam(name)?.param : undefined

const param = getSegmentParam(name)?.param

const segment: AppSegment = {
name,
param,
filePath,
config: undefined,
isDynamicSegment,
isDynamicSegment: !!param,
generateStaticParams: undefined,
}

Expand Down Expand Up @@ -157,14 +157,13 @@ function collectAppRouteSegments(

// Generate all the segments.
const segments: AppSegment[] = parts.map((name) => {
const isDynamicSegment = /^\[.*\]$/.test(name)
const param = isDynamicSegment ? getSegmentParam(name)?.param : undefined
const param = getSegmentParam(name)?.param

return {
name,
param,
filePath: undefined,
isDynamicSegment,
isDynamicSegment: !!param,
config: undefined,
generateStaticParams: undefined,
}
Expand Down
Loading

0 comments on commit e689a21

Please sign in to comment.