Skip to content
Merged
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ dhat = { version = "0.3.2" }
dunce = "1.0.3"
either = "1.15.0"
erased-serde = "0.4.5"
fixedbitset = "0.5.7"
flate2 = "1.0.28"
fs-err = "3.1.1"
futures = "0.3.31"
Expand Down
8 changes: 4 additions & 4 deletions crates/next-api/src/aggregate_hmr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl Version for AggregateHmrVersion {
let version = TraitRef::cell(version.clone());
async move {
let id = version.id().owned().await?;
Ok::<_, anyhow::Error>((path, id))
anyhow::Ok((path, id))
}
})
.try_join()
Expand Down Expand Up @@ -93,7 +93,7 @@ impl AggregateHmrVersion {
let content = *content;
async move {
let version = content.version().into_trait_ref().await?;
Ok::<_, anyhow::Error>((path, version))
anyhow::Ok((path, version))
}
})
.try_join()
Expand Down Expand Up @@ -194,9 +194,9 @@ pub async fn diff_chunks_against(
};
Some((path.clone(), *content, TraitRef::cell(prev)))
})
.map(|(path, content, prev)| async move {
.map(async |(path, content, prev)| {
let update = content.update(prev).await?;
Ok::<_, anyhow::Error>((path, update))
anyhow::Ok((path, update))
})
.try_join()
.await?;
Expand Down
2 changes: 1 addition & 1 deletion crates/next-api/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,7 @@ impl AppProject {
.any(|route| route.as_str() == pathname.to_string())
})
})
.map(|(pathname, app_entrypoint)| async {
.map(async |(pathname, app_entrypoint)| {
Ok((
pathname.to_string().into(),
app_entry_point_to_route(self, app_entrypoint.clone())
Expand Down
2 changes: 1 addition & 1 deletion crates/next-api/src/client_references.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub async fn map_client_references(
let manifest = graph
.await?
.iter_reachable_modules()?
.map(|module| async move {
.map(async |module| {
if let Some(client_reference_module) =
ResolvedVc::try_downcast_type::<EcmascriptClientReferenceModule>(module)
{
Expand Down
4 changes: 2 additions & 2 deletions crates/next-api/src/dynamic_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub(crate) async fn collect_next_dynamic_chunks(
let chunking_availability = &chunking_availability;
let dynamic_import_chunks = dynamic_import_entries
.iter()
.map(|(dynamic_entry, parent_client_reference)| async move {
.map(async |(dynamic_entry, parent_client_reference)| {
let module = ResolvedVc::upcast::<Box<dyn ChunkableModule>>(*dynamic_entry);

// This is the availability info for the parent chunk group, i.e. the client reference
Expand Down Expand Up @@ -124,7 +124,7 @@ pub async fn map_next_dynamic(
graph
.await?
.iter_reachable_modules()?
.map(|module| async move {
.map(async |module| {
if let Some(dynamic_entry_module) =
ResolvedVc::try_downcast_type::<NextDynamicEntryModule>(module)
&& module.ident().await?.layer.as_ref().is_some_and(|layer| {
Expand Down
6 changes: 3 additions & 3 deletions crates/next-api/src/module_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ impl NextDynamicGraphs {
let result = self
.0
.iter()
.map(|graph| async move {
.map(async |graph| {
Ok(graph
.get_next_dynamic_imports_for_endpoint(entry)
.await?
Expand Down Expand Up @@ -300,7 +300,7 @@ impl ServerActionsGraphs {
let result = self
.0
.iter()
.map(|graph| async move {
.map(async |graph| {
graph
.get_server_actions_for_endpoint(entry, rsc_asset_context)
.owned()
Expand Down Expand Up @@ -372,7 +372,7 @@ impl ServerActionsGraph {

let actions = data
.iter()
.map(|(module, (layer, actions))| async move {
.map(async |(module, (layer, actions))| {
let actions = actions.await?;
actions
.actions
Expand Down
4 changes: 2 additions & 2 deletions crates/next-api/src/versioned_content_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ impl VersionedContentMap {
let rel = root.get_path_to(&path)?;
Some((RcStr::from(rel), path))
})
.map(|(name, path)| async move {
.map(async |(name, path)| {
// Skip Redirect assets: they're symlinks with no file content,
// so versioning them would bail with "not a file".
let Some(asset) = *self.get_asset(path).await? else {
Expand Down Expand Up @@ -337,7 +337,7 @@ async fn get_entries(assets: OperationVc<ExpandedOutputAssets>) -> Result<Vc<Get
let assets_ref = assets.connect().await?;
let entries = assets_ref
.iter()
.map(|&asset| async move {
.map(async |&asset| {
let path = asset.path().owned().await?;
Ok((path, asset))
})
Expand Down
48 changes: 47 additions & 1 deletion crates/next-core/src/app_structure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,43 @@ impl Issue for DuplicateParallelRouteIssue {
}
}

#[turbo_tasks::value]
struct MissingRootLayoutIssue {
app_dir: FileSystemPath,
page_path: FileSystemPath,
}

#[async_trait]
#[turbo_tasks::value_impl]
impl Issue for MissingRootLayoutIssue {
async fn file_path(&self) -> Result<FileSystemPath> {
Ok(self.page_path.clone())
}

fn stage(&self) -> IssueStage {
IssueStage::AppStructure
}

fn severity(&self) -> IssueSeverity {
IssueSeverity::Error
}

async fn title(&self) -> Result<StyledString> {
let page_path = self
.app_dir
.get_path_to(&self.page_path)
.context("page should be within the app directory")?;

Ok(StyledString::Text(
format!(
"{page_path} doesn't have a root layout. To fix this error, make sure every page \
has a root layout."
)
.into(),
))
}
}

#[turbo_tasks::value]
struct MissingDefaultParallelRouteIssue {
app_dir: FileSystemPath,
Expand Down Expand Up @@ -1591,7 +1628,16 @@ async fn directory_tree_to_entrypoints_internal_untraced(
root_params
};

if modules.page.is_some() {
if let Some(page_path) = &modules.page {
if root_layouts.await?.is_empty() {
MissingRootLayoutIssue {
app_dir: app_dir.clone(),
page_path: page_path.clone(),
}
.resolved_cell()
.emit();
}

let app_path = AppPath::from(app_page.clone());

let loader_tree = *directory_tree_to_loader_tree(
Expand Down
6 changes: 3 additions & 3 deletions crates/next-core/src/next_app/app_client_references_chunks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ pub async fn get_app_client_references_chunks(

let ssr_modules = client_reference_types
.iter()
.map(|client_reference_ty| async move {
.map(async |client_reference_ty| {
Ok(match client_reference_ty {
ClientReferenceType::EcmascriptClientReference(
ecmascript_client_reference,
Expand Down Expand Up @@ -241,7 +241,7 @@ pub async fn get_app_client_references_chunks(

let client_modules = client_reference_types
.iter()
.map(|client_reference_ty| async move {
.map(async |client_reference_ty| {
Ok(match client_reference_ty {
ClientReferenceType::EcmascriptClientReference(
ecmascript_client_reference,
Expand Down Expand Up @@ -351,7 +351,7 @@ pub async fn get_client_references_chunks_for_hmr(
let mut extras: FxIndexSet<ResolvedVc<Box<dyn OutputAsset>>> = client_references_chunks_ref
.layout_segment_client_chunks
.values()
.map(|&assets| async move {
.map(async |&assets| {
let primary = assets.primary_assets().await?;
Ok(primary.iter().copied().collect::<Vec<_>>())
})
Expand Down
8 changes: 4 additions & 4 deletions crates/next-core/src/pages_structure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,12 +241,12 @@ async fn get_pages_structure_for_root_directory(
next_router_path: next_router_path.clone(),
items: items
.into_iter()
.map(|(_, v)| async move { v.to_resolved().await })
.map(|(_, v)| v.to_resolved())
.try_join()
.await?,
children: children
.into_iter()
.map(|(_, v)| async move { v.to_resolved().await })
.map(|(_, v)| v.to_resolved())
.try_join()
.await?,
}
Expand Down Expand Up @@ -386,13 +386,13 @@ async fn get_pages_structure_for_directory(
items: items
.into_iter()
.map(|(_, v)| v)
.map(|v| async move { v.to_resolved().await })
.map(|v| v.to_resolved())
.try_join()
.await?,
children: children
.into_iter()
.map(|(_, v)| v)
.map(|v| async move { v.to_resolved().await })
.map(|v| v.to_resolved())
.try_join()
.await?,
}
Expand Down
4 changes: 1 addition & 3 deletions crates/next-core/src/segment_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1361,9 +1361,7 @@ async fn parse_segment_config_from_loader_tree_internal(
let parallel_configs = loader_tree
.parallel_routes
.values()
.map(|loader_tree| async move {
Box::pin(parse_segment_config_from_loader_tree_internal(loader_tree)).await
})
.map(|loader_tree| Box::pin(parse_segment_config_from_loader_tree_internal(loader_tree)))
.try_join()
.await?;

Expand Down
2 changes: 1 addition & 1 deletion crates/next-napi-bindings/src/next_api/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1684,7 +1684,7 @@ async fn output_assets_operation(

let endpoint_assets = endpoints
.iter()
.map(|endpoint| async move { endpoint.output().await?.output_assets.await })
.map(async |endpoint| endpoint.output().await?.output_assets.await)
.try_join()
.await?;

Expand Down
6 changes: 5 additions & 1 deletion packages/next/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -1490,5 +1490,9 @@
"1489": "Route %s used \\`unstable_prefetch()\\` inside a function cached with \\`unstable_cache()\\`. The \\`unstable_prefetch()\\` function is used to indicate the subsequent code must not run in the app shell, but \\`unstable_cache()\\` caches must be able to be produced before a prefetch, so this function is not allowed in this scope. See more info here: https://nextjs.org/docs/app/api-reference/functions/unstable_cache",
"1490": "Route %s used \\`unstable_prefetch()\\` inside \\`after()\\` while rendering. The \\`unstable_prefetch()\\` function is used to indicate the subsequent code must not run in the app shell, but \\`after()\\` executes after the request, so this function is not allowed in this scope. See more info here: https://nextjs.org/docs/app/api-reference/functions/after",
"1491": "Route %s used \\`unstable_prefetch()\\`, which requires Cache Components to be enabled. Learn more: https://nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents",
"1492": "Route %s used \\`unstable_prefetch()\\` inside \"use cache: private\". This is not currently supported. Instead, move the \"use cache\" directive to a function that's called below \\`await unstable_prefetch()\\`, so that the cached content is deferred to the prefetch without caching the stage boundary itself. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache"
"1492": "Route %s used \\`unstable_prefetch()\\` inside \"use cache: private\". This is not currently supported. Instead, move the \"use cache\" directive to a function that's called below \\`await unstable_prefetch()\\`, so that the cached content is deferred to the prefetch without caching the stage boundary itself. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache",
"1493": "Route \"%s\": Next.js encountered \\`unstable_navigation()\\` in \\`generateViewport()\\`.\\n\\n\\`unstable_navigation()\\` in \\`generateViewport()\\` prevents creating a shell, leading to a slower user experience.\\n\\nWays to fix this:\\n - [static] Use a static viewport export instead of \\`generateViewport()\\`\\n - [block] Set \\`export const instant = false\\` to allow a blocking route\\n\\nLearn more: https://nextjs.org/docs/messages/blocking-prerender-viewport-runtime",
"1494": "Route \"%s\": Next.js encountered \\`unstable_navigation()\\` in \\`generateMetadata()\\`.\\n\\nThis route's metadata is blocked, but the rest of its content can be prefetched. \\`unstable_navigation()\\` called in \\`generateMetadata()\\` prevents it from being prefetched.\\n\\nWays to fix this:\\n - [static] Use a static metadata export instead of \\`generateMetadata()\\`\\n - [dynamic] Render a marker component that calls \\`await connection()\\` inside \\`<Suspense>\\` on the page\\n\\nLearn more: https://nextjs.org/docs/messages/blocking-prerender-metadata-runtime",
"1495": "Route \"%s\": Next.js encountered \\`unstable_navigation()\\` during prerendering or a navigation.\\n\\n\\`unstable_navigation()\\` called outside of \\`<Suspense>\\` may prevent the navigation from being instant, leading to a slower user experience.\\n\\nWays to fix this:\\n - [stream] Provide a placeholder with \\`<Suspense fallback={...}>\\` around the data access\\n - [block] Set \\`export const instant = false\\` to allow a blocking route\\n\\nLearn more: https://nextjs.org/docs/messages/instant-shell-url-data",
"1496": "%s segments do not unblock new data in %s prefetches"
}
Loading
Loading