Skip to content

Commit e689a21

Browse files
committed
feat: added partial shell generation using root params
1 parent 20b4910 commit e689a21

File tree

15 files changed

+550
-211
lines changed

15 files changed

+550
-211
lines changed

packages/next/errors.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,5 +613,10 @@
613613
"612": "ServerPrerenderStreamResult cannot be consumed as a stream because it is not yet complete. status: %s",
614614
"613": "Expected the input to be `string | string[]`",
615615
"614": "Route %s used \"unstable_rootParams\" inside \"use cache\". This is not currently supported.",
616-
"615": "Missing workStore in unstable_rootParams"
616+
"615": "Missing workStore in unstable_rootParams",
617+
"616": "App config not found",
618+
"617": "A required parameter (%s) was not provided as a string received %s in generateStaticParams for %s",
619+
"618": "A required parameter (%s) was not provided as an array received %s in generateStaticParams for %s",
620+
"619": "Page not found",
621+
"620": "A required parameter (%s) was not provided as %s received %s in getStaticPaths for %s"
617622
}

packages/next/src/build/index.ts

Lines changed: 64 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ import {
134134
collectMeta,
135135
} from './utils'
136136
import type { PageInfo, PageInfos } from './utils'
137+
import type { PrerenderedRoute } from './static-paths/types'
137138
import type { AppSegmentConfig } from './segment-config/app/app-segment-config'
138139
import { writeBuildId } from './write-build-id'
139140
import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path'
@@ -212,7 +213,7 @@ import {
212213
formatNodeOptions,
213214
getParsedNodeOptionsWithoutInspect,
214215
} from '../server/lib/utils'
215-
import type { PrerenderedRoute } from './static-paths/types'
216+
import { InvariantError } from '../shared/lib/invariant-error'
216217

217218
type Fallback = null | boolean | string
218219

