Skip to content

Commit e83ab18

Browse files
authored
backport: refactor collectAppPageSegments (#73996)
Backports: - #73908
1 parent ada25fc commit e83ab18

File tree

1 file changed

+37
-20
lines changed

1 file changed

+37
-20
lines changed

packages/next/src/build/segment-config/app/app-segments.ts

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ import {
2020
} from '../../../server/route-modules/checks'
2121
import { isClientReference } from '../../../lib/client-reference'
2222
import { getSegmentParam } from '../../../server/app-render/get-segment-param'
23-
import { getLayoutOrPageModule } from '../../../server/lib/app-dir-module'
23+
import {
24+
getLayoutOrPageModule,
25+
type LoaderTree,
26+
} from '../../../server/lib/app-dir-module'
2427
import { PAGE_SEGMENT_KEY } from '../../../shared/lib/segment'
2528

2629
type GenerateStaticParams = (options: { params?: Params }) => Promise<Params[]>
@@ -74,17 +77,21 @@ export type AppSegment = {
7477
* @returns the segments for the app page route module
7578
*/
7679
async function collectAppPageSegments(routeModule: AppPageRouteModule) {
77-
const segments: AppSegment[] = []
80+
// We keep track of unique segments, since with parallel routes, it's possible
81+
// to see the same segment multiple times.
82+
const uniqueSegments = new Map<string, AppSegment>()
83+
84+
// Queue will store tuples of [loaderTree, currentSegments]
85+
type QueueItem = [LoaderTree, AppSegment[]]
86+
const queue: QueueItem[] = [[routeModule.userland.loaderTree, []]]
7887

79-
// Helper function to process a loader tree path
80-
async function processLoaderTree(
81-
loaderTree: any,
82-
currentSegments: AppSegment[] = []
83-
): Promise<void> {
88+
while (queue.length > 0) {
89+
const [loaderTree, currentSegments] = queue.shift()!
8490
const [name, parallelRoutes] = loaderTree
85-
const { mod: userland, filePath } = await getLayoutOrPageModule(loaderTree)
8691

87-
const isClientComponent: boolean = userland && isClientReference(userland)
92+
// Process current node
93+
const { mod: userland, filePath } = await getLayoutOrPageModule(loaderTree)
94+
const isClientComponent = userland && isClientReference(userland)
8895
const isDynamicSegment = /\[.*\]$/.test(name)
8996
const param = isDynamicSegment ? getSegmentParam(name)?.param : undefined
9097

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

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

107-
currentSegments.push(segment)
112+
// Create a unique key for the segment
113+
const segmentKey = getSegmentKey(segment)
114+
if (!uniqueSegments.has(segmentKey)) {
115+
uniqueSegments.set(segmentKey, segment)
116+
}
108117

109-
// If this is a page segment, we know we've reached a leaf node associated with the
110-
// page we're collecting segments for. We can add the collected segments to our final result.
118+
const updatedSegments = [...currentSegments, segment]
119+
120+
// If this is a page segment, we've reached a leaf node
111121
if (name === PAGE_SEGMENT_KEY) {
112-
segments.push(...currentSegments)
122+
// Add all segments in the current path
123+
updatedSegments.forEach((seg) => {
124+
const key = getSegmentKey(seg)
125+
uniqueSegments.set(key, seg)
126+
})
113127
}
114128

115-
// Recursively process parallel routes
129+
// Add all parallel routes to the queue
116130
for (const parallelRouteKey in parallelRoutes) {
117131
const parallelRoute = parallelRoutes[parallelRouteKey]
118-
await processLoaderTree(parallelRoute, [...currentSegments])
132+
queue.push([parallelRoute, updatedSegments])
119133
}
120134
}
121135

122-
await processLoaderTree(routeModule.userland.loaderTree)
123-
return segments
136+
return Array.from(uniqueSegments.values())
137+
}
138+
139+
function getSegmentKey(segment: AppSegment) {
140+
return `${segment.name}-${segment.filePath ?? ''}-${segment.param ?? ''}`
124141
}
125142

126143
/**

0 commit comments

Comments
 (0)