[dynamicIO] Error when dynamic behavior is outside of a Suspense boundary (#70734)
When `dynamicIO` is enabled it is important that you can reason about
what parts of component tree are allowed to be dynamic and which must be
static.
The heuristic we're going with is to require that anything dynamic must
be rendered inside a Suspense boundary. The rationale here is that if
you have a static component then you don't need to define a fallback UI
for it because it will never render. Conversely to have a useful dynamic
site having a well defined fallback UI to show when the dynamic part is
not ready yet is helpful.
If PPR is turned off the Suspense boundaries won't actually define a
static UI during prerendering but we want the upgrade path to PPR to be
as unblocked as possible so we require that you follow this semantic
when `dynamicIO` is enabled even if PPR is not.
There are some special considerations we need to make for
`generateMetadata`, `generateViewport`, and use of synchronosuly dynamic
APIs like `(cookies() as unknown as UnsafeUnwrappedCookies).get(...)`.
Metadata & Viewport can't be wrapped in a Suspense boundary. So if
something else in your application is dynamic we don't error for dynamic
metadata/viewport. If dynamic metadata/viewport is the only thing that
is dynamic in your application we consider this an error. Your recourse
is to make these functions only read static/cached data or alternatively
you can make the application itself dynamic by introducing something
dynamic inside a Suspense boundary.
If you use a Synchronously dynamic API like the one mentioned above you
will likely get incomplete parts of your component tree identified as
dynamic b/c the synchronous dynamic API immediately terminates the
prerender. We provide component stacks in these cases but the correct
fix is to find the synchronous dynamic API and make it async.