Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions crates/next-api/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use next_core::{
app_segment_config::NextSegmentConfig,
app_structure::{
AppPageLoaderTree, CollectedRootParams, Entrypoint as AppEntrypoint,
Entrypoints as AppEntrypoints, FileSystemPathVec, MetadataItem, collect_root_params,
get_entrypoints,
Entrypoints as AppEntrypoints, FileSystemPathVec, MetadataItem, RootParamVecOption,
collect_root_params, get_entrypoints,
},
get_edge_resolve_options_context, get_next_package,
next_app::{
Expand Down Expand Up @@ -992,7 +992,10 @@ pub fn app_entry_point_to_route(
) -> Vc<Route> {
match entrypoint {
AppEntrypoint::AppPage {
pages, loader_tree, ..
pages,
loader_tree,
root_params,
..
} => Route::AppPage(
pages
.into_iter()
Expand All @@ -1006,6 +1009,7 @@ pub fn app_entry_point_to_route(
},
app_project,
page: page.clone(),
root_params,
}
.resolved_cell(),
),
Expand All @@ -1017,6 +1021,7 @@ pub fn app_entry_point_to_route(
},
app_project,
page,
root_params,
}
.resolved_cell(),
),
Expand All @@ -1027,6 +1032,7 @@ pub fn app_entry_point_to_route(
page,
path,
root_layouts,
root_params,
..
} => Route::AppRoute {
original_name: page.to_string().into(),
Expand All @@ -1035,17 +1041,24 @@ pub fn app_entry_point_to_route(
ty: AppEndpointType::Route { path, root_layouts },
app_project,
page,
root_params,
}
.resolved_cell(),
),
},
AppEntrypoint::AppMetadata { page, metadata, .. } => Route::AppRoute {
AppEntrypoint::AppMetadata {
page,
metadata,
root_params,
..
} => Route::AppRoute {
original_name: page.to_string().into(),
endpoint: ResolvedVc::upcast(
AppEndpoint {
ty: AppEndpointType::Metadata { metadata },
app_project,
page,
root_params,
}
.resolved_cell(),
),
Expand Down Expand Up @@ -1083,6 +1096,7 @@ struct AppEndpoint {
ty: AppEndpointType,
app_project: ResolvedVc<AppProject>,
page: AppPage,
root_params: ResolvedVc<RootParamVecOption>,
}

#[turbo_tasks::value_impl]
Expand Down Expand Up @@ -1129,6 +1143,7 @@ impl AppEndpoint {
self.app_project.project().project_path().owned().await?,
config,
next_config,
*self.root_params,
))
}

Expand Down
16 changes: 15 additions & 1 deletion crates/next-core/src/next_app/app_route_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub async fn get_app_route_entry(
project_root: FileSystemPath,
original_segment_config: Option<Vc<NextSegmentConfig>>,
next_config: Vc<NextConfig>,
root_params: Vc<crate::app_structure::RootParamVecOption>,
) -> Result<Vc<AppEntry>> {
let segment_from_source = parse_segment_config_from_source(source);
let config = if let Some(original_segment_config) = original_segment_config {
Expand Down Expand Up @@ -70,6 +71,18 @@ pub async fn get_app_route_entry(
.map(RcStr::from)
.unwrap_or_else(|| "\"\"".into());

// Convert root params to JSON array of parameter names
let root_param_names = if let Some(root_params_vec) = &*root_params.await? {
serde_json::to_string(
&root_params_vec
.iter()
.map(|param| param.param.as_str())
.collect::<Vec<_>>(),
)?
} else {
"[]".to_string()
};

// Load the file from the next.js codebase.
let virtual_source = load_next_js_template(
"app-route.js",
Expand All @@ -84,7 +97,8 @@ pub async fn get_app_route_entry(
"VAR_USERLAND" => INNER.into(),
},
fxindexmap! {
"nextConfigOutput" => output_type
"nextConfigOutput" => output_type,
"rootParamNames" => root_param_names.into(),
},
fxindexmap! {},
)
Expand Down
1 change: 1 addition & 0 deletions crates/next-core/src/next_app/metadata/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ pub async fn get_app_metadata_route_entry(
project_root,
Some(segment_config),
next_config,
Vc::cell(None), // Metadata routes don't have root params
))
}

Expand Down
4 changes: 4 additions & 0 deletions packages/next/src/build/analysis/get-page-static-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
} from '../segment-config/middleware/middleware-config'
import { normalizeAppPath } from '../../shared/lib/router/utils/app-paths'
import { normalizePagePath } from '../../shared/lib/page-path/normalize-page-path'
import type { ParamInfo } from '../webpack/loaders/next-root-params-loader'

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

