next/root-params (#80255)
Implements accessing root params (params segments that occur above root
layouts) via compiler-generated getters from the `next/root-params`
module:
```tsx
// app/[lang]/layout.tsx
// (there is no app/layout.tsx, this is the root layout)
import { lang } from "next/root-params";
// ^^^^ coresponds to the [lang] segment above this module
export default async function RootLayout({ children }) {
return (
<html lang={await lang()}>
<body>{children}</body>
</html>
);
}
```
- This API is only usable within server components (support for using it
in route handlers will be added in the future). It can be called
anywhere in the component tree, not just in a page or a layout.
- It **cannot be used in server actions**, because they're not tied to a
route.
- It will replace `unstable_rootParams`, which will be removed soon.
Note that we can also have multiple root layouts with distinct params,
like this:
```
app/product/[productId]/layout.tsx
app/brand/[brandId]/layout.tsx
```
In this case we'll generate getters for both `productId` and `brandId`.
They can be called anywhere, but they'll return `undefined` if used in a
subtree where a param isn't available.
Note that this PR does not yet generate type declarations for the
generated getters, essentially leaving them typed as `any`. This will be
handled in a follow up.
## Implementation notes
### Turbopack
We collect the root param names when analyzing the folder structure in
`app_structure.rs`. The set is then passed down (as a Vc) all the way to
`next_import_map.rs`, where we insert an alias to a virtual
`next/root-params.js` module. We use a `ImportMapping::Dynamic` to
generate its code lazily in a separate turbo task (which is the only
place that actually reads the root params). This avoids invalidating the
whole `ResolveOptionsContext` if the root params change.
### Webpack
We alias `next/root-params` to `next-root-params-loader`, which scans
the directory structure manually (i haven't found a good way to re-use
existing methods for doing this) and returns the generated module. Each
directory we traverse before finding a root layout is marked as a
`contextDependency`, which should invalidate the loader's result if new
files are added to any of them or if they get renamed, because it might
mean that the set of root params changed.