Skip to content

Commit

Permalink
Generate Params Cleanup (#59431)
Browse files Browse the repository at this point in the history
This updates the `collectGenerateParams` method to use a loop rather
than being recursive as well as updating some of the Typescript types.
This is a follow up of #59420.

Closes NEXT-1839
  • Loading branch information
wyattjoh authored Dec 11, 2023
1 parent ce92cea commit 7dd7f51
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 63 deletions.
161 changes: 100 additions & 61 deletions packages/next/src/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import type {
import type { WebpackLayerName } from '../lib/constants'
import type { AppPageModule } from '../server/future/route-modules/app-page/module'
import type { RouteModule } from '../server/future/route-modules/route-module'
import type { LoaderTree } from '../server/lib/app-dir-module'
import type { NextComponentType } from '../shared/lib/utils'

import '../server/require-hook'
import '../server/node-polyfill-crypto'
Expand Down Expand Up @@ -1140,14 +1142,21 @@ export type AppConfig = {
fetchCache?: 'force-cache' | 'only-cache'
preferredRegion?: string
}
export type GenerateParams = Array<{

type Params = Record<string, string | string[]>

type GenerateStaticParams = (options: { params?: Params }) => Promise<Params[]>

type GenerateParamsResult = {
config?: AppConfig
isDynamicSegment?: boolean
segmentPath: string
getStaticPaths?: GetStaticPaths
generateStaticParams?: any
generateStaticParams?: GenerateStaticParams
isLayout?: boolean
}>
}

export type GenerateParamsResults = GenerateParamsResult[]

export const collectAppConfig = (mod: any): AppConfig | undefined => {
let hasConfig = false
Expand Down Expand Up @@ -1177,56 +1186,81 @@ export const collectAppConfig = (mod: any): AppConfig | undefined => {
return hasConfig ? config : undefined
}

export const collectGenerateParams = async (
segment: any,
parentSegments: string[] = [],
generateParams: GenerateParams = []
): Promise<GenerateParams> => {
if (!Array.isArray(segment)) return generateParams
const isLayout = !!segment[2]?.layout
const mod = await (isLayout
? segment[2]?.layout?.[0]?.()
: segment[2]?.page?.[0]?.())
const config = collectAppConfig(mod)
const page: string | undefined = segment[0]
const isClientComponent = isClientReference(mod)
const isDynamicSegment = /^\[.+\]$/.test(page || '')
const { generateStaticParams, getStaticPaths } = mod || {}

//console.log({parentSegments, page, isDynamicSegment, isClientComponent, generateStaticParams})
if (isDynamicSegment && isClientComponent && generateStaticParams) {
throw new Error(
`Page "${page}" cannot export "generateStaticParams()" because it is a client component`
)
}
/**
* Walks the loader tree and collects the generate parameters for each segment.
*
* @param tree the loader tree
* @returns the generate parameters for each segment
*/
export async function collectGenerateParams(tree: LoaderTree) {
const generateParams: GenerateParamsResults = []
const parentSegments: string[] = []

let currentLoaderTree = tree
while (currentLoaderTree) {
const [
// TODO: check if this is ever undefined
page = '',
parallelRoutes,
components,
] = currentLoaderTree

// If the segment doesn't have any components, then skip it.
if (!components) continue

const isLayout = !!components.layout
const mod = await (isLayout
? components.layout?.[0]?.()
: components.page?.[0]?.())

if (page) {
parentSegments.push(page)
}

const config = mod ? collectAppConfig(mod) : undefined
const isClientComponent = isClientReference(mod)

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

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

const result = {
isLayout,
isDynamicSegment,
segmentPath: `/${parentSegments.join('/')}${
if (isDynamicSegment && isClientComponent && generateStaticParams) {
throw new Error(
`Page "${page}" cannot export "generateStaticParams()" because it is a client component`
)
}

const segmentPath = `/${parentSegments.join('/')}${
page && parentSegments.length > 0 ? '/' : ''
}${page}`,
config,
getStaticPaths: isClientComponent ? undefined : getStaticPaths,
generateStaticParams: isClientComponent ? undefined : generateStaticParams,
}
}${page}`

const result: GenerateParamsResult = {
isLayout,
isDynamicSegment,
segmentPath,
config,
getStaticPaths: !isClientComponent ? getStaticPaths : undefined,
generateStaticParams: !isClientComponent
? generateStaticParams
: undefined,
}

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

if (result.config || result.generateStaticParams || result.getStaticPaths) {
generateParams.push(result)
} else if (isDynamicSegment) {
// It is a dynamic route, but no config was provided
generateParams.push(result)
// Use this route's parallel route children as the next segment.
currentLoaderTree = parallelRoutes.children
}

return collectGenerateParams(
segment[1]?.children,
parentSegments,
generateParams
)
return generateParams
}

export async function buildAppStaticPaths({
Expand All @@ -1246,7 +1280,7 @@ export async function buildAppStaticPaths({
dir: string
page: string
configFileName: string
generateParams: GenerateParams
generateParams: GenerateParamsResults
incrementalCacheHandlerPath?: string
distDir: string
isrFlushToDisk?: boolean
Expand Down Expand Up @@ -1317,18 +1351,18 @@ export async function buildAppStaticPaths({
} else {
// if generateStaticParams is being used we iterate over them
// collecting them from each level
type Params = Array<Record<string, string | string[]>>
let hadAllParamsGenerated = false

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

if (idx === generateParams.length) {
return paramsItems
}

if (
typeof curGenerate.generateStaticParams !== 'function' &&
idx < generateParams.length
Expand All @@ -1343,22 +1377,27 @@ export async function buildAppStaticPaths({
}
hadAllParamsGenerated = true

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

for (const params of paramsItems) {
const result = await curGenerate.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 (curGenerate.generateStaticParams) {
for (const params of paramsItems) {
const result = await curGenerate.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 (idx < generateParams.length) {
return buildParams(newParams, idx + 1)
}

return newParams
}

const builtParams = await buildParams()
const fallback = !generateParams.some(
// TODO: dynamic params should be allowed
Expand Down Expand Up @@ -1499,7 +1538,7 @@ export async function isPageStatic({
isAppPath: pageType === 'app',
})
}
const Comp = componentsResult.Component || {}
const Comp = componentsResult.Component as NextComponentType | undefined
let staticPathsResult: GetStaticPathsResult | undefined

const routeModule: RouteModule =
Expand All @@ -1512,7 +1551,7 @@ export async function isPageStatic({

const { tree } = ComponentMod

const generateParams: GenerateParams =
const generateParams: GenerateParamsResults =
routeModule && isAppRouteRouteModule(routeModule)
? [
{
Expand Down Expand Up @@ -1604,7 +1643,7 @@ export async function isPageStatic({
}
}

const hasGetInitialProps = !!(Comp as any).getInitialProps
const hasGetInitialProps = !!Comp?.getInitialProps
const hasStaticProps = !!componentsResult.getStaticProps
const hasStaticPaths = !!componentsResult.getStaticPaths
const hasServerProps = !!componentsResult.getServerSideProps
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/server/dev/static-paths-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
buildStaticPaths,
collectGenerateParams,
} from '../../build/utils'
import type { GenerateParams } from '../../build/utils'
import type { GenerateParamsResults } from '../../build/utils'
import { loadComponents } from '../load-components'
import { setHttpClientAndAgentOptions } from '../setup-http-agent-env'
import type { IncrementalCache } from '../lib/incremental-cache'
Expand Down Expand Up @@ -83,7 +83,7 @@ export async function loadStaticPaths({

if (isAppPath) {
const { routeModule } = components
const generateParams: GenerateParams =
const generateParams: GenerateParamsResults =
routeModule && isAppRouteRouteModule(routeModule)
? [
{
Expand Down

0 comments on commit 7dd7f51

Please sign in to comment.