export interface PagesPageStaticInfo {
Expand Down Expand Up @@ -490,6 +492,7 @@ export async function getAppPageStaticInfo({
preferredRegion: undefined,
maxDuration: undefined,
hadUnsupportedValue: false,
rootParams: undefined,
}
}

Expand Down Expand Up @@ -556,6 +559,7 @@ export async function getAppPageStaticInfo({
preferredRegion: config.preferredRegion,
maxDuration: config.maxDuration,
hadUnsupportedValue,
rootParams: undefined,
}
}

Expand Down
49 changes: 33 additions & 16 deletions packages/next/src/build/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { EdgeAppRouteLoaderQuery } from './webpack/loaders/next-edge-app-ro
import type { NextConfigComplete } from '../server/config-shared'
import type { webpack } from 'next/dist/compiled/webpack/webpack'
import type {
AppPageStaticInfo,
MiddlewareConfig,
MiddlewareMatcher,
PageStaticInfo,
Expand Down Expand Up @@ -75,6 +76,7 @@ import type { PageExtensions } from './page-extensions-type'
import type { MappedPages } from './build-context'
import { PAGE_TYPES } from '../lib/page-types'
import { isAppPageRoute } from '../lib/is-app-page-route'
import { getParamsFromLayoutFilePath } from './webpack/loaders/next-root-params-loader'

export function sortByPageExts(pageExtensions: PageExtensions) {
return (a: string, b: string) => {
Expand Down Expand Up @@ -132,25 +134,26 @@ export async function getStaticInfoIncludingLayouts({

const segments = [pageStaticInfo]

// inherit from layout files only if it's a page route
if (isAppPageRoute(page)) {
const layoutFiles = []
const potentialLayoutFiles = pageExtensions.map((ext) => 'layout.' + ext)
let dir = dirname(pageFilePath)

// Uses startsWith to not include directories further up.
while (dir.startsWith(appDir)) {
for (const potentialLayoutFile of potentialLayoutFiles) {
const layoutFile = join(dir, potentialLayoutFile)
if (!fs.existsSync(layoutFile)) {
continue
}
layoutFiles.push(layoutFile)
const layoutFiles: string[] = []
const potentialLayoutFiles = pageExtensions.map((ext) => 'layout.' + ext)
let dir = dirname(pageFilePath)

// We need to find the root layout for both pages and route handlers.
// Uses startsWith to not include directories further up.
while (dir.startsWith(appDir)) {
for (const potentialLayoutFile of potentialLayoutFiles) {
const layoutFile = join(dir, potentialLayoutFile)
if (!fs.existsSync(layoutFile)) {
continue
}
// Walk up the directory tree
dir = join(dir, '..')
layoutFiles.push(layoutFile)
}
// Walk up the directory tree
dir = join(dir, '..')
}

// inherit from layout files only if it's a page route
if (isAppPageRoute(page)) {
for (const layoutFile of layoutFiles) {
const layoutStaticInfo = await getAppPageStaticInfo({
nextConfig,
Expand All @@ -164,6 +167,11 @@ export async function getStaticInfoIncludingLayouts({
}
}

const rootLayout = layoutFiles.at(-1)
const rootParams = rootLayout
? getParamsFromLayoutFilePath({ appDir, layoutFilePath: rootLayout })
: []

const config = reduceAppConfig(segments)

return {
Expand All @@ -172,6 +180,7 @@ export async function getStaticInfoIncludingLayouts({
runtime: config.runtime,
preferredRegion: config.preferredRegion,
maxDuration: config.maxDuration,
rootParams,
}
}

Expand Down Expand Up @@ -695,6 +704,10 @@ export async function createEntrypoints(
page,
name: serverBundlePath,
pagePath: absolutePagePath,
rootParams:
(staticInfo as AppPageStaticInfo).rootParams?.map(
(p) => p.param
) || [],
appDir,
appPaths: matchedAppPaths,
pageExtensions,
Expand Down Expand Up @@ -773,6 +786,10 @@ export async function createEntrypoints(
name: serverBundlePath,
page,
pagePath: absolutePagePath,
rootParams:
(staticInfo as AppPageStaticInfo).rootParams?.map(
(p) => p.param
) || [],
appDir: appDir!,
appPaths: matchedAppPaths,
pageExtensions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function collectRootParamKeys({
AppPageModule | AppRouteModule
>): readonly string[] {
if (isAppRouteRouteModule(routeModule)) {
return []
return routeModule.rootParamNames
}

if (isAppPageRouteModule(routeModule)) {
Expand Down
5 changes: 4 additions & 1 deletion packages/next/src/build/templates/app-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ import * as userland from 'VAR_USERLAND'
// instead of a replacement because this could also be `undefined` instead of
// an empty string.
declare const nextConfigOutput: AppRouteRouteModuleOptions['nextConfigOutput']
declare const rootParamNames: AppRouteRouteModuleOptions['rootParamNames']

// We inject the nextConfigOutput here so that we can use them in the route
// We inject nextConfigOutput and rootParamNames here so that we can use them in the route
// module.
// INJECT:nextConfigOutput
// INJECT:rootParamNames

const routeModule = new AppRouteRouteModule({
definition: {
Expand All @@ -52,6 +54,7 @@ const routeModule = new AppRouteRouteModule({
distDir: process.env.__NEXT_RELATIVE_DIST_DIR || '',
relativeProjectDir: process.env.__NEXT_RELATIVE_PROJECT_DIR || '',
resolvedPagePath: 'VAR_RESOLVED_PAGE_PATH',
rootParamNames,
nextConfigOutput,
userland,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export async function createAppRouteCode({
name,
page,
pagePath,
rootParams,
resolveAppRoute,
pageExtensions,
nextConfigOutput,
Expand All @@ -25,6 +26,7 @@ export async function createAppRouteCode({
name: string
page: string
pagePath: string
rootParams: string[]
resolveAppRoute: (
pathname: string
) => Promise<string | undefined> | string | undefined
Expand Down Expand Up @@ -78,6 +80,7 @@ export async function createAppRouteCode({
},
{
nextConfigOutput: JSON.stringify(nextConfigOutput),
rootParamNames: JSON.stringify(rootParams),
}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export type AppLoaderOptions = {
name: string
page: string
pagePath: string
rootParams: string[]
appDir: string
appPaths: readonly string[] | null
preferredRegion: string | string[] | undefined
Expand Down Expand Up @@ -556,6 +557,7 @@ const nextAppLoader: AppLoader = async function nextAppLoader() {
appDir,
appPaths,
pagePath,
rootParams,
pageExtensions,
rootDir,
tsconfigPath,
Expand Down Expand Up @@ -753,6 +755,7 @@ const nextAppLoader: AppLoader = async function nextAppLoader() {
page: loaderOptions.page,
name,
pagePath,
rootParams,
resolveAppRoute,
pageExtensions,
nextConfigOutput,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,9 @@ async function findRootLayouts({
return visit(appDir)
}

type ParamInfo = { param: string; type: DynamicParamTypes }
export type ParamInfo = { param: string; type: DynamicParamTypes }

function getParamsFromLayoutFilePath({
export function getParamsFromLayoutFilePath({
appDir,
layoutFilePath,
}: {
Expand Down
3 changes: 2 additions & 1 deletion packages/next/src/server/async-storage/request-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export function createRequestStoreForRender(
export function createRequestStoreForAPI(
req: RequestContext['req'],
url: RequestContext['url'],
rootParams: Params,
implicitTags: RequestContext['implicitTags'],
onUpdateCookies: RenderOpts['onUpdateCookies'],
previewProps: WrapperRenderOpts['previewProps']
Expand All @@ -145,7 +146,7 @@ export function createRequestStoreForAPI(
req,
undefined,
url,
{},
rootParams,
implicitTags,
onUpdateCookies,
undefined,
Expand Down
Loading
Loading