Skip to content

Commit 2a607fc

Browse files
committed
wip: root params in route handlers
1 parent 6cf65cb commit 2a607fc

File tree

12 files changed

+59
-22
lines changed

12 files changed

+59
-22
lines changed

crates/next-core/src/next_app/app_route_entry.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ pub async fn get_app_route_entry(
8484
"VAR_USERLAND" => INNER.into(),
8585
},
8686
fxindexmap! {
87-
"nextConfigOutput" => output_type
87+
"nextConfigOutput" => output_type,
88+
// "rootParamNames" => ... // TODO(root-params)
8889
},
8990
fxindexmap! {},
9091
)

packages/next/src/build/analysis/get-page-static-info.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
} from '../segment-config/middleware/middleware-config'
3636
import { normalizeAppPath } from '../../shared/lib/router/utils/app-paths'
3737
import { normalizePagePath } from '../../shared/lib/page-path/normalize-page-path'
38+
import type { ParamInfo } from '../webpack/loaders/next-root-params-loader'
3839

3940
const PARSE_PATTERN =
4041
/(?<!(_jsx|jsx-))runtime|preferredRegion|getStaticProps|getServerSideProps|generateStaticParams|export const|generateImageMetadata|generateSitemaps/
@@ -80,6 +81,7 @@ export interface AppPageStaticInfo {
8081
preferredRegion: AppSegmentConfig['preferredRegion'] | undefined
8182
maxDuration: number | undefined
8283
hadUnsupportedValue: boolean
84+
rootParams: ParamInfo[] | undefined
8385
}
8486

