Skip to content

Commit a93b357

Browse files
committed
Support instrumented params.then/catch/finally
1 parent 3c0b6d0 commit a93b357

File tree

4 files changed

+63
-15
lines changed

4 files changed

+63
-15
lines changed

packages/next/src/server/dynamic-rendering-utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export function makeHangingPromise<T>(
7070
return new Proxy(hangingPromise, {
7171
...handler,
7272
get: function get(target, prop, receiver) {
73-
if (prop === 'then' || prop === 'status') {
73+
if (prop === 'then' || prop === 'catch' || prop === 'finally') {
7474
// TODO: Maybe annotate dynamic access here.
7575
}
7676

packages/next/src/server/request/params.ts

+26-13
Original file line numberDiff line numberDiff line change
@@ -214,20 +214,33 @@ function makeAbortingExoticParams(
214214
'`params`',
215215
{
216216
get: function get(target, prop, receiver) {
217-
switch (prop) {
218-
case 'then':
219-
case 'status':
220-
const dynamicAccessStore = dynamicAccessAsyncStorage.getStore()
221-
222-
if (dynamicAccessStore) {
223-
dynamicAccessStore.abortController.abort(
224-
new Error(`Accessed fallback \`params\` during prerendering.`)
225-
)
226-
}
227-
// intentionally fallthrough
228-
default:
229-
return ReflectAdapter.get(target, prop, receiver)
217+
if (prop === 'then' || prop === 'catch' || prop === 'finally') {
218+
const originalMethod = ReflectAdapter.get(target, prop, receiver)
219+
220+
return {
221+
[prop]: (...args: unknown[]) => {
222+
const store = dynamicAccessAsyncStorage.getStore()
223+
224+
if (store)
225+
store.abortController.abort(
226+
new Error(`Accessed fallback \`params\` during prerendering.`)
227+
)
228+
229+
// Make sure the original method of the proxied promise is called.
230+
// We don't use the returned new promise though. This is mostly
231+
// needed to ensure the hanging promise rejection is triggered when
232+
// the prerender signal is aborted.
233+
originalMethod.apply(target, args)
234+
235+
// Instead, we return the instrumented proxied promise again, to
236+
// ensure that it's also used when params.then(), params.catch(),
237+
// or params.finally() is passed to another function.
238+
return promise
239+
},
240+
}[prop]
230241
}
242+
243+
return ReflectAdapter.get(target, prop, receiver)
231244
},
232245
}
233246
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { CachedLastModified } from '../../../last-modified'
2+
3+
export default async function Page({ params }) {
4+
return (
5+
<>
6+
<CachedLastModified params={params.then((p) => p)} />
7+
<CachedLastModified params={params.catch(() => {})} />
8+
<CachedLastModified params={params.finally(() => {})} />
9+
</>
10+
)
11+
}
12+
13+
export async function generateStaticParams() {
14+
return [{ slug: 'foo' }]
15+
}

test/e2e/app-dir/empty-fallback-shells/empty-fallback-shells.test.ts

+21-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ describe('empty-fallback-shells', () => {
9595
})
9696
})
9797

98-
describe('and the params accessed in cached non-page function', () => {
98+
describe('and the params accessed in a cached non-page function', () => {
9999
it('does not resume a postponed fallback shell', async () => {
100100
const res = await next.fetch(
101101
'/with-cached-io/without-suspense/params-not-in-page/bar'
@@ -114,6 +114,26 @@ describe('empty-fallback-shells', () => {
114114
}
115115
})
116116
})
117+
118+
describe('and params.then/catch/finally passed to a cached function', () => {
119+
it('does not resume a postponed fallback shell', async () => {
120+
const res = await next.fetch(
121+
'/with-cached-io/without-suspense/params-then-in-page/bar'
122+
)
123+
124+
const html = await res.text()
125+
expect(html).toIncludeRepeated('data-testid="page-bar"', 3)
126+
expect(html).toContain('layout-runtime')
127+
128+
if (isNextDeploy) {
129+
expect(res.headers.get('x-matched-path')).toBe(
130+
'/with-cached-io/without-suspense/params-then-in-page/[slug]'
131+
)
132+
} else {
133+
expect(res.headers.get('x-nextjs-postponed')).not.toBe('1')
134+
}
135+
})
136+
})
117137
})
118138
})
119139
})

0 commit comments

Comments
 (0)