diff --git a/crates/next-api/src/pages.rs b/crates/next-api/src/pages.rs index c10d371164f08a..8e1e5e087c7b8d 100644 --- a/crates/next-api/src/pages.rs +++ b/crates/next-api/src/pages.rs @@ -104,17 +104,17 @@ impl PagesProject { async fn add_page_to_routes( routes: &mut IndexMap, page: Vc, - make_route: impl Fn(Vc, Vc, Vc) -> Route, + make_route: impl Fn(Vc, Vc, Vc) -> Route, ) -> Result<()> { let PagesStructureItem { next_router_path, - project_path, original_path, + .. } = *page.await?; let pathname: RcStr = format!("/{}", next_router_path.await?.path).into(); let pathname_vc = Vc::cell(pathname.clone()); let original_name = Vc::cell(format!("/{}", original_path.await?.path).into()); - let route = make_route(pathname_vc, original_name, project_path); + let route = make_route(pathname_vc, original_name, page); routes.insert(pathname, route); Ok(()) } @@ -122,7 +122,7 @@ impl PagesProject { async fn add_dir_to_routes( routes: &mut IndexMap, dir: Vc, - make_route: impl Fn(Vc, Vc, Vc) -> Route, + make_route: impl Fn(Vc, Vc, Vc) -> Route, ) -> Result<()> { let mut queue = vec![dir]; while let Some(dir) = queue.pop() { @@ -143,14 +143,14 @@ impl PagesProject { } if let Some(api) = api { - add_dir_to_routes(&mut routes, *api, |pathname, original_name, path| { + add_dir_to_routes(&mut routes, *api, |pathname, original_name, page| { Route::PageApi { endpoint: Vc::upcast(PageEndpoint::new( PageEndpointType::Api, self, pathname, original_name, - path, + page, pages_structure, )), } @@ -158,13 +158,13 @@ impl PagesProject { .await?; } - let make_page_route = |pathname, original_name, path| Route::Page { + let make_page_route = |pathname, original_name, page| Route::Page { html_endpoint: Vc::upcast(PageEndpoint::new( PageEndpointType::Html, self, pathname, original_name, - path, + page, pages_structure, )), data_endpoint: Vc::upcast(PageEndpoint::new( @@ -172,7 +172,7 @@ impl PagesProject { self, pathname, original_name, - path, + page, pages_structure, )), }; @@ -196,19 +196,18 @@ impl PagesProject { ) -> Result>> { let PagesStructureItem { next_router_path, - project_path, original_path, + .. } = *item.await?; let pathname: RcStr = format!("/{}", next_router_path.await?.path).into(); let pathname_vc = Vc::cell(pathname.clone()); let original_name = Vc::cell(format!("/{}", original_path.await?.path).into()); - let path = project_path; let endpoint = Vc::upcast(PageEndpoint::new( ty, self, pathname_vc, original_name, - path, + item, self.pages_structure(), )); Ok(endpoint) @@ -570,7 +569,7 @@ struct PageEndpoint { pages_project: Vc, pathname: Vc, original_name: Vc, - path: Vc, + page: Vc, pages_structure: Vc, } @@ -601,7 +600,7 @@ impl PageEndpoint { pages_project: Vc, pathname: Vc, original_name: Vc, - path: Vc, + page: Vc, pages_structure: Vc, ) -> Vc { PageEndpoint { @@ -609,7 +608,7 @@ impl PageEndpoint { pages_project, pathname, original_name, - path, + page, pages_structure, } .cell() @@ -618,7 +617,7 @@ impl PageEndpoint { #[turbo_tasks::function] async fn source(self: Vc) -> Result>> { let this = self.await?; - Ok(Vc::upcast(FileSource::new(this.path))) + Ok(Vc::upcast(FileSource::new(this.page.project_path()))) } #[turbo_tasks::function] diff --git a/crates/next-core/src/app_structure.rs b/crates/next-core/src/app_structure.rs index c6de21beced874..abfa8f68bb515c 100644 --- a/crates/next-core/src/app_structure.rs +++ b/crates/next-core/src/app_structure.rs @@ -325,7 +325,8 @@ async fn get_directory_tree_internal( let mut metadata_twitter = Vec::new(); for (basename, entry) in entries { - match *entry { + let entry = entry.resolve_symlink().await?; + match entry { DirectoryEntry::File(file) => { let file = file.resolve().await?; // Do not process .d.ts files as routes diff --git a/crates/next-core/src/pages_structure.rs b/crates/next-core/src/pages_structure.rs index 29e64c2ae4f14b..f6bc50318eda60 100644 --- a/crates/next-core/src/pages_structure.rs +++ b/crates/next-core/src/pages_structure.rs @@ -11,7 +11,10 @@ use crate::next_import_map::get_next_package; /// A final route in the pages directory. #[turbo_tasks::value] pub struct PagesStructureItem { - pub project_path: Vc, + pub base_path: Vc, + pub extensions: Vc>, + pub fallback_path: Option>, + /// Pathname of this item in the Next.js router. pub next_router_path: Vc, /// Unique path corresponding to this item. This differs from @@ -25,18 +28,38 @@ pub struct PagesStructureItem { impl PagesStructureItem { #[turbo_tasks::function] async fn new( - project_path: Vc, + base_path: Vc, + extensions: Vc>, + fallback_path: Option>, next_router_path: Vc, original_path: Vc, ) -> Result> { Ok(PagesStructureItem { - project_path, + base_path, + extensions, + fallback_path, next_router_path, original_path, } .cell()) } + #[turbo_tasks::function] + pub async fn project_path(&self) -> Result> { + for ext in self.extensions.await?.into_iter() { + let project_path = self.base_path.append(format!(".{ext}").into()); + let ty = *project_path.get_type().await?; + if matches!(ty, FileSystemEntryType::File | FileSystemEntryType::Symlink) { + return Ok(project_path); + } + } + if let Some(fallback_path) = self.fallback_path { + Ok(fallback_path) + } else { + Ok(self.base_path) + } + } + /// Returns a completion that changes when any route in the whole tree /// changes. #[turbo_tasks::function] @@ -45,11 +68,6 @@ impl PagesStructureItem { this.next_router_path.await?; Ok(Completion::new()) } - - #[turbo_tasks::function] - pub fn project_path(&self) -> Vc { - self.project_path - } } /// A (sub)directory in the pages directory with all analyzed routes and @@ -148,12 +166,20 @@ pub async fn find_pages_structure( next_router_root: Vc, page_extensions: Vc>, ) -> Result> { - let pages_root = project_root.join("pages".into()); + let pages_root = project_root + .join("pages".into()) + .realpath() + .resolve() + .await?; let pages_root = Vc::::cell( if *pages_root.get_type().await? == FileSystemEntryType::Directory { Some(pages_root) } else { - let src_pages_root = project_root.join("src/pages".into()); + let src_pages_root = project_root + .join("src/pages".into()) + .realpath() + .resolve() + .await?; if *src_pages_root.get_type().await? == FileSystemEntryType::Directory { Some(src_pages_root) } else { @@ -185,20 +211,19 @@ async fn get_pages_structure_for_root_directory( ) -> Result> { let page_extensions_raw = &*page_extensions.await?; - let mut app_item = None; - let mut document_item = None; - let mut error_item = None; let mut api_directory = None; - let pages_directory = if let Some(project_path) = &*project_path.await? { + let project_path = project_path.await?; + let pages_directory = if let Some(project_path) = &*project_path { let mut children = vec![]; let mut items = vec![]; let dir_content = project_path.read_dir().await?; if let DirectoryContent::Entries(entries) = &*dir_content { for (name, entry) in entries.iter() { + let entry = entry.resolve_symlink().await?; match entry { - DirectoryEntry::File(file_project_path) => { + DirectoryEntry::File(_) => { // Do not process .d.ts files as routes if name.ends_with(".d.ts") { continue; @@ -206,32 +231,9 @@ async fn get_pages_structure_for_root_directory( let Some(basename) = page_basename(name, page_extensions_raw) else { continue; }; + let base_path = project_path.join(basename.into()); match basename { - "_app" => { - let item_next_router_path = next_router_path.join("_app".into()); - app_item = Some(PagesStructureItem::new( - *file_project_path, - item_next_router_path, - item_next_router_path, - )); - } - "_document" => { - let item_next_router_path = - next_router_path.join("_document".into()); - document_item = Some(PagesStructureItem::new( - *file_project_path, - item_next_router_path, - item_next_router_path, - )); - } - "_error" => { - let item_next_router_path = next_router_path.join("_error".into()); - error_item = Some(PagesStructureItem::new( - *file_project_path, - item_next_router_path, - item_next_router_path, - )); - } + "_app" | "_document" | "_error" => {} basename => { let item_next_router_path = next_router_path_for_basename(next_router_path, basename); @@ -239,7 +241,9 @@ async fn get_pages_structure_for_root_directory( items.push(( basename, PagesStructureItem::new( - *file_project_path, + base_path, + page_extensions, + None, item_next_router_path, item_original_path, ), @@ -250,7 +254,7 @@ async fn get_pages_structure_for_root_directory( DirectoryEntry::Directory(dir_project_path) => match name.as_str() { "api" => { api_directory = Some(get_pages_structure_for_directory( - *dir_project_path, + dir_project_path, next_router_path.join(name.clone()), 1, page_extensions, @@ -260,7 +264,7 @@ async fn get_pages_structure_for_root_directory( children.push(( name, get_pages_structure_for_directory( - *dir_project_path, + dir_project_path, next_router_path.join(name.clone()), 1, page_extensions, @@ -290,34 +294,40 @@ async fn get_pages_structure_for_root_directory( None }; - let app_item = if let Some(app_item) = app_item { - app_item + let pages_path = if let Some(project_path) = *project_path { + project_path } else { + project_root.join("pages".into()) + }; + + let app_item = { let app_router_path = next_router_path.join("_app".into()); PagesStructureItem::new( - get_next_package(project_root).join("app.js".into()), + pages_path.join("_app".into()), + page_extensions, + Some(get_next_package(project_root).join("app.js".into())), app_router_path, app_router_path, ) }; - let document_item = if let Some(document_item) = document_item { - document_item - } else { + let document_item = { let document_router_path = next_router_path.join("_document".into()); PagesStructureItem::new( - get_next_package(project_root).join("document.js".into()), + pages_path.join("_document".into()), + page_extensions, + Some(get_next_package(project_root).join("document.js".into())), document_router_path, document_router_path, ) }; - let error_item = if let Some(error_item) = error_item { - error_item - } else { + let error_item = { let error_router_path = next_router_path.join("_error".into()); PagesStructureItem::new( - get_next_package(project_root).join("error.js".into()), + pages_path.join("_error".into()), + page_extensions, + Some(get_next_package(project_root).join("error.js".into())), error_router_path, error_router_path, ) @@ -355,7 +365,7 @@ async fn get_pages_structure_for_directory( if let DirectoryContent::Entries(entries) = &*dir_content { for (name, entry) in entries.iter() { match entry { - DirectoryEntry::File(file_project_path) => { + DirectoryEntry::File(_) => { let Some(basename) = page_basename(name, page_extensions_raw) else { continue; }; @@ -363,11 +373,14 @@ async fn get_pages_structure_for_directory( "index" => next_router_path, _ => next_router_path.join(basename.into()), }; + let base_path = project_path.join(name.clone()); let item_original_name = next_router_path.join(basename.into()); items.push(( basename, PagesStructureItem::new( - *file_project_path, + base_path, + page_extensions, + None, item_next_router_path, item_original_name, ),