8587
export interface PagesPageStaticInfo {
@@ -490,6 +492,7 @@ export async function getAppPageStaticInfo({
490492
preferredRegion: undefined,
491493
maxDuration: undefined,
492494
hadUnsupportedValue: false,
495+
rootParams: undefined,
493496
}
494497
}
495498

@@ -556,6 +559,7 @@ export async function getAppPageStaticInfo({
556559
preferredRegion: config.preferredRegion,
557560
maxDuration: config.maxDuration,
558561
hadUnsupportedValue,
562+
rootParams: undefined,
559563
}
560564
}
561565

packages/next/src/build/entries.ts

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { EdgeAppRouteLoaderQuery } from './webpack/loaders/next-edge-app-ro
55
import type { NextConfigComplete } from '../server/config-shared'
66
import type { webpack } from 'next/dist/compiled/webpack/webpack'
77
import type {
8+
AppPageStaticInfo,
89
MiddlewareConfig,
910
MiddlewareMatcher,
1011
PageStaticInfo,
@@ -76,6 +77,7 @@ import type { PageExtensions } from './page-extensions-type'
7677
import type { MappedPages } from './build-context'
7778
import { PAGE_TYPES } from '../lib/page-types'
7879
import { isAppPageRoute } from '../lib/is-app-page-route'
80+
import { getParamsFromLayoutFilePath } from './webpack/loaders/next-root-params-loader'
7981

8082
export function sortByPageExts(pageExtensions: PageExtensions) {
8183
return (a: string, b: string) => {
@@ -133,25 +135,26 @@ export async function getStaticInfoIncludingLayouts({
133135

134136
const segments = [pageStaticInfo]
135137

136-
// inherit from layout files only if it's a page route
137-
if (isAppPageRoute(page)) {
138-
const layoutFiles = []
139-
const potentialLayoutFiles = pageExtensions.map((ext) => 'layout.' + ext)
140-
let dir = dirname(pageFilePath)
141-
142-
// Uses startsWith to not include directories further up.
143-
while (dir.startsWith(appDir)) {
144-
for (const potentialLayoutFile of potentialLayoutFiles) {
145-
const layoutFile = join(dir, potentialLayoutFile)
146-
if (!fs.existsSync(layoutFile)) {
147-
continue
148-
}
149-
layoutFiles.push(layoutFile)
138+
const layoutFiles: string[] = []
139+
const potentialLayoutFiles = pageExtensions.map((ext) => 'layout.' + ext)
140+
let dir = dirname(pageFilePath)
141+
142+
// We need to find the root layout for both pages and route handlers.
143+
// Uses startsWith to not include directories further up.
144+
while (dir.startsWith(appDir)) {
145+
for (const potentialLayoutFile of potentialLayoutFiles) {
146+
const layoutFile = join(dir, potentialLayoutFile)
147+
if (!fs.existsSync(layoutFile)) {
148+
continue
150149
}
151-
// Walk up the directory tree
152-
dir = join(dir, '..')
150+
layoutFiles.push(layoutFile)
153151
}
152+
// Walk up the directory tree
153+
dir = join(dir, '..')
154+
}
154155

156+
// inherit from layout files only if it's a page route
157+
if (isAppPageRoute(page)) {
155158
for (const layoutFile of layoutFiles) {
156159
const layoutStaticInfo = await getAppPageStaticInfo({
157160
nextConfig,
@@ -165,6 +168,11 @@ export async function getStaticInfoIncludingLayouts({
165168
}
166169
}
167170

171+
const rootLayout = layoutFiles.at(-1)
172+
const rootParams = rootLayout
173+
? getParamsFromLayoutFilePath({ appDir, layoutFilePath: rootLayout })
174+
: []
175+
168176
const config = reduceAppConfig(segments)
169177

170178
return {
@@ -173,6 +181,7 @@ export async function getStaticInfoIncludingLayouts({
173181
runtime: config.runtime,
174182
preferredRegion: config.preferredRegion,
175183
maxDuration: config.maxDuration,
184+
rootParams,
176185
}
177186
}
178187

@@ -709,6 +718,7 @@ export async function createEntrypoints(
709718
page,
710719
name: serverBundlePath,
711720
pagePath: absolutePagePath,
721+
rootParams: (staticInfo as AppPageStaticInfo).rootParams!,
712722
appDir,
713723
appPaths: matchedAppPaths,
714724
pageExtensions,
@@ -787,6 +797,7 @@ export async function createEntrypoints(
787797
name: serverBundlePath,
788798
page,
789799
pagePath: absolutePagePath,
800+
rootParams: (staticInfo as AppPageStaticInfo).rootParams!,
790801
appDir: appDir!,
791802
appPaths: matchedAppPaths,
792803
pageExtensions,

packages/next/src/build/segment-config/app/collect-root-param-keys.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export function collectRootParamKeys({
5252
AppPageModule | AppRouteModule
5353
>): readonly string[] {
5454
if (isAppRouteRouteModule(routeModule)) {
55-
return []
55+
return routeModule.rootParamNames
5656
}
5757

5858
if (isAppPageRouteModule(routeModule)) {

packages/next/src/build/templates/app-route.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@ import * as userland from 'VAR_USERLAND'
3737
// instead of a replacement because this could also be `undefined` instead of
3838
// an empty string.
3939
declare const nextConfigOutput: AppRouteRouteModuleOptions['nextConfigOutput']
40+
declare const rootParamNames: AppRouteRouteModuleOptions['rootParamNames']
4041

41-
// We inject the nextConfigOutput here so that we can use them in the route
42+
// We inject nextConfigOutput and rootParamNames here so that we can use them in the route
4243
// module.
4344
// INJECT:nextConfigOutput
45+
// INJECT:rootParamNames
4446

4547
const routeModule = new AppRouteRouteModule({
4648
definition: {
@@ -53,6 +55,7 @@ const routeModule = new AppRouteRouteModule({
5355
distDir: process.env.__NEXT_RELATIVE_DIST_DIR || '',
5456
projectDir: process.env.__NEXT_RELATIVE_PROJECT_DIR || '',
5557
resolvedPagePath: 'VAR_RESOLVED_PAGE_PATH',
58+
rootParamNames,
5659
nextConfigOutput,
5760
userland,
5861
})

packages/next/src/build/webpack/loaders/next-app-loader/create-app-route-code.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import { AppPathnameNormalizer } from '../../../../server/normalizers/built/app/
1111
import { loadEntrypoint } from '../../../load-entrypoint'
1212
import type { PageExtensions } from '../../../page-extensions-type'
1313
import { getFilenameAndExtension } from '../next-metadata-route-loader'
14+
import type { ParamInfo } from '../next-root-params-loader'
1415

1516
export async function createAppRouteCode({
1617
appDir,
1718
name,
1819
page,
1920
pagePath,
21+
rootParams,
2022
resolveAppRoute,
2123
pageExtensions,
2224
nextConfigOutput,
@@ -25,6 +27,7 @@ export async function createAppRouteCode({
2527
name: string
2628
page: string
2729
pagePath: string
30+
rootParams: ParamInfo[]
2831
resolveAppRoute: (
2932
pathname: string
3033
) => Promise<string | undefined> | string | undefined
@@ -78,6 +81,7 @@ export async function createAppRouteCode({
7881
},
7982
{
8083
nextConfigOutput: JSON.stringify(nextConfigOutput),
84+
rootParamNames: JSON.stringify(rootParams.map((p) => p.param)),
8185
}
8286
)
8387
}

packages/next/src/build/webpack/loaders/next-app-loader/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,13 @@ import type { PageExtensions } from '../../../page-extensions-type'
3333
import { PARALLEL_ROUTE_DEFAULT_PATH } from '../../../../client/components/builtin/default'
3434
import type { Compilation } from 'webpack'
3535
import { createAppRouteCode } from './create-app-route-code'
36+
import type { ParamInfo } from '../next-root-params-loader'
3637

3738
export type AppLoaderOptions = {
3839
name: string
3940
page: string
4041
pagePath: string
42+
rootParams: ParamInfo[]
4143
appDir: string
4244
appPaths: readonly string[] | null
4345
preferredRegion: string | string[] | undefined
@@ -548,6 +550,7 @@ const nextAppLoader: AppLoader = async function nextAppLoader() {
548550
appDir,
549551
appPaths,
550552
pagePath,
553+
rootParams,
551554
pageExtensions,
552555
rootDir,
553556
tsconfigPath,
@@ -745,6 +748,7 @@ const nextAppLoader: AppLoader = async function nextAppLoader() {
745748
page: loaderOptions.page,
746749
name,
747750
pagePath,
751+
rootParams,
748752
resolveAppRoute,
749753
pageExtensions,
750754
nextConfigOutput,

packages/next/src/build/webpack/loaders/next-root-params-loader.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,9 @@ async function findRootLayouts({
158158
return visit(appDir)
159159
}
160160

161-
type ParamInfo = { param: string; type: DynamicParamTypes }
161+
export type ParamInfo = { param: string; type: DynamicParamTypes }
162162

163-
function getParamsFromLayoutFilePath({
163+
export function getParamsFromLayoutFilePath({
164164
appDir,
165165
layoutFilePath,
166166
}: {

packages/next/src/server/async-storage/request-store.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ export function createRequestStoreForRender(
135135
export function createRequestStoreForAPI(
136136
req: RequestContext['req'],
137137
url: RequestContext['url'],
138+
rootParams: Params,
138139
implicitTags: RequestContext['implicitTags'],
139140
onUpdateCookies: RenderOpts['onUpdateCookies'],
140141
previewProps: WrapperRenderOpts['previewProps']
@@ -145,7 +146,7 @@ export function createRequestStoreForAPI(
145146
req,
146147
undefined,
147148
url,
148-
{},
149+
rootParams,
149150
implicitTags,
150151
onUpdateCookies,
151152
undefined,

packages/next/src/server/dev/hot-reloader-webpack.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ import { getDisableDevIndicatorMiddleware } from '../../next-devtools/server/dev
9090
import getWebpackBundler from '../../shared/lib/get-webpack-bundler'
9191
import { getRestartDevServerMiddleware } from '../../next-devtools/server/restart-dev-server-middleware'
9292
import { checkPersistentCacheInvalidationAndCleanup } from '../../build/webpack/cache-invalidation'
93+
import type { AppPageStaticInfo } from '../../build/analysis/get-page-static-info'
9394

9495
const MILLISECONDS_IN_NANOSECOND = BigInt(1_000_000)
9596

@@ -957,6 +958,7 @@ export default class HotReloaderWebpack implements NextJsHotReloaderInterface {
957958
entryData.absolutePagePath
958959
).replace(/\\/g, '/')
959960
),
961+
rootParams: (staticInfo as AppPageStaticInfo).rootParams!,
960962
appDir: this.appDir!,
961963
pageExtensions: this.config.pageExtensions,
962964
rootDir: this.dir,
@@ -1080,6 +1082,7 @@ export default class HotReloaderWebpack implements NextJsHotReloaderInterface {
10801082
page,
10811083
appPaths: entryData.appPaths,
10821084
pagePath,
1085+
rootParams: (staticInfo as AppPageStaticInfo).rootParams!,
10831086
appDir: this.appDir!,
10841087
pageExtensions: this.config.pageExtensions,
10851088
rootDir: this.dir,

0 commit comments

Comments
 (0)