@@ -2131,11 +2132,16 @@ export default async function build(
21312132
} else {
21322133
const isDynamic = isDynamicRoute(page)
21332134

2135+
if (
2136+
typeof workerResult.isRoutePPREnabled === 'boolean'
2137+
) {
2138+
isRoutePPREnabled = workerResult.isRoutePPREnabled
2139+
}
2140+
21342141
// If this route can be partially pre-rendered, then
21352142
// mark it as such and mark that it can be
21362143
// generated server-side.
21372144
if (workerResult.isRoutePPREnabled) {
2138-
isRoutePPREnabled = workerResult.isRoutePPREnabled
21392145
isSSG = true
21402146
isStatic = true
21412147

@@ -2160,7 +2166,7 @@ export default async function build(
21602166
workerResult.prerenderedRoutes
21612167
)
21622168
ssgPageRoutes = workerResult.prerenderedRoutes.map(
2163-
(route) => route.path
2169+
(route) => route.pathname
21642170
)
21652171
isSSG = true
21662172
}
@@ -2188,9 +2194,12 @@ export default async function build(
21882194
if (!isDynamic) {
21892195
staticPaths.set(originalAppPath, [
21902196
{
2191-
path: page,
2192-
encoded: page,
2197+
pathname: page,
2198+
encodedPathname: page,
21932199
fallbackRouteParams: undefined,
2200+
fallbackMode:
2201+
workerResult.prerenderFallbackMode,
2202+
fallbackRootParams: undefined,
21942203
},
21952204
])
21962205
isStatic = true
@@ -2256,7 +2265,7 @@ export default async function build(
22562265
workerResult.prerenderedRoutes
22572266
)
22582267
ssgPageRoutes = workerResult.prerenderedRoutes.map(
2259-
(route) => route.path
2268+
(route) => route.pathname
22602269
)
22612270
}
22622271

@@ -2692,7 +2701,7 @@ export default async function build(
26922701
new Map(
26932702
Array.from(additionalPaths.entries()).map(
26942703
([page, routes]): [string, string[]] => {
2695-
return [page, routes.map((route) => route.path)]
2704+
return [page, routes.map((route) => route.pathname)]
26962705
}
26972706
)
26982707
)
@@ -2743,9 +2752,9 @@ export default async function build(
27432752
// post slugs.
27442753
additionalPaths.forEach((routes, page) => {
27452754
routes.forEach((route) => {
2746-
defaultMap[route.path] = {
2755+
defaultMap[route.pathname] = {
27472756
page,
2748-
query: { __nextSsgPath: route.encoded },
2757+
query: { __nextSsgPath: route.encodedPathname },
27492758
}
27502759
})
27512760
})
@@ -2773,9 +2782,9 @@ export default async function build(
27732782
: undefined
27742783

27752784
routes.forEach((route) => {
2776-
defaultMap[route.path] = {
2785+
defaultMap[route.pathname] = {
27772786
page: originalAppPath,
2778-
query: { __nextSsgPath: route.encoded },
2787+
query: { __nextSsgPath: route.encodedPathname },
27792788
_fallbackRouteParams: route.fallbackRouteParams,
27802789
_isDynamicError: isDynamicError,
27812790
_isAppDir: true,
@@ -2885,8 +2894,11 @@ export default async function build(
28852894
}
28862895

28872896
staticPaths.forEach((prerenderedRoutes, originalAppPath) => {
2888-
const page = appNormalizedPaths.get(originalAppPath) || ''
2889-
const appConfig = appDefaultConfigs.get(originalAppPath) || {}
2897+
const page = appNormalizedPaths.get(originalAppPath)
2898+
if (!page) throw new InvariantError('Page not found')
2899+
2900+
const appConfig = appDefaultConfigs.get(originalAppPath)
2901+
if (!appConfig) throw new InvariantError('App config not found')
28902902

28912903
let hasRevalidateZero =
28922904
appConfig.revalidate === 0 ||
@@ -2928,8 +2940,8 @@ export default async function build(
29282940
// route), any routes that were generated with unknown route params
29292941
// should be collected and included in the dynamic routes part
29302942
// of the manifest instead.
2931-
const routes: string[] = []
2932-
const dynamicRoutes: string[] = []
2943+
const routes: PrerenderedRoute[] = []
2944+
const dynamicRoutes: PrerenderedRoute[] = []
29332945

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

29522964
unknownPrerenderRoutes = getSortedRouteObjects(
29532965
unknownPrerenderRoutes,
2954-
(prerenderedRoute) => prerenderedRoute.path
2966+
(prerenderedRoute) => prerenderedRoute.pathname
29552967
)
29562968
knownPrerenderRoutes = getSortedRouteObjects(
29572969
knownPrerenderRoutes,
2958-
(prerenderedRoute) => prerenderedRoute.path
2970+
(prerenderedRoute) => prerenderedRoute.pathname
29592971
)
29602972

29612973
prerenderedRoutes = [
@@ -2966,7 +2978,7 @@ export default async function build(
29662978
for (const prerenderedRoute of prerenderedRoutes) {
29672979
// TODO: check if still needed?
29682980
// Exclude the /_not-found route.
2969-
if (prerenderedRoute.path === UNDERSCORE_NOT_FOUND_ROUTE) {
2981+
if (prerenderedRoute.pathname === UNDERSCORE_NOT_FOUND_ROUTE) {
29702982
continue
29712983
}
29722984

@@ -2977,28 +2989,28 @@ export default async function build(
29772989
) {
29782990
// If the route has unknown params, then we need to add it to
29792991
// the list of dynamic routes.
2980-
dynamicRoutes.push(prerenderedRoute.path)
2992+
dynamicRoutes.push(prerenderedRoute)
29812993
} else {
29822994
// If the route doesn't have unknown params, then we need to
29832995
// add it to the list of routes.
2984-
routes.push(prerenderedRoute.path)
2996+
routes.push(prerenderedRoute)
29852997
}
29862998
}
29872999

29883000
// Handle all the static routes.
29893001
for (const route of routes) {
2990-
if (isDynamicRoute(page) && route === page) continue
2991-
if (route === UNDERSCORE_NOT_FOUND_ROUTE) continue
3002+
if (isDynamicRoute(page) && route.pathname === page) continue
3003+
if (route.pathname === UNDERSCORE_NOT_FOUND_ROUTE) continue
29923004

29933005
const {
29943006
revalidate = appConfig.revalidate ?? false,
29953007
metadata = {},
29963008
hasEmptyPrelude,
29973009
hasPostponed,
2998-
} = exportResult.byPath.get(route) ?? {}
3010+
} = exportResult.byPath.get(route.pathname) ?? {}
29993011

3000-
pageInfos.set(route, {
3001-
...(pageInfos.get(route) as PageInfo),
3012+
pageInfos.set(route.pathname, {
3013+
...(pageInfos.get(route.pathname) as PageInfo),
30023014
hasPostponed,
30033015
hasEmptyPrelude,
30043016
})
@@ -3011,7 +3023,7 @@ export default async function build(
30113023
})
30123024

30133025
if (revalidate !== 0) {
3014-
const normalizedRoute = normalizePagePath(route)
3026+
const normalizedRoute = normalizePagePath(route.pathname)
30153027

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

30343046
const meta = collectMeta(metadata)
30353047

3036-
prerenderManifest.routes[route] = {
3048+
prerenderManifest.routes[route.pathname] = {
30373049
initialStatus: meta.status,
30383050
initialHeaders: meta.headers,
30393051
renderingMode: isAppPPREnabled
@@ -3053,8 +3065,8 @@ export default async function build(
30533065
hasRevalidateZero = true
30543066
// we might have determined during prerendering that this page
30553067
// used dynamic data
3056-
pageInfos.set(route, {
3057-
...(pageInfos.get(route) as PageInfo),
3068+
pageInfos.set(route.pathname, {
3069+
...(pageInfos.get(route.pathname) as PageInfo),
30583070
isSSG: false,
30593071
isStatic: false,
30603072
})
@@ -3066,14 +3078,22 @@ export default async function build(
30663078
// they are enabled, then it'll already be included in the
30673079
// prerendered routes.
30683080
if (!isRoutePPREnabled) {
3069-
dynamicRoutes.push(page)
3081+
dynamicRoutes.push({
3082+
pathname: page,
3083+
encodedPathname: page,
3084+
fallbackRouteParams: undefined,
3085+
fallbackMode:
3086+
fallbackModes.get(originalAppPath) ??
3087+
FallbackMode.NOT_FOUND,
3088+
fallbackRootParams: undefined,
3089+
})
30703090
}
30713091

30723092
for (const route of dynamicRoutes) {
3073-
const normalizedRoute = normalizePagePath(route)
3093+
const normalizedRoute = normalizePagePath(route.pathname)
30743094

30753095
const { metadata, revalidate } =
3076-
exportResult.byPath.get(route) ?? {}
3096+
exportResult.byPath.get(route.pathname) ?? {}
30773097

30783098
let dataRoute: string | null = null
30793099
if (!isAppRouteHandler) {
@@ -3087,16 +3107,16 @@ export default async function build(
30873107
)
30883108
}
30893109

3090-
pageInfos.set(route, {
3091-
...(pageInfos.get(route) as PageInfo),
3110+
pageInfos.set(route.pathname, {
3111+
...(pageInfos.get(route.pathname) as PageInfo),
30923112
isDynamicAppRoute: true,
30933113
// if PPR is turned on and the route contains a dynamic segment,
30943114
// we assume it'll be partially prerendered
30953115
hasPostponed: isRoutePPREnabled,
30963116
})
30973117

30983118
const fallbackMode =
3099-
fallbackModes.get(originalAppPath) ?? FallbackMode.NOT_FOUND
3119+
route.fallbackMode ?? FallbackMode.NOT_FOUND
31003120

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

31093129
const fallback: Fallback = fallbackModeToFallbackField(
31103130
fallbackMode,
3111-
route
3131+
route.pathname
31123132
)
31133133

31143134
const meta =
@@ -3118,7 +3138,7 @@ export default async function build(
31183138
? collectMeta(metadata)
31193139
: {}
31203140

3121-
prerenderManifest.dynamicRoutes[route] = {
3141+
prerenderManifest.dynamicRoutes[route.pathname] = {
31223142
experimentalPPR: isRoutePPREnabled,
31233143
renderingMode: isAppPPREnabled
31243144
? isRoutePPREnabled
@@ -3127,7 +3147,7 @@ export default async function build(
31273147
: undefined,
31283148
experimentalBypassFor: bypassFor,
31293149
routeRegex: normalizeRouteRegex(
3130-
getNamedRouteRegex(route, false).re.source
3150+
getNamedRouteRegex(route.pathname, false).re.source
31313151
),
31323152
dataRoute,
31333153
fallback,
@@ -3417,18 +3437,18 @@ export default async function build(
34173437
// We must also copy specific versions of this page as defined by
34183438
// `getStaticPaths` (additionalSsgPaths).
34193439
for (const route of additionalPaths.get(page) ?? []) {
3420-
const pageFile = normalizePagePath(route.path)
3440+
const pageFile = normalizePagePath(route.pathname)
34213441
await moveExportedPage(
34223442
page,
3423-
route.path,
3443+
route.pathname,
34243444
pageFile,
34253445
isSsg,
34263446
'html',
34273447
true
34283448
)
34293449
await moveExportedPage(
34303450
page,
3431-
route.path,
3451+
route.pathname,
34323452
pageFile,
34333453
isSsg,
34343454
'json',
@@ -3456,21 +3476,21 @@ export default async function build(
34563476
}
34573477

34583478
const initialRevalidateSeconds =
3459-
exportResult.byPath.get(route.path)?.revalidate ?? false
3479+
exportResult.byPath.get(route.pathname)?.revalidate ?? false
34603480

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

3465-
prerenderManifest.routes[route.path] = {
3485+
prerenderManifest.routes[route.pathname] = {
34663486
initialRevalidateSeconds,
34673487
experimentalPPR: undefined,
34683488
renderingMode: undefined,
34693489
srcRoute: page,
34703490
dataRoute: path.posix.join(
34713491
'/_next/data',
34723492
buildId,
3473-
`${normalizePagePath(route.path)}.json`
3493+
`${normalizePagePath(route.pathname)}.json`
34743494
),
34753495
// Pages does not have a prefetch data route.
34763496
prefetchDataRoute: undefined,

packages/next/src/build/manifests/formatter/format-manifest.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,5 @@
44
* JSON string, otherwise it will return a minified JSON string.
55
*/
66
export function formatManifest<T extends object>(manifest: T): string {
7-
if (process.env.NODE_ENV === 'development') {
8-
return JSON.stringify(manifest, null, 2)
9-
}
10-
11-
return JSON.stringify(manifest)
7+
return JSON.stringify(manifest, null, 2)
128
}

packages/next/src/build/segment-config/app/app-segments.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,15 @@ async function collectAppPageSegments(routeModule: AppPageRouteModule) {
9292
// Process current node
9393
const { mod: userland, filePath } = await getLayoutOrPageModule(loaderTree)
9494
const isClientComponent = userland && isClientReference(userland)
95-
const isDynamicSegment = /\[.*\]$/.test(name)
96-
const param = isDynamicSegment ? getSegmentParam(name)?.param : undefined
95+
96+
const param = getSegmentParam(name)?.param
9797

9898
const segment: AppSegment = {
9999
name,
100100
param,
101101
filePath,
102102
config: undefined,
103-
isDynamicSegment,
103+
isDynamicSegment: !!param,
104104
generateStaticParams: undefined,
105105
}
106106

@@ -157,14 +157,13 @@ function collectAppRouteSegments(
157157

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

163162
return {
164163
name,
165164
param,
166165
filePath: undefined,
167-
isDynamicSegment,
166+
isDynamicSegment: !!param,
168167
config: undefined,
169168
generateStaticParams: undefined,
170169
}

0 commit comments

Comments
 (0)