mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
Initial version of build-time instant validation.
During prerendering, if we have `unstable_instant` configs, we perform a
complete staged render of the page (including dynamic data). This render
mocks request data (`params`/`searchParams`/`cookies`/`headers`) using
the `samples` provided in `unstable_instant`. Then, we use the staged
chunks to run instant validation in mostly the same way that we would in
dev. Most changes in the validation flow are related to samples, which
also need to be passed into the client render to mock APIs like
`useParams`.
### Providing Mock data
We run everything in a fresh workStore+workUnitStore to (hopefully)
avoid any data from the prerender from sneaking in. This is quite
involved.
A big part of the trickiness is that if you do e.g. `(await
cookies()).get('myCookie')` but the `samples` don't contain `myCookie`,
we want the validation to error and require explicitly saying what state
`myCookie` is in.
If it's available, use
```ts
export const unstable_instant = {
// ...
samples: { cookies: [{ name: "myCookie", value: "someValue" }] }
}
```
If it's not available, use
```ts
export const unstable_instant = {
// ...
samples: { cookies: [{ name: "myCookie", value: null }] }
}
```
This explicitness makes it harder to e.g. unintentionally validate a
logged-out state.
Note that even validating a `prefetch: 'static'` page might require
samples, because we need the shared parent for the navigation to be
(mostly) complete. We might be able to relax this in the future, but
it's not straightforward.
Another issue is that a segment might access a missing sample in a
"benign" way, i.e. where it wouldn't block validation (or be inside a
suspense boundary). Ideally we wouldn't require providing a sample for
usages like that, but currently we do.
### Instrumenting APIs to detect missing samples
All request APIs are instrumented in this way.
- `params` throw when accessing a property that was not declared in
`samples`. same for the object from `useParams()`. `in` is allowed
because we know the shape of the params object statically, based on the
routing structure.
- `searchParams` throw when accessing a property that was not declared
in `samples` and `in` checks.
- objects returned from `cookies()`, `headers()`, `useSearchPrams()`
throw on `get()` and `has()`.
There's a lot of tests for this behavior in `instant-validation-build`.
One gap is that iteration/enumeration is currently not instrumented.
We're punting on it for now and will revisit in a follow-up.
### Relation to prerender
We kick off the validation after the prerender, in
`renderToHTMLOrFlightImpl`. Ideally, we should kick this off somewhere
higher up, separately from the prerender itself, but we're leaving that
for future work.
Note that if we do multiple prerenders for the same page (e.g. due to
`generateStaticParams`, we'll only run validation for the first
prerender for that route. `generateStaticParams` is not currently
integrated into the validation at all, i.e. we don't use those params to
validate, so this is safe. All params need to be provided via `samples`.
This will likely change in the future, but we're not sure how yet.
### Disabling build validation
Build-time instant validation is experimental, and has more potential
problems to deal with than dev validation. To allow incremental rollout
and testing, we allow disabling it per instant config:
```ts
export const unstable_instant = {
unstable_disableBuildValidation: true
// ...
}
```
for parity, there's also `unstable_disableBuildValidation`, but we don't
expect that to see much use.
swc-plugin for Next.js Error Code Transformation
Overview
This plugin transforms Next.js source code by:
- Rewriting
new ErrororErrorto include an additional property:Object.defineProperty(new Error(...), "__NEXT_ERROR_CODE", { value: $code, enumerable: false, configurable: true }). Theenumerable: falseensures the error code won't show up in console logs while still being accessible for telemetry. Theconfigurable: trueensures the error code can be overwritten, useful for transforming errors. - This enables anonymous error code reporting for user feedback while keeping the message private.
Error Code Mapping
- File Location:
packages/next/errors.json - Structure: Append-only, increment-based mapping of error codes to messages.
- Automation:
- Running
pnpm buildautomatically updateserrors.jsonif new errors are introduced. - Commit Reminder: Always commit the updated
errors.jsonfile to avoid CI failures.
- Running
Build Process Details
-
Two-Pass Build:
- First Pass:
- Extracts new errors and temporarily stores them in
packages/next/.errors/*.json. - In CI, a build with new errors will fail.
- Extracts new errors and temporarily stores them in
- Second Pass:
- Updates
packages/next/errors.jsonand reruns the build step to confirm no further new errors. - Inspired by React's error mapping mechanism.
- Updates
- First Pass:
-
Concurrency Handling: The two-pass system ensures reliable ordering of inserted error codes during concurrent builds.
Modifying the Plugin
-
WASM Rebuild Required:
- After modifying the plugin, rebuild the WASM file via
pnpm build-error-code-pluginand commit it to the repository. - Reason: Pre-built artifacts simplify the build process (
pnpm buildruns without requiringcargo).
- After modifying the plugin, rebuild the WASM file via
-
Testing: Run
cargo testinside thecrates/next-error-code-swc-plugindirectory to test the plugin.