Skip to content

Commit

Permalink
backport: refactor collectAppPageSegments (#73996)
Browse files Browse the repository at this point in the history
Backports:
- #73908
  • Loading branch information
ztanner authored Dec 16, 2024
1 parent ada25fc commit e83ab18
Showing 1 changed file with 37 additions and 20 deletions.
57 changes: 37 additions & 20 deletions packages/next/src/build/segment-config/app/app-segments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import {
} from '../../../server/route-modules/checks'
import { isClientReference } from '../../../lib/client-reference'
import { getSegmentParam } from '../../../server/app-render/get-segment-param'
import { getLayoutOrPageModule } from '../../../server/lib/app-dir-module'
import {
getLayoutOrPageModule,
type LoaderTree,
} from '../../../server/lib/app-dir-module'
import { PAGE_SEGMENT_KEY } from '../../../shared/lib/segment'

type GenerateStaticParams = (options: { params?: Params }) => Promise<Params[]>
Expand Down Expand Up @@ -74,17 +77,21 @@ export type AppSegment = {
* @returns the segments for the app page route module
*/
async function collectAppPageSegments(routeModule: AppPageRouteModule) {
const segments: AppSegment[] = []
// We keep track of unique segments, since with parallel routes, it's possible
// to see the same segment multiple times.
const uniqueSegments = new Map<string, AppSegment>()

// Queue will store tuples of [loaderTree, currentSegments]
type QueueItem = [LoaderTree, AppSegment[]]
const queue: QueueItem[] = [[routeModule.userland.loaderTree, []]]

// Helper function to process a loader tree path
async function processLoaderTree(
loaderTree: any,
currentSegments: AppSegment[] = []
): Promise<void> {
while (queue.length > 0) {
const [loaderTree, currentSegments] = queue.shift()!
const [name, parallelRoutes] = loaderTree
const { mod: userland, filePath } = await getLayoutOrPageModule(loaderTree)

const isClientComponent: boolean = userland && isClientReference(userland)
// Process current node
const { mod: userland, filePath } = await getLayoutOrPageModule(loaderTree)
const isClientComponent = userland && isClientReference(userland)
const isDynamicSegment = /\[.*\]$/.test(name)
const param = isDynamicSegment ? getSegmentParam(name)?.param : undefined

Expand All @@ -97,30 +104,40 @@ async function collectAppPageSegments(routeModule: AppPageRouteModule) {
generateStaticParams: undefined,
}

// Only server components can have app segment configurations. If this isn't
// an object, then we should skip it. This can happen when parsing the
// error components.
// Only server components can have app segment configurations
if (!isClientComponent) {
attach(segment, userland, routeModule.definition.pathname)
}

currentSegments.push(segment)
// Create a unique key for the segment
const segmentKey = getSegmentKey(segment)
if (!uniqueSegments.has(segmentKey)) {
uniqueSegments.set(segmentKey, segment)
}

// If this is a page segment, we know we've reached a leaf node associated with the
// page we're collecting segments for. We can add the collected segments to our final result.
const updatedSegments = [...currentSegments, segment]

// If this is a page segment, we've reached a leaf node
if (name === PAGE_SEGMENT_KEY) {
segments.push(...currentSegments)
// Add all segments in the current path
updatedSegments.forEach((seg) => {
const key = getSegmentKey(seg)
uniqueSegments.set(key, seg)
})
}

// Recursively process parallel routes
// Add all parallel routes to the queue
for (const parallelRouteKey in parallelRoutes) {
const parallelRoute = parallelRoutes[parallelRouteKey]
await processLoaderTree(parallelRoute, [...currentSegments])
queue.push([parallelRoute, updatedSegments])
}
}

await processLoaderTree(routeModule.userland.loaderTree)
return segments
return Array.from(uniqueSegments.values())
}

function getSegmentKey(segment: AppSegment) {
return `${segment.name}-${segment.filePath ?? ''}-${segment.param ?? ''}`
}

/**
Expand Down

0 comments on commit e83ab18

Please sign in to comment.