mirror of
https://github.com/vercel/next.js.git
synced 2026-09-20 02:25:18 +08:00
35d91fcaae
## Summary
This is the behavior-preserving base of a four-PR stack that introduces
explicit prerender matching policy without coupling the foundational
refactor to the proposed API.
The build currently uses `PrerenderedRoute` values for two related but
distinct concepts:
- a logical request matcher that says which parameterized URL shapes the
route can handle
- a build-time render candidate that may or may not become a persisted
prerender artifact
A candidate is not guaranteed to be an output. It can be rendered only
to validate a shell, discarded, and still leave behind a matcher that
tells future requests to block.
The new relationship is:
```text
logical pathname matcher
-> zero or more render candidates
-> zero or more persisted artifacts
```
## Concrete example
Consider `/[top]/items/[bottom]`:
```ts
export function generateStaticParams() {
return [{ top: 't1', bottom: 'b1' }]
}
```
Static-path generation may consider three shapes:
| Shape | Purpose |
| --- | --- |
| `/[top]/items/[bottom]` | A generic shell candidate and logical
matcher |
| `/t1/items/[bottom]` | A shell after resolving `top` |
| `/t1/items/b1` | A concrete build-time prerender |
Suppose the generic candidate renders an allowed empty shell. The build
should discard that candidate artifact and use blocking behavior for the
generic matcher. It should not remove `/[top]/items/[bottom]` from the
valid matcher set, and it should not discard the concrete `/t1/items/b1`
artifact.
This is why the build needs two sets:
- route matchers, which describe valid request shapes
- prerender candidates, whose render results determine whether an
artifact is retained and can refine inferred fallback behavior
## Variants compatibility
Variants will make pathname-only candidate maps insufficient. Several
variant combinations can share `/t1/items/b1` as their logical pathname
while writing distinct artifacts under variant-specific output paths.
This PR keeps the route matcher keyed by logical pathname but retains
every candidate associated with it. Candidate finalization can therefore
evaluate each variant artifact independently without changing the route
tree's matcher set.
## Behavior preservation
This PR does not add a user-facing API or change `generateStaticParams`
semantics:
- a usable static shell remains a fallback prerender
- an allowed empty shell is discarded and represented by a blocking
matcher
- a route that requires a non-empty shell still fails validation
- unresolved matchers remain gated by route-level PPR support
- the most-specific shell continues to supply first-writer-wins metadata
such as prefetch hints
Render results can decide whether a candidate artifact is published and
can refine inferred matcher behavior, but they do not remove the logical
route matcher itself.
## Stack plan
1. **#97431 — model prerenders as render candidates:** land the
behavior-preserving matcher/candidate separation and post-render
finalization first.
2. **#97393 — add the experimental matcher API:** add `unstable_matcher`
and `unstable_generateMatcher`, policy aggregation, validation, and
local diagnostics.
3. **#97426 — test complex route shapes:** add test-only coverage for
catch-alls, optional catch-alls, root parameters, and parallel slots.
4. **#97427 — test foreground policy behavior:** add test-only coverage
showing blocking misses generate before responding while fallback misses
return the shared shell immediately.
This layering lets the internal model be reviewed and landed
independently of the API design. The upper test PRs validate the final
behavior without increasing the implementation diff.
## Verification
- `pnpm --filter=next types`
- `pnpm test-start-turbo
test/e2e/app-dir/sub-shell-generation/sub-shell-generation.test.ts`
- `pnpm test-start-turbo
test/e2e/app-dir/segment-cache/prefetch-inlining/prefetch-inlining.test.ts`
<!-- NEXT_JS_LLM -->