Skip to content

Commit 7e0fc3b

Browse files
authored
Turbopack: refactor segment config parsing (#83297)
Closes PACK-5382 Previously, Turbopack validated every single module for route segment config, leading to false positives (on non-page files) and worse build performance. There were also three separate implementations of this logic. Now, all of these cases are fatal errors, not just warnings. Examples for errors ``` ./bench/basic-app/app/export/inherit/page.tsx:1:28 Next.js can't recognize the exported `preferredRegion` field in route. It needs to be a static string or array of static strings. > 1 | export { default, runtime, preferredRegion } from '../basic/foo' | ^^^^^^^^^^^^^^^ The exported configuration object in a source file needs to have a very specific format from which some properties can be statically parsed at compiled-time. https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config ``` This error message is slightly different now: ``` browser log: ./test/integration/app-dir-export/app/another/[slug]/page.js:6:8 Next.js can't recognize the exported `generateStaticParams` field in route. App pages cannot use both "use client" and export function "generateStaticParams()". 4 | 5 | > 6 | export function generateStaticParams() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > 7 | return [{ slug: 'first' }, { slug: 'second' }] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > 8 | } | ^^ 9 | 10 | export default async function Page(props) { 11 | const params = await props.params The exported configuration object in a source file needs to have a very specific format from which some properties can be statically parsed at compiled-time. Import traces: Client Component Browser: ./test/integration/app-dir-export/app/another/[slug]/page.js [Client Component Browser] ./test/integration/app-dir-export/app/another/[slug]/page.js [Server Component] Client Component SSR: ./test/integration/app-dir-export/app/another/[slug]/page.js [Client Component SSR] ./test/integration/app-dir-export/app/another/[slug]/page.js [Server Component] https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config ```
1 parent f2e3357 commit 7e0fc3b

File tree

33 files changed

+4408
-6294
lines changed

33 files changed

+4408
-6294
lines changed

crates/next-api/src/app.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use anyhow::{Context, Result, bail};
22
use next_core::{
33
all_assets_from_entries,
4-
app_segment_config::NextSegmentConfig,
54
app_structure::{
65
AppPageLoaderTree, CollectedRootParams, Entrypoint as AppEntrypoint,
76
Entrypoints as AppEntrypoints, FileSystemPathVec, MetadataItem, collect_root_params,
@@ -34,6 +33,7 @@ use next_core::{
3433
},
3534
next_server_utility::{NEXT_SERVER_UTILITY_MERGE_TAG, NextServerUtilityTransition},
3635
parse_segment_config_from_source,
36+
segment_config::{NextSegmentConfig, ParseSegmentMode},
3737
util::{NextRuntime, app_function_name, module_styles_rule_condition, styles_rule_condition},
3838
};
3939
use serde::{Deserialize, Serialize};
@@ -1106,7 +1106,7 @@ impl AppEndpoint {
11061106

11071107
for layout in root_layouts.iter().rev() {
11081108
let source = Vc::upcast(FileSource::new(layout.clone()));
1109-
let layout_config = parse_segment_config_from_source(source);
1109+
let layout_config = parse_segment_config_from_source(source, ParseSegmentMode::App);
11101110
config.apply_parent_config(&*layout_config.await?);
11111111
}
11121112

crates/next-api/src/middleware.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use next_core::{
77
next_edge::entry::wrap_edge_entry,
88
next_manifests::{EdgeFunctionDefinition, MiddlewareMatcher, MiddlewaresManifestV2, Regions},
99
next_server::{ServerContextType, get_server_runtime_entries},
10-
util::{MiddlewareMatcherKind, NextRuntime, parse_config_from_source},
10+
parse_segment_config_from_source,
11+
segment_config::ParseSegmentMode,
12+
util::{MiddlewareMatcherKind, NextRuntime},
1113
};
1214
use tracing::Instrument;
1315
use turbo_rcstr::{RcStr, rcstr};
@@ -86,10 +88,12 @@ impl MiddlewareEndpoint {
8688
userland_module,
8789
);
8890

89-
let config =
90-
parse_config_from_source(*self.source, userland_module, NextRuntime::Edge).await?;
91+
let runtime = parse_segment_config_from_source(*self.source, ParseSegmentMode::Base)
92+
.await?
93+
.runtime
94+
.unwrap_or(NextRuntime::Edge);
9195

92-
if matches!(config.runtime, NextRuntime::NodeJs) {
96+
if matches!(runtime, NextRuntime::NodeJs) {
9397
return Ok(module);
9498
}
9599
Ok(wrap_edge_entry(
@@ -175,11 +179,9 @@ impl MiddlewareEndpoint {
175179
async fn output_assets(self: Vc<Self>) -> Result<Vc<OutputAssets>> {
176180
let this = self.await?;
177181

178-
let userland_module = self.userland_module();
179-
180182
let config =
181-
parse_config_from_source(*self.await?.source, userland_module, NextRuntime::Edge)
182-
.await?;
183+
parse_segment_config_from_source(*self.await?.source, ParseSegmentMode::Base).await?;
184+
let runtime = config.runtime.unwrap_or(NextRuntime::Edge);
183185

184186
let next_config = this.project.next_config().await?;
185187
let has_i18n = next_config.i18n.is_some();
@@ -190,7 +192,7 @@ impl MiddlewareEndpoint {
190192
.unwrap_or(false);
191193
let base_path = next_config.base_path.as_ref();
192194

193-
let matchers = if let Some(matchers) = config.matcher.as_ref() {
195+
let matchers = if let Some(matchers) = config.middleware_matcher.as_ref() {
194196
matchers
195197
.iter()
196198
.map(|matcher| {
@@ -247,7 +249,7 @@ impl MiddlewareEndpoint {
247249
}]
248250
};
249251

250-
if matches!(config.runtime, NextRuntime::NodeJs) {
252+
if matches!(runtime, NextRuntime::NodeJs) {
251253
let chunk = self.node_chunk().to_resolved().await?;
252254
let mut output_assets = vec![chunk];
253255
if this.project.next_mode().await?.is_production() {
@@ -296,7 +298,7 @@ impl MiddlewareEndpoint {
296298
let all_assets =
297299
get_asset_paths_from_root(&node_root_value, &all_output_assets).await?;
298300

299-
let regions = if let Some(regions) = config.regions.as_ref() {
301+
let regions = if let Some(regions) = config.preferred_region.as_ref() {
300302
if regions.len() == 1 {
301303
regions
302304
.first()

crates/next-api/src/pages.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ use next_core::{
2323
pages_structure::{
2424
PagesDirectoryStructure, PagesStructure, PagesStructureItem, find_pages_structure,
2525
},
26-
util::{
27-
NextRuntime, get_asset_prefix_from_pathname, pages_function_name, parse_config_from_source,
28-
},
26+
parse_segment_config_from_source,
27+
segment_config::ParseSegmentMode,
28+
util::{NextRuntime, get_asset_prefix_from_pathname, pages_function_name},
2929
};
3030
use serde::{Deserialize, Serialize};
3131
use tracing::Instrument;
@@ -930,7 +930,9 @@ impl PageEndpoint {
930930
.module();
931931

932932
let config =
933-
parse_config_from_source(self.source(), ssr_module, NextRuntime::default()).await?;
933+
parse_segment_config_from_source(self.source(), ParseSegmentMode::Base).await?;
934+
935+
let runtime = config.runtime.unwrap_or(NextRuntime::NodeJs);
934936

935937
Ok(
936938
// `/_app` and `/_document` never get rendered directly so they don't need to be
@@ -944,9 +946,9 @@ impl PageEndpoint {
944946
// /_app and /_document are always rendered for Node.js for this case. For edge
945947
// they're included in the page bundle.
946948
runtime: NextRuntime::NodeJs,
947-
regions: config.regions.clone(),
949+
regions: config.preferred_region.clone(),
948950
}
949-
} else if config.runtime == NextRuntime::Edge {
951+
} else if runtime == NextRuntime::Edge {
950952
let modules = create_page_ssr_entry_module(
951953
this.pathname.clone(),
952954
reference_type,
@@ -955,7 +957,7 @@ impl PageEndpoint {
955957
self.source(),
956958
this.original_name.clone(),
957959
*this.pages_structure,
958-
config.runtime,
960+
runtime,
959961
this.pages_project.project().next_config(),
960962
)
961963
.await?;
@@ -964,8 +966,8 @@ impl PageEndpoint {
964966
ssr_module: modules.ssr_module,
965967
app_module: modules.app_module,
966968
document_module: modules.document_module,
967-
runtime: config.runtime,
968-
regions: config.regions.clone(),
969+
runtime,
970+
regions: config.preferred_region.clone(),
969971
}
970972
} else {
971973
let modules = create_page_ssr_entry_module(
@@ -976,16 +978,16 @@ impl PageEndpoint {
976978
self.source(),
977979
this.original_name.clone(),
978980
*this.pages_structure,
979-
config.runtime,
981+
runtime,
980982
this.pages_project.project().next_config(),
981983
)
982984
.await?;
983985
InternalSsrChunkModule {
984986
ssr_module: modules.ssr_module,
985987
app_module: modules.app_module,
986988
document_module: modules.document_module,
987-
runtime: config.runtime,
988-
regions: config.regions.clone(),
989+
runtime,
990+
regions: config.preferred_region.clone(),
989991
}
990992
}
991993
.cell(),

crates/next-api/src/project.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ use next_core::{
2121
get_server_module_options_context, get_server_resolve_options_context,
2222
},
2323
next_telemetry::NextFeatureTelemetry,
24-
util::{NextRuntime, OptionEnvMap, parse_config_from_source},
24+
parse_segment_config_from_source,
25+
segment_config::ParseSegmentMode,
26+
util::{NextRuntime, OptionEnvMap},
2527
};
2628
use serde::{Deserialize, Serialize};
2729
use tracing::Instrument;
@@ -66,7 +68,6 @@ use turbopack_core::{
6668
export_usage::{OptionExportUsageInfo, compute_export_usage_info},
6769
},
6870
output::{OutputAsset, OutputAssets},
69-
reference_type::{EntryReferenceSubType, ReferenceType},
7071
resolve::{FindContextFileResult, find_context_file},
7172
source_map::OptionStringifiedSourceMap,
7273
version::{
@@ -1412,16 +1413,12 @@ impl Project {
14121413
};
14131414
let source = Vc::upcast(FileSource::new(fs_path.clone()));
14141415

1415-
let module = edge_module_context
1416-
.process(
1417-
source,
1418-
ReferenceType::Entry(EntryReferenceSubType::Middleware),
1419-
)
1420-
.module();
1421-
1422-
let config = parse_config_from_source(source, module, NextRuntime::Edge).await?;
1416+
let runtime = parse_segment_config_from_source(source, ParseSegmentMode::Base)
1417+
.await?
1418+
.runtime
1419+
.unwrap_or(NextRuntime::Edge);
14231420

1424-
if matches!(config.runtime, NextRuntime::NodeJs) {
1421+
if matches!(runtime, NextRuntime::NodeJs) {
14251422
Ok(self.node_middleware_context())
14261423
} else {
14271424
Ok(edge_module_context)

0 commit comments

Comments
 (0)