Skip to content

Commit

Permalink
refactor: remove ability to call getStaticPaths from app directory pa…
Browse files Browse the repository at this point in the history
…ges (#70477)

We previously supported a legacy mode of exporting a `getStaticPaths`
from app directory pages. This removes that option in favour of
`generateStaticParams`.
  • Loading branch information
wyattjoh authored Sep 26, 2024
1 parent 80974df commit 334f335
Showing 1 changed file with 101 additions and 122 deletions.
223 changes: 101 additions & 122 deletions packages/next/src/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1227,7 +1227,6 @@ type GenerateParamsResult = {
config?: AppConfig
isDynamicSegment?: boolean
segmentPath: string
getStaticPaths?: GetStaticPaths
generateStaticParams?: GenerateStaticParams
isLayout?: boolean
}
Expand Down Expand Up @@ -1306,7 +1305,7 @@ export async function collectGenerateParams(tree: LoaderTree) {

const isDynamicSegment = /^\[.+\]$/.test(page)

const { generateStaticParams, getStaticPaths } = mod || {}
const { generateStaticParams } = mod || {}

if (isDynamicSegment && isClientComponent && generateStaticParams) {
throw new Error(
Expand All @@ -1323,20 +1322,14 @@ export async function collectGenerateParams(tree: LoaderTree) {
isDynamicSegment,
segmentPath,
config,
getStaticPaths: !isClientComponent ? getStaticPaths : undefined,
generateStaticParams: !isClientComponent
? generateStaticParams
: undefined,
}

// If the configuration contributes to the static generation, then add it
// to the list.
if (
result.config ||
result.generateStaticParams ||
result.getStaticPaths ||
isDynamicSegment
) {
if (result.config || result.generateStaticParams || isDynamicSegment) {
generateParams.push(result)
}

Expand Down Expand Up @@ -1431,145 +1424,131 @@ export async function buildAppStaticPaths({
},
},
async (): Promise<PartialStaticPathsResult> => {
const pageEntry = generateParams[generateParams.length - 1]
let hadAllParamsGenerated = false

// if the page has legacy getStaticPaths we call it like normal
if (typeof pageEntry?.getStaticPaths === 'function') {
return buildStaticPaths({
page,
configFileName,
getStaticPaths: pageEntry.getStaticPaths,
})
} else {
// if generateStaticParams is being used we iterate over them
// collecting them from each level
let hadAllParamsGenerated = false

const buildParams = async (
paramsItems: Params[] = [{}],
idx = 0
): Promise<Params[]> => {
const current = generateParams[idx]

if (idx === generateParams.length) {
return paramsItems
}
const buildParams = async (
paramsItems: Params[] = [{}],
idx = 0
): Promise<Params[]> => {
const current = generateParams[idx]

if (
typeof current.generateStaticParams !== 'function' &&
idx < generateParams.length
) {
if (current.isDynamicSegment) {
// This dynamic level has no generateStaticParams so we change
// this flag to false, but it could be covered by a later
// generateStaticParams so it could be set back to true.
hadAllParamsGenerated = false
}
return buildParams(paramsItems, idx + 1)
if (idx === generateParams.length) {
return paramsItems
}

if (
typeof current.generateStaticParams !== 'function' &&
idx < generateParams.length
) {
if (current.isDynamicSegment) {
// This dynamic level has no generateStaticParams so we change
// this flag to false, but it could be covered by a later
// generateStaticParams so it could be set back to true.
hadAllParamsGenerated = false
}
hadAllParamsGenerated = true
return buildParams(paramsItems, idx + 1)
}
hadAllParamsGenerated = true

const newParams: Params[] = []
const newParams: Params[] = []

if (current.generateStaticParams) {
const store = ComponentMod.staticGenerationAsyncStorage.getStore()
if (current.generateStaticParams) {
const store = ComponentMod.staticGenerationAsyncStorage.getStore()

if (store) {
if (typeof current?.config?.fetchCache !== 'undefined') {
store.fetchCache = current.config.fetchCache
}
if (typeof current?.config?.revalidate !== 'undefined') {
store.revalidate = current.config.revalidate
}
if (current?.config?.dynamic === 'force-dynamic') {
store.forceDynamic = true
}
if (store) {
if (typeof current?.config?.fetchCache !== 'undefined') {
store.fetchCache = current.config.fetchCache
}

for (const params of paramsItems) {
const result = await current.generateStaticParams({
params,
})

// TODO: validate the result is valid here or wait for buildStaticPaths to validate?
for (const item of result) {
newParams.push({ ...params, ...item })
}
if (typeof current?.config?.revalidate !== 'undefined') {
store.revalidate = current.config.revalidate
}
if (current?.config?.dynamic === 'force-dynamic') {
store.forceDynamic = true
}
}

if (idx < generateParams.length) {
return buildParams(newParams, idx + 1)
for (const params of paramsItems) {
const result = await current.generateStaticParams({
params,
})

// TODO: validate the result is valid here or wait for buildStaticPaths to validate?
for (const item of result) {
newParams.push({ ...params, ...item })
}
}
}

return newParams
if (idx < generateParams.length) {
return buildParams(newParams, idx + 1)
}

const builtParams = await buildParams()
return newParams
}

if (
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 builtParams = await buildParams()

// 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.
const dynamicParams = generateParams.every(
(param) => param.config?.dynamicParams !== false
if (
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 isProduction = process.env.NODE_ENV === 'production'
// 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.
const dynamicParams = generateParams.every(
(param) => param.config?.dynamicParams !== false
)

const supportsStaticGeneration = hadAllParamsGenerated || isProduction
const isProduction = process.env.NODE_ENV === 'production'

const supportsPPRFallbacks =
isRoutePPREnabled && isAppPPRFallbacksEnabled
const supportsStaticGeneration = hadAllParamsGenerated || isProduction

const fallbackMode = dynamicParams
? supportsStaticGeneration
? supportsPPRFallbacks
? FallbackMode.PRERENDER
: FallbackMode.BLOCKING_STATIC_RENDER
: undefined
: FallbackMode.NOT_FOUND
const supportsPPRFallbacks = isRoutePPREnabled && isAppPPRFallbacksEnabled

let result: PartialStaticPathsResult = {
fallbackMode,
prerenderedRoutes: undefined,
}
const fallbackMode = dynamicParams
? supportsStaticGeneration
? supportsPPRFallbacks
? FallbackMode.PRERENDER
: FallbackMode.BLOCKING_STATIC_RENDER
: undefined
: FallbackMode.NOT_FOUND

if (hadAllParamsGenerated && fallbackMode) {
result = await buildStaticPaths({
staticPathsResult: {
fallback: fallbackModeToStaticPathsResult(fallbackMode),
paths: builtParams.map((params) => ({ params })),
},
page,
configFileName,
appDir: true,
})
}
let result: PartialStaticPathsResult = {
fallbackMode,
prerenderedRoutes: undefined,
}

// If the fallback mode is a prerender, we want to include the dynamic
// route in the prerendered routes too.
if (isRoutePPREnabled && isAppPPRFallbacksEnabled) {
result.prerenderedRoutes ??= []
result.prerenderedRoutes.unshift({
path: page,
encoded: page,
fallbackRouteParams: getParamKeys(page),
})
}
if (hadAllParamsGenerated && fallbackMode) {
result = await buildStaticPaths({
staticPathsResult: {
fallback: fallbackModeToStaticPathsResult(fallbackMode),
paths: builtParams.map((params) => ({ params })),
},
page,
configFileName,
appDir: true,
})
}

return result
// If the fallback mode is a prerender, we want to include the dynamic
// route in the prerendered routes too.
if (isRoutePPREnabled && isAppPPRFallbacksEnabled) {
result.prerenderedRoutes ??= []
result.prerenderedRoutes.unshift({
path: page,
encoded: page,
fallbackRouteParams: getParamKeys(page),
})
}

return result
}
)
}
Expand Down

0 comments on commit 334f335

Please sign in to comment.