Skip to content

Commit 25716c8

Browse files
jamesdanielsvercel[bot]ijjk
authored andcommitted
Deployment adapter: fix metadata for "/" route (#85820)
Updates cases where `/` isn't normalized fully when interpolating into data paths and adds regression tests. --------- Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> Co-authored-by: JJ Kasper <jj@jjsweb.site>
1 parent bf9c900 commit 25716c8

File tree

2 files changed

+39
-14
lines changed

2 files changed

+39
-14
lines changed

packages/next/src/build/adapter/build-complete.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -727,8 +727,7 @@ export async function handleBuildComplete({
727727

728728
// need to add matching .rsc output
729729
if (isAppPage) {
730-
const rscPathname =
731-
(output.pathname === '/' ? '/index' : output.pathname) + '.rsc'
730+
const rscPathname = normalizePagePath(output.pathname) + '.rsc'
732731
outputs.appPages.push({
733732
...output,
734733
pathname: rscPathname,
@@ -864,7 +863,7 @@ export async function handleBuildComplete({
864863
const dataPathname = path.posix.join(
865864
'/_next/data',
866865
buildId,
867-
page + '.json'
866+
normalizePagePath(page) + '.json'
868867
)
869868
outputs.pages.push({
870869
...output,
@@ -1002,9 +1001,8 @@ export async function handleBuildComplete({
10021001
if (output.type === AdapterOutputType.APP_PAGE) {
10031002
outputs.appPages.push({
10041003
...output,
1005-
pathname:
1006-
(output.pathname === '/' ? '/index' : output.pathname) + '.rsc',
1007-
id: (output.id === '/' ? '/index' : output.pathname) + '.rsc',
1004+
pathname: normalizePagePath(output.pathname) + '.rsc',
1005+
id: normalizePagePath(output.pathname) + '.rsc',
10081006
})
10091007
outputs.appPages.push(output)
10101008
} else {
@@ -1058,15 +1056,18 @@ export async function handleBuildComplete({
10581056
}
10591057

10601058
if (meta?.segmentPaths) {
1059+
const normalizedRoute = normalizePagePath(route)
10611060
const segmentsDir = path.join(
10621061
appDistDir,
1063-
`${route}${prefetchSegmentDirSuffix}`
1062+
`${normalizedRoute}${prefetchSegmentDirSuffix}`
10641063
)
10651064

10661065
for (const segmentPath of meta.segmentPaths) {
10671066
const outputSegmentPath =
1068-
path.join(route + prefetchSegmentDirSuffix, segmentPath) +
1069-
prefetchSegmentSuffix
1067+
path.join(
1068+
normalizedRoute + prefetchSegmentDirSuffix,
1069+
segmentPath
1070+
) + prefetchSegmentSuffix
10701071

10711072
const fallbackPathname = path.join(
10721073
segmentsDir,
@@ -1114,10 +1115,11 @@ export async function handleBuildComplete({
11141115
route: string,
11151116
isAppPage: boolean
11161117
): Promise<AppRouteMeta> => {
1118+
const basename = route.endsWith('/') ? `${route}index` : route
11171119
const meta: AppRouteMeta = isAppPage
11181120
? JSON.parse(
11191121
await fs
1120-
.readFile(path.join(appDistDir, `${route}.meta`), 'utf8')
1122+
.readFile(path.join(appDistDir, `${basename}.meta`), 'utf8')
11211123
.catch(() => '{}')
11221124
)
11231125
: {}
@@ -1192,7 +1194,7 @@ export async function handleBuildComplete({
11921194

11931195
let filePath = path.join(
11941196
isAppPage ? appDistDir : pagesDistDir,
1195-
`${route === '/' ? 'index' : route}.${isAppPage && !dataRoute ? 'body' : 'html'}`
1197+
`${normalizePagePath(route)}.${isAppPage && !dataRoute ? 'body' : 'html'}`
11961198
)
11971199

11981200
// we use the static 404 for notFound: true if available
@@ -1268,7 +1270,7 @@ export async function handleBuildComplete({
12681270
if (dataRoute) {
12691271
let dataFilePath = path.join(
12701272
pagesDistDir,
1271-
`${route === '/' ? 'index' : route}.json`
1273+
`${normalizePagePath(route)}.json`
12721274
)
12731275

12741276
if (isAppPage) {
@@ -1593,16 +1595,18 @@ export async function handleBuildComplete({
15931595
prefixRouteKeys: true,
15941596
includeSuffix: true,
15951597
})
1598+
const isDataRoute = dataRoutePages.has(page)
1599+
15961600
const destination = path.posix.join(
15971601
'/',
15981602
config.basePath,
1599-
...(dataRoutePages.has(page) ? [`_next/data`, buildId] : ''),
1603+
...(isDataRoute ? [`_next/data`, buildId] : ''),
16001604
...(page === '/'
16011605
? [shouldLocalize ? '$nextLocale.json' : 'index.json']
16021606
: [
16031607
shouldLocalize ? '$nextLocale' : '',
16041608
page +
1605-
'.json' +
1609+
(isDataRoute ? '.json' : '') +
16061610
getDestinationQuery(routeRegex.routeKeys || {}),
16071611
])
16081612
)

test/production/adapter-config/adapter-config.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ describe('adapter-config', () => {
9999
} else {
100100
expect(output.pathname).toStartWith('/docs/_next/static')
101101
}
102+
// ensure / -> /index normalizing is correct
103+
expect(output.pathname.includes('/.')).toBe(false)
102104

103105
const stats = await fs.promises.stat(output.filePath)
104106
expect(stats.isFile()).toBe(true)
@@ -118,18 +120,35 @@ describe('adapter-config', () => {
118120
expect(typeof prerenderOutput.config.bypassToken).toBe('string')
119121
expect(Array.isArray(prerenderOutput.config.allowHeader)).toBe(true)
120122
expect(Array.isArray(prerenderOutput.config.allowQuery)).toBe(true)
123+
// ensure / -> /index normalizing is correct
124+
expect(prerenderOutput.pathname.includes('/.')).toBe(false)
121125
} catch (err) {
122126
require('console').error(`invalid prerender ${prerenderOutput.id}`, err)
123127
throw err
124128
}
125129
}
126130

131+
const indexPrerender = prerenderOutputs.find(
132+
(item) => item.pathname === '/docs'
133+
)
134+
135+
expect(indexPrerender?.fallback?.initialHeaders).toEqual({
136+
'content-type': 'text/html; charset=utf-8',
137+
vary: 'rsc, next-router-state-tree, next-router-prefetch, next-router-segment-prefetch',
138+
'x-next-cache-tags': '_N_T_/layout,_N_T_/page,_N_T_/,_N_T_/index',
139+
'x-nextjs-prerender': '1',
140+
'x-nextjs-stale-time': '300',
141+
})
142+
expect(indexPrerender?.fallback?.initialRevalidate).toBe(false)
143+
127144
for (const route of nodeOutputs) {
128145
try {
129146
expect(route.id).toBeString()
130147
expect(route.config).toBeObject()
131148
expect(route.pathname).toBeString()
132149
expect(route.runtime).toBe('nodejs')
150+
// ensure / -> /index normalizing is correct
151+
expect(route.pathname.includes('/.')).toBe(false)
133152

134153
const stats = await fs.promises.stat(route.filePath)
135154
expect(stats.isFile()).toBe(true)
@@ -154,6 +173,8 @@ describe('adapter-config', () => {
154173
expect(route.id).toBeString()
155174
expect(route.config).toBeObject()
156175
expect(route.pathname).toBeString()
176+
// ensure / -> /index normalizing is correct
177+
expect(route.pathname.includes('/.')).toBe(false)
157178
expect(route.runtime).toBe('edge')
158179
expect(route.config.env).toEqual(
159180
expect.objectContaining({

0 commit comments

Comments
 (0)