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

refactor: remove ability to call getStaticPaths from app directory pages #70477

Merged
merged 1 commit into from
Sep 26, 2024
Merged
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
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
Loading