mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
`partialFallback` is an internal flag in the build output that controls
the behavior of ISR. When it's enabled, in a Cache Components app, pages
that are not generated at build time are upgraded to a full ISR entry
upon the first request.
This is a useful feature but there's a risk it could lead to an
explosion in ISR costs, because even a prefetch request is sufficient to
trigger an upgrade.
The desired behavior is that we only trigger an upgrade for an actual
navigation request, or if a link opts into prefetching with the
`prefetch` prop.
Currently there's no way for ISR to differentiate between a shell
prefetch request (no prefetch prop on the link) and an actual prefetch
(`prefetch={true}`). The plan is to introduce additional configuration
to the build output that specifies which headers should trigger which
operations.
So by default, this PR turns the `partialFallback` flag back off until
the new mechanism is available to us.
However, for apps that opt into Partial Prefetching, we will continue to
set `partialFallback` to true. That's because in a Partial Prefetching
app, per-link prefetch requests are always opt-in. So even without
additional support at the ISR behavior, we avoid an unexpected surge in
ISR costs by not sending the prefetching request in the first place.
There will still be a single request per distinct route (_not_ per
distinct URL!), which we will optimize later.
42 lines
1.6 KiB
TypeScript
42 lines
1.6 KiB
TypeScript
import { nextTestSetup } from 'e2e-utils'
|
|
import type { NextAdapter } from 'next'
|
|
|
|
describe('adapter-partial-fallback-disabled', () => {
|
|
const { next } = nextTestSetup({
|
|
files: __dirname,
|
|
})
|
|
|
|
it('should not emit partial fallback metadata when partialPrefetching is disabled', async () => {
|
|
const { outputs }: Parameters<NextAdapter['onBuildComplete']>[0] =
|
|
await next.readJSON('build-complete.json')
|
|
|
|
const withGspPrerender = outputs.prerenders.find(
|
|
(output) => output.pathname === '/with-gsp/[slug]'
|
|
)
|
|
const withGspRouteTreePrerender = outputs.prerenders.find(
|
|
(output) =>
|
|
output.pathname === '/with-gsp/[slug].segments/_tree.segment.rsc'
|
|
)
|
|
const withGspOtherSegmentPrerenders = outputs.prerenders.filter(
|
|
(output) =>
|
|
output.pathname.startsWith('/with-gsp/[slug].segments/') &&
|
|
output.pathname !== '/with-gsp/[slug].segments/_tree.segment.rsc'
|
|
)
|
|
|
|
expect(withGspPrerender).toBeDefined()
|
|
expect(withGspRouteTreePrerender).toBeDefined()
|
|
expect(withGspOtherSegmentPrerenders.length).toBeGreaterThan(0)
|
|
|
|
// Without `partialPrefetching` enabled, the route that would otherwise
|
|
// qualify for a partial fallback shell must not emit the partialFallback
|
|
// config, and its fallback shell collapses to the shared shell (empty
|
|
// allowQuery).
|
|
expect(withGspPrerender.config.partialFallback).toBeUndefined()
|
|
expect(withGspPrerender.config.allowQuery).toEqual([])
|
|
expect(withGspRouteTreePrerender.config.partialFallback).toBeUndefined()
|
|
for (const output of withGspOtherSegmentPrerenders) {
|
|
expect(output.config.partialFallback).toBeUndefined()
|
|
}
|
|
})
|
|
})
|