Skip to content

Commit

Permalink
Fix another case with app router revalidation (#51076)
Browse files Browse the repository at this point in the history
This ensures we don't attempt streaming a response during revalidation
in deployed environments.

x-ref: #51062
x-ref: [slack
thread](https://vercel.slack.com/archives/C03S8ED1DKM/p1686216024812579)
  • Loading branch information
ijjk authored Jun 9, 2023
1 parent ccc64d9 commit 32a9c35
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 17 deletions.
8 changes: 7 additions & 1 deletion packages/next/src/server/base-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1326,7 +1326,13 @@ export default abstract class Server<ServerOptions extends Options = Options> {
hasStaticPaths = true
}

if (hasFallback || staticPaths?.includes(resolvedUrlPathname)) {
if (
hasFallback ||
staticPaths?.includes(resolvedUrlPathname) ||
// this signals revalidation in deploy environments
// TODO: make this more generic
req.headers['x-now-route-matches']
) {
isSSG = true
} else if (!this.renderOpts.dev) {
const manifest = this.getPrerenderManifest()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
export const revalidate = 1

export default function Page() {
console.log('rendering app-another')
return (
<>
<p>/app-blog</p>
<p>/app-another</p>
<p>Date: {Date.now()}</p>
</>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const revalidate = 1

export function generateStaticParams() {
return [
{
slug: ['compare'],
},
]
}

export default function Page({ params: { slug } }) {
console.log('rendering app-blog')
return (
<>
<p>/app-blog</p>
<p>slug {JSON.stringify(slug)}</p>
<p>Date: {Date.now()}</p>
</>
)
}
66 changes: 51 additions & 15 deletions test/production/standalone-mode/response-cache/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,18 @@ describe('minimal-mode-response-cache', () => {
}
}
const files = glob.sync('**/*', {
cwd: join(next.testDir, 'standalone/.next/server/pages'),
cwd: join(next.testDir, 'standalone/.next/server'),
nodir: true,
dot: true,
})

for (const file of files) {
if (file.endsWith('.json') || file.endsWith('.html')) {
await fs.remove(join(next.testDir, '.next/server', file))
if (file.match(/(pages|app)[/\\]/) && !file.endsWith('.js')) {
await fs.remove(join(next.testDir, 'standalone/.next/server', file))
console.log(
'removing',
join(next.testDir, 'standalone/.next/server', file)
)
}
}

Expand Down Expand Up @@ -81,30 +86,61 @@ describe('minimal-mode-response-cache', () => {
if (server) await killApp(server)
})

it('app router revalidate should work with previous response cache', async () => {
const res1 = await fetchViaHTTP(appPort, '/app-blog.rsc', undefined, {
headers: {
'x-matched-path': '/app-blog.rsc',
RSC: '1',
},
})
it('app router revalidate should work with previous response cache dynamic', async () => {
const headers = {
vary: 'RSC, Next-Router-State-Tree, Next-Router-Prefetch',
'x-now-route-matches': '1=compare&rsc=1',
'x-matched-path': '/app-blog/compare.rsc',
'x-vercel-id': '1',
rsc: '1',
}
const res1 = await fetchViaHTTP(
appPort,
'/app-blog/compare.rsc',
undefined,
{
headers,
}
)
const content1 = await res1.text()
expect(content1).not.toContain('<html')
expect(content1).toContain('app-blog')
expect(res1.headers.get('content-type')).toContain('text/x-component')

const res2 = await fetchViaHTTP(appPort, '/app-blog', undefined, {
headers: {
'x-matched-path': '/app-blog.rsc',
RSC: '1',
},
const res2 = await fetchViaHTTP(appPort, '/app-blog/compare', undefined, {
headers,
})
const content2 = await res2.text()
expect(content2).toContain('<html')
expect(content2).toContain('app-blog')
expect(res2.headers.get('content-type')).toContain('text/html')
})

it('app router revalidate should work with previous response cache', async () => {
const headers = {
vary: 'RSC, Next-Router-State-Tree, Next-Router-Prefetch',
'x-now-route-matches': '1=app-another&rsc=1',
'x-matched-path': '/app-another.rsc',
'x-vercel-id': '1',
rsc: '1',
}
const res1 = await fetchViaHTTP(appPort, '/app-another.rsc', undefined, {
headers,
})
const content1 = await res1.text()
expect(content1).not.toContain('<html')
expect(content1).toContain('app-another')
expect(res1.headers.get('content-type')).toContain('text/x-component')

const res2 = await fetchViaHTTP(appPort, '/app-another', undefined, {
headers,
})
const content2 = await res2.text()
expect(content2).toContain('<html')
expect(content2).toContain('app-another')
expect(res2.headers.get('content-type')).toContain('text/html')
})

it('should have correct "Listening on" log', async () => {
expect(output).toContain(`Listening on port`)
expect(output).toContain(`url: http://localhost:${appPort}`)
Expand Down

0 comments on commit 32a9c35

Please sign in to comment.