next.js
abe3b50d - [PPF] `ensureStatic = "navigation"` (#99171)

Commit
32 days ago
[PPF] `ensureStatic = "navigation"` (#99171) Closes NAR-930 Initial implementation of `ensureStatic = "navigation"` ### Behavior This version of `ensureStatic = "navigation"` makes all prerenders blocking. All params must be specified via `generateStaticParams`, similar to `output: "export"`. This is meant to ensure that we don't serve a fallback + a resume for a param that wasn't prerendered, which would violate the expectation that `ensureStatic = "navigation"` means no per-request compute, which is what we're trying to provide. Note that Client-only IO (like `use(io())` or `useSearchParams()`) is allowed, because it doesn't require a resume render -- we serve HTML with fallbacks and render the missing content in the browser. (this is existing behavior and not changed here) Earlier versions of this PR attempted to allow fallback shells if they managed to produce a complete RSC result, e.g. on a route with params that are only used via `useParams()`, where could use the same browser bailout trick as other client IO. This proved complex to implement correctly, so we'll revisit that in the future ### Prerender output changes We skip producing all fallback shells except the most generic one in `buildAppStaticPaths`. Initially, i wanted to remove all of them, but it seems like the deploy output is incorrect if we don't have segment data from a fallback prerender. We also error for incomplete/missing `generateStaticParams`, which makes it impossible to create a purely-fallback param. ### Validation This PR extends the Static Shell Validation logic to be more restrictive when `ensureStatic = "navigation"`. We can't allow _any_ usages of dynamic data, even when wrapped with suspense. The new logic is expressed in `trackDynamicAccessInStaticRoute` + `getDisallowedReasonsInStaticRoute` which we use both in dev and prod. we check whether the server prerender was partial (which is already an error) and then run static shell validation so we can report where the hole was. This has some drawbacks: - if we have both server and client holes we might misreport the client ones as dynamic data. I have some ideas for how to address this, but for now this is the best we can do. (see `mixed server and client holes` in `ensure-static-navigation.test.ts` for examples) - if the client got a dynamic data promise but didn't `use()` it during the prerender (maybe because it was just seeding a cache for use in the browser), we won't have a dynamic hole to point to. in that case we'll error with a generic error message instead (see `errorPattern.body.withoutLocation` in `ensure-static-navigation.test.ts`). i've added new error messages to match the constraints of `ensureStatic = "navigation"`. we no longer recommend `instant = false` or `suspense` for dynamic data, because that's not a valid fix. there's a new suggestion: "make your \`params\` static via `generateStaticParams\`". (note that the messages don't have the "pretty" errors with suggestion cards yet, just the string message. i will update the dev error logic in a follow-up. docs are needed as well) i've also done a bit of cleanup: `trackDynamicHoleInStaticShell` and `trackDynamicHoleInRuntimeShell` are now one function that takes a `kind` parameter, because they only differed on the error message variant they reported (runtime vs dynamic).
Author
Parents
Loading