Skip to content

Commit

Permalink
refactor: reduce app config
Browse files Browse the repository at this point in the history
  • Loading branch information
wyattjoh committed Aug 16, 2024
1 parent 8dd11cc commit 20eef0b
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 113 deletions.
14 changes: 9 additions & 5 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2167,7 +2167,11 @@ export default async function build(
}
}

if (workerResult.prerenderFallback) {
if (
workerResult.prerenderFallbackMode !== undefined &&
workerResult.prerenderFallbackMode !==
FallbackMode.NOT_FOUND
) {
// whether or not to allow requests for paths not
// returned from generateStaticParams
appDynamicParamPaths.add(originalAppPath)
Expand Down Expand Up @@ -2221,13 +2225,13 @@ export default async function build(
}

if (
workerResult.prerenderFallback ===
FallbackMode.BLOCKING_RENDER
workerResult.prerenderFallbackMode ===
FallbackMode.BLOCKING_STATIC_RENDER
) {
ssgBlockingFallbackPages.add(page)
} else if (
workerResult.prerenderFallback ===
FallbackMode.SERVE_PRERENDER
workerResult.prerenderFallbackMode ===
FallbackMode.SERVE_STATIC_PRERENDER
) {
ssgStaticFallbackPages.add(page)
}
Expand Down
122 changes: 64 additions & 58 deletions packages/next/src/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ import type { Params } from '../client/components/params'
import { FallbackMode } from '../lib/fallback'
import {
fallbackToStaticPathsResult,
parseFallbackAppConfig,
parseFallbackStaticPathsResult,
} from '../lib/fallback'

Expand Down Expand Up @@ -951,7 +950,7 @@ export type PrerenderedRoute = {
}

export type StaticPathsResult = {
fallback: FallbackMode
fallbackMode: FallbackMode
prerenderedRoutes: PrerenderedRoute[]
}

Expand Down Expand Up @@ -1178,7 +1177,7 @@ export async function buildStaticPaths({
const seen = new Set<string>()

return {
fallback: parseFallbackStaticPathsResult(staticPathsResult.fallback),
fallbackMode: parseFallbackStaticPathsResult(staticPathsResult.fallback),
prerenderedRoutes: prerenderRoutes.filter((route) => {
if (seen.has(route.path)) return false

Expand Down Expand Up @@ -1487,29 +1486,41 @@ export async function buildAppStaticPaths({
}

const builtParams = await buildParams()
const fallback = parseFallbackAppConfig(
reduceAppConfig(generateParams, nextConfigOutput)
)

if (
fallback === FallbackMode.BLOCKING_RENDER &&
generateParams.some(
(generate) => generate.config?.dynamicParams === true
) &&
nextConfigOutput === 'export'
) {
throw new Error(
'"dynamicParams: true" cannot be used with "output: export". See more info here: https://nextjs.org/docs/app/building-your-application/deploying/static-exports'
)
}

const fallbackMode = !generateParams.some(
// TODO: dynamic params should be allowed
// to be granular per segment but we need
// additional information stored/leveraged in
// the prerender-manifest to allow this behavior
(generate) => generate.config?.dynamicParams === false
)
? FallbackMode.BLOCKING_STATIC_RENDER
: FallbackMode.NOT_FOUND

if (!hadAllParamsGenerated) {
return {
fallback,
fallbackMode:
process.env.NODE_ENV === 'production' && isDynamicRoute(page)
? FallbackMode.BLOCKING_STATIC_RENDER
: undefined,
prerenderedRoutes: undefined,
}
}

return buildStaticPaths({
staticPathsResult: {
fallback: fallbackToStaticPathsResult(fallback),
fallback: fallbackToStaticPathsResult(fallbackMode),
paths: builtParams.map((params) => ({ params })),
},
page,
Expand All @@ -1529,7 +1540,7 @@ type PageIsStaticResult = {
hasServerProps?: boolean
hasStaticProps?: boolean
prerenderedRoutes: PrerenderedRoute[] | undefined
prerenderFallback: FallbackMode | undefined
prerenderFallbackMode: FallbackMode | undefined
isNextImageImported?: boolean
traceIncludes?: string[]
traceExcludes?: string[]
Expand Down Expand Up @@ -1587,7 +1598,7 @@ export async function isPageStatic({

let componentsResult: LoadComponentsReturnType
let prerenderedRoutes: PrerenderedRoute[] | undefined
let prerenderFallback: FallbackMode | undefined
let prerenderFallbackMode: FallbackMode | undefined
let appConfig: AppConfig = {}
let isClientComponent: boolean = false
const pathIsEdgeRuntime = isEdgeRuntime(pageRuntime)
Expand Down Expand Up @@ -1660,7 +1671,7 @@ export async function isPageStatic({
]
: await collectGenerateParams(tree)

appConfig = reduceAppConfig(generateParams, nextConfigOutput)
appConfig = reduceAppConfig(generateParams)

if (appConfig.dynamic === 'force-static' && pathIsEdgeRuntime) {
Log.warn(
Expand All @@ -1684,7 +1695,7 @@ export async function isPageStatic({
}

if (isDynamicRoute(page)) {
;({ fallback: prerenderFallback, prerenderedRoutes } =
;({ fallbackMode: prerenderFallbackMode, prerenderedRoutes } =
await buildAppStaticPaths({
dir,
page,
Expand Down Expand Up @@ -1741,7 +1752,7 @@ export async function isPageStatic({
}

if ((hasStaticProps && hasStaticPaths) || staticPathsResult) {
;({ fallback: prerenderFallback, prerenderedRoutes } =
;({ fallbackMode: prerenderFallbackMode, prerenderedRoutes } =
await buildStaticPaths({
page,
locales,
Expand Down Expand Up @@ -1773,7 +1784,7 @@ export async function isPageStatic({
isRoutePPREnabled,
isHybridAmp: config.amp === 'hybrid',
isAmpOnly: config.amp === true,
prerenderFallback,
prerenderFallbackMode,
prerenderedRoutes,
hasStaticProps,
hasServerProps,
Expand All @@ -1790,75 +1801,70 @@ export async function isPageStatic({
})
}

function reduceAppConfig(
generateParams: GenerateParamsResults,
nextConfigOutput: 'standalone' | 'export' | undefined
): AppConfig {
return generateParams.reduce<AppConfig>((builtConfig, curGenParams) => {
type ReducedAppConfig = Pick<
AppConfig,
| 'dynamic'
| 'fetchCache'
| 'preferredRegion'
| 'revalidate'
| 'experimental_ppr'
>

/**
* Collect the app config from the generate param segments. This only gets a
* subset of the config options.
*
* @param segments the generate param segments
* @returns the reduced app config
*/
function reduceAppConfig(segments: GenerateParamsResults): ReducedAppConfig {
const config: ReducedAppConfig = {}

for (const segment of segments) {
const {
dynamic,
fetchCache,
preferredRegion,
revalidate: curRevalidate,
experimental_ppr,
dynamicParams,
} = curGenParams?.config || {}
} = segment?.config ?? {}

// TODO: should conflicting configs here throw an error
// e.g. if layout defines one region but page defines another
if (typeof builtConfig.preferredRegion === 'undefined') {
builtConfig.preferredRegion = preferredRegion
if (typeof config.preferredRegion === 'undefined') {
config.preferredRegion = preferredRegion
}
if (typeof builtConfig.dynamic === 'undefined') {
builtConfig.dynamic = dynamic

if (typeof config.dynamic === 'undefined') {
config.dynamic = dynamic
}
if (typeof builtConfig.fetchCache === 'undefined') {
builtConfig.fetchCache = fetchCache

if (typeof config.fetchCache === 'undefined') {
config.fetchCache = fetchCache
}

// If partial prerendering has been set, only override it if the current value is
// provided as it's resolved from root layout to leaf page.
if (typeof experimental_ppr !== 'undefined') {
builtConfig.experimental_ppr = experimental_ppr
config.experimental_ppr = experimental_ppr
}

// any revalidate number overrides false
// shorter revalidate overrides longer (initially)
if (typeof builtConfig.revalidate === 'undefined') {
builtConfig.revalidate = curRevalidate
if (typeof config.revalidate === 'undefined') {
config.revalidate = curRevalidate
}

if (
typeof curRevalidate === 'number' &&
(typeof builtConfig.revalidate !== 'number' ||
curRevalidate < builtConfig.revalidate)
(typeof config.revalidate !== 'number' ||
curRevalidate < config.revalidate)
) {
builtConfig.revalidate = curRevalidate
}

// The default for the dynamicParams will be based on the dynamic config
// and the next config output option. If any of these are set or the
// output config is set to export, set the dynamicParams to false,
// otherwise the default is true.
if (typeof builtConfig.dynamicParams === 'undefined') {
if (
builtConfig.dynamic === 'error' ||
builtConfig.dynamic === 'force-static' ||
nextConfigOutput === 'export'
) {
builtConfig.dynamicParams = false
} else {
builtConfig.dynamicParams = true
}
}

// If the dynamicParams is explicitly set to false, we should not
// dynamically generate params for this page.
if (dynamicParams === false) {
builtConfig.dynamicParams = false
config.revalidate = curRevalidate
}
}

return builtConfig
}, {})
return config
}

export async function hasCustomGetInitialProps({
Expand Down
58 changes: 24 additions & 34 deletions packages/next/src/lib/fallback.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
import type { AppConfig } from '../build/utils'

/**
* Describes the different fallback modes that a given page can have.
*/
export const enum FallbackMode {
/**
* A BLOCKING_RENDER fallback will block the request until the page is\
* A BLOCKING_STATIC_RENDER fallback will block the request until the page is
* generated. No fallback page will be rendered, and users will have to wait
* to render the page.
*/
BLOCKING_RENDER = 'BLOCKING_RENDER',

/**
* When set to NOT_FOUND, pages that are not already prerendered will result
* in a not found response.
*/
NOT_FOUND = 'NOT_FOUND',
BLOCKING_STATIC_RENDER = 'BLOCKING_STATIC_RENDER',

/**
* When set to PRERENDER, a fallback page will be sent to users in place of
* forcing them to wait for the page to be generated. This allows the user to
* see a rendered page earlier.
*/
SERVE_PRERENDER = 'SERVE_PRERENDER',
SERVE_STATIC_PRERENDER = 'SERVE_STATIC_PRERENDER',

/**
* When set to NOT_FOUND, pages that are not already prerendered will result
* in a not found response.
*/
NOT_FOUND = 'NOT_FOUND',
}

/**
Expand All @@ -37,14 +35,20 @@ export type GetStaticPathsFallback = boolean | 'blocking'
* @returns The fallback mode.
*/
export function parseFallbackField(
fallbackField: string | false | null
): FallbackMode {
fallbackField: string | false | null | undefined
): FallbackMode | undefined {
if (typeof fallbackField === 'string') {
return FallbackMode.SERVE_PRERENDER
return FallbackMode.SERVE_STATIC_PRERENDER
} else if (fallbackField === null) {
return FallbackMode.BLOCKING_RENDER
} else {
return FallbackMode.BLOCKING_STATIC_RENDER
} else if (fallbackField === false) {
return FallbackMode.NOT_FOUND
} else if (fallbackField === undefined) {
return undefined
} else {
throw new Error(
`Invalid fallback option: ${fallbackField}. Fallback option must be a string, null, undefined, or false.`
)
}
}

Expand All @@ -58,9 +62,9 @@ export function parseFallbackStaticPathsResult(
result: GetStaticPathsFallback
): FallbackMode {
if (result === true) {
return FallbackMode.SERVE_PRERENDER
return FallbackMode.SERVE_STATIC_PRERENDER
} else if (result === 'blocking') {
return FallbackMode.BLOCKING_RENDER
return FallbackMode.BLOCKING_STATIC_RENDER
} else {
return FallbackMode.NOT_FOUND
}
Expand All @@ -76,26 +80,12 @@ export function fallbackToStaticPathsResult(
fallback: FallbackMode
): GetStaticPathsFallback {
switch (fallback) {
case FallbackMode.SERVE_PRERENDER:
case FallbackMode.SERVE_STATIC_PRERENDER:
return true
case FallbackMode.BLOCKING_RENDER:
case FallbackMode.BLOCKING_STATIC_RENDER:
return 'blocking'
case FallbackMode.NOT_FOUND:
default:
return false
}
}

/**
* Parses the fallback field from the app config.
*
* @param appConfig The app config.
* @returns The fallback mode.
*/
export function parseFallbackAppConfig(appConfig: AppConfig): FallbackMode {
if (appConfig.dynamicParams === false) {
return FallbackMode.NOT_FOUND
} else {
return FallbackMode.BLOCKING_RENDER
}
}
Loading

0 comments on commit 20eef0b

Please sign in to comment.